feat(M08-C): 补管理员验券与经营统计
This commit is contained in:
@@ -40,6 +40,7 @@ import { IotMessageService } from '../dist/devices/iot-message-service.js';
|
||||
import { DeviceCommandService } from '../dist/devices/device-command-service.js';
|
||||
import { DeviceControlService } from '../dist/devices/device-control-service.js';
|
||||
import { MemberProfileService } from '../dist/wallets/member-profile-service.js';
|
||||
import { BusinessStatisticsRepository } from '../dist/operations/business-statistics-repository.js';
|
||||
import {
|
||||
executeMigrationPlan,
|
||||
loadMigrationPlan,
|
||||
@@ -1322,6 +1323,64 @@ async function assertThirdPartyDomain(pool, context) {
|
||||
assert.equal(voucherRows[0].voucherMasked.includes('SENSITIVE'), false);
|
||||
assert.equal(Number(voucherRows[0].redemptionCount), 1);
|
||||
|
||||
const [adminRows] = await pool.query(
|
||||
`SELECT u.id FROM qipai_users u
|
||||
INNER JOIN qipai_user_roles ur ON ur.tenant_id = u.tenant_id AND ur.user_id = u.id
|
||||
INNER JOIN qipai_roles r ON r.id = ur.role_id AND r.tenant_id = ur.tenant_id
|
||||
WHERE u.tenant_id = ? AND r.code = 'TENANT_ADMIN' LIMIT 1`,
|
||||
[context.tenantId]
|
||||
);
|
||||
const adminId = String(adminRows[0].id);
|
||||
const adminAccess = await new RbacRepository(pool).getAccessProfile(context.tenantId, adminId);
|
||||
const managerStartAt = new Date(endAt.getTime() + 3600000);
|
||||
const managerEndAt = new Date(managerStartAt.getTime() + 2 * 3600000);
|
||||
const managerOrder = await pricing.reserve({
|
||||
tenantId: context.tenantId,
|
||||
userId: customerId,
|
||||
roomId,
|
||||
startAt: managerStartAt,
|
||||
endAt: managerEndAt,
|
||||
pricingMode: 'HOURLY'
|
||||
});
|
||||
const managerRedeemed = await service.redeemVoucherAsManager({
|
||||
tenantId: context.tenantId,
|
||||
actorId: adminId,
|
||||
access: adminAccess,
|
||||
provider: 'MEITUAN',
|
||||
voucherCode: 'M08C-MANAGER-VOUCHER-002',
|
||||
orderId: managerOrder.orderId,
|
||||
clientRequestId: 'm08c-manager-redeem-002'
|
||||
});
|
||||
assert.equal(managerRedeemed.status, 'SUCCEEDED');
|
||||
const managerRecords = await service.listRecords({
|
||||
tenantId: context.tenantId,
|
||||
access: adminAccess,
|
||||
provider: 'MEITUAN',
|
||||
status: 'SUCCEEDED',
|
||||
storeId
|
||||
});
|
||||
const managerRecord = managerRecords.redemptions.find(
|
||||
(item) => String(item.id) === managerRedeemed.redemptionId
|
||||
);
|
||||
assert.equal(String(managerRecord.actorId), adminId);
|
||||
assert.equal(managerRecord.voucherMasked.includes('MANAGER'), false);
|
||||
const statistics = await new BusinessStatisticsRepository(pool).overview({
|
||||
tenantId: context.tenantId,
|
||||
userId: adminId,
|
||||
access: adminAccess,
|
||||
traceId: 'm08c-business-statistics',
|
||||
ip: '127.0.0.1',
|
||||
userAgent: 'M08-C MySQL test'
|
||||
}, {
|
||||
storeId,
|
||||
from: new Date(Date.now() - 86400000),
|
||||
to: new Date(Date.now() + 2 * 86400000)
|
||||
});
|
||||
assert.ok(statistics.summary.orderTotal >= 2);
|
||||
assert.ok(statistics.summary.collectedAmountCents > 0);
|
||||
assert.ok(statistics.summary.voucherSucceeded >= 2);
|
||||
assert.ok(statistics.paymentChannels.some((item) => item.channel === 'GROUP_BUY'));
|
||||
|
||||
await pool.query(
|
||||
`INSERT INTO qipai_third_party_mappings
|
||||
(tenant_id, provider, resource_type, external_ref, local_resource_id)
|
||||
@@ -2077,6 +2136,8 @@ try {
|
||||
'Wechat reconciliation request history'
|
||||
,
|
||||
'group voucher hash-only storage and single redemption',
|
||||
'manager voucher redemption actor and store attribution',
|
||||
'store-scoped business statistics from orders and successful payments',
|
||||
'third-party booking webhook idempotency',
|
||||
'mapped booking claim creates a paid order',
|
||||
'unmapped booking enters manual queue'
|
||||
|
||||
Reference in New Issue
Block a user