Commit 8de36361fec0d8ee181423a72b7faad09b1c8ad9

Authored by 房远帅
1 parent bdda9a27

楚江ERP:要车计划-打印

1 package com.lframework.xingyun.sc.controller.shipments.car; 1 package com.lframework.xingyun.sc.controller.shipments.car;
2 2
  3 +import cn.hutool.core.io.resource.ClassPathResource;
3 import com.lframework.starter.web.core.annotations.security.HasPermission; 4 import com.lframework.starter.web.core.annotations.security.HasPermission;
4 import com.lframework.starter.web.core.controller.DefaultBaseController; 5 import com.lframework.starter.web.core.controller.DefaultBaseController;
5 import com.lframework.starter.web.core.utils.PageResultUtil; 6 import com.lframework.starter.web.core.utils.PageResultUtil;
6 import com.lframework.starter.web.core.components.resp.PageResult; 7 import com.lframework.starter.web.core.components.resp.PageResult;
7 import com.lframework.starter.web.core.components.resp.InvokeResult; 8 import com.lframework.starter.web.core.components.resp.InvokeResult;
  9 +import javax.servlet.http.HttpServletResponse;
8 import javax.validation.constraints.NotBlank; 10 import javax.validation.constraints.NotBlank;
9 import com.lframework.xingyun.sc.bo.shipments.car.GetCarRequestPlanBo; 11 import com.lframework.xingyun.sc.bo.shipments.car.GetCarRequestPlanBo;
10 import com.lframework.xingyun.sc.entity.CarRequestPlan; 12 import com.lframework.xingyun.sc.entity.CarRequestPlan;
@@ -21,13 +23,28 @@ import com.lframework.starter.common.exceptions.impl.DefaultClientException; @@ -21,13 +23,28 @@ import com.lframework.starter.common.exceptions.impl.DefaultClientException;
21 import io.swagger.annotations.ApiOperation; 23 import io.swagger.annotations.ApiOperation;
22 import com.lframework.starter.common.utils.CollectionUtil; 24 import com.lframework.starter.common.utils.CollectionUtil;
23 import io.swagger.annotations.Api; 25 import io.swagger.annotations.Api;
  26 +import lombok.extern.slf4j.Slf4j;
  27 +import org.apache.commons.collections4.CollectionUtils;
  28 +import org.apache.poi.ss.usermodel.*;
  29 +import org.apache.poi.ss.util.CellRangeAddress;
  30 +import org.apache.poi.util.IOUtils;
  31 +import org.apache.poi.xssf.usermodel.XSSFWorkbook;
24 import org.springframework.web.bind.annotation.DeleteMapping; 32 import org.springframework.web.bind.annotation.DeleteMapping;
25 import org.springframework.beans.factory.annotation.Autowired; 33 import org.springframework.beans.factory.annotation.Autowired;
26 import org.springframework.validation.annotation.Validated; 34 import org.springframework.validation.annotation.Validated;
27 import org.springframework.web.bind.annotation.*; 35 import org.springframework.web.bind.annotation.*;
28 -  
29 import javax.validation.Valid; 36 import javax.validation.Valid;
  37 +import java.io.FileNotFoundException;
  38 +import java.io.IOException;
  39 +import java.io.InputStream;
  40 +import java.math.BigDecimal;
  41 +import java.math.RoundingMode;
  42 +import java.net.URLEncoder;
  43 +import java.time.LocalDate;
  44 +import java.time.format.DateTimeFormatter;
  45 +import java.util.HashMap;
30 import java.util.List; 46 import java.util.List;
  47 +import java.util.Map;
31 import java.util.stream.Collectors; 48 import java.util.stream.Collectors;
32 49
33 /** 50 /**
@@ -35,6 +52,7 @@ import java.util.stream.Collectors; @@ -35,6 +52,7 @@ import java.util.stream.Collectors;
35 * 52 *
36 */ 53 */
37 @Api(tags = "要车计划表") 54 @Api(tags = "要车计划表")
  55 +@Slf4j
38 @Validated 56 @Validated
39 @RestController 57 @RestController
40 @RequestMapping("/carRequestPlan") 58 @RequestMapping("/carRequestPlan")
@@ -94,7 +112,7 @@ public class CarRequestPlanController extends DefaultBaseController { @@ -94,7 +112,7 @@ public class CarRequestPlanController extends DefaultBaseController {
94 @ApiOperation("新增") 112 @ApiOperation("新增")
95 @HasPermission({"carRequestPlan:carrequestplan:add"}) 113 @HasPermission({"carRequestPlan:carrequestplan:add"})
96 @PostMapping 114 @PostMapping
97 - public InvokeResult<Void> create(@Valid CreateCarRequestPlanVo vo) { 115 + public InvokeResult<Void> create(@Valid @RequestBody CreateCarRequestPlanVo vo) {
98 116
99 carRequestPlanService.create(vo); 117 carRequestPlanService.create(vo);
100 118
@@ -107,7 +125,7 @@ public class CarRequestPlanController extends DefaultBaseController { @@ -107,7 +125,7 @@ public class CarRequestPlanController extends DefaultBaseController {
107 @ApiOperation("修改") 125 @ApiOperation("修改")
108 @HasPermission({"carRequestPlan:carrequestplan:modify"}) 126 @HasPermission({"carRequestPlan:carrequestplan:modify"})
109 @PutMapping 127 @PutMapping
110 - public InvokeResult<Void> update(@Valid UpdateCarRequestPlanVo vo) { 128 + public InvokeResult<Void> update(@Valid @RequestBody UpdateCarRequestPlanVo vo) {
111 129
112 carRequestPlanService.update(vo); 130 carRequestPlanService.update(vo);
113 131
@@ -127,4 +145,201 @@ public class CarRequestPlanController extends DefaultBaseController { @@ -127,4 +145,201 @@ public class CarRequestPlanController extends DefaultBaseController {
127 145
128 return InvokeResultBuilder.success(); 146 return InvokeResultBuilder.success();
129 } 147 }
  148 +
  149 + @ApiOperation("要车计划打印")
  150 + @GetMapping("/printCarRequestPlan")
  151 + public void printCarRequestPlan(@NotBlank(message = "id不能为空") String id, HttpServletResponse response) throws IOException {
  152 + CarRequestPlan data = carRequestPlanService.findById(id);
  153 + // 设置响应头
  154 + setupResponse(response, data.getWorkshopName() + "-" + data.getRequestCarData() + "-要车计划打印.xlsx");
  155 +
  156 + QueryRequestCarTicketVo vo = new QueryRequestCarTicketVo();
  157 + vo.setPlanId(id);
  158 + List<RequestCarTicket> requestCarTicketList = requestCarTicketService.query(vo);
  159 +
  160 +
  161 + try {
  162 + // 加载模板文件
  163 + ClassPathResource templateResource = new ClassPathResource("templates/carRequestPlanTemplate.xlsx");
  164 + try (InputStream inputStream = templateResource.getStream();
  165 + Workbook workbook = new XSSFWorkbook(inputStream)) {
  166 + try {
  167 + Sheet sheet = workbook.getSheetAt(0);
  168 + DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
  169 + int startRow = 3; // 要车单开始行
  170 + BigDecimal totalQuantity=BigDecimal.ZERO;
  171 + CellStyle borderedStyle = workbook.createCellStyle();
  172 + borderedStyle.setBorderTop(BorderStyle.THIN);
  173 + borderedStyle.setBorderBottom(BorderStyle.THIN);
  174 + borderedStyle.setBorderLeft(BorderStyle.THIN);
  175 + borderedStyle.setBorderRight(BorderStyle.THIN);
  176 + if (CollectionUtils.isNotEmpty(requestCarTicketList)) {
  177 + for (int i = 0; i < requestCarTicketList.size(); i++) {
  178 + RequestCarTicket requestCarTicket = requestCarTicketList.get(i);
  179 + setCellValue(sheet, startRow, 0, i + 1, borderedStyle);
  180 + setCellValue(sheet, startRow, 1, requestCarTicket.getDeptName(), borderedStyle);
  181 + setCellValue(sheet, startRow, 2, requestCarTicket.getDeliveryDate() == null ? "" : requestCarTicket.getDeliveryDate().format(dateFormatter), borderedStyle);
  182 + setCellValue(sheet, startRow, 3, requestCarTicket.getWorkshopName(), borderedStyle);
  183 + setCellValue(sheet, startRow, 4, requestCarTicket.getOrderNo(), borderedStyle);
  184 + setCellValue(sheet, startRow, 5, requestCarTicket.getCustomerShortName(), borderedStyle);
  185 + setCellValue(sheet, startRow, 6, requestCarTicket.getDestination(), borderedStyle);
  186 + setCellValue(sheet, startRow, 7, requestCarTicket.getQuantity(), borderedStyle);
  187 + totalQuantity = totalQuantity.add(requestCarTicket.getQuantity());
  188 + setCellValue(sheet, startRow, 8, requestCarTicket.getConsignee(), borderedStyle);
  189 + setCellValue(sheet, startRow, 9, requestCarTicket.getPhone(), borderedStyle);
  190 + setCellValue(sheet, startRow, 10, requestCarTicket.getReturnPlanArrangement(), borderedStyle);
  191 + setCellValue(sheet, startRow, 11, requestCarTicket.getOther(), borderedStyle);
  192 + setCellValue(sheet, startRow, 12, requestCarTicket.getExternalAuditorName(), borderedStyle);
  193 + setCellValue(sheet, startRow, 13, requestCarTicket.getLoadingTime(), borderedStyle);
  194 + setCellValue(sheet, startRow, 14, requestCarTicket.getSpecialLoadingRequirement(), borderedStyle);
  195 + setCellValue(sheet, startRow, 15, requestCarTicket.getBusinessOfficeAuditorName(), borderedStyle);
  196 + setCellValue(sheet, startRow, 16, requestCarTicket.getOperationsDepartmentAuditorName(), borderedStyle);
  197 + startRow++;
  198 + }
  199 + // === 写合计行(带完整边框,包括合并区域)===
  200 + Row totalRow = sheet.getRow(startRow);
  201 + if (totalRow == null) totalRow = sheet.createRow(startRow);
  202 +
  203 + // 1. 先为合并区域 [0, 6] 的每个单元格设置边框样式(即使内容为空)
  204 + for (int col = 0; col <= 6; col++) {
  205 + Cell cell = totalRow.getCell(col);
  206 + if (cell == null) {
  207 + cell = totalRow.createCell(col);
  208 + }
  209 + cell.setCellStyle(borderedStyle);
  210 + if (col == 0) {
  211 + cell.setCellValue("合计:");
  212 + } else {
  213 + cell.setCellValue(""); // 空内容
  214 + }
  215 + }
  216 +
  217 + // 2. 设置数量列(第7列)
  218 + Cell totalCell = totalRow.getCell(7);
  219 + if (totalCell == null) {
  220 + totalCell = totalRow.createCell(7);
  221 + }
  222 + totalCell.setCellValue(totalQuantity.setScale(4, RoundingMode.HALF_UP).doubleValue());
  223 + totalCell.setCellStyle(borderedStyle);
  224 +
  225 + // 3. 其他列(8~16)也建议补空 + 边框,保持表格整齐(可选但推荐)
  226 + for (int col = 8; col <= 16; col++) {
  227 + Cell cell = totalRow.getCell(col);
  228 + if (cell == null) {
  229 + cell = totalRow.createCell(col);
  230 + }
  231 + cell.setCellStyle(borderedStyle);
  232 + cell.setCellValue("");
  233 + }
  234 +
  235 + // 4. 最后再执行合并(必须在所有单元格创建之后)
  236 + sheet.addMergedRegion(new CellRangeAddress(startRow, startRow, 0, 6));
  237 + }
  238 +
  239 + Map<String, Object> dataMap = new HashMap<>();
  240 + dataMap.put("requestCarData", data.getRequestCarData() == null ? "" : data.getRequestCarData().format(dateFormatter));
  241 +
  242 + processTemplate(workbook, dataMap);
  243 +
  244 + // 写入响应流
  245 + workbook.write(response.getOutputStream());
  246 + response.getOutputStream().flush();
  247 + } finally {
  248 + IOUtils.closeQuietly(workbook);
  249 + }
  250 +
  251 + } catch (FileNotFoundException e) {
  252 + throw new RuntimeException("模板文件不存在: templates/carRequestPlanTemplate.xlsx", e);
  253 + } catch (IOException e) {
  254 + throw new RuntimeException("无法读取模板文件: templates/carRequestPlanTemplate.xlsx", e);
  255 + }
  256 + } catch (Exception e) {
  257 + log.error("要车计划打印: {}", e.getMessage(), e);
  258 + throw e;
  259 + }
  260 + }
  261 +
  262 + /**
  263 + * 设置HTTP响应头
  264 + */
  265 + private void setupResponse(HttpServletResponse response, String fileName) throws IOException {
  266 + String encodedFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
  267 +
  268 + response.setContentType("application/vnd.ms-excel");
  269 + response.setCharacterEncoding("UTF-8");
  270 + response.setHeader("Content-Disposition", "attachment;filename=" + encodedFileName);
  271 + response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
  272 + }
  273 +
  274 + /**
  275 + * 设置单元格值
  276 + */
  277 + private static void setCellValue(Sheet sheet, int rowNum, int colNum, Object value, CellStyle style) {
  278 + Row row = sheet.getRow(rowNum);
  279 + if (row == null) {
  280 + row = sheet.createRow(rowNum);
  281 + }
  282 + Cell cell = row.getCell(colNum);
  283 + if (cell == null) {
  284 + cell = row.createCell(colNum);
  285 + }
  286 + cell.setCellStyle(style); // 应用边框样式
  287 +
  288 + if (value == null) {
  289 + cell.setCellValue("");
  290 + } else if (value instanceof String) {
  291 + cell.setCellValue((String) value);
  292 + } else if (value instanceof Integer) {
  293 + cell.setCellValue((Integer) value);
  294 + } else if (value instanceof Double || value instanceof BigDecimal) {
  295 + cell.setCellValue(((Number) value).doubleValue());
  296 + } else if (value instanceof LocalDate) {
  297 + cell.setCellValue(((LocalDate) value).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
  298 + } else {
  299 + cell.setCellValue(value.toString());
  300 + }
  301 + }
  302 +
  303 + private void processTemplate(Workbook workbook, Map<String, Object> dataMap) {
  304 + Sheet sheet = workbook.getSheetAt(0);
  305 +
  306 + for (Row row : sheet) {
  307 + for (Cell cell : row) {
  308 + if (cell.getCellTypeEnum() == CellType.STRING) {
  309 + String cellValue = cell.getStringCellValue();
  310 + String newValue = replacePlaceholders(cellValue, dataMap);
  311 + if (!cellValue.equals(newValue)) {
  312 + cell.setCellValue(newValue);
  313 + }
  314 + }
  315 + }
  316 + }
  317 + }
  318 +
  319 + private String replacePlaceholders(String text, Map<String, Object> dataMap) {
  320 + if (text == null || text.isEmpty()) {
  321 + return text;
  322 + }
  323 +
  324 + String result = text;
  325 + for (Map.Entry<String, Object> entry : dataMap.entrySet()) {
  326 + String placeholder = "${" + entry.getKey() + "}";
  327 + String value = entry.getValue() != null ? entry.getValue().toString() : "";
  328 + // 将数据库中的 \n 转换为 Excel 识别的换行符
  329 + value = value.replace("\\n", "\n");
  330 +
  331 + result = result.replace(placeholder, value);
  332 + }
  333 +
  334 + for (Map.Entry<String, Object> entry : dataMap.entrySet()) {
  335 + String placeholder = "#{" + entry.getKey() + "}";
  336 + String value = entry.getValue() != null ? entry.getValue().toString() : "";
  337 + // 将数据库中的 \n 转换为 Excel 识别的换行符
  338 + value = value.replace("\\n", "\n");
  339 +
  340 + result = result.replace(placeholder, value);
  341 + }
  342 +
  343 + return result;
  344 + }
130 } 345 }
@@ -28,6 +28,7 @@ import org.springframework.stereotype.Service; @@ -28,6 +28,7 @@ import org.springframework.stereotype.Service;
28 28
29 import java.text.SimpleDateFormat; 29 import java.text.SimpleDateFormat;
30 import java.time.LocalDate; 30 import java.time.LocalDate;
  31 +import java.time.format.DateTimeFormatter;
31 import java.util.Date; 32 import java.util.Date;
32 import java.util.List; 33 import java.util.List;
33 34
@@ -67,8 +68,8 @@ public class CarRequestPlanServiceImpl extends BaseMpServiceImpl<CarRequestPlanM @@ -67,8 +68,8 @@ public class CarRequestPlanServiceImpl extends BaseMpServiceImpl<CarRequestPlanM
67 vo1.setWorkshopId(vo.getWorkshopId()); 68 vo1.setWorkshopId(vo.getWorkshopId());
68 LocalDate date = vo.getRequestCarData(); 69 LocalDate date = vo.getRequestCarData();
69 if (date != null) { 70 if (date != null) {
70 - SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");  
71 - String dateString = formatter.format(date); 71 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
  72 + String dateString = date.format(formatter); // LocalDate 自带 format 方法
72 vo1.setRequestCarDataStart(dateString); 73 vo1.setRequestCarDataStart(dateString);
73 vo1.setRequestCarDataEnd(dateString); 74 vo1.setRequestCarDataEnd(dateString);
74 } 75 }
@@ -108,7 +108,7 @@ @@ -108,7 +108,7 @@
108 AND tb.status = #{vo.status} 108 AND tb.status = #{vo.status}
109 </if> 109 </if>
110 <if test="vo.planId != null and vo.planId != ''"> 110 <if test="vo.planId != null and vo.planId != ''">
111 - AND tb.plan_id = #{planId} 111 + AND tb.plan_id = #{vo.planId}
112 </if> 112 </if>
113 </where> 113 </where>
114 ORDER BY tb.update_time DESC 114 ORDER BY tb.update_time DESC