This commit is contained in:
2026-08-07 15:38:39 +08:00
parent 044b5dc3f8
commit 0ec904f68d
6 changed files with 187 additions and 2 deletions

View File

@@ -31,6 +31,7 @@ import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import org.springblade.core.boot.ctrl.BladeController;
import org.springblade.core.excel.util.ExcelUtil;
import org.springblade.common.excel.ImportFailureExcelUtil;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.secure.annotation.PreAuth;
@@ -38,6 +39,7 @@ import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.transport.excel.ShippingTemplateExcel;
import org.springblade.transport.excel.ShippingTemplateGoodsExcel;
import org.springblade.transport.pojo.entity.ShippingTemplate;
import org.springblade.transport.pojo.vo.BusinessRemoveResultVO;
import org.springblade.transport.pojo.vo.ShippingTemplateVO;
@@ -48,6 +50,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.List;
@@ -116,4 +119,22 @@ public class ShippingTemplateController extends BladeController {
return R.data(shippingTemplateService.copy(id));
}
@PostMapping("/import-goods")
@Operation(summary = "导入发货模板货物")
public R<List<ShippingTemplateGoodsExcel>> importGoods(MultipartFile file, HttpServletResponse response) {
List<ShippingTemplateGoodsExcel> data = ExcelUtil.read(file, ShippingTemplateGoodsExcel.class);
List<ShippingTemplateGoodsExcel> failures = shippingTemplateService.importGoods(data);
if (Func.isNotEmpty(failures)) {
ImportFailureExcelUtil.export(response, "发货模板货物导入失败明细" + DateUtil.time(), "导入失败明细", failures, ShippingTemplateGoodsExcel.class);
return null;
}
return R.data(data);
}
@GetMapping("/export-goods-template")
@Operation(summary = "导出发货模板货物导入模板")
public void exportGoodsTemplate(HttpServletResponse response) {
ExcelUtil.export(response, "发货模板货物导入模板", "货物信息", new ArrayList<ShippingTemplateGoodsExcel>(), ShippingTemplateGoodsExcel.class);
}
}

View File

@@ -0,0 +1,29 @@
package org.springblade.transport.excel;
import cn.idev.excel.annotation.ExcelIgnore;
import cn.idev.excel.annotation.ExcelProperty;
import cn.idev.excel.annotation.write.style.ColumnWidth;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/** 发货模板货物导入 Excel。 */
@Data
@ColumnWidth(20)
public class ShippingTemplateGoodsExcel implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@ExcelProperty("*货物名称") private String cargoName;
@ExcelProperty("*货物类型") private String cargoType;
@ExcelProperty("数量") private String quantity;
@ExcelProperty("数量单位") private String quantityUnit;
@ExcelProperty("包装") private String packageType;
@ExcelProperty("品牌") private String brand;
@ExcelProperty("规格") private String specification;
@ExcelProperty("型号") private String model;
@ExcelProperty("物料编码") private String materialCode;
@ExcelProperty("设备编码") private String deviceCode;
@ExcelProperty("备注") private String remark;
@ExcelIgnore private String errorMessage;
}

View File

@@ -25,6 +25,7 @@ package org.springblade.transport.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springblade.core.mp.base.BaseService;
import org.springblade.transport.excel.ShippingTemplateExcel;
import org.springblade.transport.excel.ShippingTemplateGoodsExcel;
import org.springblade.transport.pojo.entity.ShippingTemplate;
import org.springblade.transport.pojo.vo.BusinessRemoveResultVO;
import org.springblade.transport.pojo.vo.ShippingTemplateVO;
@@ -45,5 +46,6 @@ public interface IShippingTemplateService extends BaseService<ShippingTemplate>
BusinessRemoveResultVO removeShippingTemplate(String ids);
List<ShippingTemplateExcel> exportShippingTemplate(ShippingTemplateVO shippingTemplate, String ids);
ShippingTemplateVO copy(Long id);
List<ShippingTemplateGoodsExcel> importGoods(List<ShippingTemplateGoodsExcel> data);
}

View File

@@ -294,6 +294,13 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
customer.setLegalPerson(trimToEmpty(customer.getLegalPerson()));
customer.setContactPhone(trimToEmpty(customer.getContactPhone()));
customer.setDeptIds(trimToNull(customer.getDeptIds()));
if (Func.isNotEmpty(customer.getDeptIds())) {
List<Long> deptIdList = Func.toLongList(customer.getDeptIds());
if (Func.isEmpty(deptIdList)) {
throw new ServiceException("所属组织不正确");
}
customer.setDeptId(deptIdList.get(0));
}
customer.setDeptName(trimToEmpty(customer.getDeptName()));
customer.setAccessType(Func.isEmpty(customer.getAccessType()) ? ACCESS_TEMPORARY : customer.getAccessType());
customer.setApprovalStatus(Func.isEmpty(customer.getApprovalStatus()) ? APPROVAL_DRAFT : customer.getApprovalStatus());

