文件
shanghaipeiwan/server/tests/platform-dispatch.spec.ts
xcbyy 77aa4cb50b feat(order): 平台派单闭环(发需求单进入待接单池)
- 下单页新增「指定大神 / 平台派单」模式:平台派单填品类、预算、地点/选点,expertId=0

- 服务端:平台派单单据支付后进入待接单池(余额支付即时入池,微信支付确认后入池),预算由买家出价但受 1~2000 元/小时约束

- 抢单由认领大神写入 expert_id 并转履约中;30 分钟无人接单沿用自动全额退款

- 修复订单 INSERT 占位符少一个导致下单报 Column count doesn't match 的问题,并新增列数/占位符一致性护栏

- 111 套件/737 用例全绿;线上 1 元实测:下单→待接单→池内 640m,测试数据已回滚
2026-10-21 15:16:03 +08:00

64 行
2.9 KiB
TypeScript

// 已读取AGENTS.md
import * as fs from 'fs';
import * as path from 'path';
/**
* 第九十五批:平台派单(发需求单)闭环闸门。
* 背景:抢单池只查 status='待接单',但下单流程全是「指定大神 → 支付 → 履约中」,
* 没有任何流程产出待接单订单,导致雷达/接单大厅永远是空池。
*/
describe('第九十五批平台派单闭环', () => {
const root = path.resolve(__dirname, '../../');
const read = (rel: string) => fs.readFileSync(path.join(root, rel), 'utf-8');
const repo = read('server/src/db/repository.ts');
const indexCode = read('server/src/index.ts');
const createPage = read('pwuniapp/pages/order/create.vue');
it('平台派单订单 expertId=0,余额支付后直接进入待接单池', () => {
expect(repo).toContain('const isMarketplace = !Number(data.expertId)');
expect(repo).toContain("const expertId = isMarketplace ? 0 : Number(data.expertId)");
expect(repo).toContain("isMarketplace ? '待接单' : '履约中'");
});
it('平台派单预算由买家出价但受服务端上下限约束', () => {
expect(repo).toContain('hourlyRate = Math.min(2000, Math.max(1, Number(data.hourlyRate) || 35))');
});
it('微信支付确认后:无大神订单转入待接单而不是履约中', () => {
expect(indexCode).toContain("const paidStatus = isMarketplace ? '待接单' : '履约中'");
expect(indexCode).toContain('已进入派单池,等待附近大神接单');
expect(indexCode).toContain('30 分钟内无人接单将自动全额退款');
});
it('抢单后由认领大神写入 expert_id 并进入履约中', () => {
expect(repo).toContain("SET status = '履约中', expert_id = ?, companion_name = ?");
expect(repo).toContain("AND status = '待接单'");
});
it('下单页提供「平台派单」模式并提交 expertId=0', () => {
expect(createPage).toContain("const orderMode = ref('EXPERT')");
expect(createPage).toContain('platformCategories');
expect(createPage).toContain('expertId: isPlatform ? 0 : expert.value.id');
expect(createPage).toContain('平台派单需求');
});
it('平台派单时不会沿用指定大神页的默认地点', () => {
expect(createPage).toContain('const switchMode = (mode) =>');
expect(createPage).toContain("meetVenue.value = '';");
});
it('订单 INSERT 的列数必须与占位符数量一致(含 paid_at/created_at 两个表达式值)', () => {
const start = repo.indexOf('INSERT INTO orders');
const end = repo.indexOf('`, [', start);
const seg = repo.slice(start, end);
const columns = seg
.slice(seg.indexOf('(') + 1, seg.indexOf(')'))
.split(',')
.filter((s) => s.trim()).length;
const values = seg.slice(seg.indexOf('VALUES'));
const placeholders = (values.replace(/\$\{[^}]*\}/g, '').match(/\?/g) || []).length;
expect(columns).toBe(24);
expect(placeholders + 2).toBe(columns);
});
});