添加工资未发放接口 考勤机绑定记录信息接口 分包班组联动接口修改

This commit is contained in:
haozq 2026-02-10 16:18:20 +08:00
parent def6fb0a7c
commit 7cd61b935c
17 changed files with 1090 additions and 5 deletions

View File

@ -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<PayFailVo> 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<PayFailVo> util = new ExcelUtil<>(PayFailVo.class);
List<PayFailVo> 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<PayFailVo> list = service.exportList(o);
ExcelUtil<PayFailVo> util = new ExcelUtil<PayFailVo>(PayFailVo.class);
util.exportExcel(response, list, "参数数据");
}catch (Exception e){
ExcelUtil<SysConfig> util = new ExcelUtil<SysConfig>(SysConfig.class);
util.exportExcel(response, new ArrayList<SysConfig>(), "参数数据");
log.error(e.toString(),e);
}
}
@GetMapping("/downloadFile")
public ResponseEntity<byte[]> 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")) {
// FirefoxRFC 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;
}
}

View File

@ -140,4 +140,8 @@ public class PmSub {
* 分公司名称
*/
private String subComName;
/**
* 工程id
*/
private String proId;
}

View File

@ -89,4 +89,7 @@ public class PmSubTeam {
* 分公司id
*/
private Integer subComId;
private String proId;
}

View File

@ -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;
}
}

View File

@ -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<UploadFileVo> fileList;
private int fileSiz;
}

View File

@ -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<PayFailVo> getPageList(PayFailVo o);
/**
* 数据插入
* @param vo
* @return
*/
int addData(PayFailVo vo);
/**
* 修改数据
* @param vo
* @return
*/
int updateData(PayFailVo vo);
/**
* 批量删除数据
* @param list
*/
int deleteData(@Param("list")List<String> list);
/**
* 新增数据集合
* @param list
*/
int addDataList(@Param("list") List<PayFailVo> list);
/**
* 数据查询历史没发放成功
* @param vo
* @return
*/
List<PayFailVo> getHistoryPayList(PayFailVo vo);
}

View File

@ -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);
}

View File

@ -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<PayFailVo> 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<PayFailVo> list);
/**
* 数据导出
* @param o
* @return
*/
List<PayFailVo> exportList(PayFailVo o);
/**
* 查询人员
* @param vo
* @return
*/
AjaxResult getHistoryPay(PayFailVo vo);
}

View File

@ -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<PayFailVo> getPageList(PayFailVo o) {
try{
List<PayFailVo> list=mapper.getPageList(o);
for(PayFailVo vo:list){
R<List<UploadFileVo>> res=service.getFileList(null,vo.getId(),"bm_pay_fail",null, SecurityConstants.INNER);
if(res.getCode()==R.SUCCESS){
List<UploadFileVo> 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<PayFailVo> exportList(PayFailVo o) {
try{
List<PayFailVo> 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<PayFailVo> 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<List<UploadFileVo>> 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<List<UploadFileVo>> 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<PayFailVo> 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;
}
}

View File

@ -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);
}

View File

@ -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<DateTimeFormatter> 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/18Sun 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);
}
}
}

View File

@ -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();
}
}
}

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bonus.bmw.mapper.PayFailMapper">
<insert id="addData" useGeneratedKeys="true" keyProperty="id">
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>
<insert id="addDataList">
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
<foreach collection="list" item="item" separator="),(" open="(" close=")" >
#{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
</foreach>
</insert>
<update id="updateData">
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>
<update id="deleteData">
UPDATE bm_pay_fail set del_flag=1
where id in
<foreach collection="list" item="item" separator="," open="(" close=")" >
#{item}
</foreach>
</update>
<select id="getPageList" resultType="com.bonus.bmw.domain.vo.PayFailVo">
SELECT bpf.id, bpf.user_name userName, bpf.pro_name proName,
bpf.sub_name subName, bpf.id_card idCard, bpf.fail_month failMonth,
bpf.money, bpf.fail_reason failReason,bpf.fail_status failStatusString , IF(bpf.fail_status=0,'未处理','已处理') failStatus,
bpf.remark,bpf.create_user createUser, bpf.create_time createTime,
bpf.update_time updateTime, bpf.update_user updateUser, bpf.is_import isImport
FROM bm_pay_fail bpf
where del_flag=0
<if test="userName != null and userName != ''">
and bpf.user_name LIKE CONCAT('%', #{userName}, '%')
</if>
<if test="proName != null and proName != ''">
and bpf.pro_name LIKE CONCAT('%', #{proName}, '%')
</if>
<if test="subName != null and subName != ''">
and bpf.sub_name LIKE CONCAT('%', #{subName}, '%')
</if>
<if test="idCard != null and idCard != ''">
and bpf.id_card LIKE CONCAT('%', #{idCard}, '%')
</if>
<if test="failStatus != null and failStatus != ''">
and bpf.fail_status =#{failStatus}
</if>
</select>
<select id="getHistoryPayList" resultType="com.bonus.bmw.domain.vo.PayFailVo">
select bpf.user_name userName, bpf.pro_name proName,
bpf.sub_name subName, bpf.id_card idCard, bpf.fail_month failMonth,
bpf.money, bpf.fail_reason failReason
from bm_pay_fail bpf
where bpf.del_flag=0 and bpf.fail_status=0 and bpf.id_card=#{idCard}
</select>
</mapper>

View File

@ -13,6 +13,13 @@
values (#{deviceCode}, #{deviceName},#{serialNumber},#{proId}, #{createUser}, #{updateUser},
#{createTime}, #{updateTime},#{subId},#{teamId})
</insert>
<insert id="insertDevBind">
insert into pm_att_device_bind_record(
dev_code,bind_time,pro_id,team_id,sub_id
)values (
#{devCode},now(),#{proId},#{teamId},#{subId}
)
</insert>
<update id="updatePmAttDevice">
update pm_att_device
<set>
@ -30,6 +37,9 @@
</set>
where is_active = '1' and device_code = #{deviceCode}
</update>
<update id="updateBindDev">
update pm_att_device_bind_record set un_bind_time=now() where dev_code=#{devCode} and un_bind_time is NULL
</update>
<delete id="delPmAttDevice">
delete from pm_att_device where device_code = #{deviceCode}
</delete>

View File

@ -92,10 +92,15 @@
from pm_sub where is_active='1' and sub_name= #{subName}
</select>
<select id="selectSubListAll" resultType="com.bonus.bmw.domain.po.PmSub">
select id,sub_name
from pm_sub where is_active='1'
select distinct ps.id,ps.sub_name
from pm_sub ps
left join bm_sub_contract psc on psc.sub_id=ps.id
where ps.is_active='1'
<if test="proId!=null and proId!=''">
and psc.pro_id=#{proId}
</if>
<if test="subComId!= null " >
and sub_com_id=#{subComId}
and ps.sub_com_id=#{subComId}
</if>
</select>
<select id="getSublistByProId" resultType="com.bonus.bmw.domain.po.PmSub">

View File

@ -57,10 +57,14 @@
</if>
</select>
<select id="selectSubTeamListAll" resultType="com.bonus.bmw.domain.po.PmSubTeam">
select pst.id as id,
pst.team_name as teamName
select distinct pst.id as id,
pst.team_name as teamName
from pm_sub_team pst
left join pm_sub_team_contract pstc on pstc.team_id=pst.id
where pst.is_active = '1'
<if test="proId !=null and proId!= ''" >
and pstc.pro_id=#{proId}
</if>
<if test="subId !=null and subId!= ''" >
and pst.sub_id=#{subId}
</if>