package com.vincent.rsf.openApi.controller; import com.vincent.rsf.framework.common.R; import com.vincent.rsf.framework.exception.CoolException; import com.vincent.rsf.openApi.entity.dto.CommonResponse; import com.vincent.rsf.openApi.entity.params.ExMsgCallbackParams; import com.vincent.rsf.openApi.entity.params.LocSiteParams; import com.vincent.rsf.openApi.entity.params.LocationAllocateParams; import com.vincent.rsf.openApi.entity.params.RcsPubTaskParams; import com.vincent.rsf.openApi.entity.params.SyncRcsLocsParam; import com.vincent.rsf.openApi.entity.params.TaskReportParams; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.vincent.rsf.httpaudit.support.HttpAuditSupport; import com.vincent.rsf.openApi.service.WmsRcsService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.StreamUtils; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.nio.charset.Charset; import java.util.Map; import java.util.Objects; @Slf4j @RestController @Api("RCS调度交互接口") @RequestMapping("/rcs") public class WmsRcsController { private static final String API_AGV_ERROR = "RCS-AGV异常上报(仅接收)"; private static final String API_PUB_TASK = "调度任务下发"; private static final String API_CANCEL_TASK = "取消调度任务"; private static final String API_CALLBACK_EVENT = "状态上报回调"; private static final String API_SYNC_LOCS = "RCS库位信息同步"; private static final String API_MODIFY_STATUS = "RCS修改库位或站点状态"; private static final String API_TASK_REPORT = "RCS回调接口"; private static final String API_ALLOCATE = "RCS-申请入库任务"; @Autowired private WmsRcsService wmsRcsService; @Resource private ObjectMapper objectMapper; /** * RCS AGV 异常上报:原样接收请求体,打日志;http-audit 需配置 URI 白名单见 version/db */ @ApiOperation(API_AGV_ERROR) @PostMapping("/api/open/agvError") public CommonResponse agvError(HttpServletRequest request) throws IOException { Charset charset = HttpAuditSupport.resolveCharset(request); String body = StreamUtils.copyToString(request.getInputStream(), charset); log.info("RCS POST /rcs/api/open/agvError | {} contentType={} charset={} bytes={}\n{}", API_AGV_ERROR, request.getContentType(), charset.name(), body.getBytes(charset).length, formatBodyForLog(body)); return CommonResponse.ok(); } private String formatBodyForLog(String body) { if (body.isEmpty()) { return "(empty body)"; } try { JsonNode n = objectMapper.readTree(body); return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(n); } catch (Exception e) { int max = 16384; return body.length() > max ? body.substring(0, max) + "...(truncated,len=" + body.length() + ")" : body; } } /** RCS 入站请求体打日志(含 @ApiOperation 中文说明) */ private void logRcsRequest(String action, String apiOperationValue, Object body) { if (body == null) { log.info("RCS {} | {} request body=null", action, apiOperationValue); return; } try { log.info("RCS {} | {} request:\n{}", action, apiOperationValue, objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(body)); } catch (Exception e) { log.info("RCS {} | {} request: {}", action, apiOperationValue, body); } } /** * @author Ryan * @date 2025/8/27 * @description: 任务下发 * @version 1.0 */ @ApiOperation(API_PUB_TASK) @PostMapping("/pub/task") public CommonResponse pubTasks(@RequestBody RcsPubTaskParams params) { logRcsRequest("POST /rcs/pub/task", API_PUB_TASK, params); if (Objects.isNull(params)) { throw new CoolException("参数不能为空!!"); } return wmsRcsService.pubTasks(params); } /** * @author Ryan * @date 2025/8/27 * @description: 取消任务 * @version 1.0 */ @ApiOperation(API_CANCEL_TASK) @PostMapping("/cancel/task") public CommonResponse cancelTasks(@RequestBody Map params) { logRcsRequest("POST /rcs/cancel/task", API_CANCEL_TASK, params); return wmsRcsService.cancelTasks(params); } /** * @author Ryan * @date 2025/8/27 * @description: 任务回调,状态回写 * @version 1.0 */ @ApiOperation(API_CALLBACK_EVENT) @PostMapping("/callback/event") public CommonResponse callBackEvent(@RequestBody ExMsgCallbackParams params) { logRcsRequest("POST /rcs/callback/event", API_CALLBACK_EVENT, params); return wmsRcsService.callBackEvent(params); } /** * @author Ryan * @date 2025/8/27 * @description: RCS库位信息同步 * @version 1.0 */ @ApiOperation(API_SYNC_LOCS) @PostMapping("/sync/locs") public R syncLocsToWms(@RequestBody SyncRcsLocsParam params) { logRcsRequest("POST /rcs/sync/locs", API_SYNC_LOCS, params); if (Objects.isNull(params)) { return R.error("参数不能为空!!"); } return R.ok().add(wmsRcsService.syncLocs(params)); } /** * @author Ryan * @date 2025/11/10 * @description: WMS 出库成功后,修改库位、站点状态 * @version 1.0 */ @ApiOperation(API_MODIFY_STATUS) @PostMapping("/modify/status") public R modifyLocOrSite(@RequestBody LocSiteParams params) { logRcsRequest("POST /rcs/modify/status", API_MODIFY_STATUS, params); if (Objects.isNull(params)) { return R.error("参数不能为空!!"); } return wmsRcsService.modifyLocOrSite(params); } /** * @author Ryan * @date 2026/2/3 * @description: RCS回调接口 * @version 1.0 */ @ApiOperation(API_TASK_REPORT) @PostMapping("/api/open/task/report") public CommonResponse reportTask(@RequestBody TaskReportParams params) { logRcsRequest("POST /rcs/api/open/task/report", API_TASK_REPORT, params); if (Objects.isNull(params)) { throw new CoolException("参数不能为空!!"); } return wmsRcsService.reportTask(params); } /** * @author Ryan * @date 2026/2/6 * @description: 申请入库任务 * @version 1.0 */ @ApiOperation(API_ALLOCATE) @PostMapping("/api/open/location/allocate") public R allocateLocation(@RequestBody LocationAllocateParams params) { logRcsRequest("POST /rcs/api/open/location/allocate", API_ALLOCATE, params); if (Objects.isNull(params)) { return R.error("参数不能为空!!"); } if (Objects.isNull(params.getBarcode()) || params.getBarcode().isEmpty()) { return R.error("料箱码不能为空!!"); } if (Objects.isNull(params.getStaNo()) || params.getStaNo().isEmpty()) { return R.error("入库站点不能为空!!"); } if (Objects.isNull(params.getType())) { params.setType(18); } return wmsRcsService.allocateLocation(params); } }