openapi: 3.0.3 info: title: 车辆运维记录 version: 1.0.0 description: >- 平台车辆运维事实,支持保养、加油、维修、紧急故障、应急救援和异常。每条记录固定属于一辆车,使用VR业务编号;读取、修改、照片和历史均校验路径中的车辆归属。 原始里程必须明确KM或MI,加油量必须明确L、US_GAL或IMP_GAL;不自动推测或换算,不更新车辆主档公里数。 核验状态是人工记录的PENDING或VERIFIED,不代表财务审批或车辆可派,不自动产生账单、付款或变更车辆状态。 创建按车辆、操作者和幂等键去重;更正须版本及原因。照片上传按记录与幂等键去重并核对内容摘要,丢失响应可原样重试。 照片最多20张,每张非空JPG/PNG且最多10MiB;File按平台0、FLEET_VEHICLE_RECORD_PHOTO和记录内部主键复合归属,验证内容签名。 维修和救援项目、地点独立保存;处理中或已完成须有处理人,异常须有明确分类。 费用只是运维申报金额,填金额则必须填有效币种;未填金额和零金额有区别,不生成应付或付款。 运维响应paymentStatus为空,不保存可变财务结论;维修/救援通过finance-status批量读取公司财务状态,不得由processingStatus推定付款。 显式提交公司成本时取已保存申报金额、服务端供应商与月份汇率,来源映射及Outbox同事务保存;不会自动入账或付款。 已提交费用须先在Finance作废才可修改费用项目/金额/币种或重新提交;处理进度、地点和处理人仍可独立更新。 不提供物理删除;编辑与上传历史同事务不可变保存。本接口不提供财务结算或车辆占用/状态自动变更。 security: [{bearerAuth: []}] paths: /api/platform/v1/last-leg/fleet/vehicles/{vehicleId}/records/finance-status: parameters: [{$ref: '#/components/parameters/Vehicle'}] post: description: platform:overseas-master-data:vehicle:list;校验本车维修/救援记录,最多100条批量读取,不逐行调用Finance;依赖失败明确返回错误,不能伪造未关联或付款状态 requestBody: required: true content: application/json: schema: type: object required: [recordNos] properties: recordNos: {type: array, minItems: 1, maxItems: 100, items: {type: string, minLength: 1, maxLength: 64}} responses: '200': description: ApiResponse;data为每条请求记录的状态;Finance接收状态PENDING为待入账,APPROVED为已入账未付,不等于付款 content: application/json: schema: type: object properties: code: {type: string} data: {type: array, items: {$ref: '#/components/schemas/FinanceStatus'}} /api/platform/v1/last-leg/fleet/vehicles/{vehicleId}/records/{recordNo}/finance: parameters: - {$ref: '#/components/parameters/Vehicle'} - {name: recordNo, in: path, required: true, schema: {type: string}} post: description: platform:overseas-master-data:vehicle:record-finance-submit;不复用普通运维编辑权限。前端选择供应商候选另需platform:supplier:list。首次提交或Finance已作废后才可登记新来源版本,未知结果保留原键重试 requestBody: required: true content: application/json: schema: type: object required: [recordVersion, supplierId, businessDate, idempotencyKey] properties: recordVersion: {type: integer, format: int64, minimum: 0} supplierId: {type: integer, format: int64, minimum: 1} businessDate: {type: string, format: date, description: 明确选择实际费用业务日期,用于获取业务月份有效汇率} idempotencyKey: {type: string, minLength: 8, maxLength: 128} responses: '200': description: ApiResponse;仅表示提交请求已登记,PENDING_FINANCE不是已入账或付款 content: application/json: schema: type: object properties: code: {type: string} data: type: object properties: recordNo: {type: string} sourceVersion: {type: integer, format: int64} submittedRecordVersion: {type: integer, format: int64} replayed: {type: boolean} status: {type: string, enum: [PENDING_FINANCE]} /api/platform/v1/last-leg/fleet/vehicles/{vehicleId}/records: parameters: [{$ref: '#/components/parameters/Vehicle'}] get: summary: 分页查询当前车辆运维记录 description: '权限:platform:overseas-master-data:vehicle:list;按发生时间倒序,列表包含已关联照片元数据,不读取图片内容。' parameters: - {name: page, in: query, schema: {type: integer, minimum: 1, default: 1}} - {name: size, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 20}} - {name: recordType, in: query, schema: {type: string, enum: [MAINTENANCE, FUEL, REPAIR, BREAKDOWN, RESCUE, EXCEPTION]}} responses: '200': {description: '统一响应data为records/page/size/total/pages,records元素为Record'} post: summary: 新增记录 description: '权限:platform:overseas-master-data:vehicle:record-create;同键同内容返回原记录,不同内容冲突。' requestBody: required: true content: application/json: schema: type: object required: [idempotencyKey, details] properties: idempotencyKey: {type: string, minLength: 1, maxLength: 128} details: {$ref: '#/components/schemas/Draft'} responses: '200': {$ref: '#/components/responses/Record'} /api/platform/v1/last-leg/fleet/vehicles/{vehicleId}/records/{recordNo}: parameters: [{$ref: '#/components/parameters/Vehicle'}, {$ref: '#/components/parameters/RecordNumber'}] get: summary: 读取记录详情 description: '权限:platform:overseas-master-data:vehicle:list;跨车辆记录返回404,不泄露其他车辆数据。' responses: '200': {$ref: '#/components/responses/Record'} put: summary: 更正记录 description: '权限:platform:overseas-master-data:vehicle:record-edit;类型及车辆不可更换,保留历史。' requestBody: required: true content: application/json: schema: type: object required: [recordVersion, reason, details] properties: recordVersion: {type: integer, format: int64, minimum: 0} reason: {type: string, minLength: 1, maxLength: 1000} details: {$ref: '#/components/schemas/Draft'} responses: '200': {$ref: '#/components/responses/Record'} /api/platform/v1/last-leg/fleet/vehicles/{vehicleId}/records/{recordNo}/files: parameters: [{$ref: '#/components/parameters/Vehicle'}, {$ref: '#/components/parameters/RecordNumber'}] post: summary: 上传并关联运维图片 description: '权限:platform:overseas-master-data:vehicle:record-upload;记录保存成功后再上传,失败重试不得重复建记录。' requestBody: required: true content: multipart/form-data: schema: type: object required: [recordVersion, idempotencyKey, file] properties: recordVersion: {type: integer, format: int64, minimum: 0} idempotencyKey: {type: string, minLength: 1, maxLength: 128} file: {type: string, format: binary} responses: '200': {$ref: '#/components/responses/Record'} /api/platform/v1/last-leg/fleet/vehicles/{vehicleId}/records/{recordNo}/files/{fileId}: parameters: - {$ref: '#/components/parameters/Vehicle'} - {$ref: '#/components/parameters/RecordNumber'} - {name: fileId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} get: summary: 读取当前记录已关联图片 description: '权限:platform:overseas-master-data:vehicle:list;先校验记录关联,再校验File复合归属;no-store和nosniff,不返回存储路径。' responses: '200': {description: 图片字节, content: {image/png: {schema: {type: string, format: binary}}, image/jpeg: {schema: {type: string, format: binary}}}} /api/platform/v1/last-leg/fleet/vehicles/{vehicleId}/records/{recordNo}/history: parameters: [{$ref: '#/components/parameters/Vehicle'}, {$ref: '#/components/parameters/RecordNumber'}] get: summary: 分页读取不可变修改历史 description: '权限:platform:overseas-master-data:vehicle:list;版本倒序,仅按需读取。' parameters: - {name: page, in: query, schema: {type: integer, minimum: 1, default: 1}} - {name: size, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 20}} responses: '200': {description: '统一响应data为分页对象;records包含recordVersion/action/reason/operatorId/occurredAt/snapshot,snapshot为Record'} components: securitySchemes: bearerAuth: {type: http, scheme: bearer, bearerFormat: JWT} parameters: Vehicle: {name: vehicleId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} RecordNumber: {name: recordNo, in: path, required: true, schema: {type: string}} responses: Record: description: 统一业务响应;失败保留code、message及诊断标识 content: application/json: schema: type: object properties: code: {type: string} data: {$ref: '#/components/schemas/Record'} schemas: Draft: type: object required: [recordType, occurredAt] oneOf: - properties: {recordType: {enum: [MAINTENANCE]}} required: [distanceUnit, odometer, verificationStatus, lastServiceOdometer, nextServiceOdometer] - properties: {recordType: {enum: [FUEL]}} required: [distanceUnit, odometer, verificationStatus, fuelQuantity, fuelUnit] - properties: {recordType: {enum: [REPAIR, BREAKDOWN, RESCUE, EXCEPTION]}} required: [incident] properties: recordType: {type: string, enum: [MAINTENANCE, FUEL, REPAIR, BREAKDOWN, RESCUE, EXCEPTION]} occurredAt: {type: string, format: date-time, description: 带偏移或Z的实际发生时间,不晚于当前时间,保存UTC} distanceUnit: {type: string, nullable: true, enum: [KM, MI], description: 保养加油必填;其他类型必须为空} odometer: {type: number, nullable: true, minimum: 0, multipleOf: 0.001, description: 保养加油必填;其他类型必须为空} lastServiceOdometer: {type: number, nullable: true, minimum: 0, description: 保养必填,不大于记录时里程;加油必须为空} nextServiceOdometer: {type: number, nullable: true, minimum: 0, description: 保养必填且大于上次保养里程,可低于当前里程表示已超保养里程;不默认一万迈} fuelQuantity: {type: number, nullable: true, minimum: 0, exclusiveMinimum: true, description: 加油必填正数;保养必须为空} fuelUnit: {type: string, nullable: true, enum: [L, US_GAL, IMP_GAL], description: 加油必填明确单位;保养必须为空} verificationStatus: {type: string, nullable: true, enum: [PENDING, VERIFIED], description: 保养加油必填;其他类型必须为空,不能混作处理状态} remark: {type: string, nullable: true, maxLength: 1000} incident: {$ref: '#/components/schemas/Incident'} Incident: type: object nullable: true description: 维修/故障/救援/异常必填;保养加油必须为空,并禁止混入里程、加油或核验字段 required: [description, processingStatus] properties: description: {type: string, minLength: 1, maxLength: 2000} item: {type: string, nullable: true, maxLength: 256, description: 维修或救援项目,此两类必填;其他类型为空} place: {type: string, nullable: true, maxLength: 512, description: 维修或救援地点,此两类必填;其他类型为空} handler: {type: string, nullable: true, maxLength: 128, description: 实际处理人;处理中或完成时必填,与系统操作人分别保留} processingStatus: {type: string, enum: [PENDING, IN_PROGRESS, COMPLETED]} exceptionType: {type: string, nullable: true, enum: [ROUTE, TIMEOUT, DELIVERY, VEHICLE_BREAKDOWN], description: 异常记录必填;其他类型为空} reportedCost: {type: number, nullable: true, minimum: 0, multipleOf: 0.01, maximum: 999999999999.99, description: 仅维修/救援允许;空为未登记,零为已登记零费用,不表示付款} currency: {type: string, nullable: true, pattern: '^[A-Z]{3}$', description: 金额非空时必填有效币种;未填金额时为空,不自动换算} File: type: object properties: fileId: {type: string} fileName: {type: string} contentType: {type: string, enum: [image/jpeg, image/png]} sizeBytes: {type: integer, format: int64} Record: type: object properties: recordNo: {type: string} vehicleId: {type: string} vehiclePlate: {type: string, description: 建档时车牌快照} details: {$ref: '#/components/schemas/Draft'} files: {type: array, items: {$ref: '#/components/schemas/File'}} recordVersion: {type: integer, format: int64, minimum: 0} updatedBy: {type: string} updatedAt: {type: string, format: date-time} paymentStatus: {type: string, nullable: true, readOnly: true, description: 运维快照不记录可变财务状态,当前为空;读取finance-status获取财务事实} FinanceStatus: type: object properties: recordNo: {type: string} status: {type: string, enum: [UNLINKED, PENDING_FINANCE, FINANCE_ATTENTION, PENDING, APPROVED, PARTIALLY_PAID, PAID, VOIDED]} sourceVersion: {type: integer, format: int64, nullable: true} submittedRecordVersion: {type: integer, format: int64, nullable: true} finance: {type: object, nullable: true, description: Finance公司费用Cost完整只读快照;来源版本及内容须与本地提交一致;未接收不虚构金额,具体结构见finance-vehicle-costs-v1.yaml的Cost}