252 行
8.2 KiB
TypeScript
252 行
8.2 KiB
TypeScript
import {
|
|
CompanionOrderService,
|
|
CreateOrderParams
|
|
} from '../src/core/order/companion-order.service';
|
|
import { CompanionTrack, ExpenseBurdenType } from '../src/core/companion/service-category';
|
|
import { OrderStatus } from '../src/core/order/order-state-machine';
|
|
|
|
describe('CompanionOrderService (TDD 测试套件: 线上游戏与同城陪玩·陪吃·陪喝订单及现场核销闭环)', () => {
|
|
let orderService: CompanionOrderService;
|
|
|
|
beforeEach(() => {
|
|
orderService = new CompanionOrderService();
|
|
});
|
|
|
|
describe('1. 订单创建与差异化字段校验', () => {
|
|
it('成功创建线上游戏陪玩订单', () => {
|
|
const params: CreateOrderParams = {
|
|
buyerId: 1001,
|
|
expertId: 2001,
|
|
track: CompanionTrack.ONLINE_GAME,
|
|
serviceTitle: '王者荣耀 · 排位双排包星',
|
|
hours: 2,
|
|
totalAmountCents: 7000,
|
|
gameDetails: {
|
|
gameName: '王者荣耀',
|
|
gameId: '峡谷第一野王',
|
|
serverDistrict: '微信区',
|
|
},
|
|
};
|
|
|
|
const order = orderService.createOrder(params);
|
|
expect(order.id).toBeDefined();
|
|
expect(order.status).toBe(OrderStatus.PENDING_PAYMENT);
|
|
expect(order.track).toBe(CompanionTrack.ONLINE_GAME);
|
|
expect(order.gameDetails?.gameId).toBe('峡谷第一野王');
|
|
});
|
|
|
|
it('成功创建同城线下陪吃订单并指定消费AA分摊', () => {
|
|
const params: CreateOrderParams = {
|
|
buyerId: 1001,
|
|
expertId: 2002,
|
|
track: CompanionTrack.OFFLINE_MEAL,
|
|
serviceTitle: '同城美食探店 · 网红火锅饭搭子',
|
|
hours: 3,
|
|
totalAmountCents: 15000,
|
|
offlineDetails: {
|
|
city: '北京市',
|
|
venueName: '朝阳大悦城海底捞',
|
|
meetTime: '今晚 19:30',
|
|
expenseBurden: ExpenseBurdenType.AA_SHARE,
|
|
},
|
|
};
|
|
|
|
const order = orderService.createOrder(params);
|
|
expect(order.track).toBe(CompanionTrack.OFFLINE_MEAL);
|
|
expect(order.offlineDetails?.venueName).toBe('朝阳大悦城海底捞');
|
|
expect(order.offlineDetails?.expenseBurden).toBe(ExpenseBurdenType.AA_SHARE);
|
|
});
|
|
|
|
it('成功创建同城量贩KTV陪唱订单并前置约定包厢消费全包', () => {
|
|
const params: CreateOrderParams = {
|
|
buyerId: 1001,
|
|
expertId: 2003,
|
|
track: CompanionTrack.OFFLINE_SING,
|
|
serviceTitle: '量贩KTV麦霸对唱与气氛陪伴',
|
|
hours: 3,
|
|
totalAmountCents: 24000,
|
|
offlineDetails: {
|
|
city: '北京市',
|
|
venueName: '工体纯K量贩KTV',
|
|
meetTime: '今晚 20:00',
|
|
expenseBurden: ExpenseBurdenType.BUYER_TREAT,
|
|
},
|
|
};
|
|
|
|
const order = orderService.createOrder(params);
|
|
expect(order.track).toBe(CompanionTrack.OFFLINE_SING);
|
|
expect(order.offlineDetails?.venueName).toBe('工体纯K量贩KTV');
|
|
expect(order.offlineDetails?.expenseBurden).toBe(ExpenseBurdenType.BUYER_TREAT);
|
|
});
|
|
|
|
it('线下订单缺少商圈场所或城市时应抛出异常', () => {
|
|
expect(() => {
|
|
orderService.createOrder({
|
|
buyerId: 1001,
|
|
expertId: 2002,
|
|
track: CompanionTrack.OFFLINE_DRINK,
|
|
serviceTitle: '星巴克手冲咖啡聊天',
|
|
hours: 2,
|
|
totalAmountCents: 10000,
|
|
offlineDetails: {
|
|
city: '',
|
|
venueName: '',
|
|
meetTime: '下午 15:00',
|
|
expenseBurden: ExpenseBurdenType.BUYER_TREAT,
|
|
},
|
|
});
|
|
}).toThrow('线下陪伴订单必须提供有效服务城市与公开碰面场所');
|
|
});
|
|
});
|
|
|
|
describe('2. 线下订单 4 位现场核销码流转与履约签到', () => {
|
|
it('线下订单完成支付并被大神接单后,生成 4 位纯数字现场核销码', () => {
|
|
const order = orderService.createOrder({
|
|
buyerId: 1001,
|
|
expertId: 2002,
|
|
track: CompanionTrack.OFFLINE_MEAL,
|
|
serviceTitle: '火锅探店',
|
|
hours: 2,
|
|
totalAmountCents: 12000,
|
|
offlineDetails: {
|
|
city: '北京市',
|
|
venueName: '大悦城餐厅',
|
|
meetTime: '19:00',
|
|
expenseBurden: ExpenseBurdenType.AA_SHARE,
|
|
},
|
|
});
|
|
|
|
// 买家付款
|
|
orderService.payOrder(order.id);
|
|
expect(order.status).toBe(OrderStatus.PENDING_ACCEPT);
|
|
|
|
// 大神接单,系统为该订单生成4位现场核销码
|
|
orderService.expertAccept(order.id, 2002);
|
|
expect(order.status).toBe(OrderStatus.PENDING_CONFIRM); // 等待现场核销确认进入服务
|
|
expect(order.offlineVerifyCode).toMatch(/^\d{4}$/);
|
|
});
|
|
|
|
it('大神在现场输入正确的 4 位核销码完成到场签到,进入服务中状态', () => {
|
|
const order = orderService.createOrder({
|
|
buyerId: 1001,
|
|
expertId: 2002,
|
|
track: CompanionTrack.OFFLINE_DRINK,
|
|
serviceTitle: '下午茶清吧',
|
|
hours: 2,
|
|
totalAmountCents: 10000,
|
|
offlineDetails: {
|
|
city: '上海市',
|
|
venueName: '静安嘉里中心咖啡店',
|
|
meetTime: '15:00',
|
|
expenseBurden: ExpenseBurdenType.BUYER_TREAT,
|
|
},
|
|
});
|
|
|
|
orderService.payOrder(order.id);
|
|
orderService.expertAccept(order.id, 2002);
|
|
|
|
const code = order.offlineVerifyCode!;
|
|
const checkinResult = orderService.verifyOfflineCheckin({
|
|
orderId: order.id,
|
|
operatorExpertId: 2002,
|
|
verifyCode: code,
|
|
});
|
|
|
|
expect(checkinResult.success).toBe(true);
|
|
expect(order.status).toBe(OrderStatus.IN_SERVICE);
|
|
expect(order.checkedInAt).toBeDefined();
|
|
});
|
|
|
|
it('输入错误的 4 位核销码应报错拒绝', () => {
|
|
const order = orderService.createOrder({
|
|
buyerId: 1001,
|
|
expertId: 2002,
|
|
track: CompanionTrack.OFFLINE_PLAY,
|
|
serviceTitle: '桌游探案',
|
|
hours: 2,
|
|
totalAmountCents: 9000,
|
|
offlineDetails: {
|
|
city: '深圳市',
|
|
venueName: '万象天地桌游吧',
|
|
meetTime: '14:00',
|
|
expenseBurden: ExpenseBurdenType.AA_SHARE,
|
|
},
|
|
});
|
|
|
|
orderService.payOrder(order.id);
|
|
orderService.expertAccept(order.id, 2002);
|
|
|
|
expect(() => {
|
|
orderService.verifyOfflineCheckin({
|
|
orderId: order.id,
|
|
operatorExpertId: 2002,
|
|
verifyCode: '0000', // 错误码
|
|
});
|
|
}).toThrow('现场核销码错误');
|
|
});
|
|
|
|
it('已核销订单不可重复核销(防重签到)', () => {
|
|
const order = orderService.createOrder({
|
|
buyerId: 1001,
|
|
expertId: 2002,
|
|
track: CompanionTrack.OFFLINE_PLAY,
|
|
serviceTitle: '桌游探案',
|
|
hours: 2,
|
|
totalAmountCents: 9000,
|
|
offlineDetails: {
|
|
city: '深圳市',
|
|
venueName: '万象天地桌游吧',
|
|
meetTime: '14:00',
|
|
expenseBurden: ExpenseBurdenType.AA_SHARE,
|
|
},
|
|
});
|
|
|
|
orderService.payOrder(order.id);
|
|
orderService.expertAccept(order.id, 2002);
|
|
|
|
orderService.verifyOfflineCheckin({
|
|
orderId: order.id,
|
|
operatorExpertId: 2002,
|
|
verifyCode: order.offlineVerifyCode!,
|
|
});
|
|
|
|
// 再次核销
|
|
expect(() => {
|
|
orderService.verifyOfflineCheckin({
|
|
orderId: order.id,
|
|
operatorExpertId: 2002,
|
|
verifyCode: order.offlineVerifyCode!,
|
|
});
|
|
}).toThrow('该订单已完成现场核销签到');
|
|
});
|
|
|
|
it('非接单大神尝试核销应被拒绝', () => {
|
|
const order = orderService.createOrder({
|
|
buyerId: 1001,
|
|
expertId: 2002,
|
|
track: CompanionTrack.OFFLINE_PLAY,
|
|
serviceTitle: '密室逃脱',
|
|
hours: 2,
|
|
totalAmountCents: 10000,
|
|
offlineDetails: {
|
|
city: '成都市',
|
|
venueName: '春熙路密室馆',
|
|
meetTime: '16:00',
|
|
expenseBurden: ExpenseBurdenType.BUYER_TREAT,
|
|
},
|
|
});
|
|
|
|
orderService.payOrder(order.id);
|
|
orderService.expertAccept(order.id, 2002);
|
|
|
|
expect(() => {
|
|
orderService.verifyOfflineCheckin({
|
|
orderId: order.id,
|
|
operatorExpertId: 9999, // 非该订单的大神
|
|
verifyCode: order.offlineVerifyCode!,
|
|
});
|
|
}).toThrow('无权核销该订单');
|
|
});
|
|
});
|
|
});
|