7 changed files with 920 additions and 0 deletions
-
80src/main/java/com/xujie/sys/modules/pms/controller/PartSparePurchaseImportController.java
-
69src/main/java/com/xujie/sys/modules/pms/data/PartSparePurchaseImportQueryData.java
-
109src/main/java/com/xujie/sys/modules/pms/entity/PartSparePurchaseImportEntity.java
-
22src/main/java/com/xujie/sys/modules/pms/mapper/PartSparePurchaseImportMapper.java
-
497src/main/java/com/xujie/sys/modules/pms/service/Impl/PartSparePurchaseImportServiceImpl.java
-
28src/main/java/com/xujie/sys/modules/pms/service/PartSparePurchaseImportService.java
-
115src/main/resources/mapper/pms/PartSparePurchaseImportMapper.xml
@ -0,0 +1,80 @@ |
|||||
|
package com.xujie.sys.modules.pms.controller; |
||||
|
|
||||
|
import com.xujie.sys.common.utils.PageUtils; |
||||
|
import com.xujie.sys.common.utils.R; |
||||
|
import com.xujie.sys.modules.pms.data.PartSparePurchaseImportQueryData; |
||||
|
import com.xujie.sys.modules.pms.entity.PartSparePurchaseImportEntity; |
||||
|
import com.xujie.sys.modules.pms.service.PartSparePurchaseImportService; |
||||
|
import jakarta.servlet.http.HttpServletResponse; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.web.bind.annotation.*; |
||||
|
import org.springframework.web.multipart.MultipartFile; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
@Slf4j |
||||
|
@RestController |
||||
|
@RequestMapping("/pms/partspare/purchaseImport") |
||||
|
public class PartSparePurchaseImportController { |
||||
|
|
||||
|
@Autowired |
||||
|
private PartSparePurchaseImportService partSparePurchaseImportService; |
||||
|
|
||||
|
@PostMapping("/queryPage") |
||||
|
public R queryPage(@RequestBody PartSparePurchaseImportQueryData data) { |
||||
|
PageUtils page = partSparePurchaseImportService.queryPage(data); |
||||
|
return R.ok() |
||||
|
.put("page", page) |
||||
|
.put("summary", partSparePurchaseImportService.querySummary(data)); |
||||
|
} |
||||
|
|
||||
|
@PostMapping("/save") |
||||
|
public R save(@RequestBody PartSparePurchaseImportEntity data) { |
||||
|
partSparePurchaseImportService.saveData(data); |
||||
|
return R.ok("保存成功"); |
||||
|
} |
||||
|
|
||||
|
@PostMapping("/update") |
||||
|
public R update(@RequestBody PartSparePurchaseImportEntity data) { |
||||
|
partSparePurchaseImportService.updateData(data); |
||||
|
return R.ok("修改成功"); |
||||
|
} |
||||
|
|
||||
|
@PostMapping("/delete") |
||||
|
public R delete(@RequestBody Object[] ids) { |
||||
|
if (ids == null || ids.length == 0) { |
||||
|
return R.error("请先选择需要删除的数据"); |
||||
|
} |
||||
|
List<Long> idList = new ArrayList<>(); |
||||
|
for (Object id : ids) { |
||||
|
if (id == null) { |
||||
|
continue; |
||||
|
} |
||||
|
try { |
||||
|
idList.add(Long.parseLong(String.valueOf(id))); |
||||
|
} catch (Exception e) { |
||||
|
return R.error("删除参数错误,ID格式无效"); |
||||
|
} |
||||
|
} |
||||
|
if (idList.isEmpty()) { |
||||
|
return R.error("请先选择需要删除的数据"); |
||||
|
} |
||||
|
partSparePurchaseImportService.deleteData(idList.toArray(new Long[0])); |
||||
|
return R.ok("删除成功"); |
||||
|
} |
||||
|
|
||||
|
@PostMapping("/importExcel") |
||||
|
public R importExcel(@RequestParam("file") MultipartFile file, |
||||
|
@RequestParam("site") String site, |
||||
|
@RequestParam("buNo") String buNo) { |
||||
|
int count = partSparePurchaseImportService.importExcel(file, site, buNo); |
||||
|
return R.ok("导入成功").put("count", count); |
||||
|
} |
||||
|
|
||||
|
@GetMapping("/downloadTemplate") |
||||
|
public void downloadTemplate(HttpServletResponse response) { |
||||
|
partSparePurchaseImportService.downloadTemplate(response); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,69 @@ |
|||||
|
package com.xujie.sys.modules.pms.data; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonFormat; |
||||
|
import lombok.Data; |
||||
|
import org.springframework.format.annotation.DateTimeFormat; |
||||
|
|
||||
|
import java.math.BigDecimal; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
/** |
||||
|
* 备件采购订单导入记录查询条件 |
||||
|
*/ |
||||
|
@Data |
||||
|
public class PartSparePurchaseImportQueryData { |
||||
|
|
||||
|
private String username; |
||||
|
|
||||
|
private String site; |
||||
|
|
||||
|
private String buNo; |
||||
|
|
||||
|
private String supplierName; |
||||
|
|
||||
|
private String currencyType; |
||||
|
|
||||
|
private String orderNo; |
||||
|
|
||||
|
private String itemCode; |
||||
|
|
||||
|
private String itemName; |
||||
|
|
||||
|
private String projectBu; |
||||
|
|
||||
|
private String requestNo; |
||||
|
|
||||
|
private String requestBy; |
||||
|
|
||||
|
private String importBy; |
||||
|
|
||||
|
private String sourceFileName; |
||||
|
|
||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd") |
||||
|
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") |
||||
|
private Date orderDateStart; |
||||
|
|
||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
||||
|
private Date orderDateEnd; |
||||
|
|
||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd") |
||||
|
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") |
||||
|
private Date importDateStart; |
||||
|
|
||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
||||
|
private Date importDateEnd; |
||||
|
|
||||
|
private BigDecimal qtyStart; |
||||
|
|
||||
|
private BigDecimal qtyEnd; |
||||
|
|
||||
|
private BigDecimal localTotalAmountStart; |
||||
|
|
||||
|
private BigDecimal localTotalAmountEnd; |
||||
|
|
||||
|
private Integer page = 1; |
||||
|
|
||||
|
private Integer limit = 20; |
||||
|
} |
||||
@ -0,0 +1,109 @@ |
|||||
|
package com.xujie.sys.modules.pms.entity; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.annotation.IdType; |
||||
|
import com.baomidou.mybatisplus.annotation.TableField; |
||||
|
import com.baomidou.mybatisplus.annotation.TableId; |
||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||
|
import com.fasterxml.jackson.annotation.JsonFormat; |
||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize; |
||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; |
||||
|
import lombok.Data; |
||||
|
import org.springframework.format.annotation.DateTimeFormat; |
||||
|
|
||||
|
import java.math.BigDecimal; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
/** |
||||
|
* 备件采购订单导入记录实体 |
||||
|
*/ |
||||
|
@Data |
||||
|
@TableName("part_spare_purchase_import") |
||||
|
public class PartSparePurchaseImportEntity { |
||||
|
|
||||
|
@TableId(type = IdType.ASSIGN_ID) |
||||
|
@JsonSerialize(using = ToStringSerializer.class) |
||||
|
private Long id; |
||||
|
|
||||
|
private String site; |
||||
|
|
||||
|
@TableField("bu_no") |
||||
|
private String buNo; |
||||
|
|
||||
|
@TableField("order_date") |
||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd") |
||||
|
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") |
||||
|
private Date orderDate; |
||||
|
|
||||
|
@TableField("supplier_name") |
||||
|
private String supplierName; |
||||
|
|
||||
|
@TableField("currency_type") |
||||
|
private String currencyType; |
||||
|
|
||||
|
@TableField("order_no") |
||||
|
private String orderNo; |
||||
|
|
||||
|
@TableField("item_code") |
||||
|
private String itemCode; |
||||
|
|
||||
|
@TableField("item_name") |
||||
|
private String itemName; |
||||
|
|
||||
|
private BigDecimal qty; |
||||
|
|
||||
|
@TableField("local_unit_price") |
||||
|
private BigDecimal localUnitPrice; |
||||
|
|
||||
|
@TableField("local_amount") |
||||
|
private BigDecimal localAmount; |
||||
|
|
||||
|
@TableField("local_tax_amount") |
||||
|
private BigDecimal localTaxAmount; |
||||
|
|
||||
|
@TableField("local_total_amount") |
||||
|
private BigDecimal localTotalAmount; |
||||
|
|
||||
|
@TableField("unit_name") |
||||
|
private String unitName; |
||||
|
|
||||
|
@TableField("project_bu") |
||||
|
private String projectBu; |
||||
|
|
||||
|
@TableField("request_no") |
||||
|
private String requestNo; |
||||
|
|
||||
|
@TableField("request_by") |
||||
|
private String requestBy; |
||||
|
|
||||
|
@TableField("import_date") |
||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
||||
|
private Date importDate; |
||||
|
|
||||
|
@TableField("import_by") |
||||
|
private String importBy; |
||||
|
|
||||
|
@TableField("source_file_name") |
||||
|
private String sourceFileName; |
||||
|
|
||||
|
private String remark; |
||||
|
|
||||
|
@TableField("created_by") |
||||
|
private String createdBy; |
||||
|
|
||||
|
@TableField("created_date") |
||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
||||
|
private Date createdDate; |
||||
|
|
||||
|
@TableField("updated_by") |
||||
|
private String updatedBy; |
||||
|
|
||||
|
@TableField("updated_date") |
||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
||||
|
private Date updatedDate; |
||||
|
|
||||
|
@TableField("is_deleted") |
||||
|
private String isDeleted; |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
package com.xujie.sys.modules.pms.mapper; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
import com.baomidou.mybatisplus.core.metadata.IPage; |
||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
|
import com.xujie.sys.modules.pms.data.PartSparePurchaseImportQueryData; |
||||
|
import com.xujie.sys.modules.pms.entity.PartSparePurchaseImportEntity; |
||||
|
import org.apache.ibatis.annotations.Mapper; |
||||
|
import org.apache.ibatis.annotations.Param; |
||||
|
|
||||
|
import java.util.Map; |
||||
|
|
||||
|
@Mapper |
||||
|
public interface PartSparePurchaseImportMapper extends BaseMapper<PartSparePurchaseImportEntity> { |
||||
|
|
||||
|
IPage<PartSparePurchaseImportEntity> queryPage( |
||||
|
Page<PartSparePurchaseImportEntity> page, |
||||
|
@Param("query") PartSparePurchaseImportQueryData query |
||||
|
); |
||||
|
|
||||
|
Map<String, Object> querySummary(@Param("query") PartSparePurchaseImportQueryData query); |
||||
|
} |
||||
@ -0,0 +1,497 @@ |
|||||
|
package com.xujie.sys.modules.pms.service.Impl; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
||||
|
import com.baomidou.mybatisplus.core.metadata.IPage; |
||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
|
import com.xujie.sys.common.utils.PageUtils; |
||||
|
import com.xujie.sys.modules.pms.data.PartSparePurchaseImportQueryData; |
||||
|
import com.xujie.sys.modules.pms.entity.PartSparePurchaseImportEntity; |
||||
|
import com.xujie.sys.modules.pms.mapper.PartSparePurchaseImportMapper; |
||||
|
import com.xujie.sys.modules.pms.service.PartSparePurchaseImportService; |
||||
|
import com.xujie.sys.modules.sys.entity.SysUserEntity; |
||||
|
import jakarta.servlet.http.HttpServletResponse; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.apache.poi.ss.usermodel.*; |
||||
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook; |
||||
|
import org.apache.shiro.SecurityUtils; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.springframework.transaction.annotation.Transactional; |
||||
|
import org.springframework.util.StringUtils; |
||||
|
import org.springframework.web.multipart.MultipartFile; |
||||
|
|
||||
|
import java.io.IOException; |
||||
|
import java.net.URLEncoder; |
||||
|
import java.math.BigDecimal; |
||||
|
import java.time.LocalDate; |
||||
|
import java.time.LocalDateTime; |
||||
|
import java.time.ZoneId; |
||||
|
import java.time.format.DateTimeFormatter; |
||||
|
import java.time.format.DateTimeFormatterBuilder; |
||||
|
import java.time.format.DateTimeParseException; |
||||
|
import java.time.temporal.TemporalAccessor; |
||||
|
import java.util.*; |
||||
|
|
||||
|
@Slf4j |
||||
|
@Service |
||||
|
public class PartSparePurchaseImportServiceImpl extends ServiceImpl<PartSparePurchaseImportMapper, PartSparePurchaseImportEntity> |
||||
|
implements PartSparePurchaseImportService { |
||||
|
|
||||
|
private static final int IMPORT_BATCH_SIZE = 100; |
||||
|
|
||||
|
@Override |
||||
|
public PageUtils queryPage(PartSparePurchaseImportQueryData queryData) { |
||||
|
queryData.setUsername(getCurrentUsername()); |
||||
|
queryData.setBuNo(normalizeBuNo(queryData.getSite(), queryData.getBuNo())); |
||||
|
long pageNo = queryData.getPage() == null || queryData.getPage() < 1 ? 1 : queryData.getPage(); |
||||
|
long pageSize = queryData.getLimit() == null || queryData.getLimit() < 1 ? 20 : queryData.getLimit(); |
||||
|
IPage<PartSparePurchaseImportEntity> page = baseMapper.queryPage( |
||||
|
new Page<>(pageNo, pageSize), |
||||
|
queryData |
||||
|
); |
||||
|
return new PageUtils(page); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Map<String, BigDecimal> querySummary(PartSparePurchaseImportQueryData queryData) { |
||||
|
queryData.setUsername(getCurrentUsername()); |
||||
|
queryData.setBuNo(normalizeBuNo(queryData.getSite(), queryData.getBuNo())); |
||||
|
Map<String, Object> summary = baseMapper.querySummary(queryData); |
||||
|
Map<String, BigDecimal> result = new HashMap<>(2); |
||||
|
result.put("totalQty", toBigDecimal(summary == null ? null : summary.get("totalQty"))); |
||||
|
result.put("totalLocalTotalAmount", toBigDecimal(summary == null ? null : summary.get("totalLocalTotalAmount"))); |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public void saveData(PartSparePurchaseImportEntity data) { |
||||
|
data.setBuNo(normalizeBuNo(data.getSite(), data.getBuNo())); |
||||
|
validateBaseData(data); |
||||
|
String username = getCurrentUsername(); |
||||
|
Date now = new Date(); |
||||
|
data.setId(null); |
||||
|
data.setImportDate(now); |
||||
|
data.setImportBy(username); |
||||
|
data.setCreatedBy(username); |
||||
|
data.setCreatedDate(now); |
||||
|
data.setUpdatedBy(username); |
||||
|
data.setUpdatedDate(now); |
||||
|
data.setIsDeleted("0"); |
||||
|
this.save(data); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public void updateData(PartSparePurchaseImportEntity data) { |
||||
|
if (data.getId() == null) { |
||||
|
throw new RuntimeException("参数错误:记录ID不能为空"); |
||||
|
} |
||||
|
data.setBuNo(normalizeBuNo(data.getSite(), data.getBuNo())); |
||||
|
validateBaseData(data); |
||||
|
PartSparePurchaseImportEntity dbData = this.getOne( |
||||
|
new LambdaQueryWrapper<PartSparePurchaseImportEntity>() |
||||
|
.eq(PartSparePurchaseImportEntity::getId, data.getId()) |
||||
|
.eq(PartSparePurchaseImportEntity::getIsDeleted, "0") |
||||
|
); |
||||
|
if (dbData == null) { |
||||
|
throw new RuntimeException("记录不存在或已删除,请刷新后重试"); |
||||
|
} |
||||
|
|
||||
|
dbData.setSite(data.getSite()); |
||||
|
dbData.setBuNo(data.getBuNo()); |
||||
|
dbData.setOrderDate(data.getOrderDate()); |
||||
|
dbData.setSupplierName(data.getSupplierName()); |
||||
|
dbData.setCurrencyType(data.getCurrencyType()); |
||||
|
dbData.setOrderNo(data.getOrderNo()); |
||||
|
dbData.setItemCode(data.getItemCode()); |
||||
|
dbData.setItemName(data.getItemName()); |
||||
|
dbData.setQty(data.getQty()); |
||||
|
dbData.setLocalUnitPrice(data.getLocalUnitPrice()); |
||||
|
dbData.setLocalAmount(data.getLocalAmount()); |
||||
|
dbData.setLocalTaxAmount(data.getLocalTaxAmount()); |
||||
|
dbData.setLocalTotalAmount(data.getLocalTotalAmount()); |
||||
|
dbData.setUnitName(data.getUnitName()); |
||||
|
dbData.setProjectBu(data.getProjectBu()); |
||||
|
dbData.setRequestNo(data.getRequestNo()); |
||||
|
dbData.setRequestBy(data.getRequestBy()); |
||||
|
dbData.setRemark(data.getRemark()); |
||||
|
dbData.setUpdatedBy(getCurrentUsername()); |
||||
|
dbData.setUpdatedDate(new Date()); |
||||
|
this.updateById(dbData); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public void deleteData(Long[] ids) { |
||||
|
if (ids == null || ids.length == 0) { |
||||
|
throw new RuntimeException("请先选择需要删除的数据"); |
||||
|
} |
||||
|
List<Long> idList = Arrays.asList(ids); |
||||
|
long existsCount = this.lambdaQuery() |
||||
|
.in(PartSparePurchaseImportEntity::getId, idList) |
||||
|
.count(); |
||||
|
if (existsCount == 0) { |
||||
|
throw new RuntimeException("记录不存在或已删除,请刷新后重试"); |
||||
|
} |
||||
|
boolean success = this.removeByIds(idList); |
||||
|
if (!success) { |
||||
|
throw new RuntimeException("删除失败,请稍后重试"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public int importExcel(MultipartFile file, String site, String buNo) { |
||||
|
if (!StringUtils.hasText(site)) { |
||||
|
throw new RuntimeException("工厂不能为空"); |
||||
|
} |
||||
|
String normalizedBuNo = normalizeBuNo(site, buNo); |
||||
|
if (!StringUtils.hasText(normalizedBuNo)) { |
||||
|
throw new RuntimeException("BU不能为空"); |
||||
|
} |
||||
|
if (file == null || file.isEmpty()) { |
||||
|
throw new RuntimeException("上传文件不能为空"); |
||||
|
} |
||||
|
String fileName = file.getOriginalFilename(); |
||||
|
if (!StringUtils.hasText(fileName)) { |
||||
|
throw new RuntimeException("上传文件名不能为空"); |
||||
|
} |
||||
|
String lowerName = fileName.toLowerCase(); |
||||
|
if (!lowerName.endsWith(".xlsx") && !lowerName.endsWith(".xls")) { |
||||
|
throw new RuntimeException("请上传Excel文件(.xlsx 或 .xls)"); |
||||
|
} |
||||
|
|
||||
|
String username = getCurrentUsername(); |
||||
|
Date importTime = new Date(); |
||||
|
List<PartSparePurchaseImportEntity> rows = parseExcel(file, site, normalizedBuNo, fileName, username, importTime); |
||||
|
if (rows.isEmpty()) { |
||||
|
throw new RuntimeException("未读取到有效导入数据"); |
||||
|
} |
||||
|
this.saveBatch(rows, IMPORT_BATCH_SIZE); |
||||
|
return rows.size(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void downloadTemplate(HttpServletResponse response) { |
||||
|
try (Workbook workbook = new XSSFWorkbook()) { |
||||
|
Sheet sheet = workbook.createSheet("采购订单导入"); |
||||
|
Row header = sheet.createRow(0); |
||||
|
String[] headers = { |
||||
|
"日期", "供应商", "币种", "订单编号", "存货编码", "存货名称", "数量", |
||||
|
"本币单价", "本币金额", "本币税额", "本币价税合计", "单位", |
||||
|
"项目目录(BU)", "请单编号", "请购员" |
||||
|
}; |
||||
|
for (int i = 0; i < headers.length; i++) { |
||||
|
Cell cell = header.createCell(i); |
||||
|
cell.setCellValue(headers[i]); |
||||
|
sheet.setColumnWidth(i, 18 * 256); |
||||
|
} |
||||
|
|
||||
|
Row sample = sheet.createRow(1); |
||||
|
sample.createCell(0).setCellValue("2026-04-02"); |
||||
|
sample.createCell(1).setCellValue("南海发展工程有限公司"); |
||||
|
sample.createCell(2).setCellValue("人民币"); |
||||
|
sample.createCell(3).setCellValue("CPMFL2026040019"); |
||||
|
sample.createCell(4).setCellValue("401.000000"); |
||||
|
sample.createCell(5).setCellValue("叉车手柄切片"); |
||||
|
sample.createCell(6).setCellValue("3"); |
||||
|
sample.createCell(7).setCellValue("1"); |
||||
|
sample.createCell(8).setCellValue("3"); |
||||
|
sample.createCell(9).setCellValue("0.39"); |
||||
|
sample.createCell(10).setCellValue("3.39"); |
||||
|
sample.createCell(11).setCellValue("起"); |
||||
|
sample.createCell(12).setCellValue("39"); |
||||
|
sample.createCell(13).setCellValue("CPMFL2026040019"); |
||||
|
sample.createCell(14).setCellValue("段晓浩"); |
||||
|
|
||||
|
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); |
||||
|
response.setCharacterEncoding("UTF-8"); |
||||
|
response.setHeader( |
||||
|
"Content-Disposition", |
||||
|
"attachment;filename=" + URLEncoder.encode("采购订单导入模板.xlsx", "UTF-8") |
||||
|
); |
||||
|
workbook.write(response.getOutputStream()); |
||||
|
response.flushBuffer(); |
||||
|
} catch (Exception e) { |
||||
|
log.error("下载采购订单导入模板失败: {}", e.getMessage(), e); |
||||
|
throw new RuntimeException("下载模板失败: " + e.getMessage()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private List<PartSparePurchaseImportEntity> parseExcel( |
||||
|
MultipartFile file, |
||||
|
String site, |
||||
|
String buNo, |
||||
|
String sourceFileName, |
||||
|
String username, |
||||
|
Date importTime |
||||
|
) { |
||||
|
List<PartSparePurchaseImportEntity> result = new ArrayList<>(); |
||||
|
DataFormatter formatter = new DataFormatter(); |
||||
|
try (Workbook workbook = WorkbookFactory.create(file.getInputStream())) { |
||||
|
Sheet sheet = workbook.getSheetAt(0); |
||||
|
if (sheet == null) { |
||||
|
return result; |
||||
|
} |
||||
|
Row headerRow = sheet.getRow(0); |
||||
|
Map<String, Integer> headerMap = buildHeaderMap(headerRow, formatter); |
||||
|
HeaderIndex index = resolveHeaderIndex(headerMap); |
||||
|
|
||||
|
for (int i = 1; i <= sheet.getLastRowNum(); i++) { |
||||
|
Row row = sheet.getRow(i); |
||||
|
if (row == null || isEmptyRow(row, formatter)) { |
||||
|
continue; |
||||
|
} |
||||
|
PartSparePurchaseImportEntity entity = new PartSparePurchaseImportEntity(); |
||||
|
entity.setSite(site); |
||||
|
entity.setBuNo(buNo); |
||||
|
entity.setOrderDate(parseDateCell(row.getCell(index.orderDate), formatter)); |
||||
|
entity.setSupplierName(getCellString(row.getCell(index.supplierName), formatter)); |
||||
|
entity.setCurrencyType(getCellString(row.getCell(index.currencyType), formatter)); |
||||
|
entity.setOrderNo(getCellString(row.getCell(index.orderNo), formatter)); |
||||
|
entity.setItemCode(getCellString(row.getCell(index.itemCode), formatter)); |
||||
|
entity.setItemName(getCellString(row.getCell(index.itemName), formatter)); |
||||
|
entity.setQty(parseDecimal(row.getCell(index.qty), formatter)); |
||||
|
entity.setLocalUnitPrice(parseDecimal(row.getCell(index.localUnitPrice), formatter)); |
||||
|
entity.setLocalAmount(parseDecimal(row.getCell(index.localAmount), formatter)); |
||||
|
entity.setLocalTaxAmount(parseDecimal(row.getCell(index.localTaxAmount), formatter)); |
||||
|
entity.setLocalTotalAmount(parseDecimal(row.getCell(index.localTotalAmount), formatter)); |
||||
|
entity.setUnitName(getCellString(row.getCell(index.unitName), formatter)); |
||||
|
entity.setProjectBu(getCellString(row.getCell(index.projectBu), formatter)); |
||||
|
entity.setRequestNo(getCellString(row.getCell(index.requestNo), formatter)); |
||||
|
entity.setRequestBy(getCellString(row.getCell(index.requestBy), formatter)); |
||||
|
entity.setImportDate(importTime); |
||||
|
entity.setImportBy(username); |
||||
|
entity.setSourceFileName(sourceFileName); |
||||
|
entity.setCreatedBy(username); |
||||
|
entity.setCreatedDate(importTime); |
||||
|
entity.setUpdatedBy(username); |
||||
|
entity.setUpdatedDate(importTime); |
||||
|
entity.setIsDeleted("0"); |
||||
|
|
||||
|
// 至少有订单编号或存货编码才作为有效行 |
||||
|
if (!StringUtils.hasText(entity.getOrderNo()) && !StringUtils.hasText(entity.getItemCode())) { |
||||
|
continue; |
||||
|
} |
||||
|
validateBaseData(entity); |
||||
|
result.add(entity); |
||||
|
} |
||||
|
} catch (IOException e) { |
||||
|
throw new RuntimeException("解析Excel失败: " + e.getMessage()); |
||||
|
} catch (Exception e) { |
||||
|
throw new RuntimeException("导入失败: " + e.getMessage()); |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
private Map<String, Integer> buildHeaderMap(Row headerRow, DataFormatter formatter) { |
||||
|
Map<String, Integer> headerMap = new HashMap<>(); |
||||
|
if (headerRow == null) { |
||||
|
return headerMap; |
||||
|
} |
||||
|
for (int i = 0; i < headerRow.getLastCellNum(); i++) { |
||||
|
String value = normalizeHeader(getCellString(headerRow.getCell(i), formatter)); |
||||
|
if (StringUtils.hasText(value)) { |
||||
|
headerMap.put(value, i); |
||||
|
} |
||||
|
} |
||||
|
return headerMap; |
||||
|
} |
||||
|
|
||||
|
private HeaderIndex resolveHeaderIndex(Map<String, Integer> headerMap) { |
||||
|
HeaderIndex index = new HeaderIndex(); |
||||
|
index.orderDate = resolveColumn(headerMap, 1, "日期", "订单日期"); |
||||
|
index.supplierName = resolveColumn(headerMap, 2, "供应商", "供应商名称"); |
||||
|
index.currencyType = resolveColumn(headerMap, 3, "币种"); |
||||
|
index.orderNo = resolveColumn(headerMap, 4, "订单编号", "采购订单号"); |
||||
|
index.itemCode = resolveColumn(headerMap, 5, "存货编码", "物料编码"); |
||||
|
index.itemName = resolveColumn(headerMap, 6, "存货名称", "物料名称"); |
||||
|
index.qty = resolveColumn(headerMap, 7, "数量", "订单数量"); |
||||
|
index.localUnitPrice = resolveColumn(headerMap, 8, "本币单价"); |
||||
|
index.localAmount = resolveColumn(headerMap, 9, "本币金额"); |
||||
|
index.localTaxAmount = resolveColumn(headerMap, 10, "本币税额"); |
||||
|
index.localTotalAmount = resolveColumn(headerMap, 11, "本币价税合计"); |
||||
|
index.unitName = resolveColumn(headerMap, 12, "单位"); |
||||
|
index.projectBu = resolveColumn(headerMap, 13, "项目目录(bu)", "项目目录(bu)", "项目目录bu", "项目目录"); |
||||
|
index.requestNo = resolveColumn(headerMap, 14, "请单编号", "请购单号", "请购编号"); |
||||
|
index.requestBy = resolveColumn(headerMap, 15, "请购员", "请购人", "采购员"); |
||||
|
return index; |
||||
|
} |
||||
|
|
||||
|
private Integer resolveColumn(Map<String, Integer> headerMap, int fallbackIndex, String... aliases) { |
||||
|
for (String alias : aliases) { |
||||
|
Integer index = headerMap.get(normalizeHeader(alias)); |
||||
|
if (index != null) { |
||||
|
return index; |
||||
|
} |
||||
|
} |
||||
|
return fallbackIndex; |
||||
|
} |
||||
|
|
||||
|
private String normalizeHeader(String header) { |
||||
|
if (!StringUtils.hasText(header)) { |
||||
|
return ""; |
||||
|
} |
||||
|
return header.trim() |
||||
|
.replace("(", "(") |
||||
|
.replace(")", ")") |
||||
|
.replace(" ", "") |
||||
|
.toLowerCase(); |
||||
|
} |
||||
|
|
||||
|
private boolean isEmptyRow(Row row, DataFormatter formatter) { |
||||
|
int cells = row.getLastCellNum(); |
||||
|
for (int i = 0; i < cells; i++) { |
||||
|
if (StringUtils.hasText(getCellString(row.getCell(i), formatter))) { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
private String getCellString(Cell cell, DataFormatter formatter) { |
||||
|
if (cell == null) { |
||||
|
return ""; |
||||
|
} |
||||
|
return formatter.formatCellValue(cell).trim(); |
||||
|
} |
||||
|
|
||||
|
private BigDecimal parseDecimal(Cell cell, DataFormatter formatter) { |
||||
|
String value = getCellString(cell, formatter); |
||||
|
if (!StringUtils.hasText(value)) { |
||||
|
return null; |
||||
|
} |
||||
|
String normalized = value.replace(",", "").replace(" ", ""); |
||||
|
if (!StringUtils.hasText(normalized)) { |
||||
|
return null; |
||||
|
} |
||||
|
try { |
||||
|
return new BigDecimal(normalized); |
||||
|
} catch (Exception e) { |
||||
|
throw new RuntimeException("数值格式错误: " + value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private Date parseDateCell(Cell cell, DataFormatter formatter) { |
||||
|
if (cell == null) { |
||||
|
return null; |
||||
|
} |
||||
|
if (cell.getCellType() == CellType.NUMERIC && DateUtil.isCellDateFormatted(cell)) { |
||||
|
return cell.getDateCellValue(); |
||||
|
} |
||||
|
String value = getCellString(cell, formatter); |
||||
|
if (!StringUtils.hasText(value)) { |
||||
|
return null; |
||||
|
} |
||||
|
return parseDateString(value); |
||||
|
} |
||||
|
|
||||
|
private Date parseDateString(String value) { |
||||
|
List<DateTimeFormatter> formatters = Arrays.asList( |
||||
|
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"), |
||||
|
DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"), |
||||
|
DateTimeFormatter.ofPattern("yyyy-MM-dd"), |
||||
|
DateTimeFormatter.ofPattern("yyyy/MM/dd"), |
||||
|
DateTimeFormatter.ofPattern("yyyyMMdd"), |
||||
|
new DateTimeFormatterBuilder() |
||||
|
.appendOptional(DateTimeFormatter.ofPattern("yyyy-M-d")) |
||||
|
.appendOptional(DateTimeFormatter.ofPattern("yyyy/M/d")) |
||||
|
.toFormatter() |
||||
|
); |
||||
|
for (DateTimeFormatter formatter : formatters) { |
||||
|
try { |
||||
|
TemporalAccessor accessor = formatter.parseBest(value, LocalDateTime::from, LocalDate::from); |
||||
|
if (accessor instanceof LocalDateTime) { |
||||
|
return Date.from(((LocalDateTime) accessor).atZone(ZoneId.systemDefault()).toInstant()); |
||||
|
} |
||||
|
if (accessor instanceof LocalDate) { |
||||
|
return Date.from(((LocalDate) accessor).atStartOfDay(ZoneId.systemDefault()).toInstant()); |
||||
|
} |
||||
|
} catch (DateTimeParseException ignored) { |
||||
|
} |
||||
|
} |
||||
|
throw new RuntimeException("日期格式错误: " + value); |
||||
|
} |
||||
|
|
||||
|
private BigDecimal toBigDecimal(Object value) { |
||||
|
if (value == null) { |
||||
|
return BigDecimal.ZERO; |
||||
|
} |
||||
|
if (value instanceof BigDecimal) { |
||||
|
return (BigDecimal) value; |
||||
|
} |
||||
|
if (value instanceof Number) { |
||||
|
return new BigDecimal(value.toString()); |
||||
|
} |
||||
|
String text = value.toString(); |
||||
|
if (!StringUtils.hasText(text)) { |
||||
|
return BigDecimal.ZERO; |
||||
|
} |
||||
|
try { |
||||
|
return new BigDecimal(text.trim()); |
||||
|
} catch (Exception e) { |
||||
|
return BigDecimal.ZERO; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private String normalizeBuNo(String site, String buNo) { |
||||
|
if (!StringUtils.hasText(buNo)) { |
||||
|
return buNo; |
||||
|
} |
||||
|
String siteText = StringUtils.hasText(site) ? site.trim() : ""; |
||||
|
String buText = buNo.trim(); |
||||
|
if (!StringUtils.hasText(siteText)) { |
||||
|
return buText; |
||||
|
} |
||||
|
String prefix = siteText + "_"; |
||||
|
if (buText.startsWith(prefix)) { |
||||
|
return buText.substring(prefix.length()); |
||||
|
} |
||||
|
return buText; |
||||
|
} |
||||
|
|
||||
|
private void validateBaseData(PartSparePurchaseImportEntity data) { |
||||
|
if (!StringUtils.hasText(data.getSite())) { |
||||
|
throw new RuntimeException("工厂不能为空"); |
||||
|
} |
||||
|
if (!StringUtils.hasText(data.getBuNo())) { |
||||
|
throw new RuntimeException("BU不能为空"); |
||||
|
} |
||||
|
if (!StringUtils.hasText(data.getOrderNo())) { |
||||
|
throw new RuntimeException("订单编号不能为空"); |
||||
|
} |
||||
|
if (!StringUtils.hasText(data.getItemCode())) { |
||||
|
throw new RuntimeException("存货编码不能为空"); |
||||
|
} |
||||
|
if (data.getQty() != null && data.getQty().compareTo(BigDecimal.ZERO) < 0) { |
||||
|
throw new RuntimeException("数量不能小于0"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private String getCurrentUsername() { |
||||
|
Object principal = SecurityUtils.getSubject().getPrincipal(); |
||||
|
if (principal instanceof SysUserEntity) { |
||||
|
return ((SysUserEntity) principal).getUsername(); |
||||
|
} |
||||
|
return "system"; |
||||
|
} |
||||
|
|
||||
|
private static class HeaderIndex { |
||||
|
private Integer orderDate; |
||||
|
private Integer supplierName; |
||||
|
private Integer currencyType; |
||||
|
private Integer orderNo; |
||||
|
private Integer itemCode; |
||||
|
private Integer itemName; |
||||
|
private Integer qty; |
||||
|
private Integer localUnitPrice; |
||||
|
private Integer localAmount; |
||||
|
private Integer localTaxAmount; |
||||
|
private Integer localTotalAmount; |
||||
|
private Integer unitName; |
||||
|
private Integer projectBu; |
||||
|
private Integer requestNo; |
||||
|
private Integer requestBy; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
package com.xujie.sys.modules.pms.service; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.extension.service.IService; |
||||
|
import com.xujie.sys.common.utils.PageUtils; |
||||
|
import com.xujie.sys.modules.pms.data.PartSparePurchaseImportQueryData; |
||||
|
import com.xujie.sys.modules.pms.entity.PartSparePurchaseImportEntity; |
||||
|
import org.springframework.web.multipart.MultipartFile; |
||||
|
|
||||
|
import jakarta.servlet.http.HttpServletResponse; |
||||
|
import java.math.BigDecimal; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
public interface PartSparePurchaseImportService extends IService<PartSparePurchaseImportEntity> { |
||||
|
|
||||
|
PageUtils queryPage(PartSparePurchaseImportQueryData queryData); |
||||
|
|
||||
|
Map<String, BigDecimal> querySummary(PartSparePurchaseImportQueryData queryData); |
||||
|
|
||||
|
void saveData(PartSparePurchaseImportEntity data); |
||||
|
|
||||
|
void updateData(PartSparePurchaseImportEntity data); |
||||
|
|
||||
|
void deleteData(Long[] ids); |
||||
|
|
||||
|
int importExcel(MultipartFile file, String site, String buNo); |
||||
|
|
||||
|
void downloadTemplate(HttpServletResponse response); |
||||
|
} |
||||
@ -0,0 +1,115 @@ |
|||||
|
<?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.xujie.sys.modules.pms.mapper.PartSparePurchaseImportMapper"> |
||||
|
|
||||
|
<sql id="queryWhereClause"> |
||||
|
<where> |
||||
|
a.is_deleted = '0' |
||||
|
and a.site in (select site from eam_access_site where username = #{query.username}) |
||||
|
and (a.site + '-' + a.bu_no) in (select * from dbo.query_bu(#{query.username})) |
||||
|
<if test="query.site != null and query.site != ''"> |
||||
|
and a.site = #{query.site} |
||||
|
</if> |
||||
|
<if test="query.buNo != null and query.buNo != ''"> |
||||
|
and a.bu_no = #{query.buNo} |
||||
|
</if> |
||||
|
<if test="query.supplierName != null and query.supplierName != ''"> |
||||
|
and a.supplier_name like '%' + #{query.supplierName} + '%' |
||||
|
</if> |
||||
|
<if test="query.currencyType != null and query.currencyType != ''"> |
||||
|
and a.currency_type = #{query.currencyType} |
||||
|
</if> |
||||
|
<if test="query.orderNo != null and query.orderNo != ''"> |
||||
|
and a.order_no like '%' + #{query.orderNo} + '%' |
||||
|
</if> |
||||
|
<if test="query.itemCode != null and query.itemCode != ''"> |
||||
|
and a.item_code like '%' + #{query.itemCode} + '%' |
||||
|
</if> |
||||
|
<if test="query.itemName != null and query.itemName != ''"> |
||||
|
and a.item_name like '%' + #{query.itemName} + '%' |
||||
|
</if> |
||||
|
<if test="query.projectBu != null and query.projectBu != ''"> |
||||
|
and a.project_bu like '%' + #{query.projectBu} + '%' |
||||
|
</if> |
||||
|
<if test="query.requestNo != null and query.requestNo != ''"> |
||||
|
and a.request_no like '%' + #{query.requestNo} + '%' |
||||
|
</if> |
||||
|
<if test="query.requestBy != null and query.requestBy != ''"> |
||||
|
and a.request_by like '%' + #{query.requestBy} + '%' |
||||
|
</if> |
||||
|
<if test="query.importBy != null and query.importBy != ''"> |
||||
|
and a.import_by like '%' + #{query.importBy} + '%' |
||||
|
</if> |
||||
|
<if test="query.sourceFileName != null and query.sourceFileName != ''"> |
||||
|
and a.source_file_name like '%' + #{query.sourceFileName} + '%' |
||||
|
</if> |
||||
|
<if test="query.orderDateStart != null"> |
||||
|
and a.order_date >= #{query.orderDateStart} |
||||
|
</if> |
||||
|
<if test="query.orderDateEnd != null"> |
||||
|
and a.order_date <= #{query.orderDateEnd} |
||||
|
</if> |
||||
|
<if test="query.importDateStart != null"> |
||||
|
and a.import_date >= #{query.importDateStart} |
||||
|
</if> |
||||
|
<if test="query.importDateEnd != null"> |
||||
|
and a.import_date <= #{query.importDateEnd} |
||||
|
</if> |
||||
|
<if test="query.qtyStart != null"> |
||||
|
and a.qty >= #{query.qtyStart} |
||||
|
</if> |
||||
|
<if test="query.qtyEnd != null"> |
||||
|
and a.qty <= #{query.qtyEnd} |
||||
|
</if> |
||||
|
<if test="query.localTotalAmountStart != null"> |
||||
|
and a.local_total_amount >= #{query.localTotalAmountStart} |
||||
|
</if> |
||||
|
<if test="query.localTotalAmountEnd != null"> |
||||
|
and a.local_total_amount <= #{query.localTotalAmountEnd} |
||||
|
</if> |
||||
|
</where> |
||||
|
</sql> |
||||
|
|
||||
|
<select id="queryPage" resultType="com.xujie.sys.modules.pms.entity.PartSparePurchaseImportEntity"> |
||||
|
select |
||||
|
a.id, |
||||
|
a.site, |
||||
|
a.bu_no as buNo, |
||||
|
a.order_date as orderDate, |
||||
|
a.supplier_name as supplierName, |
||||
|
a.currency_type as currencyType, |
||||
|
a.order_no as orderNo, |
||||
|
a.item_code as itemCode, |
||||
|
a.item_name as itemName, |
||||
|
a.qty, |
||||
|
a.local_unit_price as localUnitPrice, |
||||
|
a.local_amount as localAmount, |
||||
|
a.local_tax_amount as localTaxAmount, |
||||
|
a.local_total_amount as localTotalAmount, |
||||
|
a.unit_name as unitName, |
||||
|
a.project_bu as projectBu, |
||||
|
a.request_no as requestNo, |
||||
|
a.request_by as requestBy, |
||||
|
a.import_date as importDate, |
||||
|
a.import_by as importBy, |
||||
|
a.source_file_name as sourceFileName, |
||||
|
a.remark, |
||||
|
a.created_by as createdBy, |
||||
|
a.created_date as createdDate, |
||||
|
a.updated_by as updatedBy, |
||||
|
a.updated_date as updatedDate, |
||||
|
a.is_deleted as isDeleted |
||||
|
from part_spare_purchase_import a |
||||
|
<include refid="queryWhereClause"/> |
||||
|
order by a.import_date desc, a.id desc |
||||
|
</select> |
||||
|
|
||||
|
<select id="querySummary" resultType="java.util.HashMap"> |
||||
|
select |
||||
|
ISNULL(sum(a.qty), 0) as totalQty, |
||||
|
ISNULL(sum(a.local_total_amount), 0) as totalLocalTotalAmount |
||||
|
from part_spare_purchase_import a |
||||
|
<include refid="queryWhereClause"/> |
||||
|
</select> |
||||
|
|
||||
|
</mapper> |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue