Commit 8539b86e9c6e047766bba290d379f4e8560be110

Authored by 王彪总
2 parents f41986d9 0b398542

Merge remote-tracking branch 'origin/dev'

urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/costfee/CostFeeController.java
@@ -5,16 +5,24 @@ import com.zteits.urbanops.framework.excel.core.util.ExcelUtils; @@ -5,16 +5,24 @@ import com.zteits.urbanops.framework.excel.core.util.ExcelUtils;
5 import com.zteits.urbanops.module.garden.controller.admin.costfee.vo.WaterFeeImport; 5 import com.zteits.urbanops.module.garden.controller.admin.costfee.vo.WaterFeeImport;
6 import com.zteits.urbanops.module.garden.enums.TemplateEnum; 6 import com.zteits.urbanops.module.garden.enums.TemplateEnum;
7 import com.zteits.urbanops.module.garden.service.costfee.CostFeeService; 7 import com.zteits.urbanops.module.garden.service.costfee.CostFeeService;
  8 +import com.zteits.urbanops.module.system.api.dept.DeptApi;
  9 +import com.zteits.urbanops.module.system.api.dept.dto.DeptRespDTO;
8 import io.swagger.v3.oas.annotations.Operation; 10 import io.swagger.v3.oas.annotations.Operation;
9 import io.swagger.v3.oas.annotations.tags.Tag; 11 import io.swagger.v3.oas.annotations.tags.Tag;
  12 +import jakarta.annotation.security.PermitAll;
10 import jakarta.servlet.http.HttpServletResponse; 13 import jakarta.servlet.http.HttpServletResponse;
11 import lombok.AllArgsConstructor; 14 import lombok.AllArgsConstructor;
12 import lombok.Data; 15 import lombok.Data;
13 import lombok.extern.slf4j.Slf4j; 16 import lombok.extern.slf4j.Slf4j;
  17 +import org.apache.poi.ss.usermodel.*;
  18 +import org.apache.poi.ss.util.CellRangeAddressList;
  19 +import org.apache.poi.xssf.usermodel.XSSFWorkbook;
14 import org.springframework.beans.factory.annotation.Autowired; 20 import org.springframework.beans.factory.annotation.Autowired;
15 import org.springframework.beans.factory.annotation.Value; 21 import org.springframework.beans.factory.annotation.Value;
16 -import org.springframework.core.io.ClassPathResource; 22 +import org.springframework.core.io.Resource;
  23 +import org.springframework.core.io.ResourceLoader;
17 import org.springframework.util.CollectionUtils; 24 import org.springframework.util.CollectionUtils;
  25 +import org.springframework.util.StringUtils;
18 import org.springframework.web.bind.annotation.*; 26 import org.springframework.web.bind.annotation.*;
19 import org.springframework.web.multipart.MultipartFile; 27 import org.springframework.web.multipart.MultipartFile;
20 28
@@ -23,7 +31,6 @@ import java.io.InputStream; @@ -23,7 +31,6 @@ import java.io.InputStream;
23 import java.io.OutputStream; 31 import java.io.OutputStream;
24 import java.lang.reflect.Method; 32 import java.lang.reflect.Method;
25 import java.net.URLEncoder; 33 import java.net.URLEncoder;
26 -import java.nio.charset.StandardCharsets;  
27 import java.util.*; 34 import java.util.*;
28 35
29 import static com.zteits.urbanops.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; 36 import static com.zteits.urbanops.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
@@ -49,6 +56,12 @@ public class CostFeeController{ @@ -49,6 +56,12 @@ public class CostFeeController{
49 @Value("${static.resource.server.enable}") 56 @Value("${static.resource.server.enable}")
50 private Boolean staticResourceServerenable; 57 private Boolean staticResourceServerenable;
51 58
  59 + @Autowired
  60 + private ResourceLoader resourceLoader;
  61 +
  62 + @jakarta.annotation.Resource
  63 + private DeptApi deptApi;
  64 +
52 // Excel文件校验相关 65 // Excel文件校验相关
53 private static final List<String> ALLOWED_EXCEL_SUFFIX = Arrays.asList(".xlsx", ".xlsm"); 66 private static final List<String> ALLOWED_EXCEL_SUFFIX = Arrays.asList(".xlsx", ".xlsm");
54 private static final String EXCEL_MIME_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; 67 private static final String EXCEL_MIME_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
@@ -59,6 +72,7 @@ public class CostFeeController{ @@ -59,6 +72,7 @@ public class CostFeeController{
59 */ 72 */
60 @GetMapping("/get-import-template") 73 @GetMapping("/get-import-template")
61 @Operation(summary = "获得导入用户模板") 74 @Operation(summary = "获得导入用户模板")
  75 + @PermitAll
62 public void importTemplate(HttpServletResponse response, 76 public void importTemplate(HttpServletResponse response,
63 @RequestParam("templateName") String templateName) throws IOException { 77 @RequestParam("templateName") String templateName) throws IOException {
64 // 参数校验 78 // 参数校验
@@ -74,29 +88,145 @@ public class CostFeeController{ @@ -74,29 +88,145 @@ public class CostFeeController{
74 return; 88 return;
75 } 89 }
76 90
77 - if (staticResourceServerenable) {  
78 - // 构建完整的静态服务器URL(确保路径正确)  
79 - String serverUrl = staticResourceServerUrl;  
80 - if (serverUrl != null && !serverUrl.endsWith("/")) {  
81 - serverUrl += "/"; // 确保URL以/结尾,避免拼接错误  
82 - } 91 + writeAndModifyTemplate(response, template.getName(), template.getStartIndex());
  92 + }
  93 +
  94 + private void writeAndModifyTemplate(HttpServletResponse response,
  95 + String downloadFileName,
  96 + int startIndexRow) throws IOException {
  97 +
  98 + // 拼接模板路径
  99 + String serverUrl = staticResourceServerUrl;
  100 + if (serverUrl != null && !serverUrl.endsWith("/")) {
  101 + serverUrl += "/";
  102 + }
  103 + String fullPath = serverUrl + downloadFileName;
  104 + fullPath = fullPath.replace("\\", "/");
83 105
84 - String encodedFileName = URLEncoder.encode(template.getName().split("\\.")[0], StandardCharsets.UTF_8.name()); 106 + Resource resource = resourceLoader.getResource(fullPath);
85 107
86 - String templateFileName = encodedFileName + "." + template.getName().split("\\.")[1]; 108 + // 检查文件是否存在
  109 + if (!resource.exists()) {
  110 + response.sendError(HttpServletResponse.SC_NOT_FOUND, "模板文件不存在");
  111 + return;
  112 + }
87 113
88 - String templateUrl = serverUrl + templateFileName; // 包含templates目录 114 + // 设置下载响应头
  115 + response.reset();
  116 + response.setContentType(EXCEL_MIME_TYPE);
  117 + // response.setCharacterEncoding("UTF-8");
  118 + String encodedName = URLEncoder.encode(downloadFileName, "UTF-8").replaceAll("\\+", "%20");
  119 + response.setHeader("Content-Disposition", "attachment;filename=" + encodedName);
  120 + response.setHeader("Cache-Control", "no-store"); // 禁止缓存
  121 +
  122 + try (InputStream in = resource.getInputStream();
  123 + XSSFWorkbook workbook = new XSSFWorkbook(in);
  124 + OutputStream out = response.getOutputStream()) {
89 125
90 - // 方式1:重定向到静态资源服务器  
91 - response.sendRedirect(templateUrl);  
92 - } else {  
93 - String templateFileName = templateName + "." + template.getName().split("\\.")[1]; 126 + Sheet sheet = workbook.getSheetAt(0);
94 127
95 - // 写入模板文件到响应  
96 - writeTemplateToResponse(response, template.getName(), templateFileName); 128 + // ==============================================
  129 + // 获取最新单位列表
  130 + // ==============================================
  131 + List<DeptRespDTO> companyList = deptApi.getSubDeptList();
  132 + List<String> newUnits = companyList.stream()
  133 + .map(DeptRespDTO::getName)
  134 + .filter(Objects::nonNull)
  135 + .toList();
  136 +
  137 + log.info("加载到单位列表:{},共{}条", newUnits, newUnits.size());
  138 +
  139 + // ==============================================
  140 + // 生成新下拉 + 自动赋值
  141 + // ==============================================
  142 + createDropdownAndSetValue(sheet, newUnits, startIndexRow);
  143 +
  144 + // 写入并强制刷新
  145 + workbook.write(out);
  146 + out.flush();
  147 +
  148 + } catch (Exception e) {
  149 + log.error("模板处理失败", e);
  150 + throw new IOException("文件处理失败", e);
97 } 151 }
98 } 152 }
99 153
  154 + /**
  155 + * 创建下拉框 + 给单元格赋值
  156 + */
  157 + private void createDropdownAndSetValue(Sheet sheet, List<String> options, int startRow) {
  158 + if (options.isEmpty()) {
  159 + log.error("下拉选项为空,不创建");
  160 + return;
  161 + }
  162 +
  163 + // 获取表头行
  164 + Row headerRow = sheet.getRow(startRow - 1);
  165 + if (headerRow == null) {
  166 + log.error("表头行不存在:{}", startRow - 1);
  167 + return;
  168 + }
  169 +
  170 + // 查找“所属单位”列
  171 + int targetCol = -1;
  172 + for (Cell cell : headerRow) {
  173 + if (cell != null && cell.getCellType() == CellType.STRING) {
  174 + String val = cell.getStringCellValue().trim();
  175 + if (val.contains("所属单位") || val.contains("所属公司") ) {
  176 + targetCol = cell.getColumnIndex();
  177 + break;
  178 + }
  179 + }
  180 + }
  181 +
  182 + if (targetCol == -1) {
  183 + log.error("未找到【所属单位】列表头");
  184 + return;
  185 + }
  186 +
  187 + // 取消列隐藏
  188 + sheet.setColumnHidden(targetCol, false);
  189 +
  190 + // ==============================================
  191 + // 遍历行:清空原有内容 + 设置默认值
  192 + // ==============================================
  193 + int endRow = 100;
  194 + for (int r = startRow; r <= endRow; r++) {
  195 + Row row = sheet.getRow(r);
  196 + if (row == null) {
  197 + row = sheet.createRow(r);
  198 + }
  199 +
  200 + Cell cell = row.getCell(targetCol);
  201 + if (cell == null) {
  202 + cell = row.createCell(targetCol);
  203 + }
  204 + String unit = cell.getStringCellValue();
  205 + // 默认赋值【第一个单位】,你可以根据业务改成其他值
  206 + if (!StringUtils.isEmpty(unit)) {
  207 + cell.setCellValue(options.get(2));
  208 + }
  209 + }
  210 +
  211 + // ==============================================
  212 + // 创建下拉框(作用范围:startRow ~ 1000行)
  213 + // ==============================================
  214 + CellRangeAddressList regions = new CellRangeAddressList(startRow, endRow, targetCol, targetCol);
  215 + DataValidationHelper helper = sheet.getDataValidationHelper();
  216 + DataValidationConstraint constraint = helper.createExplicitListConstraint(
  217 + options.toArray(new String[0])
  218 + );
  219 + DataValidation validation = helper.createValidation(constraint, regions);
  220 + // 下拉框基础配置
  221 + validation.setEmptyCellAllowed(true);
  222 + validation.setSuppressDropDownArrow(true); // 显示箭头
  223 + validation.setShowErrorBox(true);
  224 + validation.setShowPromptBox(true); // 启用点击弹出提示
  225 + sheet.addValidationData(validation);
  226 +
  227 + log.info("【成功】为列{}添加下拉并赋值,行{}~{},选项数:{}",
  228 + targetCol, startRow, endRow, options.size());
  229 + }
100 230
101 /** 231 /**
102 * Excel文件上传预览 232 * Excel文件上传预览
@@ -214,31 +344,6 @@ public class CostFeeController{ @@ -214,31 +344,6 @@ public class CostFeeController{
214 } 344 }
215 345
216 /** 346 /**
217 - * 写入模板文件到响应流  
218 - */  
219 - private void writeTemplateToResponse(HttpServletResponse response, String filename, String templatePath) throws IOException {  
220 - // 设置响应头  
221 - response.setContentType(EXCEL_MIME_TYPE);  
222 - response.setCharacterEncoding("UTF-8");  
223 - String encodedFileName = URLEncoder.encode(filename, "UTF-8").replaceAll("\\+", "%20");  
224 - response.setHeader("Content-Disposition", "attachment;filename=" + encodedFileName);  
225 - response.setHeader("Cache-Control", "no-store"); // 禁止缓存  
226 -  
227 - // 读取模板文件并写入响应  
228 - try (InputStream in = new ClassPathResource("template/" + templatePath).getInputStream();  
229 - OutputStream out = response.getOutputStream()) {  
230 -  
231 - byte[] buffer = new byte[4096];  
232 - int bytesRead;  
233 - while ((bytesRead = in.read(buffer)) != -1) {  
234 - out.write(buffer, 0, bytesRead);  
235 - }  
236 - out.flush();  
237 - }  
238 - }  
239 -  
240 -  
241 - /**  
242 * 校验Excel文件有效性(后缀+MIME类型) 347 * 校验Excel文件有效性(后缀+MIME类型)
243 */ 348 */
244 private boolean isValidExcelFile(MultipartFile file) { 349 private boolean isValidExcelFile(MultipartFile file) {
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/convert/AppGardenWorkOrderConvert.java
@@ -24,7 +24,7 @@ public interface AppGardenWorkOrderConvert { @@ -24,7 +24,7 @@ public interface AppGardenWorkOrderConvert {
24 orderReqVO.setPressingType(2);//紧急 24 orderReqVO.setPressingType(2);//紧急
25 orderReqVO.setOrderName(inspectionPlanDO.getPlanName()); 25 orderReqVO.setOrderName(inspectionPlanDO.getPlanName());
26 orderReqVO.setSourceId(1);//来源ID 工单来源 1、巡查 26 orderReqVO.setSourceId(1);//来源ID 工单来源 1、巡查
27 - orderReqVO.setSourceName("园林巡查上报"); 27 + orderReqVO.setSourceName("巡查上报");
28 orderReqVO.setThirdWorkNo(planNo); 28 orderReqVO.setThirdWorkNo(planNo);
29 orderReqVO.setOrderType("C"); 29 orderReqVO.setOrderType("C");
30 orderReqVO.setPressingType(createReqVO.getPressingType()); 30 orderReqVO.setPressingType(createReqVO.getPressingType());
urbanops-module-workorder/src/main/java/com/zteits/urbanops/module/workorder/enums/BusiLineTeamLeaderRoleCodeEnum.java
@@ -25,6 +25,10 @@ public enum BusiLineTeamLeaderRoleCodeEnum { // 注意:枚举类必须用enum @@ -25,6 +25,10 @@ public enum BusiLineTeamLeaderRoleCodeEnum { // 注意:枚举类必须用enum
25 ROLECODE_WY("wy", "team_leader_wy"), 25 ROLECODE_WY("wy", "team_leader_wy"),
26 /** 市政业务线-角色编码 */ 26 /** 市政业务线-角色编码 */
27 ROLECODE_SZ("sz", "team_leader_sz"), 27 ROLECODE_SZ("sz", "team_leader_sz"),
  28 + /** 秩序管理业务线-角色编码 */
  29 + ROLECODE_ZX("zx", "team_leader_zxgl"),
  30 + /** 环境卫生业务线-角色编码 */
  31 + ROLECODE_HJ("hj", "team_leader_hjws"),
28 /** 园林业务线-角色编码 */ 32 /** 园林业务线-角色编码 */
29 ROLECODE_YL("yl", "team_leader_yl"); // 最后一个常量必须以分号结尾 33 ROLECODE_YL("yl", "team_leader_yl"); // 最后一个常量必须以分号结尾
30 34
urbanops-module-workorder/src/main/java/com/zteits/urbanops/module/workorder/enums/BusiLineWorkerRoleCodeEnum.java
@@ -25,6 +25,10 @@ public enum BusiLineWorkerRoleCodeEnum { @@ -25,6 +25,10 @@ public enum BusiLineWorkerRoleCodeEnum {
25 ROLECODE_WORKER_WY("wy", "wy_worker"), 25 ROLECODE_WORKER_WY("wy", "wy_worker"),
26 /** 市政业务线-角色编码 */ 26 /** 市政业务线-角色编码 */
27 ROLECODE_WORKER_SZ("sz", "sz_worker"), 27 ROLECODE_WORKER_SZ("sz", "sz_worker"),
  28 + /** 环境卫生业务线-角色编码 */
  29 + ROLECODE_HJ("hj", "hjws_worker"),
  30 + /** 秩序管理业务线-角色编码 */
  31 + ROLECODE_ZX("zx", "zxgl_worker"),
28 /** 园林业务线-角色编码 */ 32 /** 园林业务线-角色编码 */
29 ROLECODE_WORKER_YL("yl", "yl_worker"); // 最后一个常量必须以分号结尾 33 ROLECODE_WORKER_YL("yl", "yl_worker"); // 最后一个常量必须以分号结尾
30 34