const BASE = '/api/platform/v1/last-leg/fleet/vehicles' const VEHICLES = '/api/platform/v1/last-leg/master-data/vehicles' const OPTIONS = { scenario: 'vehicle-care-lifecycle', deploymentBlocker: true } const PNG = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+aH1kAAAAASUVORK5CYII=', 'base64') /** Run only from an authorized fresh TEST lifecycle. Keep every created record and its evidence. */ export async function verifyVehicleCare(context, module, vehicle, proof) { const id = String(vehicle.id), base = `${BASE}/${encodeURIComponent(id)}/records` proof.status = 'CHECKING'; proof.records = [] function check(label, response, condition) { context.assert(module, label, response, Boolean(condition), '', OPTIONS) if (!condition) throw new Error(label) } async function command(method, path, body, label) { const response = await context.request(method, path, body) const data = context.apiSuccess(module, label, response, '', OPTIONS) if (response?.http !== 200 || String(response?.body?.code) !== '200' || data == null) throw new Error(label) return data } const occurredAt = new Date(Date.now() - 86400000).toISOString() const maintenance = { recordType: 'MAINTENANCE', occurredAt, distanceUnit: 'KM', odometer: 80, lastServiceOdometer: 20, nextServiceOdometer: 100, fuelQuantity: null, fuelUnit: null, verificationStatus: 'PENDING', remark: context.marker } const createRequest = { idempotencyKey: `${context.marker}:CARE`, details: maintenance } let record = await command('POST', base, createRequest, '建立本轮车辆保养记录') proof.records.push({ recordNo: record.recordNo, createRequest }) check('保养记录属于本车且原始单位不变', null, record.vehicleId === id && record.details?.distanceUnit === 'KM' && Number(record.details.odometer) === 80) const replay = await command('POST', base, createRequest, '重放同一保养创建请求') check('保养创建重放没有重复记录', null, replay.recordNo === record.recordNo && Number(replay.recordVersion) === Number(record.recordVersion)) const originalVersion = Number(record.recordVersion) const correction = { recordVersion: originalVersion, reason: '受控验收更正', details: { ...maintenance, odometer: 85 } } record = await command('PUT', `${base}/${record.recordNo}`, correction, '更正本轮保养里程') check('更正后业务值与版本均生效', null, Number(record.details.odometer) === 85 && Number(record.recordVersion) === originalVersion + 1) const stale = await context.request('PUT', `${base}/${record.recordNo}`, correction) check('旧版本不得覆盖运维记录', stale, stale?.http === 409 || (stale?.http === 200 && ['A0409', '409'].includes(String(stale.body?.code)))) const history = await command('GET', `${base}/${record.recordNo}/history?page=1&size=20`, undefined, '读取保养更正历史') check('保养历史保留旧里程与更正原因', null, history.records?.length === 2 && history.records[0].reason === correction.reason && Number(history.records[1].snapshot?.details?.odometer) === 80) const fuelRequest = { idempotencyKey: `${context.marker}:FUEL`, details: { ...maintenance, recordType: 'FUEL', distanceUnit: 'MI', lastServiceOdometer: null, nextServiceOdometer: null, fuelQuantity: 28.6, fuelUnit: 'US_GAL' } } let fuel = await command('POST', base, fuelRequest, '建立本轮加油记录') proof.records.push({ recordNo: fuel.recordNo, createRequest: fuelRequest }) const uploadPath = `${base}/${fuel.recordNo}/files?recordVersion=${fuel.recordVersion}&idempotencyKey=${encodeURIComponent(`${context.marker}:PHOTO`)}` const upload = () => context.requestMultipart('POST', uploadPath, { name: 'controlled-meter.png', type: 'image/png', content: PNG }) const first = await upload(); fuel = context.apiSuccess(module, '上传本轮受控运维照片', first, '', OPTIONS) check('运维照片正式关联本记录', first, Boolean(fuel?.files?.length === 1 && fuel.files[0].fileId)) const repeated = await upload(), repeatedFuel = context.apiSuccess(module, '同键重放运维照片', repeated, '', OPTIONS) check('照片重放不增加附件或版本', repeated, repeatedFuel?.files?.length === 1 && repeatedFuel.files[0].fileId === fuel.files[0].fileId && Number(repeatedFuel.recordVersion) === Number(fuel.recordVersion)) proof.photoId = fuel.files[0].fileId const photo = await context.requestBinary('GET', `${base}/${fuel.recordNo}/files/${encodeURIComponent(proof.photoId)}`) check('运维图片下载真实字节一致', photo, photo?.http === 200 && String(photo.contentType).startsWith('image/png') && photo.bytes instanceof Uint8Array && Buffer.from(photo.bytes).equals(PNG)) for (const type of ['REPAIR', 'BREAKDOWN', 'RESCUE', 'EXCEPTION']) { const priced = ['REPAIR', 'RESCUE'].includes(type) const details = { recordType: type, occurredAt, distanceUnit: null, odometer: null, lastServiceOdometer: null, nextServiceOdometer: null, fuelQuantity: null, fuelUnit: null, verificationStatus: null, remark: context.marker, incident: { description: `${context.marker} 受控故障处置记录`, item: priced ? '更换轮胎' : null, place: priced ? '本轮测试作业点' : null, handler: null, processingStatus: 'PENDING', exceptionType: type === 'EXCEPTION' ? 'VEHICLE_BREAKDOWN' : null, reportedCost: type === 'REPAIR' ? 125.5 : priced ? 0 : null, currency: priced ? 'USD' : null } } const initial = { idempotencyKey: `${context.marker}:${type}`, details } let incident = await command('POST', base, initial, `创建本轮${type}记录`) proof.records.push({ recordNo: incident.recordNo, createRequest: initial }) check('事件项目地点和分类完整回显', null, incident.vehicleId === id && (incident.details?.incident?.item ?? null) === details.incident.item && (incident.details.incident.place ?? null) === details.incident.place && (incident.details.incident.exceptionType ?? null) === details.incident.exceptionType) incident = await command('PUT', `${base}/${incident.recordNo}`, { recordVersion: Number(incident.recordVersion), reason: '受控任务实际处理完成', details: { ...details, incident: { ...details.incident, handler: '本轮测试处理人', processingStatus: 'COMPLETED' } } }, `完成本轮${type}处理记录`) check('处理完成保持独立的费用及付款事实', null, incident.details.incident.processingStatus === 'COMPLETED' && incident.paymentStatus == null && (!priced || (Number(incident.details.incident.reportedCost) === details.incident.reportedCost && incident.details.incident.currency === details.incident.currency))) if (priced) { const financial = await command('POST', `${base}/finance-status`, { recordNos: [incident.recordNo] }, '独立批量查询本轮处理记录付款事实') check('处理完成未自动提交财务或标记付款', null, Array.isArray(financial) && financial.length === 1 && financial[0].recordNo === incident.recordNo && financial[0].status === 'UNLINKED' && financial[0].sourceVersion == null && financial[0].submittedRecordVersion == null && financial[0].finance == null) } const refreshed = await command('GET', `${base}/${incident.recordNo}`, undefined, `重新读取本轮${type}记录`) check('处理记录重新读取保持全部现场字段', null, (refreshed.details.incident.item ?? null) === details.incident.item && (refreshed.details.incident.place ?? null) === details.incident.place && refreshed.details.incident.handler === '本轮测试处理人') const changes = await command('GET', `${base}/${incident.recordNo}/history?page=1&size=20`, undefined, `核对本轮${type}处理历史`) check('处理历史保留原待处理事实及完成结果', null, changes.records?.length === 2 && changes.records[0].snapshot?.details?.incident?.processingStatus === 'COMPLETED' && changes.records[1].snapshot?.details?.incident?.processingStatus === 'PENDING') } let other try { other = await command('POST', VEHICLES, { plateNumber: `CARE-${context.marker}`.slice(0, 64), model: 'Care isolation fixture', fuelConsumptionPer100Km: 10, mileageKm: 0, region: 'OVERSEAS', ownership: 'OWNED', status: 'IN_SERVICE', remark: context.marker }, '建立运维归属隔离测试车辆') proof.otherVehicleId = String(other.id) const foreignBase = `${BASE}/${encodeURIComponent(proof.otherVehicleId)}/records` for (const [method, suffix, body] of [['GET', `/${record.recordNo}`, undefined], ['PUT', `/${record.recordNo}`, { ...correction, recordVersion: record.recordVersion }], ['GET', `/${fuel.recordNo}/files/${encodeURIComponent(proof.photoId)}`, undefined]]) { const response = await context.request(method, foreignBase + suffix, body) check('其他车辆不能读取或修改本车记录和照片', response, response?.http === 404 || (response?.http === 200 && ['404', 'A0404'].includes(String(response.body?.code)))) } const foreign = await command('GET', `${foreignBase}?page=1&size=10`, undefined, '核对其他车辆记录列表') check('其他车辆列表没有串入本车记录', null, Number(foreign.total) === 0 && foreign.records?.length === 0) for (const type of ['MAINTENANCE', 'FUEL', 'REPAIR', 'BREAKDOWN', 'RESCUE', 'EXCEPTION']) { const page = await command('GET', `${base}?recordType=${type}&page=1&size=10`, undefined, '按车辆和类型查询运维记录') check('每类运维记录唯一且保留单位', null, Number(page.total) === 1 && page.records?.length === 1 && page.records[0].details.recordType === type && page.records[0].vehicleId === id && (type !== 'FUEL' || page.records[0].details.fuelUnit === 'US_GAL')) } const after = await command('GET', `${VEHICLES}/${encodeURIComponent(id)}`, undefined, '回查车辆主档未被运维记录改写') check('运维保存不改车辆里程状态或主档版本', null, Number(after.mileageKm) === Number(vehicle.mileageKm) && after.status === vehicle.status && Number(after.recordVersion) === Number(vehicle.recordVersion)) } finally { if (other?.id) { const ended = await command('PATCH', `${VEHICLES}/${encodeURIComponent(String(other.id))}/status`, { recordVersion: Number(other.recordVersion), status: 'INACTIVE' }, '停用本轮隔离用车辆并保留记录') check('隔离用车辆已停用', null, ended.status === 'INACTIVE'); proof.otherVehicleStatus = ended.status } } proof.status = 'VERIFIED' return proof }