From 7cd61b935cec8cafd0bc42fa2a57f6c8a6f9139a Mon Sep 17 00:00:00 2001 From: haozq <1611483981@qq.com> Date: Tue, 10 Feb 2026 16:18:20 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=B7=A5=E8=B5=84=E6=9C=AA?= =?UTF-8?q?=E5=8F=91=E6=94=BE=E6=8E=A5=E5=8F=A3=20=E8=80=83=E5=8B=A4?= =?UTF-8?q?=E6=9C=BA=E7=BB=91=E5=AE=9A=E8=AE=B0=E5=BD=95=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=20=E5=88=86=E5=8C=85=E7=8F=AD=E7=BB=84?= =?UTF-8?q?=E8=81=94=E5=8A=A8=E6=8E=A5=E5=8F=A3=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bmw/controller/PayFailController.java | 172 ++++++++++ .../java/com/bonus/bmw/domain/po/PmSub.java | 4 + .../com/bonus/bmw/domain/po/PmSubTeam.java | 3 + .../bonus/bmw/domain/vo/DevBindRecordVo.java | 43 +++ .../com/bonus/bmw/domain/vo/PayFailVo.java | 123 +++++++ .../com/bonus/bmw/mapper/PayFailMapper.java | 51 +++ .../bonus/bmw/mapper/PmAttDeviceMapper.java | 13 + .../com/bonus/bmw/service/PayFailService.java | 60 ++++ .../bmw/service/impl/PayFailServiceImpl.java | 306 ++++++++++++++++++ .../service/impl/PmAttDeviceServiceImpl.java | 5 + .../bonus/bmw/utils/DateStringFormatter.java | 87 +++++ .../com/bonus/bmw/utils/ImageCropper.java | 125 +++++++ .../resources/mapper/bmw/PayFailMapper.xml | 74 +++++ .../mapper/bmw/PmAttDeviceMapper.xml | 10 + .../main/resources/mapper/bmw/PmSubMapper.xml | 11 +- .../resources/mapper/bmw/PmSubTeamMapper.xml | 8 +- .../main/resources/templates/zfsb-import.xlsx | Bin 0 -> 10648 bytes 17 files changed, 1090 insertions(+), 5 deletions(-) create mode 100644 bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/controller/PayFailController.java create mode 100644 bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/domain/vo/DevBindRecordVo.java create mode 100644 bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/domain/vo/PayFailVo.java create mode 100644 bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/mapper/PayFailMapper.java create mode 100644 bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/service/PayFailService.java create mode 100644 bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/service/impl/PayFailServiceImpl.java create mode 100644 bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/utils/DateStringFormatter.java create mode 100644 bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/utils/ImageCropper.java create mode 100644 bonus-modules/bonus-bmw/src/main/resources/mapper/bmw/PayFailMapper.xml create mode 100644 bonus-modules/bonus-bmw/src/main/resources/templates/zfsb-import.xlsx diff --git a/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/controller/PayFailController.java b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/controller/PayFailController.java new file mode 100644 index 0000000..9a3d291 --- /dev/null +++ b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/controller/PayFailController.java @@ -0,0 +1,172 @@ +package com.bonus.bmw.controller; + + +import com.bonus.bmw.domain.vo.PayFailVo; +import com.bonus.bmw.service.PayFailService; +import com.bonus.common.core.utils.poi.ExcelUtil; +import com.bonus.common.core.web.controller.BaseController; +import com.bonus.common.core.web.domain.AjaxResult; +import com.bonus.common.core.web.page.TableDataInfo; +import com.bonus.common.log.annotation.SysLog; +import com.bonus.common.log.enums.OperaType; + +import com.bonus.system.api.domain.SysConfig; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.util.StreamUtils; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.InputStream; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +@Slf4j +@RestController +@RequestMapping("/payFail") +public class PayFailController extends BaseController { + + @Autowired + private PayFailService service; + + + @GetMapping("/getPageList") + @SysLog(title = "发放失败列表", businessType = OperaType.QUERY, module = "支付失败登记->获取失败人员列表", details = "获取失败人员列表") + public TableDataInfo getPageList(PayFailVo o) { + try { + startPage(); + List list = service.getPageList(o); + return getDataTable(list); + } catch (Exception e) { + logger.error(e.toString(), e); + } + return getDataTableError(new ArrayList<>()); + } + + + @PostMapping("/addData") + @SysLog(title = "新增发放失败", businessType = OperaType.INSERT, module = "支付失败登记->新增记录", details = "新增发放失败数据") + public AjaxResult addData(@RequestPart(value = "file",required = false) MultipartFile[] file,@Validated PayFailVo vo) { + return service.addData(file,vo); + } + + @PostMapping("/updateData") + @SysLog(title = "修改发放失败", businessType = OperaType.INSERT, module = "支付失败登记->修改记录", details = "修改发放失败数据") + public AjaxResult updateData(@RequestPart(value = "file",required = false) MultipartFile[] file,@Validated PayFailVo vo) { + return service.updateData(file,vo); + } + @PostMapping("/delData") + @SysLog(title = "删除记录", businessType = OperaType.INSERT, module = "支付失败登记->删除记录", details = "删除记录") + public AjaxResult delData(@RequestBody PayFailVo vo) { + return service.delData(vo); + } + + @PostMapping("/getHistoryPay") + public AjaxResult getHistoryPay(@RequestBody PayFailVo vo) { + return service.getHistoryPay(vo); + } + + /** + * 导入 + * @param file + * @param updateSupport + * @return + * @throws Exception + */ + @PostMapping("/importFile") + @SysLog(title = "失信人员导入", businessType = OperaType.IMPORT, logType = 0, module = "施工人员->红绿灯管理->失信人员管理", details = "失信人员导入") + public AjaxResult importFile(@RequestParam(value = "file") MultipartFile file, boolean updateSupport) throws Exception { + try { + ExcelUtil util = new ExcelUtil<>(PayFailVo.class); + List list = util.importExcel(file.getInputStream(),0); + boolean allNull = list.stream().allMatch(Objects::isNull); + if (allNull) { + return error("导入文件为空或字段不匹配"); + } + return service.importFile(list); + } catch (Exception e) { + logger.error(e.toString(), e); + return error(e.getMessage()); + } + } + + @GetMapping("/export") + @SysLog(title = "参数配置", businessType = OperaType.EXPORT,logType = 0,module = "系统管理->参数配置") + public void export(HttpServletResponse response, PayFailVo o) { + try{ + List list = service.exportList(o); + ExcelUtil util = new ExcelUtil(PayFailVo.class); + util.exportExcel(response, list, "参数数据"); + }catch (Exception e){ + ExcelUtil util = new ExcelUtil(SysConfig.class); + util.exportExcel(response, new ArrayList(), "参数数据"); + log.error(e.toString(),e); + } + } + + @GetMapping("/downloadFile") + public ResponseEntity downloadFile(HttpServletRequest request) throws IOException { + // 1. 读取classpath下的模板文件 + ClassPathResource resource = new ClassPathResource("templates/zfsb-import.xlsx"); + if (!resource.exists()) { + return ResponseEntity.notFound().build(); + } + + // 2. 读取文件字节流(关键:确保完整读取文件内容) + byte[] fileBytes; + try (InputStream inputStream = resource.getInputStream()) { + fileBytes = StreamUtils.copyToByteArray(inputStream); // 完整读取字节数组 + } + // 3. 处理文件名编码(兼容所有浏览器) + String filename = "导入模版.xlsx"; + String encodedFilename = getEncodedFilename(request, filename); + // 4. 构建响应头(补充Content-Length,确保文件完整) + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; " + encodedFilename); + headers.add(HttpHeaders.CONTENT_TYPE, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(fileBytes.length)); + // 防止浏览器缓存导致的文件损坏 + headers.add(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, must-revalidate"); + headers.add(HttpHeaders.PRAGMA, "no-cache"); + headers.add(HttpHeaders.EXPIRES, "0"); + + // 5. 返回字节数组(而非直接返回Resource,避免流传输问题) + return ResponseEntity.ok() + .headers(headers) + .body(fileBytes); + } + + /** + * 兼容所有浏览器的文件名编码处理 + */ + private String getEncodedFilename(HttpServletRequest request, String filename) throws IOException { + String userAgent = request.getHeader("User-Agent"); + if (userAgent == null) { + userAgent = ""; + } + String encodedFilename; + if (userAgent.contains("MSIE") || userAgent.contains("Trident") || userAgent.contains("Edge")) { + // IE/Edge浏览器:URL编码,替换+为%20 + encodedFilename = "filename=" + URLEncoder.encode(filename, StandardCharsets.UTF_8.name()).replace("+", "%20"); + } else if (userAgent.contains("Firefox")) { + // Firefox:RFC 5987 标准 + encodedFilename = "filename*=UTF-8''" + URLEncoder.encode(filename, StandardCharsets.UTF_8.name()); + } else { + // Chrome/Safari等:RFC 6266 标准 + encodedFilename = "filename=\"" + new String(filename.getBytes(StandardCharsets.UTF_8), "ISO-8859-1") + "\""; + } + return encodedFilename; + } + + +} diff --git a/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/domain/po/PmSub.java b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/domain/po/PmSub.java index b3b6a74..a35ffb7 100644 --- a/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/domain/po/PmSub.java +++ b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/domain/po/PmSub.java @@ -140,4 +140,8 @@ public class PmSub { * 分公司名称 */ private String subComName; + /** + * 工程id + */ + private String proId; } diff --git a/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/domain/po/PmSubTeam.java b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/domain/po/PmSubTeam.java index 6c329e6..cb49640 100644 --- a/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/domain/po/PmSubTeam.java +++ b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/domain/po/PmSubTeam.java @@ -89,4 +89,7 @@ public class PmSubTeam { * 分公司id */ private Integer subComId; + + private String proId; + } diff --git a/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/domain/vo/DevBindRecordVo.java b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/domain/vo/DevBindRecordVo.java new file mode 100644 index 0000000..43fcd5c --- /dev/null +++ b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/domain/vo/DevBindRecordVo.java @@ -0,0 +1,43 @@ +package com.bonus.bmw.domain.vo; + +import lombok.Data; + +@Data +public class DevBindRecordVo { + + private String id; + /** + * 设备部i俺妈 + */ + private String devCode; + /** + * 邦迪时间 + */ + private String bindTime; + /** + * 解绑时间 + */ + private String unBindTime; + /** + * 工程id + */ + private int proId; + /** + * 班组id + */ + private int teamId; + /** + * 分包id + */ + private int subId; + + public DevBindRecordVo() { + } + + public DevBindRecordVo(String devCode, int proId, int teamId, int subId) { + this.devCode = devCode; + this.proId = proId; + this.teamId = teamId; + this.subId = subId; + } +} diff --git a/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/domain/vo/PayFailVo.java b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/domain/vo/PayFailVo.java new file mode 100644 index 0000000..ff44c15 --- /dev/null +++ b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/domain/vo/PayFailVo.java @@ -0,0 +1,123 @@ +package com.bonus.bmw.domain.vo; + +import com.bonus.common.core.annotation.Excel; +import com.bonus.system.api.model.UploadFileVo; +import lombok.Data; +import org.apache.poi.hpsf.Decimal; + +import javax.validation.constraints.DecimalMin; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.math.BigDecimal; +import java.util.List; + +/** + * 工资发放失败是实体类 + */ +@Data +public class PayFailVo { + /** + * 主键 + */ + private String id; + + @Excel(name = "序号", sort = 1) + private String num; + + /** + * 用户名 + */ + @NotBlank(message = "用户名不能为空") + @Size(min = 0, max = 32, message = "字典类型名称长度不能超过32个字符") + @Excel(name = "姓名", sort = 2) + private String userName; + + @NotBlank(message = "身份证号码不能为空") + @Size(min = 0, max = 18, message = "身份证号码长度不能超过18个字符") + @Excel(name = "身份证号码", sort = 3) + private String idCard; + /** + * 工程名称 + */ + @NotBlank(message = "工程名称不能为空") + @Size(min = 0, max = 256, message = "工程名称名称长度不能超过256个字符") + @Excel(name = "工程名称", sort = 4) + private String proName; + /** + * 分包单位 + */ + @NotBlank(message = "分包单位不能为空") + @Size(min = 0, max = 128, message = "分包单位名称长度不能超过128个字符") + @Excel(name = "分包名称", sort = 5) + private String subName; + /** + * 身份证号码 + */ + + /** + * 月份 + */ + @NotBlank(message = "月份不能为空") + @Size(min = 0, max = 18, message = "月份长度不能超过18个字符") + @Excel(name = "应发月份", sort = 6) + private String failMonth; + /** + * 金额 + */ + @NotNull(message = "金额不能为空") // 核心:校验BigDecimal非null + @DecimalMin(value = "0.00", message = "金额不能为负数") + @Excel(name = "应发金额", sort = 7) + private BigDecimal money; + /** + * 失败原因 + */ + @NotBlank(message = "失败原因不能为空") + @Excel(name = "支付失败原因", sort = 8) + private String failReason; + /** + * 状态 + */ + private String failStatus; + + private String failStatusString; + /** + * 备注 + */ + @Excel(name = "备注", sort = 9) + private String remark; + /** + * 创建人 + */ + private String createUser; + /** + * 创建时间 + */ + private String createTime; + /** + * 修改时间 + */ + private String updateTime; + /** + * 修改人 + */ + private String updateUser; + /** + * 数据源 是否导入 + */ + private int isImport=1; + /** + * 删除状态 + */ + private int delFlag; + /** + * 删除文件id + */ + private String delFileId; + + private List fileList; + + private int fileSiz; + + +} diff --git a/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/mapper/PayFailMapper.java b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/mapper/PayFailMapper.java new file mode 100644 index 0000000..09ee034 --- /dev/null +++ b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/mapper/PayFailMapper.java @@ -0,0 +1,51 @@ +package com.bonus.bmw.mapper; + +import com.bonus.bmw.domain.vo.PayFailVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface PayFailMapper { + + /** + * 分页查询 失败人员列表 + * @param o + * @return + */ + List getPageList(PayFailVo o); + + /** + * 数据插入 + * @param vo + * @return + */ + int addData(PayFailVo vo); + + /** + * 修改数据 + * @param vo + * @return + */ + int updateData(PayFailVo vo); + + /** + * 批量删除数据 + * @param list + */ + int deleteData(@Param("list")List list); + + /** + * 新增数据集合 + * @param list + */ + int addDataList(@Param("list") List list); + + /** + * 数据查询历史没发放成功 + * @param vo + * @return + */ + List getHistoryPayList(PayFailVo vo); +} diff --git a/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/mapper/PmAttDeviceMapper.java b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/mapper/PmAttDeviceMapper.java index 4858dc0..4d91964 100644 --- a/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/mapper/PmAttDeviceMapper.java +++ b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/mapper/PmAttDeviceMapper.java @@ -1,6 +1,7 @@ package com.bonus.bmw.mapper; import com.bonus.bmw.domain.po.PmAttDevice; +import com.bonus.bmw.domain.vo.DevBindRecordVo; import com.bonus.bmw.domain.vo.PmAttDeviceVo; import java.util.List; @@ -20,4 +21,16 @@ public interface PmAttDeviceMapper { int delPmAttDevice(PmAttDevice pmAttDevice); void addPmAttDeviceHis(PmAttDevice pmAttDeviceOld); + + /** + * 解绑数据 + * @param vo + */ + void updateBindDev(DevBindRecordVo vo); + + /** + * 数据插入 + * @param vo + */ + void insertDevBind(DevBindRecordVo vo); } diff --git a/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/service/PayFailService.java b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/service/PayFailService.java new file mode 100644 index 0000000..90eca49 --- /dev/null +++ b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/service/PayFailService.java @@ -0,0 +1,60 @@ +package com.bonus.bmw.service; + +import com.bonus.bmw.domain.vo.PayFailVo; +import com.bonus.common.core.web.domain.AjaxResult; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +public interface PayFailService { + /** + * 分页查询 + * @param o + * @return + */ + List getPageList(PayFailVo o); + + /** + * 新增发放数据 + * @param file + * @param vo + * @return + */ + AjaxResult addData(MultipartFile[] file, PayFailVo vo); + + /** + * 修改数据 + * @param file + * @param vo + * @return + */ + AjaxResult updateData(MultipartFile[] file, PayFailVo vo); + + /** + * 删除数据 + * @param vo + * @return + */ + AjaxResult delData(PayFailVo vo); + + /** + * 批量导入数据 + * @param list + * @return + */ + AjaxResult importFile(List list); + + /** + * 数据导出 + * @param o + * @return + */ + List exportList(PayFailVo o); + + /** + * 查询人员 + * @param vo + * @return + */ + AjaxResult getHistoryPay(PayFailVo vo); +} diff --git a/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/service/impl/PayFailServiceImpl.java b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/service/impl/PayFailServiceImpl.java new file mode 100644 index 0000000..5b0f8c7 --- /dev/null +++ b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/service/impl/PayFailServiceImpl.java @@ -0,0 +1,306 @@ +package com.bonus.bmw.service.impl; + +import com.bonus.bmw.domain.vo.PayFailVo; +import com.bonus.bmw.mapper.PayFailMapper; +import com.bonus.bmw.service.PayFailService; +import com.bonus.common.core.constant.SecurityConstants; +import com.bonus.common.core.domain.R; +import com.bonus.common.core.utils.StringUtils; +import com.bonus.common.core.web.domain.AjaxResult; +import com.bonus.common.security.utils.SecurityUtils; +import com.bonus.system.api.RemoteUploadUtilsService; +import com.bonus.system.api.model.UploadFileVo; +import lombok.extern.slf4j.Slf4j; +import org.apache.catalina.security.SecurityUtil; +import org.apache.poi.hpsf.Decimal; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static com.bonus.bmw.utils.DateStringFormatter.formatDateStrToYearMonth; + +@Slf4j +@Service +public class PayFailServiceImpl implements PayFailService { + + @Autowired + private PayFailMapper mapper; + + @Resource + private RemoteUploadUtilsService service; + /** + * 分页查询数据 + * @param o + * @return + */ + @Override + public List getPageList(PayFailVo o) { + try{ + List list=mapper.getPageList(o); + for(PayFailVo vo:list){ + R> res=service.getFileList(null,vo.getId(),"bm_pay_fail",null, SecurityConstants.INNER); + if(res.getCode()==R.SUCCESS){ + List fileList=res.getData(); + vo.setFileList(fileList); + vo.setFileSiz(fileList.size()); + } + } + return list; + }catch (Exception e){ + log.error(e.toString(),e); + } + return Collections.emptyList(); + } + @Override + public List exportList(PayFailVo o) { + try{ + List list=mapper.getPageList(o); + int num=1; + for(PayFailVo vo:list){ + vo.setNum(String.valueOf(num++)); + } + return list; + }catch (Exception e){ + log.error(e.toString(),e); + } + return Collections.emptyList(); + } + + /** + * 查询 人员历史未 发放的数据 + * @param vo + * @return + */ + @Override + public AjaxResult getHistoryPay(PayFailVo vo) { + try{ + List list=mapper.getHistoryPayList(vo); + if(list!=null && !list.isEmpty()){ + StringBuilder sb=new StringBuilder(); + sb.append("友情提示:"); + for(PayFailVo vo2:list){ + sb.append(vo2.getUserName()).append("在").append(vo2.getProName()).append("工程"); + sb.append(vo2.getSubName()).append("分包,"); + sb.append(vo2.getFailMonth()).append("月份存在").append(vo2.getMoney()).append("元工资因"); + sb.append(vo2.getFailReason()).append("原因未发放成功"); + } + return AjaxResult.success("操作成功",sb.toString()); + } + + }catch (Exception e){ + log.error(e.toString(),e); + } + return AjaxResult.success("无数据",null); + } + + /** + * 新增数据 + * @param file + * @param vo + * @return + */ + @Override + public AjaxResult addData(MultipartFile[] file, PayFailVo vo) { + try{ + String userId= SecurityUtils.getUserId()+""; + vo.setUpdateUser(userId); + vo.setCreateUser(userId); + vo.setIsImport(0); + int num=mapper.addData(vo); + if(num>0){ + if(file!=null&&file.length>0){ + String[] sourceType=new String[file.length]; + for (int i = 0; i < file.length; i++) { + sourceType[i]="附件"; + } + R> re= service.uploadFiles(file,"bm_pay_fail",vo.getId(),sourceType,"failFile",null,SecurityConstants.INNER); + if(re.getCode()==R.SUCCESS){ + return AjaxResult.success("新增成功"); + } + } + return AjaxResult.success("新增成功"); + } + }catch (Exception e){ + log.error(e.toString(),e); + } + return AjaxResult.error("新增失败"); + } + + /** + * 修改数据 + * @param file + * @param vo + * @return + */ + @Override + public AjaxResult updateData(MultipartFile[] file, PayFailVo vo) { + try{ + String userId= SecurityUtils.getUserId()+""; + vo.setUpdateUser(userId); + vo.setIsImport(0); + int num=mapper.updateData(vo); + if(StringUtils.isNotEmpty(vo.getDelFileId())){ + String[] ids=vo.getDelFileId().split(","); + service.delFileList(ids,null,null,null,SecurityConstants.INNER); + } + if(num>0){ + if(file!=null && file.length>0){ + String[] sourceType=new String[file.length]; + for (int i = 0; i < file.length; i++) { + sourceType[i]="附件修改"; + } + R> re= service.uploadFiles(file,"bm_pay_fail",vo.getId(),sourceType,"failFile",null,SecurityConstants.INNER); + if(re.getCode()==R.SUCCESS){ + return AjaxResult.success("修改成功"); + } + } + return AjaxResult.success("修改成功"); + } + }catch (Exception e){ + log.error(e.toString(),e); + } + return AjaxResult.error("修改失败"); + } + + /** + * 批量删除 + * @param vo + * @return + */ + @Override + public AjaxResult delData(PayFailVo vo) { + try{ + if(StringUtils.isEmpty(vo.getId())){ + return AjaxResult.error("数据不存在"); + } + String[] ids=vo.getId().split(","); + String[] tables=new String[ids.length]; + for (int i = 0; i < ids.length; i++) { + tables[i]="bm_pay_fail"; + } + int delNum= mapper.deleteData(Arrays.asList(ids)); + if(delNum==ids.length){ + //删除附件 + //service.delFileList(null,ids,tables,null,SecurityConstants.INNER); + return AjaxResult.success("删除成功"); + } + }catch (Exception e){ + log.error(e.toString(),e); + } + return AjaxResult.error("删除失败"); + } + + /** + * 导入支付失败记录文件 + * @param list 支付失败记录列表 + * @return 导入结果 + */ + @Override + public AjaxResult importFile(List list) { + // 1. 入参校验:防止空列表导致后续逻辑异常 + if (list == null || list.isEmpty()) { + return AjaxResult.error("导入失败:待导入数据为空"); + } + String userId= SecurityUtils.getUserId()+""; + // 2. 校验每条记录的必填字段 + StringBuilder errorMsg = new StringBuilder(); + for (PayFailVo vo : list) { + vo.setCreateUser(userId); + vo.setUpdateUser(userId); + vo.setFailStatus("0"); + String result = formatDateStrToYearMonth(vo.getFailMonth()); + vo.setFailMonth(result); + StringBuilder singleError = new StringBuilder(); + boolean hasError = false; + // 处理序号可能为null的情况,避免空指针 + String num = vo.getNum() == null ? "未知" : vo.getNum(); + singleError.append("序号为").append(num); + // 校验用户名 + if (StringUtils.isEmpty(vo.getUserName())) { + singleError.append("用户名为空、"); + hasError = true; + } + // 校验身份证 + if (StringUtils.isEmpty(vo.getIdCard())) { + singleError.append("身份证号码为空、"); + hasError = true; + } + // 校验工程名称 + if (StringUtils.isEmpty(vo.getProName())) { + singleError.append("工程名称为空、"); + hasError = true; + } + // 校验分包单位 + if (StringUtils.isEmpty(vo.getSubName())) { + singleError.append("分包单位为空、"); + hasError = true; + } + // 校验应发月份 + if (StringUtils.isEmpty(vo.getFailMonth())) { + singleError.append("应发月份为空、"); + hasError = true; + } + // 校验应发金额 + if (isMoneyEmpty(vo.getMoney())) { + singleError.append("应发金额为空、"); + hasError = true; + } + + // 有错误时,去掉末尾顿号并拼接错误信息 + if (hasError) { + // 封装工具方法:去掉末尾的顿号 + removeLastDouHao(singleError); + errorMsg.append(singleError).append(";"); + } + } + + // 3. 存在校验错误,返回错误信息 + if (errorMsg.length() > 0) { + return AjaxResult.error("导入失败:" + errorMsg.toString()); + } + + try { + // 4. 校验通过,执行批量插入 + int insertCount = mapper.addDataList(list); + + // 5. 校验插入结果 + if (insertCount == list.size()) { + return AjaxResult.success("导入成功,共导入" + insertCount + "条记录"); + } else { + // 插入行数不匹配,返回明确错误 + return AjaxResult.error("导入异常:仅成功插入" + insertCount + "条,待导入" + list.size() + "条"); + } + } catch (Exception e) { + // 6. 捕获插入异常,记录日志并返回友好提示 + log.error("支付失败记录批量导入失败", e); + return AjaxResult.error("导入失败:" + e.getMessage()); + } + } + + /** + * 工具方法:去掉StringBuilder末尾的顿号(、) + * @param sb 待处理的字符串构建器 + */ + private void removeLastDouHao(StringBuilder sb) { + if (sb == null || sb.length() == 0) { + return; + } + char lastChar = sb.charAt(sb.length() - 1); + if (lastChar == '、') { + sb.deleteCharAt(sb.length() - 1); + } + } + + + + public boolean isMoneyEmpty(BigDecimal money) { + // 先判null,再判0(避免空指针) + return money == null || BigDecimal.ZERO.compareTo(money) == 0; + } +} diff --git a/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/service/impl/PmAttDeviceServiceImpl.java b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/service/impl/PmAttDeviceServiceImpl.java index 6a17188..12ab315 100644 --- a/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/service/impl/PmAttDeviceServiceImpl.java +++ b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/service/impl/PmAttDeviceServiceImpl.java @@ -1,6 +1,7 @@ package com.bonus.bmw.service.impl; import com.bonus.bmw.domain.po.PmAttDevice; +import com.bonus.bmw.domain.vo.DevBindRecordVo; import com.bonus.bmw.domain.vo.PmAttDeviceVo; import com.bonus.bmw.mapper.PmAttDeviceMapper; import com.bonus.bmw.service.PmAttDeviceService; @@ -103,8 +104,10 @@ public class PmAttDeviceServiceImpl implements PmAttDeviceService { if(pmAttDevice.getTeamId()!=null){ teamId=pmAttDevice.getTeamId(); } + DevBindRecordVo vo=new DevBindRecordVo(pmAttDevice.getDeviceCode(),proId,subId,teamId); //解绑的时候在历史表插入一条数据 if (StringUtils.isNull(pmAttDevice.getProId())){ + pmAttDeviceMapper.updateBindDev(vo); PmAttDevice pmAttDeviceOld = pmAttDeviceMapper.getPmAttDeviceByCoode(pmAttDevice); pmAttDeviceOld.setUpdateUser(SecurityUtils.getLoginUser().getUsername()); pmAttDeviceOld.setUpdateTime(new Date()); @@ -114,6 +117,8 @@ public class PmAttDeviceServiceImpl implements PmAttDeviceService { //考勤机解绑 uakSendService.delDevByProId(pmAttDevice.getDeviceCode(),proId,subId,teamId); }else { + pmAttDeviceMapper.insertDevBind(vo); + //新增考勤机触发 uakSendService.getUserSendToDev(pmAttDevice.getDeviceCode(),subId,teamId,proId); } diff --git a/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/utils/DateStringFormatter.java b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/utils/DateStringFormatter.java new file mode 100644 index 0000000..3576ccc --- /dev/null +++ b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/utils/DateStringFormatter.java @@ -0,0 +1,87 @@ +package com.bonus.bmw.utils; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +public class DateStringFormatter { + + // 定义需要支持解析的日期格式(覆盖Excel导入常见的日期字符串格式,新增Date默认格式) + private static final List PARSE_FORMATTERS = new ArrayList<>(); + // 目标格式化器(yyyy-MM) + private static final DateTimeFormatter TARGET_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM"); + + static { + // 1. 常规日期格式 + PARSE_FORMATTERS.add(DateTimeFormatter.ofPattern("yyyy-MM-dd")); + PARSE_FORMATTERS.add(DateTimeFormatter.ofPattern("yyyy/MM/dd")); + PARSE_FORMATTERS.add(DateTimeFormatter.ofPattern("yyyyMMdd")); + PARSE_FORMATTERS.add(DateTimeFormatter.ofPattern("yyyy.MM.dd")); + PARSE_FORMATTERS.add(DateTimeFormatter.ofPattern("yyyy年MM月dd日")); + + // 2. 新增:兼容 Date 对象默认toString格式(Sun Jun 07 00:00:00 CST 2026) + // 必须指定Locale.ENGLISH,否则中文环境下解析英文月份/星期会失败 + PARSE_FORMATTERS.add(DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ENGLISH)); + } + + /** + * 将任意常见格式的日期字符串转换为 yyyy-MM 格式 + * @param dateStr 原始日期字符串(如2024/05/18、Sun Jun 07 00:00:00 CST 2026等) + * @return 格式化后的字符串(如2024-05),解析失败返回空字符串 + */ + public static String formatDateStrToYearMonth(String dateStr) { + // 空值/空白处理 + if (dateStr == null || dateStr.trim().isEmpty()) { + return ""; + } + String cleanStr = dateStr.trim(); + + // 遍历所有支持的格式,尝试解析 + for (DateTimeFormatter formatter : PARSE_FORMATTERS) { + try { + LocalDate date = LocalDate.parse(cleanStr, formatter); + // 解析成功,格式化为yyyy-MM + return date.format(TARGET_FORMATTER); + } catch (DateTimeParseException e) { + // 该格式解析失败,尝试下一个 + continue; + } + } + + // 额外兼容:如果是Date对象直接toString的格式解析失败(极端情况),尝试另一种方式 + try { + // 将字符串转回Date对象,再转LocalDate + Date date = new java.text.SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ENGLISH).parse(cleanStr); + LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + return localDate.format(TARGET_FORMATTER); + } catch (Exception e) { + // 所有格式都解析失败 + System.err.println("无法解析日期字符串: " + cleanStr); + return ""; + } + } + + // 测试示例(包含你提到的Date格式) + public static void main(String[] args) { + // 测试各种常见的日期字符串格式,新增Date默认格式测试 + String[] testDates = { + "2024-05-18", + "2024/05/18", + "20240518", + "Sun Jun 07 00:00:00 CST 2026", // 重点测试这个格式 + "Mon Jul 15 12:34:56 CST 2025", + "无效日期", + null + }; + + for (String date : testDates) { + String result = formatDateStrToYearMonth(date); + System.out.printf("原始值: [%s] → 格式化后: [%s]%n", date, result); + } + } +} \ No newline at end of file diff --git a/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/utils/ImageCropper.java b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/utils/ImageCropper.java new file mode 100644 index 0000000..16b5dcc --- /dev/null +++ b/bonus-modules/bonus-bmw/src/main/java/com/bonus/bmw/utils/ImageCropper.java @@ -0,0 +1,125 @@ +package com.bonus.bmw.utils; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +/** + * Java根据浮点型左上角/右下角坐标截图图片工具类 + */ +public class ImageCropper { + + /** + * 截取图片指定区域(适配浮点型坐标) + * @param srcImagePath 源图片路径 (如: "D:/test.jpg") + * @param destImagePath 截取后图片保存路径 (如: "D:/crop.jpg") + * @param x1 截取区域左上角x坐标(浮点型) + * @param y1 截取区域左上角y坐标(浮点型) + * @param x2 截取区域右下角x坐标(浮点型) + * @param y2 截取区域右下角y坐标(浮点型) + * @throws IOException 图片读取/写入异常 + */ + public static void cropImage(String srcImagePath, String destImagePath, + double x1, double y1, double x2, double y2) throws IOException { + // 1. 读取源图片 + File srcFile = new File(srcImagePath); + if (!srcFile.exists()) { + throw new IOException("源图片文件不存在: " + srcImagePath); + } + + BufferedImage srcImage = ImageIO.read(srcFile); + + // 2. 获取图片原始尺寸 + int imageWidth = srcImage.getWidth(); + int imageHeight = srcImage.getHeight(); + + // 3. 浮点坐标转整数(四舍五入保留精度) + int intX1 = (int) Math.round(x1); + int intY1 = (int) Math.round(y1); + int intX2 = (int) Math.round(x2); + int intY2 = (int) Math.round(y2); + + // 4. 校验坐标合法性 + // 检查左上角坐标 + if (intX1 < 0 || intY1 < 0 || intX1 >= imageWidth || intY1 >= imageHeight) { + throw new IllegalArgumentException( + String.format("左上角坐标越界!图片尺寸: %dx%d, 转换后坐标: (%d, %d) (原始: %.4f, %.4f)", + imageWidth, imageHeight, intX1, intY1, x1, y1) + ); + } + // 检查右下角坐标 + if (intX2 < 0 || intY2 < 0 || intX2 >= imageWidth || intY2 >= imageHeight) { + throw new IllegalArgumentException( + String.format("右下角坐标越界!图片尺寸: %dx%d, 转换后坐标: (%d, %d) (原始: %.4f, %.4f)", + imageWidth, imageHeight, intX2, intY2, x2, y2) + ); + } + // 检查坐标逻辑 + if (intX2 <= intX1) { + throw new IllegalArgumentException( + String.format("右下角x坐标必须大于左上角x坐标!转换后: x1=%d, x2=%d (原始: %.4f, %.4f)", + intX1, intX2, x1, x2) + ); + } + if (intY2 <= intY1) { + throw new IllegalArgumentException( + String.format("右下角y坐标必须大于左上角y坐标!转换后: y1=%d, y2=%d (原始: %.4f, %.4f)", + intY1, intY2, y1, y2) + ); + } + + // 5. 计算截取区域的宽高 + int cropWidth = intX2 - intX1; + int cropHeight = intY2 - intY1; + + // 6. 创建截取后的图片缓冲区 + BufferedImage destImage = new BufferedImage(cropWidth, cropHeight, srcImage.getType()); + + // 7. 绘制截取的区域 + Graphics2D g = destImage.createGraphics(); + // 增强截图清晰度(抗锯齿+插值) + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.drawImage(srcImage, 0, 0, cropWidth, cropHeight, intX1, intY1, intX2, intY2, null); + g.dispose(); // 释放资源 + + // 8. 获取目标文件格式 + String format = destImagePath.substring(destImagePath.lastIndexOf(".") + 1).toUpperCase(); + if (!format.matches("(JPG|JPEG|PNG|BMP)")) { + format = "PNG"; + } + + // 9. 保存截取后的图片 + File destFile = new File(destImagePath); + File parentDir = destFile.getParentFile(); + if (parentDir != null && !parentDir.exists()) { + parentDir.mkdirs(); + } + + ImageIO.write(destImage, format, destFile); + System.out.printf("截图成功!\n原始坐标: (%.4f,%.4f) -> (%.4f,%.4f)\n转换后坐标: (%d,%d) -> (%d,%d)\n保存路径: %s\n", + x1, y1, x2, y2, intX1, intY1, intX2, intY2, destImagePath); + } + + // 测试方法(使用你提供的浮点坐标) + public static void main(String[] args) { + try { + // 替换为你的实际图片路径 + String srcPath = "D:\\File\\dd.jpg"; + String destPath = "D:\\File\\dd3.jpg"; + + // 你提供的浮点坐标 + double x1 = 1300.4857177734375; + double y1 = 722.174560546875; + double x2 =1936.892578125; + double y2 = 1726.121337890625; + // 调用截图方法 + cropImage(srcPath, destPath, x1, y1, x2, y2); + } catch (Exception e) { + System.err.println("截图失败: " + e.getMessage()); + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/bonus-modules/bonus-bmw/src/main/resources/mapper/bmw/PayFailMapper.xml b/bonus-modules/bonus-bmw/src/main/resources/mapper/bmw/PayFailMapper.xml new file mode 100644 index 0000000..4c42204 --- /dev/null +++ b/bonus-modules/bonus-bmw/src/main/resources/mapper/bmw/PayFailMapper.xml @@ -0,0 +1,74 @@ + + + + + INSERT INTO bm_pay_fail( + user_name,pro_name,sub_name,id_card,fail_month, + money,fail_reason,fail_status,remark,create_user, + create_time,update_time, update_user,is_import,del_flag + )VALUES (#{userName},#{proName},#{subName},#{idCard},#{failMonth}, + #{money},#{failReason},#{failStatus},#{remark},#{createUser}, + now(),now(),#{updateUser},#{isImport},0 + ) + + + INSERT INTO bm_pay_fail( + user_name,pro_name,sub_name,id_card,fail_month, + money,fail_reason,fail_status,remark,create_user, + create_time,update_time, update_user,is_import,del_flag + )values + + #{item.userName}, #{item.proName}, #{item.subName}, + #{item.idCard}, #{item.failMonth},#{item.money}, + #{item.failReason}, #{item.failStatus}, #{item.remark}, + #{item.createUser}, NOW(), NOW(), + #{item.updateUser}, 1, 0 + + + + + UPDATE bm_pay_fail SET user_name=#{userName},pro_name=#{proName},sub_name=#{subName},id_card=#{idCard},fail_month=#{failMonth}, + money=#{money},fail_reason=#{failReason},fail_status=#{failStatus},remark=#{remark}, + update_time=now(), update_user=#{updateUser} + where id=#{id} + + + UPDATE bm_pay_fail set del_flag=1 + where id in + + #{item} + + + + + diff --git a/bonus-modules/bonus-bmw/src/main/resources/mapper/bmw/PmAttDeviceMapper.xml b/bonus-modules/bonus-bmw/src/main/resources/mapper/bmw/PmAttDeviceMapper.xml index e980a1e..0f9b0d4 100644 --- a/bonus-modules/bonus-bmw/src/main/resources/mapper/bmw/PmAttDeviceMapper.xml +++ b/bonus-modules/bonus-bmw/src/main/resources/mapper/bmw/PmAttDeviceMapper.xml @@ -13,6 +13,13 @@ values (#{deviceCode}, #{deviceName},#{serialNumber},#{proId}, #{createUser}, #{updateUser}, #{createTime}, #{updateTime},#{subId},#{teamId}) + + insert into pm_att_device_bind_record( + dev_code,bind_time,pro_id,team_id,sub_id + )values ( + #{devCode},now(),#{proId},#{teamId},#{subId} + ) + update pm_att_device @@ -30,6 +37,9 @@ where is_active = '1' and device_code = #{deviceCode} + + update pm_att_device_bind_record set un_bind_time=now() where dev_code=#{devCode} and un_bind_time is NULL + delete from pm_att_device where device_code = #{deviceCode} diff --git a/bonus-modules/bonus-bmw/src/main/resources/mapper/bmw/PmSubMapper.xml b/bonus-modules/bonus-bmw/src/main/resources/mapper/bmw/PmSubMapper.xml index 5d0c33f..0fc0843 100644 --- a/bonus-modules/bonus-bmw/src/main/resources/mapper/bmw/PmSubMapper.xml +++ b/bonus-modules/bonus-bmw/src/main/resources/mapper/bmw/PmSubMapper.xml @@ -92,10 +92,15 @@ from pm_sub where is_active='1' and sub_name= #{subName}