View File

@@ -22,6 +22,9 @@
*/
package org.springblade.transport.service.impl;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -29,11 +32,14 @@ import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.system.cache.UserCache;
import org.springblade.system.pojo.entity.Dept;
import org.springblade.transport.excel.ShippingTemplateExcel;
import org.springblade.transport.excel.ShippingTemplateGoodsExcel;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.math.BigDecimal;
import org.springblade.transport.mapper.ShippingTemplateMapper;
import org.springblade.transport.pojo.entity.ShippingTemplate;
import org.springblade.transport.pojo.vo.BusinessRemoveResultVO;
@@ -164,6 +170,27 @@ public class ShippingTemplateServiceImpl extends BaseServiceImpl<ShippingTemplat
return detail(target.getId());
}
@Override
public List<ShippingTemplateGoodsExcel> importGoods(List<ShippingTemplateGoodsExcel> data) {
if (Func.isEmpty(data)) {
throw new ServiceException("导入数据不能为空");
}
List<ShippingTemplateGoodsExcel> failures = new ArrayList<>();
for (int index = 0; index < data.size(); index++) {
ShippingTemplateGoodsExcel item = data.get(index);
try {
if (Func.isEmpty(TransportBusinessSupport.trimToNull(item.getCargoName()))) throw new ServiceException("货物名称不能为空");
if (Func.isEmpty(TransportBusinessSupport.trimToNull(item.getCargoType()))) throw new ServiceException("货物类型不能为空");
if (StringUtil.isNotBlank(item.getQuantity()) && !item.getQuantity().trim().matches("^\\d+(\\.\\d{1,3})?$")) throw new ServiceException("数量只能输入非负数字最多保留3位小数");
if (StringUtil.isNotBlank(item.getRemark()) && item.getRemark().trim().length() > 200) throw new ServiceException("备注不能超过200个字符");
} catch (Exception e) {
item.setErrorMessage("" + (index + 2) + "行:" + e.getMessage());
failures.add(item);
}
}
return failures;
}
private LambdaQueryWrapper<ShippingTemplate> buildQuery(ShippingTemplateVO shippingTemplate) {
TransportBusinessSupport.validateAllDept(shippingTemplate.getAllDept(), "发货模板");
LambdaQueryWrapper<ShippingTemplate> queryWrapper = Wrappers.<ShippingTemplate>lambdaQuery().eq(ShippingTemplate::getIsDeleted, 0);
@@ -273,11 +300,110 @@ public class ShippingTemplateServiceImpl extends BaseServiceImpl<ShippingTemplat
TransportBusinessSupport.validateLength(shippingTemplate.getArrivalPhone(), 255, "收货联系方式不能超过255字");
TransportBusinessSupport.validateLength(shippingTemplate.getGoodsJson(), 8000, "货物信息不能超过8000字");
TransportBusinessSupport.validateLength(shippingTemplate.getFreightJson(), 8000, "运费信息不能超过8000字");
validateFreight(shippingTemplate);
TransportBusinessSupport.validateLength(shippingTemplate.getAttachmentsJson(), 8000, "附件不能超过8000字");
TransportBusinessSupport.validateLength(shippingTemplate.getDeptName(), 255, "所属组织不能超过255字");
TransportBusinessSupport.validateLength(shippingTemplate.getRemark(), 500, "备注不能超过500字");
}
private void validateFreight(ShippingTemplate shippingTemplate) {
if (!"运单".equals(shippingTemplate.getTemplateType())) {
shippingTemplate.setFreightJson(null);
return;
}
if (StringUtil.isBlank(shippingTemplate.getFreightJson())) {
throw new ServiceException("运单模板的运费信息不能为空");
}
final JSONObject freight;
final JSONArray goods;
try {
freight = JSON.parseObject(shippingTemplate.getFreightJson());
goods = StringUtil.isBlank(shippingTemplate.getGoodsJson())
? new JSONArray()
: JSON.parseArray(shippingTemplate.getGoodsJson());
} catch (Exception e) {
throw new ServiceException("运费信息或货物信息格式不正确");
}
if (freight == null) {
throw new ServiceException("运费信息格式不正确");
}
String currency = TransportBusinessSupport.trimToNull(freight.getString("currency"));
if (StringUtil.isBlank(currency)) {
throw new ServiceException("运费信息的币种不能为空");
}
freight.put("currency", currency);
freight.put("otherFreightAmount", amountText(freight.get("otherFreightAmount"), "其他运费合计"));
if (isRoadTransport(shippingTemplate.getTransportType())) {
JSONArray freightItems = freight.getJSONArray("freightItems");
if (freightItems == null || freightItems.size() != goods.size()) {
throw new ServiceException("公路运输的运费明细必须与货物信息一一对应");
}
BigDecimal totalFreightAmount = BigDecimal.ZERO;
for (int index = 0; index < freightItems.size(); index++) {
JSONObject freightItem = freightItems.getJSONObject(index);
if (freightItem == null) {
throw new ServiceException("" + (index + 1) + "条运费明细格式不正确");
}
BigDecimal unitPrice = amountValue(freightItem.get("unitPrice"), "" + (index + 1) + "条货物单价");
String priceUnit = TransportBusinessSupport.trimToNull(freightItem.getString("priceUnit"));
if (StringUtil.isBlank(priceUnit)) {
throw new ServiceException("" + (index + 1) + "条货物计价单位不能为空");
}
JSONObject goodsItem = goods.getJSONObject(index);
BigDecimal quantity = amountValue(goodsItem == null ? null : goodsItem.get("quantity"), "" + (index + 1) + "条货物数量");
BigDecimal freightAmount = unitPrice.multiply(quantity);
freightItem.put("cargoIndex", index);
freightItem.put("unitPrice", decimalText(unitPrice));
freightItem.put("priceUnit", priceUnit);
freightItem.put("quantity", decimalText(quantity));
freightItem.put("freightAmount", decimalText(freightAmount));
totalFreightAmount = totalFreightAmount.add(freightAmount);
}
freight.put("totalFreightAmount", decimalText(totalFreightAmount));
freight.remove("freightAmount");
freight.remove("quantity");
} else {
BigDecimal quantity = goods.stream()
.map(item -> item instanceof JSONObject ? (JSONObject) item : null)
.map(item -> amountValue(item == null ? null : item.get("quantity"), "货物数量"))
.reduce(BigDecimal.ZERO, BigDecimal::add);
freight.put("quantity", decimalText(quantity));
freight.put("freightAmount", amountText(freight.get("freightAmount"), "运费"));
freight.remove("freightItems");
freight.remove("totalFreightAmount");
}
shippingTemplate.setFreightJson(freight.toJSONString());
}
private boolean isRoadTransport(String transportType) {
String value = transportType == null ? "" : transportType.toLowerCase();
return value.contains("公路") || value.contains("道路") || value.contains("road") || "gl".equals(value);
}
private String amountText(Object value, String fieldName) {
return decimalText(amountValue(value, fieldName));
}
private BigDecimal amountValue(Object value, String fieldName) {
if (value == null || StringUtil.isBlank(String.valueOf(value))) {
return BigDecimal.ZERO;
}
try {
BigDecimal amount = new BigDecimal(String.valueOf(value));
if (amount.compareTo(BigDecimal.ZERO) < 0) {
throw new ServiceException(fieldName + "不能小于0");
}
return amount;
} catch (NumberFormatException e) {
throw new ServiceException(fieldName + "必须为非负数字");
}
}
private String decimalText(BigDecimal value) {
return value.stripTrailingZeros().toPlainString();
}
private ShippingTemplate loadEditable(Long id, boolean checkDept) {
if (Func.isEmpty(id)) {
throw new ServiceException("主键不能为空");

View File

@@ -88,7 +88,7 @@ public class TransportPlanServiceImpl extends BaseServiceImpl<TransportPlanMappe
transportPlan.setDeptName(oldRecord.getDeptName());
}
prepare(transportPlan);
if (created && Func.isEmpty(transportPlan.getPlanNo())) {
if (created) {
transportPlan.setPlanNo(nextCode());
}
validate(transportPlan);
@@ -490,7 +490,7 @@ public class TransportPlanServiceImpl extends BaseServiceImpl<TransportPlanMappe
}
private synchronized String nextCode() {
String prefix = "JH" + LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
String prefix = "JH-" + LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
List<TransportPlan> latestList = list(Wrappers.<TransportPlan>lambdaQuery()
.select(TransportPlan::getPlanNo)
.likeRight(TransportPlan::getPlanNo, prefix)