user-orders-routes.integration.test.ts 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060
  1. import { describe, it, expect, beforeEach } from 'vitest';
  2. import { testClient } from 'hono/testing';
  3. import { IntegrationTestDatabase, setupIntegrationDatabaseHooksWithEntities } from '@d8d/shared-test-util';
  4. import { JWTUtil } from '@d8d/shared-utils';
  5. import { UserEntityMt, RoleMt } from '@d8d/user-module-mt';
  6. import { DeliveryAddressMt } from '@d8d/delivery-address-module-mt';
  7. import { AreaEntityMt } from '@d8d/geo-areas-mt';
  8. import { MerchantMt } from '@d8d/merchant-module-mt';
  9. import { SupplierMt } from '@d8d/supplier-module-mt';
  10. import { FileMt } from '@d8d/file-module-mt';
  11. import { GoodsMt, GoodsCategoryMt } from '@d8d/goods-module-mt';
  12. import userOrderRoutes from '../../src/routes/user/orders.mt';
  13. import { OrderMt, OrderGoodsMt } from '../../src/entities';
  14. import { OrdersTestFactory } from '../factories/orders-test-factory';
  15. // 设置集成测试钩子
  16. setupIntegrationDatabaseHooksWithEntities([
  17. UserEntityMt, RoleMt, OrderMt, OrderGoodsMt, DeliveryAddressMt, MerchantMt, SupplierMt, FileMt, AreaEntityMt, GoodsMt, GoodsCategoryMt
  18. ])
  19. describe('多租户用户订单管理API集成测试', () => {
  20. let client: ReturnType<typeof testClient<typeof userOrderRoutes>>;
  21. let testFactory: OrdersTestFactory;
  22. let userToken: string;
  23. let otherUserToken: string;
  24. let otherTenantUserToken: string;
  25. let testUser: UserEntityMt;
  26. let otherUser: UserEntityMt;
  27. let otherTenantUser: UserEntityMt;
  28. beforeEach(async () => {
  29. // 创建测试客户端
  30. client = testClient(userOrderRoutes);
  31. // 获取数据源并创建测试工厂
  32. const dataSource = await IntegrationTestDatabase.getDataSource();
  33. testFactory = new OrdersTestFactory(dataSource);
  34. // 创建测试用户
  35. testUser = await testFactory.createTestUser(1);
  36. otherUser = await testFactory.createTestUser(1);
  37. otherTenantUser = await testFactory.createTestUser(2);
  38. // 生成JWT令牌
  39. userToken = JWTUtil.generateToken({ id: testUser.id, username: testUser.username, tenantId: 1 });
  40. otherUserToken = JWTUtil.generateToken({ id: otherUser.id, username: otherUser.username, tenantId: 1 });
  41. otherTenantUserToken = JWTUtil.generateToken({ id: otherTenantUser.id, username: otherTenantUser.username, tenantId: 2 });
  42. });
  43. describe('租户数据隔离验证', () => {
  44. it('应该只能访问自己租户的订单', async () => {
  45. // 创建租户1的订单
  46. const tenant1Order = await testFactory.createTestOrder(testUser.id, { tenantId: 1 });
  47. // 创建租户2的订单
  48. const tenant2Order = await testFactory.createTestOrder(otherTenantUser.id, { tenantId: 2 });
  49. // 使用租户1的用户查询订单列表
  50. const response = await client.index.$get({
  51. query: {}
  52. }, {
  53. headers: {
  54. 'Authorization': `Bearer ${userToken}`
  55. }
  56. });
  57. expect(response.status).toBe(200);
  58. if(response.status === 200){
  59. const data = await response.json();
  60. // 应该只返回租户1的订单
  61. expect(data.data).toHaveLength(1);
  62. expect(data.data[0].tenantId).toBe(1);
  63. expect(data.data[0].id).toBe(tenant1Order.id);
  64. }
  65. });
  66. it('不应该访问其他租户的订单详情', async () => {
  67. // 创建租户2的订单
  68. const otherTenantOrder = await testFactory.createTestOrder(otherTenantUser.id, { tenantId: 2 });
  69. // 使用租户1的用户尝试访问租户2的订单
  70. const response = await client[':id'].$get({
  71. param: { id: otherTenantOrder.id }
  72. }, {
  73. headers: {
  74. 'Authorization': `Bearer ${userToken}`
  75. }
  76. });
  77. // 应该返回404,因为订单不在当前租户
  78. expect(response.status).toBe(404);
  79. });
  80. it('应该正确过滤跨租户订单访问', async () => {
  81. // 创建租户1的订单
  82. const tenant1Order = await testFactory.createTestOrder(testUser.id, { tenantId: 1 });
  83. // 使用租户2的用户尝试访问租户1的订单
  84. const response = await client[':id'].$get({
  85. param: { id: tenant1Order.id }
  86. }, {
  87. headers: {
  88. 'Authorization': `Bearer ${otherTenantUserToken}`
  89. }
  90. });
  91. // 应该返回404,因为订单不在当前租户
  92. expect(response.status).toBe(404);
  93. });
  94. });
  95. describe('用户数据权限验证', () => {
  96. it('应该只能访问自己的订单', async () => {
  97. // 创建当前用户的订单
  98. const myOrder = await testFactory.createTestOrder(testUser.id, { tenantId: 1 });
  99. // 创建其他用户的订单(同一租户)
  100. const otherUserOrder = await testFactory.createTestOrder(otherUser.id, { tenantId: 1 });
  101. // 使用当前用户查询订单列表
  102. const response = await client.index.$get({
  103. query: {}
  104. }, {
  105. headers: {
  106. 'Authorization': `Bearer ${userToken}`
  107. }
  108. });
  109. expect(response.status).toBe(200);
  110. if (response.status === 200) {
  111. const data = await response.json();
  112. // 应该只返回当前用户的订单
  113. expect(data.data).toHaveLength(1);
  114. expect(data.data[0].userId).toBe(testUser.id);
  115. expect(data.data[0].id).toBe(myOrder.id);
  116. }
  117. });
  118. it('不应该访问其他用户的订单详情', async () => {
  119. // 创建其他用户的订单
  120. const otherUserOrder = await testFactory.createTestOrder(otherUser.id, { tenantId: 1 });
  121. console.debug('创建的订单:', { id: otherUserOrder.id, userId: otherUserOrder.userId, tenantId: otherUserOrder.tenantId });
  122. // 使用当前用户尝试访问其他用户的订单
  123. const response = await client[':id'].$get({
  124. param: { id: otherUserOrder.id }
  125. }, {
  126. headers: {
  127. 'Authorization': `Bearer ${userToken}`
  128. }
  129. });
  130. // 应该返回403,因为无权访问其他用户的订单
  131. console.debug('响应状态码:', response.status);
  132. expect(response.status).toBe(403);
  133. });
  134. });
  135. describe('订单商品关联验证', () => {
  136. it('应该返回包含订单商品信息的订单详情', async () => {
  137. // 创建测试订单和订单商品
  138. const order = await testFactory.createTestOrder(testUser.id, { tenantId: 1 });
  139. const testGoods = await testFactory.createTestGoods(testUser.id, { tenantId: 1 });
  140. const orderGoods = await testFactory.createTestOrderGoods(order.id, testGoods.id, { tenantId: 1 });
  141. // 查询订单详情
  142. const response = await client[':id'].$get({
  143. param: { id: order.id }
  144. }, {
  145. headers: {
  146. 'Authorization': `Bearer ${userToken}`
  147. }
  148. });
  149. expect(response.status).toBe(200);
  150. if (response.status === 200) {
  151. const orderDetail = await response.json();
  152. // 验证订单详情包含订单商品信息
  153. expect(orderDetail.orderGoods).toBeDefined();
  154. expect(Array.isArray(orderDetail.orderGoods)).toBe(true);
  155. expect(orderDetail.orderGoods).toHaveLength(1);
  156. const goods = orderDetail.orderGoods![0];
  157. expect(goods.id).toBe(orderGoods.id);
  158. expect(goods.goodsId).toBe(orderGoods.goodsId);
  159. expect(goods.goodsName).toBe(orderGoods.goodsName);
  160. expect(goods.price).toBe(Number(orderGoods.price));
  161. expect(goods.num).toBe(orderGoods.num);
  162. }
  163. });
  164. it('应该返回包含订单商品信息的订单列表', async () => {
  165. // 创建测试订单和订单商品
  166. const order = await testFactory.createTestOrder(testUser.id, { tenantId: 1 });
  167. const testGoods = await testFactory.createTestGoods(testUser.id, { tenantId: 1 });
  168. await testFactory.createTestOrderGoods(order.id, testGoods.id, { tenantId: 1 });
  169. // 查询订单列表
  170. const response = await client.index.$get({
  171. query: {}
  172. }, {
  173. headers: {
  174. 'Authorization': `Bearer ${userToken}`
  175. }
  176. });
  177. expect(response.status).toBe(200);
  178. if (response.status === 200) {
  179. const data = await response.json();
  180. // 验证订单列表包含订单商品信息
  181. expect(data.data).toHaveLength(1);
  182. expect(data.data[0].orderGoods).toBeDefined();
  183. expect(Array.isArray(data.data[0].orderGoods)).toBe(true);
  184. expect(data.data[0].orderGoods).toHaveLength(1);
  185. const goods = data.data[0].orderGoods![0];
  186. expect(goods.goodsId).toBeGreaterThan(0);
  187. expect(goods.goodsName).toBeDefined();
  188. expect(goods.price).toBeGreaterThan(0);
  189. expect(goods.num).toBeGreaterThan(0);
  190. }
  191. });
  192. });
  193. describe('订单创建验证', () => {
  194. it('应该自动设置租户ID', async () => {
  195. // 创建必要的关联实体
  196. const testSupplier = await testFactory.createTestSupplier(testUser.id, { tenantId: 1 });
  197. const testMerchant = await testFactory.createTestMerchant(testUser.id, { tenantId: 1 });
  198. const testDeliveryAddress = await testFactory.createTestDeliveryAddress(testUser.id, { tenantId: 1 });
  199. const testGoods = await testFactory.createTestGoods(testUser.id, {
  200. tenantId: 1,
  201. merchantId: testMerchant.id,
  202. supplierId: testSupplier.id
  203. });
  204. const orderData = {
  205. addressId: testDeliveryAddress.id,
  206. productOwn: '自营',
  207. consumeFrom: '积分兑换',
  208. products: [
  209. { id: testGoods.id, num: 2 }
  210. ]
  211. };
  212. const response = await client['create-order'].$post({
  213. json: orderData
  214. }, {
  215. headers: {
  216. 'Authorization': `Bearer ${userToken}`
  217. }
  218. });
  219. console.debug('订单创建响应状态码:', response.status);
  220. if (response.status !== 201) {
  221. const errorResult = await response.json();
  222. console.debug('订单创建错误响应:', errorResult);
  223. }
  224. expect(response.status).toBe(201);
  225. if (response.status === 201) {
  226. const createdOrder = await response.json();
  227. // 验证订单创建成功
  228. expect(createdOrder.success).toBe(true);
  229. expect(createdOrder.orderId).toBeGreaterThan(0);
  230. expect(createdOrder.orderNo).toBeDefined();
  231. expect(createdOrder.amount).toBeGreaterThan(0);
  232. expect(createdOrder.payAmount).toBeGreaterThan(0);
  233. }
  234. });
  235. it('应该为子商品订单快照生成包含父商品名称的商品名称', async () => {
  236. // 创建必要的关联实体
  237. const testSupplier = await testFactory.createTestSupplier(testUser.id, { tenantId: 1 });
  238. const testMerchant = await testFactory.createTestMerchant(testUser.id, { tenantId: 1 });
  239. const testDeliveryAddress = await testFactory.createTestDeliveryAddress(testUser.id, { tenantId: 1 });
  240. // 创建父商品
  241. const parentGoods = await testFactory.createTestGoods(testUser.id, {
  242. tenantId: 1,
  243. merchantId: testMerchant.id,
  244. supplierId: testSupplier.id,
  245. name: '连衣裙'
  246. });
  247. // 创建子商品(规格商品)
  248. const childGoods = await testFactory.createTestGoods(testUser.id, {
  249. tenantId: 1,
  250. merchantId: testMerchant.id,
  251. supplierId: testSupplier.id,
  252. name: '红色 大码',
  253. spuId: parentGoods.id
  254. });
  255. const orderData = {
  256. addressId: testDeliveryAddress.id,
  257. productOwn: '自营',
  258. consumeFrom: '积分兑换',
  259. products: [
  260. { id: childGoods.id, num: 1 }
  261. ]
  262. };
  263. const response = await client['create-order'].$post({
  264. json: orderData
  265. }, {
  266. headers: {
  267. 'Authorization': `Bearer ${userToken}`
  268. }
  269. });
  270. console.debug('子商品订单创建响应状态码:', response.status);
  271. if (response.status !== 201) {
  272. const errorResult = await response.json();
  273. console.debug('子商品订单创建错误响应:', errorResult);
  274. }
  275. expect(response.status).toBe(201);
  276. if (response.status === 201) {
  277. const createdOrder = await response.json();
  278. // 验证订单创建成功
  279. expect(createdOrder.success).toBe(true);
  280. expect(createdOrder.orderId).toBeGreaterThan(0);
  281. // 查询订单商品明细,验证商品名称格式
  282. const dataSource = await IntegrationTestDatabase.getDataSource();
  283. const orderGoods = await dataSource.getRepository(OrderGoodsMt).find({
  284. where: { orderId: createdOrder.orderId, tenantId: 1 }
  285. });
  286. expect(orderGoods).toHaveLength(1);
  287. expect(orderGoods[0].goodsName).toBe('连衣裙 红色 大码');
  288. }
  289. });
  290. it('应该为单规格商品保持原有商品名称', async () => {
  291. // 创建必要的关联实体
  292. const testSupplier = await testFactory.createTestSupplier(testUser.id, { tenantId: 1 });
  293. const testMerchant = await testFactory.createTestMerchant(testUser.id, { tenantId: 1 });
  294. const testDeliveryAddress = await testFactory.createTestDeliveryAddress(testUser.id, { tenantId: 1 });
  295. // 创建单规格商品(spuId = 0)
  296. const singleSpecGoods = await testFactory.createTestGoods(testUser.id, {
  297. tenantId: 1,
  298. merchantId: testMerchant.id,
  299. supplierId: testSupplier.id,
  300. name: '单规格商品',
  301. spuId: 0
  302. });
  303. const orderData = {
  304. addressId: testDeliveryAddress.id,
  305. productOwn: '自营',
  306. consumeFrom: '积分兑换',
  307. products: [
  308. { id: singleSpecGoods.id, num: 1 }
  309. ]
  310. };
  311. const response = await client['create-order'].$post({
  312. json: orderData
  313. }, {
  314. headers: {
  315. 'Authorization': `Bearer ${userToken}`
  316. }
  317. });
  318. console.debug('单规格商品订单创建响应状态码:', response.status);
  319. expect(response.status).toBe(201);
  320. if (response.status === 201) {
  321. const createdOrder = await response.json();
  322. // 验证订单创建成功
  323. expect(createdOrder.success).toBe(true);
  324. expect(createdOrder.orderId).toBeGreaterThan(0);
  325. // 查询订单商品明细,验证商品名称保持不变
  326. const dataSource = await IntegrationTestDatabase.getDataSource();
  327. const orderGoods = await dataSource.getRepository(OrderGoodsMt).find({
  328. where: { orderId: createdOrder.orderId, tenantId: 1 }
  329. });
  330. expect(orderGoods).toHaveLength(1);
  331. expect(orderGoods[0].goodsName).toBe('单规格商品');
  332. }
  333. });
  334. it('应该正确处理规格商品的价格计算和库存', async () => {
  335. // 创建必要的关联实体
  336. const testSupplier = await testFactory.createTestSupplier(testUser.id, { tenantId: 1 });
  337. const testMerchant = await testFactory.createTestMerchant(testUser.id, { tenantId: 1 });
  338. const testDeliveryAddress = await testFactory.createTestDeliveryAddress(testUser.id, { tenantId: 1 });
  339. // 创建父商品
  340. const parentGoods = await testFactory.createTestGoods(testUser.id, {
  341. tenantId: 1,
  342. merchantId: testMerchant.id,
  343. supplierId: testSupplier.id,
  344. name: '智能手机',
  345. price: 2999.00, // 父商品价格
  346. stock: 100
  347. });
  348. // 创建规格商品(子商品)- 256GB版本
  349. const specGoods256GB = await testFactory.createTestGoods(testUser.id, {
  350. tenantId: 1,
  351. merchantId: testMerchant.id,
  352. supplierId: testSupplier.id,
  353. name: '256GB 黑色',
  354. spuId: parentGoods.id,
  355. price: 3299.00, // 规格特有价格
  356. stock: 50, // 规格特有库存
  357. originalPrice: 3499.00 // 规格原价
  358. });
  359. // 创建规格商品(子商品)- 512GB版本
  360. const specGoods512GB = await testFactory.createTestGoods(testUser.id, {
  361. tenantId: 1,
  362. merchantId: testMerchant.id,
  363. supplierId: testSupplier.id,
  364. name: '512GB 黑色',
  365. spuId: parentGoods.id,
  366. price: 3799.00, // 规格特有价格
  367. stock: 30, // 规格特有库存
  368. originalPrice: 3999.00 // 规格原价
  369. });
  370. const orderData = {
  371. addressId: testDeliveryAddress.id,
  372. productOwn: '自营',
  373. consumeFrom: '积分兑换',
  374. products: [
  375. { id: specGoods256GB.id, num: 2 }, // 购买2件256GB版本
  376. { id: specGoods512GB.id, num: 1 } // 购买1件512GB版本
  377. ]
  378. };
  379. const response = await client['create-order'].$post({
  380. json: orderData
  381. }, {
  382. headers: {
  383. 'Authorization': `Bearer ${userToken}`
  384. }
  385. });
  386. console.debug('规格商品订单创建响应状态码:', response.status);
  387. if (response.status !== 201) {
  388. const errorResult = await response.json();
  389. console.debug('规格商品订单创建错误响应:', errorResult);
  390. }
  391. expect(response.status).toBe(201);
  392. if (response.status === 201) {
  393. const createdOrder = await response.json();
  394. // 验证订单创建成功
  395. expect(createdOrder.success).toBe(true);
  396. expect(createdOrder.orderId).toBeGreaterThan(0);
  397. expect(createdOrder.orderNo).toBeDefined();
  398. // 验证订单总金额计算正确
  399. // 256GB: 3299.00 * 2 = 6598.00
  400. // 512GB: 3799.00 * 1 = 3799.00
  401. // 总计: 6598.00 + 3799.00 = 10397.00
  402. expect(createdOrder.amount).toBe(10397.00);
  403. expect(createdOrder.payAmount).toBe(10397.00);
  404. // 查询订单商品明细,验证规格信息正确保存
  405. const dataSource = await IntegrationTestDatabase.getDataSource();
  406. const orderGoods = await dataSource.getRepository(OrderGoodsMt).find({
  407. where: { orderId: createdOrder.orderId, tenantId: 1 },
  408. order: { goodsId: 'ASC' }
  409. });
  410. expect(orderGoods).toHaveLength(2);
  411. // 验证第一个商品(256GB)
  412. const goods256GB = orderGoods.find(og => og.goodsId === specGoods256GB.id);
  413. expect(goods256GB).toBeDefined();
  414. expect(goods256GB?.goodsName).toBe('智能手机 256GB 黑色');
  415. expect(Number(goods256GB?.price)).toBe(3299.00);
  416. expect(goods256GB?.num).toBe(2);
  417. // totalPrice字段在OrderGoodsMt实体中不存在,移除验证
  418. // 验证第二个商品(512GB)
  419. const goods512GB = orderGoods.find(og => og.goodsId === specGoods512GB.id);
  420. expect(goods512GB).toBeDefined();
  421. expect(goods512GB?.goodsName).toBe('智能手机 512GB 黑色');
  422. expect(Number(goods512GB?.price)).toBe(3799.00);
  423. expect(goods512GB?.num).toBe(1);
  424. // totalPrice字段在OrderGoodsMt实体中不存在,移除验证
  425. }
  426. });
  427. it('应该拒绝创建库存不足的规格商品订单', async () => {
  428. // 创建必要的关联实体
  429. const testSupplier = await testFactory.createTestSupplier(testUser.id, { tenantId: 1 });
  430. const testMerchant = await testFactory.createTestMerchant(testUser.id, { tenantId: 1 });
  431. const testDeliveryAddress = await testFactory.createTestDeliveryAddress(testUser.id, { tenantId: 1 });
  432. // 创建父商品
  433. const parentGoods = await testFactory.createTestGoods(testUser.id, {
  434. tenantId: 1,
  435. merchantId: testMerchant.id,
  436. supplierId: testSupplier.id,
  437. name: '限量版商品',
  438. price: 100.00,
  439. stock: 100
  440. });
  441. // 创建规格商品(子商品)- 库存较少
  442. const specGoods = await testFactory.createTestGoods(testUser.id, {
  443. tenantId: 1,
  444. merchantId: testMerchant.id,
  445. supplierId: testSupplier.id,
  446. name: '红色',
  447. spuId: parentGoods.id,
  448. price: 120.00,
  449. stock: 5 // 只有5件库存
  450. });
  451. // 尝试购买6件,超过库存数量
  452. const orderData = {
  453. addressId: testDeliveryAddress.id,
  454. productOwn: '自营',
  455. consumeFrom: '积分兑换',
  456. products: [
  457. { id: specGoods.id, num: 6 } // 库存只有5,尝试购买6件
  458. ]
  459. };
  460. const response = await client['create-order'].$post({
  461. json: orderData
  462. }, {
  463. headers: {
  464. 'Authorization': `Bearer ${userToken}`
  465. }
  466. });
  467. console.debug('库存不足订单创建响应状态码:', response.status);
  468. // 应该返回错误状态码(不是201),如400、422等
  469. expect(response.status).not.toBe(201);
  470. // 验证错误消息包含库存不足的信息
  471. if (response.status !== 201) {
  472. const errorResult = await response.json();
  473. console.debug('库存不足错误响应:', errorResult);
  474. // 错误响应结构为 { error: '商品 红色 库存不足' }
  475. expect(errorResult.error).toContain('库存');
  476. }
  477. });
  478. });
  479. describe('取消订单功能验证', () => {
  480. it('应该成功取消未支付订单', async () => {
  481. // 创建未支付订单
  482. const order = await testFactory.createTestOrder(testUser.id, {
  483. tenantId: 1,
  484. payState: 0, // 未支付
  485. state: 0
  486. });
  487. const cancelData = {
  488. orderId: order.id,
  489. reason: '用户主动取消'
  490. };
  491. const response = await client['cancel-order'].$post({
  492. json: cancelData
  493. }, {
  494. headers: {
  495. 'Authorization': `Bearer ${userToken}`
  496. }
  497. });
  498. expect(response.status).toBe(200);
  499. if (response.status === 200) {
  500. const result = await response.json();
  501. expect(result.success).toBe(true);
  502. expect(result.message).toBe('订单取消成功');
  503. }
  504. // 验证订单状态已更新
  505. const dataSource = await IntegrationTestDatabase.getDataSource();
  506. const updatedOrder = await dataSource.getRepository(OrderMt).findOne({
  507. where: { id: order.id, tenantId: 1 }
  508. });
  509. expect(updatedOrder?.payState).toBe(5); // 订单关闭
  510. expect(updatedOrder?.cancelReason).toBe('用户主动取消');
  511. expect(updatedOrder?.cancelTime).toBeInstanceOf(Date);
  512. });
  513. it('应该成功取消已支付订单', async () => {
  514. // 创建已支付订单
  515. const order = await testFactory.createTestOrder(testUser.id, {
  516. tenantId: 1,
  517. payState: 2, // 支付成功
  518. state: 0
  519. });
  520. const cancelData = {
  521. orderId: order.id,
  522. reason: '用户主动取消(已支付)'
  523. };
  524. const response = await client['cancel-order'].$post({
  525. json: cancelData
  526. }, {
  527. headers: {
  528. 'Authorization': `Bearer ${userToken}`
  529. }
  530. });
  531. expect(response.status).toBe(200);
  532. if (response.status === 200) {
  533. const result = await response.json();
  534. expect(result.success).toBe(true);
  535. expect(result.message).toBe('订单取消成功');
  536. }
  537. // 验证订单状态已更新
  538. const dataSource = await IntegrationTestDatabase.getDataSource();
  539. const updatedOrder = await dataSource.getRepository(OrderMt).findOne({
  540. where: { id: order.id, tenantId: 1 }
  541. });
  542. expect(updatedOrder?.payState).toBe(5); // 订单关闭
  543. expect(updatedOrder?.cancelReason).toBe('用户主动取消(已支付)');
  544. expect(updatedOrder?.cancelTime).toBeInstanceOf(Date);
  545. });
  546. it('应该拒绝取消不允许的订单状态', async () => {
  547. // 创建已发货订单(支付状态=2,订单状态=1)
  548. const order = await testFactory.createTestOrder(testUser.id, {
  549. tenantId: 1,
  550. payState: 2, // 支付成功
  551. state: 1 // 已发货
  552. });
  553. const cancelData = {
  554. orderId: order.id,
  555. reason: '尝试取消已发货订单'
  556. };
  557. const response = await client['cancel-order'].$post({
  558. json: cancelData
  559. }, {
  560. headers: {
  561. 'Authorization': `Bearer ${userToken}`
  562. }
  563. });
  564. // 应该返回403,因为已发货订单不允许取消
  565. expect(response.status).toBe(403);
  566. if (response.status === 403) {
  567. const result = await response.json();
  568. expect(result.message).toBe('当前订单状态不允许取消');
  569. }
  570. });
  571. it('应该拒绝取消不存在的订单', async () => {
  572. const cancelData = {
  573. orderId: 99999, // 不存在的订单ID
  574. reason: '取消不存在的订单'
  575. };
  576. const response = await client['cancel-order'].$post({
  577. json: cancelData
  578. }, {
  579. headers: {
  580. 'Authorization': `Bearer ${userToken}`
  581. }
  582. });
  583. // 应该返回404
  584. expect(response.status).toBe(404);
  585. if (response.status === 404) {
  586. const result = await response.json();
  587. expect(result.message).toBe('订单不存在');
  588. }
  589. });
  590. it('应该拒绝跨租户取消订单', async () => {
  591. // 创建租户2的订单
  592. const otherTenantOrder = await testFactory.createTestOrder(otherTenantUser.id, {
  593. tenantId: 2,
  594. payState: 0
  595. });
  596. const cancelData = {
  597. orderId: otherTenantOrder.id,
  598. reason: '跨租户取消尝试'
  599. };
  600. const response = await client['cancel-order'].$post({
  601. json: cancelData
  602. }, {
  603. headers: {
  604. 'Authorization': `Bearer ${userToken}`
  605. }
  606. });
  607. // 应该返回404,因为订单不在当前租户
  608. expect(response.status).toBe(404);
  609. if (response.status === 404) {
  610. const result = await response.json();
  611. expect(result.message).toBe('订单不存在');
  612. }
  613. });
  614. it('应该拒绝跨用户取消订单', async () => {
  615. // 创建其他用户的订单(同一租户)
  616. const otherUserOrder = await testFactory.createTestOrder(otherUser.id, {
  617. tenantId: 1,
  618. payState: 0
  619. });
  620. const cancelData = {
  621. orderId: otherUserOrder.id,
  622. reason: '跨用户取消尝试'
  623. };
  624. const response = await client['cancel-order'].$post({
  625. json: cancelData
  626. }, {
  627. headers: {
  628. 'Authorization': `Bearer ${userToken}`
  629. }
  630. });
  631. // 应该返回404,因为无权访问其他用户的订单
  632. expect(response.status).toBe(404);
  633. const result = await response.json();
  634. expect(result.message).toBe('订单不存在');
  635. });
  636. });
  637. describe('订单超时自动取消功能验证', () => {
  638. it('应该自动取消超过24小时未支付的订单', async () => {
  639. // 创建未支付订单,设置创建时间为25小时前
  640. const twentyFiveHoursAgo = new Date();
  641. twentyFiveHoursAgo.setHours(twentyFiveHoursAgo.getHours() - 25);
  642. const order = await testFactory.createTestOrder(testUser.id, {
  643. tenantId: 1,
  644. payState: 0, // 未支付
  645. state: 0,
  646. createdAt: twentyFiveHoursAgo,
  647. updatedAt: twentyFiveHoursAgo,
  648. expireTime: new Date() // 设置为当前时间,表示已过期
  649. });
  650. console.debug(`创建的超时订单: ID=${order.id}, createdAt=${order.createdAt}, expireTime=${order.expireTime}`);
  651. // 验证订单初始状态
  652. expect(order.payState).toBe(0);
  653. expect(order.state).toBe(0);
  654. // 创建调度器服务并手动触发超时订单处理
  655. const { OrderTimeoutSchedulerService } = await import('../../src/services/order-timeout-scheduler.service');
  656. const dataSource = await IntegrationTestDatabase.getDataSource();
  657. const schedulerService = new OrderTimeoutSchedulerService(dataSource, 1);
  658. const result = await schedulerService.triggerManualProcess(1);
  659. console.debug(`手动处理结果:`, result);
  660. expect(result.success).toBe(true);
  661. expect(result.processedOrders).toBe(1);
  662. expect(result.message).toContain('成功处理 1 个超时订单');
  663. // 验证订单状态已更新为取消
  664. const updatedOrder = await dataSource.getRepository(OrderMt).findOne({
  665. where: { id: order.id, tenantId: 1 }
  666. });
  667. expect(updatedOrder).toBeDefined();
  668. expect(updatedOrder?.payState).toBe(5); // 订单关闭
  669. expect(updatedOrder?.cancelReason).toBe('订单超时未支付,系统自动取消');
  670. expect(updatedOrder?.cancelTime).toBeInstanceOf(Date);
  671. });
  672. it('不应该取消未超过24小时的订单', async () => {
  673. // 创建未支付订单,设置创建时间为23小时前(未超过24小时)
  674. const twentyThreeHoursAgo = new Date();
  675. twentyThreeHoursAgo.setHours(twentyThreeHoursAgo.getHours() - 23);
  676. const order = await testFactory.createTestOrder(testUser.id, {
  677. tenantId: 1,
  678. payState: 0,
  679. state: 0,
  680. createdAt: twentyThreeHoursAgo,
  681. updatedAt: twentyThreeHoursAgo,
  682. expireTime: new Date(Date.now() + 3600000) // 1小时后过期
  683. });
  684. console.debug(`创建的未超时订单: ID=${order.id}, createdAt=${order.createdAt}, expireTime=${order.expireTime}`);
  685. // 创建调度器服务并手动触发超时订单处理
  686. const { OrderTimeoutSchedulerService } = await import('../../src/services/order-timeout-scheduler.service');
  687. const dataSource = await IntegrationTestDatabase.getDataSource();
  688. const schedulerService = new OrderTimeoutSchedulerService(dataSource, 1);
  689. const result = await schedulerService.triggerManualProcess(1);
  690. console.debug(`手动处理结果:`, result);
  691. // 应该没有处理任何订单
  692. expect(result.success).toBe(true);
  693. expect(result.processedOrders).toBe(0);
  694. expect(result.message).toContain('成功处理 0 个超时订单');
  695. // 验证订单状态未改变
  696. const updatedOrder = await dataSource.getRepository(OrderMt).findOne({
  697. where: { id: order.id, tenantId: 1 }
  698. });
  699. expect(updatedOrder).toBeDefined();
  700. expect(updatedOrder?.payState).toBe(0); // 保持未支付状态
  701. expect(updatedOrder?.cancelReason).toBeNull();
  702. expect(updatedOrder?.cancelTime).toBeNull();
  703. });
  704. it('不应该取消已支付的订单,即使超过24小时', async () => {
  705. // 创建已支付订单,设置创建时间为25小时前
  706. const twentyFiveHoursAgo = new Date();
  707. twentyFiveHoursAgo.setHours(twentyFiveHoursAgo.getHours() - 25);
  708. const order = await testFactory.createTestOrder(testUser.id, {
  709. tenantId: 1,
  710. payState: 2, // 支付成功
  711. state: 0,
  712. createdAt: twentyFiveHoursAgo,
  713. updatedAt: twentyFiveHoursAgo,
  714. expireTime: new Date() // 已过期
  715. });
  716. console.debug(`创建的已支付超时订单: ID=${order.id}, payState=${order.payState}, createdAt=${order.createdAt}`);
  717. // 创建调度器服务并手动触发超时订单处理
  718. const { OrderTimeoutSchedulerService } = await import('../../src/services/order-timeout-scheduler.service');
  719. const dataSource = await IntegrationTestDatabase.getDataSource();
  720. const schedulerService = new OrderTimeoutSchedulerService(dataSource, 1);
  721. const result = await schedulerService.triggerManualProcess(1);
  722. console.debug(`手动处理结果:`, result);
  723. // 应该没有处理已支付的订单
  724. expect(result.success).toBe(true);
  725. expect(result.processedOrders).toBe(0);
  726. expect(result.message).toContain('成功处理 0 个超时订单');
  727. // 验证订单状态未改变
  728. const updatedOrder = await dataSource.getRepository(OrderMt).findOne({
  729. where: { id: order.id, tenantId: 1 }
  730. });
  731. expect(updatedOrder).toBeDefined();
  732. expect(updatedOrder?.payState).toBe(2); // 保持支付成功状态
  733. expect(updatedOrder?.cancelReason).toBeNull();
  734. expect(updatedOrder?.cancelTime).toBeNull();
  735. });
  736. it('不应该取消已发货的订单,即使超过24小时未支付', async () => {
  737. // 创建已发货订单,设置创建时间为25小时前
  738. const twentyFiveHoursAgo = new Date();
  739. twentyFiveHoursAgo.setHours(twentyFiveHoursAgo.getHours() - 25);
  740. const order = await testFactory.createTestOrder(testUser.id, {
  741. tenantId: 1,
  742. payState: 0, // 未支付
  743. state: 1, // 已发货
  744. createdAt: twentyFiveHoursAgo,
  745. updatedAt: twentyFiveHoursAgo,
  746. expireTime: new Date() // 已过期
  747. });
  748. console.debug(`创建的已发货超时订单: ID=${order.id}, state=${order.state}, createdAt=${order.createdAt}`);
  749. // 创建调度器服务并手动触发超时订单处理
  750. const { OrderTimeoutSchedulerService } = await import('../../src/services/order-timeout-scheduler.service');
  751. const dataSource = await IntegrationTestDatabase.getDataSource();
  752. const schedulerService = new OrderTimeoutSchedulerService(dataSource, 1);
  753. const result = await schedulerService.triggerManualProcess(1);
  754. console.debug(`手动处理结果:`, result);
  755. // 应该没有处理已发货的订单
  756. expect(result.success).toBe(true);
  757. expect(result.processedOrders).toBe(0);
  758. expect(result.message).toContain('成功处理 0 个超时订单');
  759. // 验证订单状态未改变
  760. const updatedOrder = await dataSource.getRepository(OrderMt).findOne({
  761. where: { id: order.id, tenantId: 1 }
  762. });
  763. expect(updatedOrder).toBeDefined();
  764. expect(updatedOrder?.payState).toBe(0); // 保持未支付状态
  765. expect(updatedOrder?.state).toBe(1); // 保持已发货状态
  766. expect(updatedOrder?.cancelReason).toBeNull();
  767. expect(updatedOrder?.cancelTime).toBeNull();
  768. });
  769. it('应该恢复未支付超时订单的商品库存', async () => {
  770. // 创建测试商品
  771. const testGoods = await testFactory.createTestGoods(testUser.id, {
  772. tenantId: 1,
  773. stock: 100,
  774. salesNum: 0
  775. });
  776. // 创建未支付订单,设置创建时间为25小时前
  777. const twentyFiveHoursAgo = new Date();
  778. twentyFiveHoursAgo.setHours(twentyFiveHoursAgo.getHours() - 25);
  779. const order = await testFactory.createTestOrder(testUser.id, {
  780. tenantId: 1,
  781. payState: 0,
  782. state: 0,
  783. createdAt: twentyFiveHoursAgo,
  784. updatedAt: twentyFiveHoursAgo,
  785. expireTime: new Date()
  786. });
  787. // 创建订单商品记录
  788. const orderGoods = await testFactory.createTestOrderGoods(order.id, testGoods.id, {
  789. tenantId: 1,
  790. num: 2
  791. });
  792. console.debug(`创建带商品的超时订单: orderId=${order.id}, goodsId=${testGoods.id}, num=${orderGoods.num}`);
  793. // 获取商品初始库存
  794. const dataSource = await IntegrationTestDatabase.getDataSource();
  795. const goodsRepository = dataSource.getRepository(GoodsMt);
  796. const initialGoods = await goodsRepository.findOne({
  797. where: { id: testGoods.id, tenantId: 1 }
  798. });
  799. console.debug(`商品初始库存: stock=${initialGoods?.stock}, salesNum=${initialGoods?.salesNum}`);
  800. // 模拟订单创建时的库存减少:在真实订单创建流程中,库存会减少,销售量会增加
  801. // 这里我们手动更新库存,模拟订单创建后的状态
  802. await goodsRepository.update(
  803. { id: testGoods.id, tenantId: 1 },
  804. {
  805. stock: () => `stock - ${orderGoods.num}`,
  806. salesNum: () => `sales_num + ${orderGoods.num}`
  807. }
  808. );
  809. // 验证库存已减少
  810. const afterOrderCreationGoods = await goodsRepository.findOne({
  811. where: { id: testGoods.id, tenantId: 1 }
  812. });
  813. console.debug(`订单创建后商品库存: stock=${afterOrderCreationGoods?.stock}, salesNum=${afterOrderCreationGoods?.salesNum}`);
  814. // 创建调度器服务并手动触发超时订单处理
  815. const { OrderTimeoutSchedulerService } = await import('../../src/services/order-timeout-scheduler.service');
  816. const schedulerService = new OrderTimeoutSchedulerService(dataSource, 1);
  817. const result = await schedulerService.triggerManualProcess(1);
  818. console.debug(`手动处理结果:`, result);
  819. expect(result.success).toBe(true);
  820. expect(result.processedOrders).toBe(1);
  821. // 验证商品库存已恢复(取消订单后库存应恢复到初始值)
  822. const updatedGoods = await goodsRepository.findOne({
  823. where: { id: testGoods.id, tenantId: 1 }
  824. });
  825. console.debug(`订单取消后商品库存: stock=${updatedGoods?.stock}, salesNum=${updatedGoods?.salesNum}`);
  826. expect(updatedGoods).toBeDefined();
  827. // 库存应该恢复到100(初始值)
  828. expect(Number(updatedGoods?.stock)).toBe(100);
  829. // 销售量应该恢复到0(初始值)
  830. expect(Number(updatedGoods?.salesNum)).toBe(0);
  831. });
  832. it('应该支持跨租户订单超时处理', async () => {
  833. // 创建租户1的超时订单
  834. const twentyFiveHoursAgo = new Date();
  835. twentyFiveHoursAgo.setHours(twentyFiveHoursAgo.getHours() - 25);
  836. const tenant1Order = await testFactory.createTestOrder(testUser.id, {
  837. tenantId: 1,
  838. payState: 0,
  839. state: 0,
  840. createdAt: twentyFiveHoursAgo,
  841. updatedAt: twentyFiveHoursAgo,
  842. expireTime: new Date()
  843. });
  844. // 创建租户2的超时订单
  845. const tenant2Order = await testFactory.createTestOrder(otherTenantUser.id, {
  846. tenantId: 2,
  847. payState: 0,
  848. state: 0,
  849. createdAt: twentyFiveHoursAgo,
  850. updatedAt: twentyFiveHoursAgo,
  851. expireTime: new Date()
  852. });
  853. console.debug(`跨租户测试: 租户1订单ID=${tenant1Order.id}, 租户2订单ID=${tenant2Order.id}`);
  854. // 创建调度器服务(tenantId = null,处理所有租户)
  855. const { OrderTimeoutSchedulerService } = await import('../../src/services/order-timeout-scheduler.service');
  856. const dataSource = await IntegrationTestDatabase.getDataSource();
  857. const schedulerService = new OrderTimeoutSchedulerService(dataSource); // 不指定租户ID
  858. // 获取所有有超时订单的租户
  859. const tenantIds = await (schedulerService as any).getTenantsWithTimeoutOrders();
  860. console.debug(`有超时订单的租户:`, tenantIds);
  861. // 应该包含两个租户
  862. expect(tenantIds).toContain(1);
  863. expect(tenantIds).toContain(2);
  864. expect(tenantIds.length).toBeGreaterThanOrEqual(2);
  865. // 手动处理租户1的超时订单
  866. const result1 = await schedulerService.triggerManualProcess(1);
  867. console.debug(`租户1处理结果:`, result1);
  868. expect(result1.success).toBe(true);
  869. expect(result1.processedOrders).toBe(1);
  870. // 验证租户1订单已取消
  871. const updatedTenant1Order = await dataSource.getRepository(OrderMt).findOne({
  872. where: { id: tenant1Order.id, tenantId: 1 }
  873. });
  874. expect(updatedTenant1Order?.payState).toBe(5);
  875. // 手动处理租户2的超时订单
  876. const result2 = await schedulerService.triggerManualProcess(2);
  877. console.debug(`租户2处理结果:`, result2);
  878. expect(result2.success).toBe(true);
  879. expect(result2.processedOrders).toBe(1);
  880. // 验证租户2订单已取消
  881. const updatedTenant2Order = await dataSource.getRepository(OrderMt).findOne({
  882. where: { id: tenant2Order.id, tenantId: 2 }
  883. });
  884. expect(updatedTenant2Order?.payState).toBe(5);
  885. });
  886. });
  887. });