json串多了<pre>标签,ext报错

1.返回的json串前面多出了<pre style="word-wrap: break-word; .....></pre>的东西,导致转js对象时报错,查看后台也没见到添加<pre>标签,应该是浏览器添加的。
解决办法,把response的contentType设置为text/html即可:
this.getResponse().setContentType("text/html;charset=UTF-8");
Content-Type对浏览器解析的一些影响

当为web服务器输出的内容指定Content-Type为text/html时,浏览器会对内容做一些额外的转换工作,比如"""这样的实体形式就会被替换成最终的字符(双引号")。这在普通情况下并不会引起什么问题,但是不幸的是如果你采用了JSON格式来传输数据,例如:{status:0;data:"content"data"}这样的格式,当"被替换成"后,Javascript就会得到一个错误的数据格式,因为这时候Javascript拿到的数据就变成了{status:0;data:"content"data"}。

这时候,你可以设置Content-Type为text/x-json来解决此问题,但是text/x-json有可能引起一些浏览器兼容性上的问题(一些浏览器会提示下载Content-Type为text/x-json的内容),而更好的做法就是设置Content-Type为text/plain。不过即使这样也并没有完美的解决问题,考虑这样一种情况,你需要上传文件但是你并不想刷新上传页面,这时候一般的做法就是让form的target指向一个Iframe,代码如下:
<form target="iframeId" method="post" action="accept.php"><input type="file" /></form>
当accept.php接收请求并处理完成返回一段JSON数据后问题就发生了,因为accept.php输出的内容被定向到了iframe,而在iframe里浏览器会自动地给数据上加上<pre>标记,这时候JSON又出现格式错误了。

最终的解决方法就是,在JSON传输时指定Content-Type为text/plain,但是遇到上面提的文件上传情况,就应该指定Content-Type为text/html。
package cn.iocoder.yudao.module.yavwarehouse.controller.admin.iomanagementmain; import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApiImpl; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.marketing.controller.admin.aftersalesmanagement.vo.AftersalesManagementSaveReqVO; import cn.iocoder.yudao.module.yavwarehouse.controller.admin.iomanagementmain.vo.IoManagementMainPageReqVO; import cn.iocoder.yudao.module.yavwarehouse.controller.admin.iomanagementmain.vo.IoManagementMainRespVO; import cn.iocoder.yudao.module.yavwarehouse.controller.admin.iomanagementmain.vo.IoManagementMainSaveReqVO; import cn.iocoder.yudao.module.yavwarehouse.controller.admin.iomanagementmain.vo.IoManagementMainStartFlowReqVO; import cn.iocoder.yudao.module.yavwarehouse.dal.dataobject.iomanagementmain.IoManagementMainDO; import cn.iocoder.yudao.module.yavwarehouse.service.iomanagementdetail.IoManagementDetailService; import cn.iocoder.yudao.module.yavwarehouse.service.iomanagementmain.IoManagementMainService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; @Tag(name = "管理后台 - 出入库管理") @RestController @RequestMapping("/yavwarehouse/io-management-main") @Validated public class IoManagementMainController { @Resource private IoManagementMainService ioManagementMainService; @Resource private IoManagementDetailService ioManagementDetailService; @Resource private BpmProcessInstanceApiImpl bpmProcessInstanceApi; // 变更说明: // 1) 参数校验、鉴权保持不变;业务逻辑仍在 Service 层,控制器保持薄。 @PostMapping("/create") @Operation(summary = "创建出入库主") @PreAuthorize("@ss.hasPermission('yavwarehouse:io-management-main:create')") public CommonResult<Long> createIoManagementMain(@Valid @RequestBody IoManagementMainSaveReqVO createReqVO) { return success(ioManagementMainService.createIoManagementMain(createReqVO)); } @PutMapping("/update") @Operation(summary = "更新出入库主") @PreAuthorize("@ss.hasPermission('yavwarehouse:io-management-main:update')") public CommonResult<Boolean> updateIoManagementMain(@Valid @RequestBody IoManagementMainSaveReqVO updateReqVO) { ioManagementMainService.updateIoManagementMain(updateReqVO); return success(true); } @PostMapping("/approval") @Operation(summary = "审批出入库主") @PreAuthorize("@ss.hasPermission('yavwarehouse:io-management-main:approval')") public CommonResult<Boolean> approvalIoManagementMain(@Valid @RequestBody IoManagementMainSaveReqVO updateReqVO) { ioManagementMainService.approvalIoManagementMain(updateReqVO); return success(true); } @DeleteMapping("/delete") @Operation(summary = "删除出入库主") @Parameter(name = "id", description = "编号", required = true) @PreAuthorize("@ss.hasPermission('yavwarehouse:io-management-main:delete')") public CommonResult<Boolean> deleteIoManagementMain(@RequestParam("id") Long id) { ioManagementMainService.deleteIoManagementMain(id); return success(true); } @DeleteMapping("/delete-list") @Parameter(name = "ids", description = "编号", required = true) @Operation(summary = "批量删除出入库主") @PreAuthorize("@ss.hasPermission('yavwarehouse:io-management-main:delete')") public CommonResult<Boolean> deleteIoManagementMainList(@RequestParam("ids") List<Long> ids) { ioManagementMainService.deleteIoManagementMainListByIds(ids); return success(true); } @GetMapping("/get") @Operation(summary = "获得出入库主") @Parameter(name = "id", description = "编号", required = true, example = "1024") @PreAuthorize("@ss.hasPermission('yavwarehouse:io-management-main:query')") public CommonResult<IoManagementMainRespVO> getIoManagementMain(@RequestParam("id") Long id, @RequestParam(value = "withItems", required = false, defaultValue = "false") boolean withItems) { // 变更:将“查明细与组装”下沉到 Service;Controller 只透传参数 return success(ioManagementMainService.getIoManagementMainWithItems(id, withItems)); } @GetMapping("/page") @Operation(summary = "获得出入库主分页") @PreAuthorize("@ss.hasPermission('yavwarehouse:io-management-main:query')") public CommonResult<PageResult<IoManagementMainRespVO>> getIoManagementMainPage(@Valid IoManagementMainPageReqVO pageReqVO) { return success(ioManagementMainService.getIoManagementMainPages(pageReqVO)); } @GetMapping("/export-excel") @Operation(summary = "导出出入库主 Excel") @PreAuthorize("@ss.hasPermission('yavwarehouse:io-management-main:export')") @ApiAccessLog(operateType = EXPORT) public void exportIoManagementMainExcel(@Valid IoManagementMainPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); List<IoManagementMainDO> list = ioManagementMainService.getIoManagementMainPage(pageReqVO).getList(); // 导出 Excel ExcelUtils.write(response, "出入库主.xls", "数据", IoManagementMainRespVO.class, BeanUtils.toBean(list, IoManagementMainRespVO.class)); } @GetMapping("/getOrderPage") @Operation(summary = "根据业务类型查询单据") @PreAuthorize("@ss.hasPermission('yavwarehouse:io-management-main:query')") public CommonResult getOrderPage(@RequestParam String businessType, @RequestParam String businessTypeValue, @RequestParam String applyType, PageParam pageParam) { return success(ioManagementMainService.getOrderPage(businessType, businessTypeValue, applyType, pageParam)); } @PutMapping("/startFlow") @Operation(summary = "发起出入库流程") @PreAuthorize("@ss.hasPermission('yavwarehouse:io-management-main:update')") public CommonResult<Boolean> startFlow(@Valid @RequestBody IoManagementMainStartFlowReqVO reqVO) { Map<String, Object> variables = new HashMap<>(); variables.put("ioId", reqVO.getId()); String processInstanceId = bpmProcessInstanceApi.createProcessInstance(getLoginUserId(), new BpmProcessInstanceCreateReqDTO() .setProcessDefinitionKey("inbound") .setVariables(variables) .setBusinessKey(String.valueOf(reqVO.getId())) .setStartUserSelectAssignees(reqVO.getNextAssignees())); ioManagementMainService.updateProcessInfo(reqVO.getId(), processInstanceId, "RUNNING"); return success(true); } } package cn.iocoder.yudao.module.yavwarehouse.service.iomanagementmain; import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.enums.AuditStateEnum; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.marketing.controller.admin.profilesalesorder.vo.ProfileSalesOrderPageReqVO; import cn.iocoder.yudao.module.marketing.controller.admin.projectsalesorder.vo.ProjectSalesOrderPageReqVO; import cn.iocoder.yudao.module.marketing.controller.admin.salesorder.vo.KreSalesOrderPageReqVO; import cn.iocoder.yudao.module.marketing.dal.dataobject.profilesalesorder.ProfileSalesOrderDO; import cn.iocoder.yudao.module.marketing.dal.dataobject.projectsalesorder.ProjectSalesOrderDO; import cn.iocoder.yudao.module.marketing.dal.dataobject.salesorder.KreSalesOrderDO; import cn.iocoder.yudao.module.marketing.dal.mysql.profilesalesorder.ProfileSalesOrderMapper; import cn.iocoder.yudao.module.marketing.dal.mysql.projectsalesorder.ProjectSalesOrderMapper; import cn.iocoder.yudao.module.marketing.dal.mysql.salesorder.KreSalesOrderMapper; import cn.iocoder.yudao.module.plan.controller.admin.factorymaterials.vo.FactoryMaterialsPageReqVO; import cn.iocoder.yudao.module.plan.controller.admin.fenestrationmanagement.vo.FenestrationManagementPageReqVO; import cn.iocoder.yudao.module.plan.controller.admin.profilecheck.vo.ProfileCheckPageReqVO; import cn.iocoder.yudao.module.plan.dal.dataobject.factorymaterials.FactoryMaterialsDO; import cn.iocoder.yudao.module.plan.dal.dataobject.fenestrationmanagement.FenestrationManagementDO; import cn.iocoder.yudao.module.plan.dal.dataobject.profilecheck.ProfileCheckDO; import cn.iocoder.yudao.module.plan.dal.mysql.factorymaterials.FactoryMaterialsMapper; import cn.iocoder.yudao.module.plan.dal.mysql.fenestrationmanagement.FenestrationManagementMapper; import cn.iocoder.yudao.module.plan.dal.mysql.profilecheck.ProfileCheckMapper; import cn.iocoder.yudao.module.purchase.controller.admin.receipt.vo.ReceiptPageReqVO; import cn.iocoder.yudao.module.purchase.dal.dataobject.material.MaterialDO; import cn.iocoder.yudao.module.purchase.dal.dataobject.materialinventory.MaterialInventoryDO; import cn.iocoder.yudao.module.purchase.dal.dataobject.receipt.ReceiptDO; import cn.iocoder.yudao.module.purchase.dal.mysql.material.MaterialMapper; import cn.iocoder.yudao.module.purchase.dal.mysql.materialinventory.MaterialInventoryMapper; import cn.iocoder.yudao.module.purchase.dal.mysql.receipt.ReceiptMapper; import cn.iocoder.yudao.module.purchase.utils.OrderNoGeneratorUtil; import cn.iocoder.yudao.module.yavwarehouse.controller.admin.inventorydetails.vo.InventoryDetailsSaveReqVO; import cn.iocoder.yudao.module.yavwarehouse.controller.admin.iomanagementdetail.vo.IoManagementDetailRespVO; import cn.iocoder.yudao.module.yavwarehouse.controller.admin.iomanagementdetail.vo.IoManagementDetailSaveReqVO; import cn.iocoder.yudao.module.yavwarehouse.controller.admin.iomanagementmain.vo.IoManagementMainPageReqVO; import cn.iocoder.yudao.module.yavwarehouse.controller.admin.iomanagementmain.vo.IoManagementMainRespVO; import cn.iocoder.yudao.module.yavwarehouse.controller.admin.iomanagementmain.vo.IoManagementMainSaveReqVO; import cn.iocoder.yudao.module.yavwarehouse.dal.dataobject.inventorydetails.InventoryDetailsDO; import cn.iocoder.yudao.module.yavwarehouse.dal.dataobject.iomanagementdetail.IoManagementDetailDO; import cn.iocoder.yudao.module.yavwarehouse.dal.dataobject.iomanagementmain.IoManagementMainDO; import cn.iocoder.yudao.module.yavwarehouse.dal.dataobject.yavwarehouse.YavWarehouseDO; import cn.iocoder.yudao.module.yavwarehouse.dal.mysql.iomanagementdetail.IoManagementDetailMapper; import cn.iocoder.yudao.module.yavwarehouse.dal.mysql.iomanagementmain.IoManagementMainMapper; import cn.iocoder.yudao.module.yavwarehouse.dal.mysql.yavwarehouse.YavWarehouseMapper; import cn.iocoder.yudao.module.yavwarehouse.enums.BILL_TYPE; import cn.iocoder.yudao.module.yavwarehouse.enums.CostStrategyType; import cn.iocoder.yudao.module.yavwarehouse.enums.YavConstants; import cn.iocoder.yudao.module.yavwarehouse.service.inventorydetails.InventoryDetailsService; import cn.iocoder.yudao.module.yavwarehouse.service.iomanagementdetail.IoManagementDetailService; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.datical.liquibase.ext.checks.config.IFileAccessor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.yavwarehouse.enums.ErrorCodeConstants.*; /** * 出入库主 Service 实现类 */ @Service @Validated @Slf4j public class IoManagementMainServiceImpl implements IoManagementMainService { @Resource private IoManagementMainMapper ioManagementMainMapper; @Resource private IoManagementDetailService ioManagementDetailService; @Resource private InventoryDetailsService inventoryDetailsService; @Resource private IoManagementDetailMapper ioManagementDetailMapper; @Resource private ProfileCheckMapper profileCheckMapper; @Resource private FenestrationManagementMapper fenestrationManagementMapper; @Resource private FactoryMaterialsMapper factoryMaterialsMapper; @Resource private ReceiptMapper receiptMapper; @Resource private KreSalesOrderMapper kreSalesOrderMapper; @Resource private ProjectSalesOrderMapper projectSalesOrderMapper; @Resource private ProfileSalesOrderMapper profileSalesOrderMapper; @Resource private MaterialInventoryMapper materialInventoryMapper; @Resource private MaterialMapper materialMapper; @Resource private YavWarehouseMapper warehouseMapper; @Override @Transactional(rollbackFor = Exception.class) public Long createIoManagementMain(IoManagementMainSaveReqVO createReqVO) { // 插入主表记录 if (StringUtils.isBlank(createReqVO.getIoBillNo())) { String next = OrderNoGeneratorUtil.next(); if (createReqVO.getIoBillType().equals(BILL_TYPE.OUTBOUND)) { createReqVO.setIoBillNo(BILL_TYPE.CK + "_" + next); } else { createReqVO.setIoBillNo(BILL_TYPE.RK + "_" + next); } } IoManagementMainDO ioManagementMain = BeanUtils.toBean(createReqVO, IoManagementMainDO.class); if (ObjectUtil.equals(createReqVO.getAuditState(), AuditStateEnum.NO.getStatus())) { checkSave(createReqVO); ioManagementMain.setAuditState(AuditStateEnum.NO.getStatus()); } if (ObjectUtil.equals(createReqVO.getAuditState(), AuditStateEnum.NOT_PASS.getStatus())) { checkAuditState(createReqVO); ioManagementMain.setAuditState(AuditStateEnum.NOT_PASS.getStatus()); } ioManagementMainMapper.insert(ioManagementMain); // 根据 ioBillType 判断入库/出库,影响库存增减方向与校验 boolean isInbound = BILL_TYPE.INBOUND.equals(createReqVO.getIoBillType()); // 默认策略:FIFO(保持现有行为不变)。如需切换策略,仅需改此处。 final CostStrategyType costStrategy = CostStrategyType.FIFO; // 明细持久化:先统一补主表关联字段,再调用明细创建(可后续优化为批量) List<IoManagementDetailDO> ioManagementDetailDOS = BeanUtils.toBean(createReqVO.getItems(), IoManagementDetailDO.class); ioManagementDetailDOS.forEach(item -> { item.setMainId(ioManagementMain.getId()); item.setIoBillNo(ioManagementMain.getIoBillNo()); if (!isInbound) item.setIoMainUnitQty(item.getIoMainUnitQty().negate()); Long detailId = ioManagementDetailService.createIoManagementDetail(item); Long warehouseId = item.getIoWarehouseId(); if (!isInbound) { if (costStrategy == CostStrategyType.FIFO) { // FIFO:按入库明细顺序分摊并写回成本 updateInboundDetail(warehouseId, item.getIoMaterialId(), detailId, item.getIoMainUnitQty().negate()); } else { // 其他策略:计算并写回成本(不改变入库明细的已出库数量) computeAndWriteOutboundCostByStrategy(warehouseId, item.getIoMaterialId(), detailId, item.getIoMainUnitQty().negate(), costStrategy); } } }); if (CollectionUtils.isNotEmpty(ioManagementDetailDOS)) { ioManagementDetailDOS.stream().map(p -> p.getIoWarehouseId()).distinct().forEach(warehouseId -> { // 合并明细:按物料ID汇总数量;使用已方向(出库为负、入库为正)的明细集合 Map<Long, BigDecimal> materialIdToDelta = ioManagementDetailDOS.stream() .filter(p -> Objects.equals(p.getIoWarehouseId(), warehouseId)) .collect(Collectors.toMap( IoManagementDetailDO::getIoMaterialId, IoManagementDetailDO::getIoMainUnitQty, BigDecimal::add)); // 一次性查询所有涉及物料的现存库存,返回以 materialId 为 key 的 Map Map<Long, InventoryDetailsDO> materialIdToInv = inventoryDetailsService .listByWarehouseIdAndMaterialIds(warehouseId, materialIdToDelta.keySet()); // 分流创建/更新:出库校验库存存在与不为负 for (Map.Entry<Long, BigDecimal> entry : materialIdToDelta.entrySet()) { Long materialId = entry.getKey(); BigDecimal delta = entry.getValue(); // 已包含方向(入库+,出库-) InventoryDetailsDO inv = materialIdToInv.get(materialId); if (inv == null) { if (!isInbound) { // 出库:库存不存在,直接拒绝 throw exception(INVENTORY_REFUSE_OUTBOUND); } // 入库:不存在则创建 YavWarehouseDO warehouseDO = warehouseMapper.selectById(warehouseId); List<MaterialInventoryDO> materialInventoryDOS = materialInventoryMapper.selectList(new LambdaQueryWrapper<MaterialInventoryDO>().eq(Objects.nonNull(warehouseDO) && StringUtils.isNotBlank(warehouseDO.getWhName()), MaterialInventoryDO::getMWarehouse, warehouseDO.getWhName())); String position = ""; String unit = ""; String ioCustomOrder = ""; if (CollectionUtils.isNotEmpty(materialInventoryDOS)) { MaterialInventoryDO materialInventoryDO = materialInventoryDOS.get(0); position = materialInventoryDO.getMWarehouseShelf() + "-" + materialInventoryDO.getMWarehouseColumn() + "-" + materialInventoryDO.getMWarehouseNum(); } MaterialDO materialDO = materialMapper.selectById(materialId); List<IoManagementDetailDO> detailDOS = ioManagementDetailDOS.stream() .filter(p -> Objects.equals(p.getIoWarehouseId(), warehouseId) && Objects.equals(p.getIoMaterialCode(), Objects.nonNull(materialDO) ? materialDO.getMCode() : null)) .collect(Collectors.toList()); if (CollectionUtils.isNotEmpty(detailDOS)) { position = detailDOS.get(0).getIoWarehouseLocation(); unit = detailDOS.get(0).getIoMainUnit(); ioCustomOrder = detailDOS.get(0).getIoCustomOrder(); } inventoryDetailsService.createInventoryDetails(InventoryDetailsSaveReqVO.builder() .idWhId(warehouseId) .idMaterialId(materialId) .idMaterialName(materialDO.getMName()) .idMaterialCode(materialDO.getMCode()) .idAvailableQuantity(delta) .idStockQuantity(delta) .idInventoryUnit(unit) .idCustomOrder(StringUtils.isNotBlank(ioCustomOrder) ? Integer.valueOf(ioCustomOrder) : null) .idMaterialPosition(position) .idWhName(Objects.nonNull(warehouseDO) ? warehouseDO.getWhName() : "") .idWhCode(Objects.nonNull(warehouseDO) ? warehouseDO.getWhNumber() : "") .build()); } else { BigDecimal newAvailable = inv.getIdAvailableQuantity().add(delta); BigDecimal newStock = inv.getIdStockQuantity().add(delta); if (!isInbound && newStock.compareTo(BigDecimal.ZERO) < 0) { // 出库:扣减后为负,拒绝 throw exception(BOUND_RUN_OUT); } YavWarehouseDO warehouseDO = warehouseMapper.selectById(warehouseId); List<MaterialInventoryDO> materialInventoryDOS = materialInventoryMapper.selectList(new LambdaQueryWrapper<MaterialInventoryDO>().eq(Objects.nonNull(warehouseDO) && StringUtils.isNotBlank(warehouseDO.getWhName()), MaterialInventoryDO::getMWarehouse, warehouseDO.getWhName())); String position = ""; String unit = ""; String ioCustomOrder = ""; if (CollectionUtils.isNotEmpty(materialInventoryDOS)) { MaterialInventoryDO materialInventoryDO = materialInventoryDOS.get(0); position = materialInventoryDO.getMWarehouseShelf() + "-" + materialInventoryDO.getMWarehouseColumn() + "-" + materialInventoryDO.getMWarehouseNum(); } MaterialDO materialDO = materialMapper.selectById(materialId); List<IoManagementDetailDO> detailDOS = ioManagementDetailDOS.stream() .filter(p -> Objects.equals(p.getIoWarehouseId(), warehouseId) && Objects.equals(p.getIoMaterialCode(), Objects.nonNull(materialDO) ? materialDO.getMCode() : null)) .collect(Collectors.toList()); if (CollectionUtils.isNotEmpty(detailDOS)) { position = detailDOS.get(0).getIoWarehouseLocation(); unit = detailDOS.get(0).getIoMainUnit(); ioCustomOrder = detailDOS.get(0).getIoCustomOrder(); } inventoryDetailsService.updateInventoryDetails(InventoryDetailsSaveReqVO.builder() .id(inv.getId()) .idWhId(warehouseId) .idMaterialId(materialId) .idMaterialName(Objects.nonNull(materialDO) ? materialDO.getMName() : "") .idMaterialCode(Objects.nonNull(materialDO) ? materialDO.getMCode() : "") .idAvailableQuantity(newAvailable) .idStockQuantity(newStock) .idInventoryUnit(unit) .idCustomOrder(StringUtils.isNotBlank(ioCustomOrder) ? Integer.valueOf(ioCustomOrder) : null) .idMaterialPosition(position) .idMaterialPosition(position) .idWhName(Objects.nonNull(warehouseDO) ? warehouseDO.getWhName() : "") .idWhCode(Objects.nonNull(warehouseDO) ? warehouseDO.getWhNumber() : "") .build()); } } }); } //更改对应单据来源表出入库状态 if (Objects.nonNull(createReqVO.getIoRelateNum())) { updateSourceBillStatus(createReqVO, createReqVO.getIoBillType()); } return ioManagementMain.getId(); } private void checkSave(IoManagementMainSaveReqVO createReqVO) { } private void checkAuditState(IoManagementMainSaveReqVO createReqVO) { //入库类型、入库时间、应用类型、入库总成本、关联单号、入库用途、选择物料、仓库、仓库类型、入库数量、单位 if (StringUtils.isBlank(createReqVO.getIoBillType())) { throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST_PARAM.getCode(), "入库/出库类型不能为空"); } String name = createReqVO.getIoBillType().equals(BILL_TYPE.INBOUND) ? "入库" : "出库"; if (ObjectUtil.isEmpty(createReqVO.getIoBillDate())) { throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST_PARAM.getCode(), name + "时间不能为空"); } if (StringUtils.isBlank(createReqVO.getIoApplyType())) { throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST_PARAM.getCode(), "应用类型不能为空"); } if (ObjectUtil.isEmpty(createReqVO.getIoTotalCost())) { throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST_PARAM.getCode(), name + "总成本不能为空"); } if (ObjectUtil.isEmpty(createReqVO.getIoRelateNum())) { throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST_PARAM.getCode(), "关联单号不能为空"); } if (StringUtils.isBlank(createReqVO.getIoPurpose())) { throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST_PARAM.getCode(), name + "用途不能为空"); } List<IoManagementDetailSaveReqVO> items = createReqVO.getItems(); if (CollectionUtils.isEmpty(items)) { throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST_PARAM.getCode(), "选择物料不能为空"); } if (items.stream().anyMatch(item -> StringUtils.isBlank(item.getIoWarehouse()))) { throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST_PARAM.getCode(), "仓库名字不能为空"); } if (items.stream().anyMatch(item -> StringUtils.isBlank(item.getIoWarehouseType()))) { throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST_PARAM.getCode(), "仓库类型不能为空"); } if (items.stream().anyMatch(item -> ObjectUtil.isEmpty(item.getIoMainUnitQty()))) { throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST_PARAM.getCode(), name + "数量不能为空"); } if (items.stream().anyMatch(item -> ObjectUtil.isEmpty(item.getIoMainUnit()))) { throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST_PARAM.getCode(), "主单位不能为空"); } } /** * 更新源单据状态 */ private void updateSourceBillStatus(IoManagementMainSaveReqVO createReqVO, String ioBillType) { Long ioRelateNum = createReqVO.getIoRelateNum(); if (ioRelateNum == null) { log.warn("关联单据ID为空,无法更新源单据状态"); return; } String ioApplyType = createReqVO.getIoApplyType(); String ioBusinessType = createReqVO.getIoBusinessType(); try { if (BILL_TYPE.INBOUND.equals(ioBillType)) { updateInboundSourceStatus(ioBusinessType, ioRelateNum); } else { updateOutboundSourceStatus(ioBusinessType, ioApplyType, ioRelateNum); } } catch (Exception e) { log.error("更新源单据状态失败: billType={}, businessType={}, applyType={}, relateNum={}", ioBillType, ioBusinessType, ioApplyType, ioRelateNum, e); } } /** * 更新入库源单据状态 */ private void updateInboundSourceStatus(String ioBusinessType, Long ioRelateNum) { if (YavConstants.ONE_STR.equals(ioBusinessType)) { // 采购入库(收料通知单表) receiptMapper.update(new LambdaUpdateWrapper<ReceiptDO>() .eq(ReceiptDO::getId, ioRelateNum) .set(ReceiptDO::getRWareStatus, YavConstants.ONE_STR)); } // 可以在这里添加其他入库类型的处理 } /** * 更新出库源单据状态 */ private void updateOutboundSourceStatus(String ioBusinessType, String ioApplyType, Long ioRelateNum) { if (YavConstants.ONE_STR.equals(ioBusinessType)) { switch (ioApplyType) { case YavConstants.ONE_STR: // 型材配置 profileCheckMapper.update(new LambdaUpdateWrapper<ProfileCheckDO>() .eq(ProfileCheckDO::getId, ioRelateNum) .set(ProfileCheckDO::getRWareStatus, YavConstants.ONE_STR)); break; case YavConstants.TWO_STR: // 门窗原料 fenestrationManagementMapper.update(new LambdaUpdateWrapper<FenestrationManagementDO>() .eq(FenestrationManagementDO::getId, ioRelateNum) .set(FenestrationManagementDO::getRWareStatus, YavConstants.ONE_STR)); break; case YavConstants.THREE_STR: // 门窗工厂 factoryMaterialsMapper.update(new LambdaUpdateWrapper<FactoryMaterialsDO>() .eq(FactoryMaterialsDO::getId, ioRelateNum) .set(FactoryMaterialsDO::getRWareStatus, YavConstants.ONE_STR)); break; default: log.warn("未知的出库应用类型: {}", ioApplyType); } } else { //出库-销售订单 switch (ioApplyType) { case YavConstants.ONE_STR: // 型材配置 profileSalesOrderMapper.update(new LambdaUpdateWrapper<ProfileSalesOrderDO>() .eq(ProfileSalesOrderDO::getId, ioRelateNum) .set(ProfileSalesOrderDO::getPsShipper, YavConstants.ONE)); break; case YavConstants.TWO_STR: // 家装销售订单 kreSalesOrderMapper.update(new LambdaUpdateWrapper<KreSalesOrderDO>() .eq(KreSalesOrderDO::getId, ioRelateNum) .set(KreSalesOrderDO::getSoShipper, YavConstants.ONE)); break; case YavConstants.THREE_STR: // 工程销售订单 projectSalesOrderMapper.update(new LambdaUpdateWrapper<ProjectSalesOrderDO>() .eq(ProjectSalesOrderDO::getId, ioRelateNum) .set(ProjectSalesOrderDO::getEsShipper, YavConstants.ONE)); break; default: log.warn("未知的出库应用类型: {}", ioApplyType); } return; } } // 计算除 FIFO 以外策略下的出库成本并写回当前出库明细(不改变入库明细) private void computeAndWriteOutboundCostByStrategy(Long warehouseId, Long ioMaterialId, Long outboundDetailId, BigDecimal outboundQty, CostStrategyType strategy) { if (outboundQty == null || outboundQty.compareTo(BigDecimal.ZERO) <= 0) { return; } BigDecimal unitCost; switch (strategy) { case AVERAGE: unitCost = computeAverageUnitCost(warehouseId, ioMaterialId); break; case WEIGHTED: unitCost = computeWeightedUnitCost(warehouseId, ioMaterialId); break; case MOVING_AVERAGE: unitCost = computeMovingAverageUnitCost(warehouseId, ioMaterialId); break; default: unitCost = BigDecimal.ZERO; } writeOutboundCost(outboundDetailId, unitCost, outboundQty); } // 简单平均:历史“可消耗入库明细”的单价算术平均(不考虑数量) private BigDecimal computeAverageUnitCost(Long warehouseId, Long materialId) { List<IoManagementDetailDO> inboundList = ioManagementDetailMapper.selectConsumableInbound(warehouseId, materialId); if (inboundList == null || inboundList.isEmpty()) return BigDecimal.ZERO; BigDecimal sum = BigDecimal.ZERO; int count = 0; for (IoManagementDetailDO d : inboundList) { if (d.getIoCostPrice() != null) { sum = sum.add(d.getIoCostPrice()); count++; } } if (count == 0) return BigDecimal.ZERO; return sum.divide(new BigDecimal(count), 6, RoundingMode.HALF_UP); } // 加权平均:按“剩余可用数量”加权 private BigDecimal computeWeightedUnitCost(Long warehouseId, Long materialId) { List<IoManagementDetailDO> inboundList = ioManagementDetailMapper.selectConsumableInbound(warehouseId, materialId); if (inboundList == null || inboundList.isEmpty()) return BigDecimal.ZERO; BigDecimal weightedSum = BigDecimal.ZERO; BigDecimal totalQty = BigDecimal.ZERO; for (IoManagementDetailDO d : inboundList) { BigDecimal inQty = d.getIoMainUnitQty(); BigDecimal used = d.getIoOutboundQuantity() == null ? BigDecimal.ZERO : d.getIoOutboundQuantity(); BigDecimal remain = inQty.subtract(used); if (remain.compareTo(BigDecimal.ZERO) <= 0) continue; BigDecimal unit = d.getIoCostPrice() == null ? BigDecimal.ZERO : d.getIoCostPrice(); weightedSum = weightedSum.add(unit.multiply(remain)); totalQty = totalQty.add(remain); } if (totalQty.compareTo(BigDecimal.ZERO) == 0) return BigDecimal.ZERO; return weightedSum.divide(totalQty, 6, RoundingMode.HALF_UP); } // 移动加权平均:优先使用主表平均成本;没有则回退为当前加权平均 private BigDecimal computeMovingAverageUnitCost(Long warehouseId, Long materialId) { // 主表暂未保存 avgCost 字段,先回退到加权平均 return computeWeightedUnitCost(warehouseId, materialId); } private void writeOutboundCost(Long outboundDetailId, BigDecimal unitCost, BigDecimal qty) { if (outboundDetailId == null || unitCost == null || qty == null) return; BigDecimal line = unitCost.multiply(qty); IoManagementDetailDO update = new IoManagementDetailDO(); update.setId(outboundDetailId); update.setIoCostPrice(unitCost); update.setIoLineCost(line); ioManagementDetailMapper.updateById(update); } // FIFO:按时间顺序(先进先出)累计入库明细的已出库数量,并计算本次出库成本 private void updateInboundDetail(Long warehouseId, Long ioMaterialId, Long outboundDetailId, BigDecimal outboundQty) { if (outboundQty == null || outboundQty.compareTo(BigDecimal.ZERO) <= 0) { return; } BigDecimal need = outboundQty; BigDecimal costSum = BigDecimal.ZERO; List<IoManagementDetailDO> inboundList = ioManagementDetailMapper.selectConsumableInbound(warehouseId, ioMaterialId); for (IoManagementDetailDO inbound : inboundList) { if (need.compareTo(BigDecimal.ZERO) <= 0) { break; } BigDecimal inQty = inbound.getIoMainUnitQty(); BigDecimal used = inbound.getIoOutboundQuantity() == null ? BigDecimal.ZERO : inbound.getIoOutboundQuantity(); BigDecimal remain = inQty.subtract(used); if (remain.compareTo(BigDecimal.ZERO) <= 0) { continue; } BigDecimal alloc = need.min(remain); int affected = ioManagementDetailMapper.increaseOutboundQuantity(inbound.getId(), alloc); if (affected != 1) { inboundList = ioManagementDetailMapper.selectConsumableInbound(warehouseId, ioMaterialId); continue; } BigDecimal unitCost = inbound.getIoCostPrice() == null ? BigDecimal.ZERO : inbound.getIoCostPrice(); costSum = costSum.add(unitCost.multiply(alloc)); need = need.subtract(alloc); if (need.compareTo(BigDecimal.ZERO) > 0) { throw exception(BOUND_RUN_OUT); } } if (outboundDetailId != null) { IoManagementDetailDO update = new IoManagementDetailDO(); update.setId(outboundDetailId); update.setIoLineCost(costSum); BigDecimal unit = costSum.divide(outboundQty, 6, RoundingMode.HALF_UP); update.setIoCostPrice(unit); ioManagementDetailMapper.updateById(update); } } @Override @Transactional(readOnly = true) public IoManagementMainRespVO getIoManagementMainWithItems(Long id, boolean withItems) { IoManagementMainDO main = ioManagementMainMapper.selectById(id); if (main == null) { throw exception(IO_MANAGEMENT_MAIN_NOT_EXISTS); } IoManagementMainRespVO vo = BeanUtils.toBean(main, IoManagementMainRespVO.class); List<IoManagementDetailDO> details = ioManagementDetailService.listByMainId(id); vo.setItems(BeanUtils.toBean(details, IoManagementDetailRespVO.class)); vo.setIoApplyType(CollectionUtils.isNotEmpty(details) ? details.get(0).getIoApplyType() : ""); return vo; } @Transactional(rollbackFor = Exception.class) @Override public void updateIoManagementMain(IoManagementMainSaveReqVO updateReqVO) { // 校验存在 validateIoManagementMainExists(updateReqVO.getId()); // 更新 IoManagementMainDO updateObj = BeanUtils.toBean(updateReqVO, IoManagementMainDO.class); if (ObjectUtil.equals(updateReqVO.getAuditState(), AuditStateEnum.NO.getStatus())) { checkSave(updateReqVO); updateObj.setAuditState(AuditStateEnum.NO.getStatus()); } if (ObjectUtil.equals(updateReqVO.getAuditState(), AuditStateEnum.NOT_PASS.getStatus())) { checkAuditState(updateReqVO); updateObj.setAuditState(AuditStateEnum.NOT_PASS.getStatus()); //更新明细 if (CollectionUtils.isNotEmpty(updateReqVO.getItems())) { updateReqVO.getItems().forEach(updateItem -> { IoManagementDetailDO newUpdateItem = BeanUtils.toBean(updateItem, IoManagementDetailDO.class); ioManagementDetailMapper.insertOrUpdate(newUpdateItem); }); } } ioManagementMainMapper.updateById(updateObj); } @Override public void approvalIoManagementMain(IoManagementMainSaveReqVO updateReqVO) { Long id = updateReqVO.getId(); IoManagementMainDO ioManagementMainDO = ioManagementMainMapper.selectById(id); // 状态校验 审批不通过,数据状态变为保存 if (ObjectUtil.equals(updateReqVO.getAuditState(), AuditStateEnum.NO.getStatus())) { ioManagementMainDO.setAuditState(AuditStateEnum.NO.getStatus()); } ioManagementMainDO.setAuditState(updateReqVO.getAuditState()); ioManagementMainMapper.updateById(ioManagementMainDO); } @Override public void deleteIoManagementMain(Long id) { // 校验存在 validateIoManagementMainExists(id); // 删除 ioManagementMainMapper.deleteById(id); } @Transactional(rollbackFor = Exception.class) @Override public void deleteIoManagementMainListByIds(List<Long> ids) { // 删除 ioManagementMainMapper.deleteByIds(ids); //删除出入库明细 ioManagementDetailMapper.delete(new LambdaUpdateWrapper<IoManagementDetailDO>().in(CollectionUtils.isNotEmpty(ids), IoManagementDetailDO::getMainId, ids)); } private void validateIoManagementMainExists(Long id) { if (ioManagementMainMapper.selectById(id) == null) { throw exception(IO_MANAGEMENT_MAIN_NOT_EXISTS); } } @Override public IoManagementMainDO getIoManagementMain(Long id) { return ioManagementMainMapper.selectById(id); } @Override public PageResult<IoManagementMainDO> getIoManagementMainPage(IoManagementMainPageReqVO pageReqVO) { return ioManagementMainMapper.selectPage(pageReqVO); } //@Override //public PageResult<IoManagementMainRespVO> getIoManagementMainPages(IoManagementMainPageReqVO pageReqVO) { // PageResult<IoManagementMainDO> pageResult = ioManagementMainMapper.selectPage(pageReqVO); // PageResult<IoManagementMainRespVO> result = BeanUtils.toBean(pageResult, IoManagementMainRespVO.class); // if (CollectionUtils.isEmpty(result.getList())) { // return result; // } // List<Long> ids = result.getList().stream().map(p -> p.getId()).collect(Collectors.toList()); // List<IoManagementDetailDO> detailDOS = ioManagementDetailMapper.selectList(new LambdaQueryWrapper<IoManagementDetailDO>().in(IoManagementDetailDO::getMainId, ids)); // // 5. 将明细数据按mainId分组 // Map<Long, List<IoManagementDetailDO>> detailMap = detailDOS.stream() // .collect(Collectors.groupingBy(IoManagementDetailDO::getMainId)); // result.getList().forEach(p -> { // List<IoManagementDetailDO> details = detailMap.get(p.getId()); // p.setIoApplyType(details.get(0).getIoApplyType()); // }); // return result; //} @Override public PageResult<IoManagementMainRespVO> getIoManagementMainPages(IoManagementMainPageReqVO pageReqVO) { PageResult<IoManagementMainDO> pageResult = ioManagementMainMapper.selectPage(pageReqVO); PageResult<IoManagementMainRespVO> result = BeanUtils.toBean(pageResult, IoManagementMainRespVO.class); if (CollectionUtils.isEmpty(result.getList())) { return result; } List<Long> ids = result.getList().stream().map(p -> p.getId()).collect(Collectors.toList()); List<IoManagementDetailDO> detailDOS = ioManagementDetailMapper.selectList( new LambdaQueryWrapper<IoManagementDetailDO>().in(IoManagementDetailDO::getMainId, ids) ); Map<Long, List<IoManagementDetailDO>> detailMap = detailDOS.stream() .collect(Collectors.groupingBy(IoManagementDetailDO::getMainId)); // 修复:增加非空判断,避免空指针 result.getList().forEach(p -> { List<IoManagementDetailDO> details = detailMap.get(p.getId()); if (CollectionUtils.isNotEmpty(details)) { // 只有明细数据存在时才赋值 p.setIoApplyType(details.get(0).getIoApplyType()); } else { p.setIoApplyType(null); // 无明细时设为null或默认值 } }); return result; } /** * @Description:根据业务类型查询单据列表 * @Author: hjs * @Date 2025/9/3 **/ @Override public PageResult<?> getOrderPage(String businessType, String businessTypeValue, String applyType, PageParam pageParam) { log.info("根据业务类型查询单据列表入参,businessType={},businessTypeValue={},applyType={}", businessType, businessTypeValue, applyType); if (BILL_TYPE.CK_BUSINESS_TYPE.equals(businessType)) { if (businessTypeValue.equals(YavConstants.ONE_STR)) { //出库-生产领料(字典出库明细应用类型 ck_apply_type) if (applyType.equals(YavConstants.ONE_STR)) { //型材配置 ProfileCheckPageReqVO pageReqVO = new ProfileCheckPageReqVO(); pageReqVO.setPageSize(pageParam.getPageSize()); pageReqVO.setPageNo(pageParam.getPageNo()); pageReqVO.setRWareStatus(YavConstants.TWO_STR); return profileCheckMapper.selectPage(pageReqVO); } else if (applyType.equals(YavConstants.TWO_STR)) { //门窗原料-家装 FenestrationManagementPageReqVO pageReqVO = new FenestrationManagementPageReqVO(); pageReqVO.setPageSize(pageParam.getPageSize()); pageReqVO.setPageNo(pageParam.getPageNo()); pageReqVO.setRWareStatus(YavConstants.TWO_STR); return fenestrationManagementMapper.selectPage(pageReqVO); } else if (applyType.equals(YavConstants.THREE_STR)) { //门窗工厂 FactoryMaterialsPageReqVO pageReqVO = new FactoryMaterialsPageReqVO(); pageReqVO.setPageSize(pageParam.getPageSize()); pageReqVO.setPageNo(pageParam.getPageNo()); pageReqVO.setRWareStatus(YavConstants.TWO_STR); return factoryMaterialsMapper.selectPage(pageReqVO); } } else if (businessTypeValue.equals(YavConstants.TWO_STR)) { //出库-销售出库(字典出库明细应用类型 xs_ck_apply_type) if (applyType.equals(YavConstants.ONE_STR)) { //型材销售订单 ProfileSalesOrderPageReqVO pageReqVO = new ProfileSalesOrderPageReqVO(); pageReqVO.setPageSize(pageParam.getPageSize()); pageReqVO.setPageNo(pageParam.getPageNo()); pageReqVO.setPsShipper(YavConstants.ZERO); return profileSalesOrderMapper.selectPage(pageReqVO); } else if (applyType.equals(YavConstants.TWO_STR)) { //家装销售订单 KreSalesOrderPageReqVO pageReqVO = new KreSalesOrderPageReqVO(); pageReqVO.setPageSize(pageParam.getPageSize()); pageReqVO.setPageNo(pageParam.getPageNo()); pageReqVO.setSoShipper(YavConstants.ZERO); return kreSalesOrderMapper.selectPage(pageReqVO); } else if (applyType.equals(YavConstants.THREE_STR)) { //工程销售订单 ProjectSalesOrderPageReqVO pageReqVO = new ProjectSalesOrderPageReqVO(); pageReqVO.setPageSize(pageParam.getPageSize()); pageReqVO.setPageNo(pageParam.getPageNo()); pageReqVO.setEsShipper(YavConstants.ZERO); return projectSalesOrderMapper.selectPage(pageReqVO); } } } else { if (businessTypeValue.equals(YavConstants.ONE_STR)) { //入库-采购入库(收料通知单表) ReceiptPageReqVO pageReqVO = new ReceiptPageReqVO(); pageReqVO.setPageNo(pageParam.getPageNo()); pageReqVO.setPageSize(pageParam.getPageSize()); pageReqVO.setRWareStatus(YavConstants.TWO_STR); return receiptMapper.selectPage(pageReqVO); } else if (businessTypeValue.equals(YavConstants.TWO_STR)) { //入库-生产入库(来源生产订单,目前还没有) return null; } } return null; } @Override public void updateProcessInfo(Long id, String processInstanceId, String status) { validateIoManagementMainExists(id); IoManagementMainDO updateObj = new IoManagementMainDO(); updateObj.setId(id); updateObj.setProcessInstanceId(processInstanceId); updateObj.setStatus(status); ioManagementMainMapper.updateById(updateObj); } @Override public void updateProcessStatus(Long id, String status) { validateIoManagementMainExists(id); IoManagementMainDO updateObj = new IoManagementMainDO(); updateObj.setId(id); updateObj.setStatus(status); ioManagementMainMapper.updateById(updateObj); } } package cn.iocoder.yudao.module.yavwarehouse.dal.mysql.iomanagementmain; import java.util.*; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.module.yavwarehouse.dal.dataobject.iomanagementmain.IoManagementMainDO; import org.apache.ibatis.annotations.Mapper; import cn.iocoder.yudao.module.yavwarehouse.controller.admin.iomanagementmain.vo.*; /** * 出入库主 Mapper * * @author YAVII */ @Mapper public interface IoManagementMainMapper extends BaseMapperX<IoManagementMainDO> { default PageResult<IoManagementMainDO> selectPage(IoManagementMainPageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX<IoManagementMainDO>() .eqIfPresent(IoManagementMainDO::getIoBillNo, reqVO.getIoBillNo()) .eqIfPresent(IoManagementMainDO::getIoBillType, reqVO.getIoBillType()) .eqIfPresent(IoManagementMainDO::getIoBusinessType, reqVO.getIoBusinessType()) .betweenIfPresent(IoManagementMainDO::getIoBillDate, reqVO.getIoBillDate()) .eqIfPresent(IoManagementMainDO::getIoWarehouseId, reqVO.getIoWarehouseId()) .eqIfPresent(IoManagementMainDO::getIoWarehouse, reqVO.getIoWarehouse()) .eqIfPresent(IoManagementMainDO::getIoWarehouseCode, reqVO.getIoWarehouseCode()) .eqIfPresent(IoManagementMainDO::getIoTotalCost, reqVO.getIoTotalCost()) .eqIfPresent(IoManagementMainDO::getIoReferenceTotalCost, reqVO.getIoReferenceTotalCost()) .eqIfPresent(IoManagementMainDO::getIoRemark, reqVO.getIoRemark()) .eqIfPresent(IoManagementMainDO::getReserve1, reqVO.getReserve1()) .eqIfPresent(IoManagementMainDO::getReserve2, reqVO.getReserve2()) .eqIfPresent(IoManagementMainDO::getReserve3, reqVO.getReserve3()) .eqIfPresent(IoManagementMainDO::getReserve4, reqVO.getReserve4()) .eqIfPresent(IoManagementMainDO::getReserve5, reqVO.getReserve5()) .eqIfPresent(IoManagementMainDO::getState, reqVO.getState()) .eqIfPresent(IoManagementMainDO::getIoRelateNum, reqVO.getIoRelateNum()) .eqIfPresent(IoManagementMainDO::getIoOwnership, reqVO.getIoOwnership()) .eqIfPresent(IoManagementMainDO::getIoPurpose, reqVO.getIoPurpose()) .eqIfPresent(IoManagementMainDO::getStatus, reqVO.getStatus()) .eqIfPresent(IoManagementMainDO::getAuditState, reqVO.getAuditState()) .betweenIfPresent(IoManagementMainDO::getCreateTime, reqVO.getCreateTime()) .orderByDesc(IoManagementMainDO::getId)); } }分页查询不显示
10-01
实验环境:centos7虚拟机环境 我有4个html文件,文件中可能包含图片等内容,前端文件如下 . ├── About.html ├── Contact.html ├── css │ ├── reset.css │ ├── style.css │ └── style_mobile.css ├── data │ └── contact.json ├── images │ ├── About_baby.png │ ├── About_ball.png │ ├── About_be.png │ ├── About_bird.png │ ├── About_content.png │ ├── About_message.png │ ├── About_@.png │ ├── body.png │ ├── footer_baby.png │ ├── footer_ball.png │ ├── footer_be.png │ ├── footer_bird.png │ ├── footer_message.png │ ├── footer_@.png │ ├── footer.png │ ├── header.png │ └── Thumbs.db ├── img │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.jpg │ ├── 6.jpg │ └── Thumbs.db ├── Index.html ├── js │ ├── action.js │ └── jquery-1.9.1.min.js ├── test.html ├── webserv_linux ├── webserv_linux.c └── Work.html 请使用进程的方式实现一个简单的web server(端口80),使得我能够在浏览器实现对各个html网页的访问。 浏览器兼容要求:Firefox,chrome、ie8+ Web server需完成get post等基础请求处理(必修) 使用makefile构建(必修) 我想利用进程实现server客户端,代码如下,帮我检查修改main函数 #define _XOPEN_SOURCE // 消除sigaction相关报错 #include <stdio.h> // 标准输入输出 #include <stdlib.h> // 标准库函数 #include <string.h> // 字符处理 #include <unistd.h> // UNIX标准函数 #include <sys/socket.h> // 套接字接口 #include <netinet/in.h> // 网络地址结构 #include <arpa/inet.h> // 地址转换函数 #include <sys/epoll.h> // epoll I/O路复用 #include <fcntl.h> // 文件控制 #include <sys/stat.h> // 文件状态 #include <sys/sendfile.h> // 高效文件传输 #include <sys/types.h> // 系统类型 #include <dirent.h> // 目录操作 #include <errno.h> // 错误码 #include <sys/wait.h> // 关闭子进程 #include <signal.h> // 信号处理 #define PORT 80 // 监听端口 #define MAX_EVENTS 1024 // epoll最大事件数 #define BUFFER_SIZE 4096 // 请求缓冲区大小 #define ROOT_DIR “./web/” // Web根目录 typedef struct { int fd; char buffer[BUFFER_SIZE]; int buffer_len; int status; } client_data; /* 信号处理函数,用于销毁子进程 */ void read_childproc(int sig) { pid_t pid; int status; pid = waitpid(-1,&status,WNOHANG); if(WIFEIXITED(&status)) { printf(“normal removed proc id: %d\n”,pid); } else { printf(“abnormal removed proc id: %d\n”,pid); } } /** 根据文件扩展名获取MIME类型 @param path 文件路径 @return 对应的MIME类型字符 */ const char *get_mime_type(const char *path) { const char *ext = strrchr(path, ‘.’); // 查找最后一个点号 if (!ext) return “text/plain”; // 无扩展名默认纯文本 // 常见文件类型匹配 if (strcmp(ext, “.html”) == 0) return “text/html”; if (strcmp(ext, “.htm”) == 0) return “text/html”; if (strcmp(ext, “.css”) == 0) return “text/css”; if (strcmp(ext, “.js”) == 0) return “application/javascript”; if (strcmp(ext, “.jpg”) == 0) return “image/jpeg”; if (strcmp(ext, “.jpeg”) == 0) return “image/jpeg”; if (strcmp(ext, “.png”) == 0) return “image/png”; if (strcmp(ext, “.gif”) == 0) return “image/gif”; if (strcmp(ext, “.json”) == 0) return “application/json”; if (strcmp(ext, “.ico”) == 0) return “image/x-icon”; return “text/plain”; // 未知扩展名默认纯文本 } /** 发送HTTP响应 @param fd 客户端套接字 @param status HTTP状态码 @param status_msg 状态描述 @param content_type 内容类型 @param body 响应体内容 @param body_len 响应体长度 */ void send_response(int fd, int status, const char *status_msg, const char *content_type, const char *body, int body_len) { char header[BUFFER_SIZE]; // 构建HTTP响应头 int header_len = snprintf(header, BUFFER_SIZE, “HTTP/1.1 %d %s\r\n” // 状态行 “Content-Type: %s\r\n” // 内容类型 “Content-Length: %d\r\n” // 内容长度 “Connection: close\r\n” // 关闭连接 “\r\n”, // 空行结束头部 status, status_msg, content_type, body_len); // 发送响应头 write(fd, header, header_len); // 发送响应体(如果有) if (body && body_len > 0) { write(fd, body, body_len); } } /** 发送文件内容 @param fd 客户端套接字 @param path 文件路径 */ void send_file(int fd, const char *path) { struct stat file_stat; // 获取文件状态信息 if (stat(path, &file_stat) < 0 || !S_ISREG(file_stat.st_mode)) { // 文件不存在或不是普通文件 send_response(fd, 404, “Not Found”, “text/html”, “ 404 Not Found”, 22); return; } // 打开文件 int file_fd = open(path, O_RDONLY); if (file_fd < 0) { // 文件打开失败 send_response(fd, 500, “Internal Server Error”, “text/html”, “ 500 Internal Error”, 26); return; } // 获取MIME类型 const char *mime_type = get_mime_type(path); fprintf(stdout, “mime_type:\n%s\n”, mime_type); char header[BUFFER_SIZE]; // 构建成功响应头 int header_len = snprintf(header, BUFFER_SIZE, “HTTP/1.1 200 OK\r\n” “Content-Type: %s\r\n” “Content-Length: %ld\r\n” “Connection: close\r\n” “\r\n”, mime_type, file_stat.st_size); // 发送响应头 write(fd, header, header_len); // 使用sendfile零拷贝发送文件内容 sendfile(fd, file_fd, NULL, file_stat.st_size); // 关闭文件 close(file_fd); } /** 处理GET请求 @param fd 客户端套接字 @param path 请求路径 */ void handle_get_request(int fd, const char *path) { char full_path[BUFFER_SIZE]; // 构建完整文件路径 snprintf(full_path, BUFFER_SIZE, “%s%s”, ROOT_DIR, path); // 路径安全检测(防止路径遍历攻击) if (strstr(path, “…/”) || strstr(path, “…\”)) { send_response(fd, 403, “Forbidden”, “text/html”, “ 403 Forbidden”, 22); return; } // 处理目录请求(以’/'结尾) if (path[strlen(path) - 1] == ‘/’) { DIR *dir = opendir(full_path); if (!dir) { // 目录打开失败 send_response(fd, 404, “Not Found”, “text/html”, “ 404 Not Found”, 22); return; } // 生成目录列表HTML char body[BUFFER_SIZE * 2] = "<html><head><title>Index of "; strcat(body, path); strcat(body, "</title></head><body><h1>Index of "); strcat(body, path); strcat(body, "</h1><hr><ul>"); struct dirent *entry; // 遍历目录项 while ((entry = readdir(dir)) != NULL) { // 跳过.和.. if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; char li[256]; // 构建列表项 snprintf(li, sizeof(li), "<li><a href=\"%s%s\">%s</a></li>", path, entry->d_name, entry->d_name); strcat(body, li); } strcat(body, "</ul><hr></body></html>"); // 发送目录列表 send_response(fd, 200, "OK", "text/html", body, strlen(body)); closedir(dir); return; } // 发送文件内容 send_file(fd, full_path); } /** 处理POST请求 @param fd 客户端套接字 @param path 请求路径 @param body 请求体内容 @param body_len 请求体长度 */ void handle_post_request(int fd, const char *path, const char *body, int body_len) { // 示例:处理联系表单提交 if (strcmp(path, “/Contact.html”) == 0) { // 在实际应用中应验证和存储数据 printf(“Received form data: %.*s\n”, body_len, body); // 返回成功响应 send_response(fd, 200, “OK”, “text/html”, “ Thank You! Form submitted successfully ”, 60); } else { // 不支持其他POST路径 send_response(fd, 404, “Not Found”, “text/html”, “ 404 Not Found”, 22); } } /** 处理HTTP请求 @param fd 客户端套接字 @param request 请求数据 */ void handle_request(int fd, const char *request) { char method[16], path[256], protocol[16]; // 解析请求行 sscanf(request, “%s %s %s”, method, path, protocol); // fprintf(stdout,“%s\n%s\n %s\n”,method, path, protocol); // 处理根路径请求 if (strcmp(path, “/”) == 0) { strcpy(path, “/Index.html”); // 默认返回主页 } // 查找请求体起始位置 const char *body = strstr(request, “\r\n\r\n”); if (body) body += 4; // 跳过空行 // 方法分发 if (strcmp(method, “GET”) == 0) { handle_get_request(fd, path); } else if (strcmp(method, “POST”) == 0) { handle_post_request(fd, path, body, (body ? strlen(body) : 0)); } else { // 不支持的方法 send_response(fd, 501, “Not Implemented”, “text/html”, “ 501 Not Implemented”, 25); } } /** 主函数:设置服务器并运行事件循环 */ int main() { int server_fd; struct sockaddr_in addr; socklen_t addrlen = sizeof(addr); pid_t pid; struct sigaction act; act.sa_handler = read_childproc; sigemptyset(&act.sa_mask); act.sa_flags = 0; int state = sigaction(SIGCHLD,&act,0); // if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { } memset(&server_fd,0,addrlen); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(PORT); // 设置套接字选项(允许地址重用) int opt = 1; setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); // 绑定套接字 if ((bind(server_fd, (struct sockaddr *)&addr, addrlen)) < 0) { } // 4. 开始监听 if((listen(server_fd,SOMAXCONN)) < 0) { } printf(“Web server running on port %d…\n”, PORT); printf(“Root directory: %s\n”, ROOT_DIR); // 7. 分配客户端数据存储空间 client_data *client = calloc(1, sizeof(client_data)); // while (1) { int client_fd = accept(server_fd,(struct sockaddr *) &addr,addrlen); if(client_fd < 0) { perror(“accept”); continue; } else { printf(“New connection: %s:%d\n”, inet_ntoa(addr.sin_addr),ntohs(addr.sin_port)); } pid = fork(); if(pid < 0) { close(client_fd); continue; } if(0 == pid) // child process { // 读取客户端数据 int bytes_read = read(client_fd, client->buffer + client->buffer_len, BUFFER_SIZE - client->buffer_len); if(bytes_read <=0 ) { close(client_fd); continue; } // 更新缓冲区 client->buffer_len += bytes_read; client->buffer[client->buffer_len] = '\0'; if(strstr(client->buffer, "\r\n\r\n") != NULL) { // 处理请求 handle_request(client_fd,client->buffer); // 重置缓冲区 client->buffer_len = 0; memset(client->buffer, 0, BUFFER_SIZE); } } else // father process { close(client_fd); } } close(server_fd); return 0; }
08-10
Creating isolated environment: venv+pip... * Installing packages in isolated environment: - setuptools >= 40.8.0 * Getting build dependencies for wheel... [WARNING] Unable to import torch, pre-compiling ops will be disabled. Please visit https://pytorch.org/ to see how to properly install torch on your system. [WARNING] unable to import torch, please install it if you want to pre-compile any deepspeed ops. DS_BUILD_OPS=1 Traceback (most recent call last): File "C:\SoftWare\anadonda\envs\unlearning\Lib\site-packages\pyproject_hooks\_in_process\_in_process.py", line 389, in <module> main() File "C:\SoftWare\anadonda\envs\unlearning\Lib\site-packages\pyproject_hooks\_in_process\_in_process.py", line 373, in main json_out["return_val"] = hook(**hook_input["kwargs"]) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\SoftWare\anadonda\envs\unlearning\Lib\site-packages\pyproject_hooks\_in_process\_in_process.py", line 143, in get_requires_for_build_wheel return hook(config_settings) ^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\emma_\AppData\Local\Temp\build-env-w83w2bui\Lib\site-packages\setuptools\build_meta.py", line 331, in get_requires_for_build_wheel return self._get_build_requires(config_settings, requirements=[]) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\emma_\AppData\Local\Temp\build-env-w83w2bui\Lib\site-packages\setuptools\build_meta.py", line 301, in _get_build_requires self.run_setup() File "C:\Users\emma_\AppData\Local\Temp\build-env-w83w2bui\Lib\site-packages\setuptools\build_meta.py", line 512, in run_setup super().run_setup(setup_script=setup_script) File "C:\Users\emma_\AppData\Local\Temp\build-env-w83w2bui\Lib\site-packages\setuptools\build_meta.py", line 317, in run_setup exec(code, locals()) File "<string>", line 157, in <module> AssertionError: Unable to pre-compile ops without torch installed. Please install torch before attempting to pre-compile ops. ERROR Backend subprocess exited when trying to invoke get_requires_for_build_wheel在执行python -m build --wheel时出现上述报错
最新发布
10-28
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值