feat(api): 引入文件上传工具类及优化OCR接口
- 新增FileUploadUtil工具类,用于安全处理文件名和生成对象名 - 优化OcrController中的文件上传逻辑,使用新的工具类处理文件名 - UserController中增加上传信息日志记录 - 引入SupplierPackageUsage和UploadFileItem实体类,支持更丰富的文件信息存储 - 修改OrderSupplierDO、OrderPackageDO等实体类,将字符串类型字段改为Long类型 - 调整PurchaseOrderDO及相关映射配置,移除冗余字段并优化结构 - 更新ShipOrderPackageDO中的boxSpecId为Long类型 - 在OrderSupplierMapper.xml中新增typeHandler配置以支持JSON序列化
This commit is contained in:
parent
633af42f5a
commit
56acc46b07
1
.gitignore
vendored
1
.gitignore
vendored
@ -74,6 +74,7 @@ log/
|
|||||||
|
|
||||||
# Claude
|
# Claude
|
||||||
.claude/commands/openspec
|
.claude/commands/openspec
|
||||||
|
.claude/skills/codegen
|
||||||
.spec-workflow
|
.spec-workflow
|
||||||
.bmad-core
|
.bmad-core
|
||||||
.claude/commands/BMad
|
.claude/commands/BMad
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
package com.xunhong.erp.turbo.admin.controller;
|
package com.xunhong.erp.turbo.admin.controller;
|
||||||
|
|
||||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
|
||||||
import com.alibaba.cola.dto.SingleResponse;
|
import com.alibaba.cola.dto.SingleResponse;
|
||||||
import com.xunhong.erp.turbo.api.facade.api.WxMaServiceI;
|
import com.xunhong.erp.turbo.api.facade.api.WxMaServiceI;
|
||||||
import com.xunhong.erp.turbo.api.facade.dto.vo.WxMaOcrBankCardVO;
|
import com.xunhong.erp.turbo.api.facade.dto.vo.WxMaOcrBankCardVO;
|
||||||
import com.xunhong.erp.turbo.api.facade.dto.vo.WxMaOcrIdCardVO;
|
import com.xunhong.erp.turbo.api.facade.dto.vo.WxMaOcrIdCardVO;
|
||||||
|
import com.xunhong.erp.turbo.base.utils.FileUploadUtil;
|
||||||
import com.xunhong.erp.turbo.file.FileService;
|
import com.xunhong.erp.turbo.file.FileService;
|
||||||
import com.xunhong.erp.turbo.file.config.OssProperties;
|
import com.xunhong.erp.turbo.file.config.OssProperties;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
@ -40,7 +40,8 @@ public class OcrController {
|
|||||||
@PostMapping(value = "ocrIdCard", consumes = "multipart/form-data")
|
@PostMapping(value = "ocrIdCard", consumes = "multipart/form-data")
|
||||||
@Operation(summary = "OCR识别身份证", method = "POST", hidden = true)
|
@Operation(summary = "OCR识别身份证", method = "POST", hidden = true)
|
||||||
public SingleResponse<WxMaOcrIdCardVO> ocrIdCard(@RequestParam @Schema(title = "图片文件", requiredMode = Schema.RequiredMode.REQUIRED, type = "file") MultipartFile file) throws IOException {
|
public SingleResponse<WxMaOcrIdCardVO> ocrIdCard(@RequestParam @Schema(title = "图片文件", requiredMode = Schema.RequiredMode.REQUIRED, type = "file") MultipartFile file) throws IOException {
|
||||||
String objectName = "uploads/" + LocalDateTimeUtil.format(LocalDateTimeUtil.now(), "yyMM/dd") + "/" + file.getOriginalFilename();
|
// 使用FileUploadUtil安全处理文件名
|
||||||
|
String objectName = FileUploadUtil.generateObjectName(file);
|
||||||
fileService.upload(objectName, file.getInputStream());
|
fileService.upload(objectName, file.getInputStream());
|
||||||
String ocrUrl = properties.getDomain() + objectName;
|
String ocrUrl = properties.getDomain() + objectName;
|
||||||
|
|
||||||
@ -51,7 +52,8 @@ public class OcrController {
|
|||||||
@PostMapping(value = "ocrBankCard", consumes = "multipart/form-data")
|
@PostMapping(value = "ocrBankCard", consumes = "multipart/form-data")
|
||||||
@Operation(summary = "OCR识别银行卡", method = "POST", hidden = true)
|
@Operation(summary = "OCR识别银行卡", method = "POST", hidden = true)
|
||||||
public SingleResponse<WxMaOcrBankCardVO> ocrBankCard(@RequestParam @Schema(title = "图片文件", requiredMode = Schema.RequiredMode.REQUIRED, type = "file") MultipartFile file) throws IOException {
|
public SingleResponse<WxMaOcrBankCardVO> ocrBankCard(@RequestParam @Schema(title = "图片文件", requiredMode = Schema.RequiredMode.REQUIRED, type = "file") MultipartFile file) throws IOException {
|
||||||
String objectName = "uploads/" + LocalDateTimeUtil.format(LocalDateTimeUtil.now(), "yyMM/dd") + "/" + file.getOriginalFilename();
|
// 使用FileUploadUtil安全处理文件名
|
||||||
|
String objectName = FileUploadUtil.generateObjectName(file);
|
||||||
fileService.upload(objectName, file.getInputStream());
|
fileService.upload(objectName, file.getInputStream());
|
||||||
String ocrUrl = properties.getDomain() + objectName;
|
String ocrUrl = properties.getDomain() + objectName;
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,6 @@ package com.xunhong.erp.turbo.auth.controller;
|
|||||||
|
|
||||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
|
||||||
import cn.hutool.core.lang.tree.Tree;
|
import cn.hutool.core.lang.tree.Tree;
|
||||||
import com.alibaba.cola.dto.MultiResponse;
|
import com.alibaba.cola.dto.MultiResponse;
|
||||||
import com.alibaba.cola.dto.Response;
|
import com.alibaba.cola.dto.Response;
|
||||||
@ -30,6 +29,7 @@ import com.xunhong.erp.turbo.api.user.dto.vo.EmployeeVO;
|
|||||||
import com.xunhong.erp.turbo.api.user.dto.vo.UserAuthVO;
|
import com.xunhong.erp.turbo.api.user.dto.vo.UserAuthVO;
|
||||||
import com.xunhong.erp.turbo.api.user.dto.vo.UserVO;
|
import com.xunhong.erp.turbo.api.user.dto.vo.UserVO;
|
||||||
import com.xunhong.erp.turbo.base.dto.UserSession;
|
import com.xunhong.erp.turbo.base.dto.UserSession;
|
||||||
|
import com.xunhong.erp.turbo.base.utils.FileUploadUtil;
|
||||||
import com.xunhong.erp.turbo.file.FileService;
|
import com.xunhong.erp.turbo.file.FileService;
|
||||||
import com.xunhong.erp.turbo.file.config.OssProperties;
|
import com.xunhong.erp.turbo.file.config.OssProperties;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
@ -191,7 +191,14 @@ public class UserController {
|
|||||||
@PostMapping(value = "upload", consumes = "multipart/form-data")
|
@PostMapping(value = "upload", consumes = "multipart/form-data")
|
||||||
@Operation(summary = "上传图片", method = "POST", hidden = true)
|
@Operation(summary = "上传图片", method = "POST", hidden = true)
|
||||||
public SingleResponse<String> upload(@RequestParam @Schema(title = "图片文件", requiredMode = Schema.RequiredMode.REQUIRED, type = "file") MultipartFile file) throws IOException {
|
public SingleResponse<String> upload(@RequestParam @Schema(title = "图片文件", requiredMode = Schema.RequiredMode.REQUIRED, type = "file") MultipartFile file) throws IOException {
|
||||||
String objectName = "uploads/" + LocalDateTimeUtil.format(LocalDateTimeUtil.now(), "yyMM/dd") + "/" + file.getOriginalFilename();
|
// 使用FileUploadUtil安全处理文件名
|
||||||
|
String objectName = FileUploadUtil.generateObjectName(file);
|
||||||
|
|
||||||
|
// 记录上传信息
|
||||||
|
System.out.println("上传文件: " + file.getOriginalFilename() +
|
||||||
|
", Content-Type: " + file.getContentType() +
|
||||||
|
", 生成对象名: " + objectName);
|
||||||
|
|
||||||
fileService.upload(objectName, file.getInputStream());
|
fileService.upload(objectName, file.getInputStream());
|
||||||
return SingleResponse.of(properties.getDomain() + objectName);
|
return SingleResponse.of(properties.getDomain() + objectName);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import com.xunhong.erp.turbo.api.biz.dto.enums.PurchaseOrderStateEnum;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -51,51 +50,6 @@ public class PurchaseOrder extends DTO {
|
|||||||
*/
|
*/
|
||||||
private PurchaseOrderPricingMethodEnum pricingMethod;
|
private PurchaseOrderPricingMethodEnum pricingMethod;
|
||||||
|
|
||||||
/**
|
|
||||||
* 销售金额
|
|
||||||
*/
|
|
||||||
private BigDecimal saleAmount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 包装费
|
|
||||||
*/
|
|
||||||
private BigDecimal packageFee;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 平均单价(元/斤)
|
|
||||||
*/
|
|
||||||
private BigDecimal avgUnitPrice;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否返点
|
|
||||||
*/
|
|
||||||
private Boolean rebate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 毛重(斤)
|
|
||||||
*/
|
|
||||||
private BigDecimal grossWeight;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 净重(斤)
|
|
||||||
*/
|
|
||||||
private BigDecimal netWeight;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 成本合计
|
|
||||||
*/
|
|
||||||
private BigDecimal totalCost;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 运费
|
|
||||||
*/
|
|
||||||
private BigDecimal freightCharge;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 瓜农数量
|
|
||||||
*/
|
|
||||||
private Integer supplierCount;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 采购订单状态: 0_草稿;1_审核中;2_已完成;3_已驳回;4_已关闭;
|
* 采购订单状态: 0_草稿;1_审核中;2_已完成;3_已驳回;4_已关闭;
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -27,18 +27,9 @@ public interface PurchaseOrderConvert {
|
|||||||
@Mapping(target = "orderCostItemDOList", ignore = true)
|
@Mapping(target = "orderCostItemDOList", ignore = true)
|
||||||
@Mapping(target = "auditState", ignore = true)
|
@Mapping(target = "auditState", ignore = true)
|
||||||
@Mapping(target = "orderPackageDOList", ignore = true)
|
@Mapping(target = "orderPackageDOList", ignore = true)
|
||||||
@Mapping(target = "totalCost", ignore = true)
|
|
||||||
@Mapping(target = "supplierCount", ignore = true)
|
|
||||||
@Mapping(target = "saleAmount", ignore = true)
|
|
||||||
@Mapping(target = "rebate", ignore = true)
|
|
||||||
@Mapping(target = "pricingMethod", ignore = true)
|
@Mapping(target = "pricingMethod", ignore = true)
|
||||||
@Mapping(target = "packageFee", ignore = true)
|
|
||||||
@Mapping(target = "orderRebateDO", ignore = true)
|
@Mapping(target = "orderRebateDO", ignore = true)
|
||||||
@Mapping(target = "orderCompanyDO", ignore = true)
|
@Mapping(target = "orderCompanyDO", ignore = true)
|
||||||
@Mapping(target = "netWeight", ignore = true)
|
|
||||||
@Mapping(target = "grossWeight", ignore = true)
|
|
||||||
@Mapping(target = "freightCharge", ignore = true)
|
|
||||||
@Mapping(target = "avgUnitPrice", ignore = true)
|
|
||||||
@Mapping(target = "orderDealerDO", ignore = true)
|
@Mapping(target = "orderDealerDO", ignore = true)
|
||||||
@Mapping(target = "orderCostDOList", ignore = true)
|
@Mapping(target = "orderCostDOList", ignore = true)
|
||||||
@Mapping(target = "orderSn", ignore = true)
|
@Mapping(target = "orderSn", ignore = true)
|
||||||
@ -55,18 +46,9 @@ public interface PurchaseOrderConvert {
|
|||||||
@Mapping(target = "orderCostItemDOList", ignore = true)
|
@Mapping(target = "orderCostItemDOList", ignore = true)
|
||||||
@Mapping(target = "auditState", ignore = true)
|
@Mapping(target = "auditState", ignore = true)
|
||||||
@Mapping(target = "orderPackageDOList", ignore = true)
|
@Mapping(target = "orderPackageDOList", ignore = true)
|
||||||
@Mapping(target = "totalCost", ignore = true)
|
|
||||||
@Mapping(target = "supplierCount", ignore = true)
|
|
||||||
@Mapping(target = "saleAmount", ignore = true)
|
|
||||||
@Mapping(target = "rebate", ignore = true)
|
|
||||||
@Mapping(target = "pricingMethod", ignore = true)
|
@Mapping(target = "pricingMethod", ignore = true)
|
||||||
@Mapping(target = "packageFee", ignore = true)
|
|
||||||
@Mapping(target = "orderRebateDO", ignore = true)
|
@Mapping(target = "orderRebateDO", ignore = true)
|
||||||
@Mapping(target = "orderCompanyDO", ignore = true)
|
@Mapping(target = "orderCompanyDO", ignore = true)
|
||||||
@Mapping(target = "netWeight", ignore = true)
|
|
||||||
@Mapping(target = "grossWeight", ignore = true)
|
|
||||||
@Mapping(target = "freightCharge", ignore = true)
|
|
||||||
@Mapping(target = "avgUnitPrice", ignore = true)
|
|
||||||
@Mapping(target = "orderDealerDO", ignore = true)
|
@Mapping(target = "orderDealerDO", ignore = true)
|
||||||
@Mapping(target = "orderCostDOList", ignore = true)
|
@Mapping(target = "orderCostDOList", ignore = true)
|
||||||
@Mapping(target = "orderVehicleDO", ignore = true)
|
@Mapping(target = "orderVehicleDO", ignore = true)
|
||||||
@ -93,14 +75,9 @@ public interface PurchaseOrderConvert {
|
|||||||
@Mapping(target = "originPrincipal", source = "createdByName")
|
@Mapping(target = "originPrincipal", source = "createdByName")
|
||||||
@Mapping(target = "version", ignore = true)
|
@Mapping(target = "version", ignore = true)
|
||||||
@Mapping(target = "updatedAt", ignore = true)
|
@Mapping(target = "updatedAt", ignore = true)
|
||||||
@Mapping(target = "totalCost", ignore = true)
|
|
||||||
@Mapping(target = "supplierCount", ignore = true)
|
|
||||||
@Mapping(target = "state", ignore = true)
|
@Mapping(target = "state", ignore = true)
|
||||||
@Mapping(target = "saleAmount", ignore = true)
|
|
||||||
@Mapping(target = "remark", ignore = true)
|
@Mapping(target = "remark", ignore = true)
|
||||||
@Mapping(target = "rebate", ignore = true)
|
|
||||||
@Mapping(target = "pricingMethod", ignore = true)
|
@Mapping(target = "pricingMethod", ignore = true)
|
||||||
@Mapping(target = "packageFee", ignore = true)
|
|
||||||
@Mapping(target = "orderVehicleDO", ignore = true)
|
@Mapping(target = "orderVehicleDO", ignore = true)
|
||||||
@Mapping(target = "orderSupplierDOList", ignore = true)
|
@Mapping(target = "orderSupplierDOList", ignore = true)
|
||||||
@Mapping(target = "orderSn", ignore = true)
|
@Mapping(target = "orderSn", ignore = true)
|
||||||
@ -108,12 +85,8 @@ public interface PurchaseOrderConvert {
|
|||||||
@Mapping(target = "orderDealerDO", ignore = true)
|
@Mapping(target = "orderDealerDO", ignore = true)
|
||||||
@Mapping(target = "orderCostDOList", ignore = true)
|
@Mapping(target = "orderCostDOList", ignore = true)
|
||||||
@Mapping(target = "orderCompanyDO", ignore = true)
|
@Mapping(target = "orderCompanyDO", ignore = true)
|
||||||
@Mapping(target = "netWeight", ignore = true)
|
|
||||||
@Mapping(target = "isDelete", ignore = true)
|
@Mapping(target = "isDelete", ignore = true)
|
||||||
@Mapping(target = "grossWeight", ignore = true)
|
|
||||||
@Mapping(target = "freightCharge", ignore = true)
|
|
||||||
@Mapping(target = "createdAt", ignore = true)
|
@Mapping(target = "createdAt", ignore = true)
|
||||||
@Mapping(target = "avgUnitPrice", ignore = true)
|
|
||||||
PurchaseOrderDO toPurchaseOrderDO(PurchaseOrderStep1Cmd purchaseOrderStep1Cmd);
|
PurchaseOrderDO toPurchaseOrderDO(PurchaseOrderStep1Cmd purchaseOrderStep1Cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -66,7 +66,7 @@ public class OrderPackageDO extends BaseDO<OrderPackageDO> {
|
|||||||
* 箱子规格id
|
* 箱子规格id
|
||||||
*/
|
*/
|
||||||
@TableField(value = "box_spec_id")
|
@TableField(value = "box_spec_id")
|
||||||
private String boxSpecId;
|
private Long boxSpecId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 箱子规格名称
|
* 箱子规格名称
|
||||||
|
|||||||
@ -5,6 +5,9 @@ import com.baomidou.mybatisplus.annotation.TableField;
|
|||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||||
|
import com.xunhong.erp.turbo.api.biz.dto.common.SupplierPackageUsage;
|
||||||
|
import com.xunhong.erp.turbo.api.biz.dto.common.UploadFileItem;
|
||||||
|
import com.xunhong.erp.turbo.api.biz.dto.enums.PurchaseOrderPricingMethodEnum;
|
||||||
import com.xunhong.erp.turbo.datasource.domain.entity.BaseDO;
|
import com.xunhong.erp.turbo.datasource.domain.entity.BaseDO;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
@ -110,12 +113,24 @@ public class OrderSupplierDO extends BaseDO<OrderSupplierDO> {
|
|||||||
@TableField(value = "purchase_price")
|
@TableField(value = "purchase_price")
|
||||||
private BigDecimal purchasePrice;
|
private BigDecimal purchasePrice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 箱子类型
|
||||||
|
*/
|
||||||
|
@TableField(value = "package_usage", typeHandler = JacksonTypeHandler.class)
|
||||||
|
private List<SupplierPackageUsage> packageUsage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 销售单价(元/斤)
|
* 销售单价(元/斤)
|
||||||
*/
|
*/
|
||||||
@TableField(value = "sale_price")
|
@TableField(value = "sale_price")
|
||||||
private BigDecimal salePrice;
|
private BigDecimal salePrice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 报价方式:1_按毛重报价;2_按净重报价;
|
||||||
|
*/
|
||||||
|
@TableField(value = "pricing_method")
|
||||||
|
private PurchaseOrderPricingMethodEnum pricingMethod;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发票金额
|
* 发票金额
|
||||||
*/
|
*/
|
||||||
@ -144,7 +159,7 @@ public class OrderSupplierDO extends BaseDO<OrderSupplierDO> {
|
|||||||
* 发票
|
* 发票
|
||||||
*/
|
*/
|
||||||
@TableField(value = "invoice_img", typeHandler = JacksonTypeHandler.class)
|
@TableField(value = "invoice_img", typeHandler = JacksonTypeHandler.class)
|
||||||
private List<String> invoiceImg;
|
private List<UploadFileItem> invoiceImg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否上传合同
|
* 是否上传合同
|
||||||
@ -156,7 +171,7 @@ public class OrderSupplierDO extends BaseDO<OrderSupplierDO> {
|
|||||||
* 合同
|
* 合同
|
||||||
*/
|
*/
|
||||||
@TableField(value = "contract_img", typeHandler = JacksonTypeHandler.class)
|
@TableField(value = "contract_img", typeHandler = JacksonTypeHandler.class)
|
||||||
private List<String> contractImg;
|
private List<UploadFileItem> contractImg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 产品ID
|
* 产品ID
|
||||||
|
|||||||
@ -11,7 +11,6 @@ import com.xunhong.erp.turbo.datasource.domain.entity.BaseDO;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,60 +57,6 @@ public class PurchaseOrderDO extends BaseDO<PurchaseOrderDO> {
|
|||||||
@TableField(value = "pricing_method")
|
@TableField(value = "pricing_method")
|
||||||
private PurchaseOrderPricingMethodEnum pricingMethod;
|
private PurchaseOrderPricingMethodEnum pricingMethod;
|
||||||
|
|
||||||
/**
|
|
||||||
* 销售金额
|
|
||||||
*/
|
|
||||||
@TableField(value = "sale_amount")
|
|
||||||
private BigDecimal saleAmount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 包装费
|
|
||||||
*/
|
|
||||||
@TableField(value = "package_fee")
|
|
||||||
private BigDecimal packageFee;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 平均单价(元/斤)
|
|
||||||
*/
|
|
||||||
@TableField(value = "avg_unit_price")
|
|
||||||
private BigDecimal avgUnitPrice;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否返点
|
|
||||||
*/
|
|
||||||
@TableField(value = "rebate")
|
|
||||||
private Boolean rebate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 毛重(斤)
|
|
||||||
*/
|
|
||||||
@TableField(value = "gross_weight")
|
|
||||||
private BigDecimal grossWeight;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 净重(斤)
|
|
||||||
*/
|
|
||||||
@TableField(value = "net_weight")
|
|
||||||
private BigDecimal netWeight;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 成本合计
|
|
||||||
*/
|
|
||||||
@TableField(value = "total_cost")
|
|
||||||
private BigDecimal totalCost;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 运费
|
|
||||||
*/
|
|
||||||
@TableField(value = "freight_charge")
|
|
||||||
private BigDecimal freightCharge;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 瓜农数量
|
|
||||||
*/
|
|
||||||
@TableField(value = "supplier_count")
|
|
||||||
private Integer supplierCount;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 采购订单状态: 0_草稿;1_审核中;2_已完成;3_已驳回;4_已关闭;
|
* 采购订单状态: 0_草稿;1_审核中;2_已完成;3_已驳回;4_已关闭;
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -34,7 +34,7 @@ public class ShipOrderPackageDO extends BaseDO<ShipOrderPackageDO> {
|
|||||||
* 箱型规格id
|
* 箱型规格id
|
||||||
*/
|
*/
|
||||||
@TableField(value = "box_spec_id")
|
@TableField(value = "box_spec_id")
|
||||||
private String boxSpecId;
|
private Long boxSpecId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 箱型规格名称
|
* 箱型规格名称
|
||||||
|
|||||||
@ -8,7 +8,10 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.xunhong.erp.turbo.api.biz.dto.cmd.*;
|
import com.xunhong.erp.turbo.api.biz.dto.cmd.*;
|
||||||
import com.xunhong.erp.turbo.api.biz.dto.common.*;
|
import com.xunhong.erp.turbo.api.biz.dto.common.*;
|
||||||
import com.xunhong.erp.turbo.api.biz.dto.enums.*;
|
import com.xunhong.erp.turbo.api.biz.dto.enums.OrderAuditStateEnum;
|
||||||
|
import com.xunhong.erp.turbo.api.biz.dto.enums.OrderAuditTypeEnum;
|
||||||
|
import com.xunhong.erp.turbo.api.biz.dto.enums.PurchaseOrderAuditStateEnum;
|
||||||
|
import com.xunhong.erp.turbo.api.biz.dto.enums.PurchaseOrderStateEnum;
|
||||||
import com.xunhong.erp.turbo.api.biz.dto.qry.*;
|
import com.xunhong.erp.turbo.api.biz.dto.qry.*;
|
||||||
import com.xunhong.erp.turbo.api.biz.exception.BizErrorCode;
|
import com.xunhong.erp.turbo.api.biz.exception.BizErrorCode;
|
||||||
import com.xunhong.erp.turbo.api.biz.exception.BizException;
|
import com.xunhong.erp.turbo.api.biz.exception.BizException;
|
||||||
@ -21,8 +24,6 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.math.RoundingMode;
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -503,230 +504,6 @@ public class PurchaseOrderGatewayImpl implements PurchaseOrderGateway {
|
|||||||
throw new BizException(BizErrorCode.B_BIZ_ORDER_AUDIT_NOT_FOUND);
|
throw new BizException(BizErrorCode.B_BIZ_ORDER_AUDIT_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 自动生成发货单 purchaseOrderDO 生成 shipOrder,orderPackage 变成 shipOrderPackage(按照报价价格分组插入),orderSupplier 变成shipOrderItem(按照规格分组插入)
|
|
||||||
// 1. 创建发货单
|
|
||||||
ShipOrderDO shipOrderDO = new ShipOrderDO();
|
|
||||||
shipOrderDO.setPurchaseOrderId(purchaseOrderDO.getOrderId());
|
|
||||||
shipOrderDO.setOrderSn("FH" + generateShipOrderSn()); // 生成发货单编号
|
|
||||||
|
|
||||||
// 从车辆信息中获取相关信息
|
|
||||||
OrderVehicleDO orderVehicleDO = orderVehicleMapper.selectOne(
|
|
||||||
Wrappers.lambdaQuery(OrderVehicleDO.class)
|
|
||||||
.eq(OrderVehicleDO::getOrderId, purchaseOrderDO.getOrderId())
|
|
||||||
);
|
|
||||||
|
|
||||||
if (orderVehicleDO != null) {
|
|
||||||
shipOrderDO.setLicensePlate(orderVehicleDO.getPlate());
|
|
||||||
shipOrderDO.setDriverName(orderVehicleDO.getDriver());
|
|
||||||
shipOrderDO.setDriverPhone(orderVehicleDO.getPhone());
|
|
||||||
shipOrderDO.setShippingDate(orderVehicleDO.getDeliveryTime());
|
|
||||||
shipOrderDO.setShippingAddress(orderVehicleDO.getOrigin());
|
|
||||||
shipOrderDO.setReceivingAddress(orderVehicleDO.getDestination());
|
|
||||||
shipOrderDO.setFreightDebt(orderVehicleDO.getPrice());
|
|
||||||
if (orderVehicleDO.getOpenStrawCurtain()) {
|
|
||||||
shipOrderDO.setStrawMatDebt(orderVehicleDO.getStrawCurtainPrice());
|
|
||||||
}
|
|
||||||
shipOrderDO.setDealerId(orderVehicleDO.getDealerId());
|
|
||||||
shipOrderDO.setDealerName(orderVehicleDO.getDealerName());
|
|
||||||
shipOrderDO.setVehicleNo(orderVehicleDO.getVehicleNo());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从公司信息中获取相关信息
|
|
||||||
OrderCompanyDO orderCompanyDO = orderCompanyMapper.selectOne(
|
|
||||||
Wrappers.lambdaQuery(OrderCompanyDO.class)
|
|
||||||
.eq(OrderCompanyDO::getOrderId, purchaseOrderDO.getOrderId())
|
|
||||||
);
|
|
||||||
|
|
||||||
if (orderCompanyDO != null) {
|
|
||||||
shipOrderDO.setCompanyId(orderCompanyDO.getCompanyId());
|
|
||||||
shipOrderDO.setCompanyName(orderCompanyDO.getFullName());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从经销商信息中获取相关信息
|
|
||||||
OrderDealerDO orderDealerDO = orderDealerMapper.selectOne(
|
|
||||||
Wrappers.lambdaQuery(OrderDealerDO.class)
|
|
||||||
.eq(OrderDealerDO::getOrderId, purchaseOrderDO.getOrderId())
|
|
||||||
);
|
|
||||||
|
|
||||||
// 设置其他基本信息
|
|
||||||
shipOrderDO.setCreatedBy(purchaseOrderFinalApproveCmd.getCreatedBy());
|
|
||||||
shipOrderDO.setCreatedByName(purchaseOrderFinalApproveCmd.getCreatedByName());
|
|
||||||
|
|
||||||
// 4. 从OrderCostDO中提取人工费、商标费等费用信息
|
|
||||||
List<OrderCostDO> orderCostDOList = orderCostMapper.selectList(
|
|
||||||
Wrappers.lambdaQuery(OrderCostDO.class)
|
|
||||||
.eq(OrderCostDO::getOrderId, purchaseOrderDO.getOrderId())
|
|
||||||
);
|
|
||||||
|
|
||||||
// 计算人工费总额
|
|
||||||
BigDecimal totalLaborFee = orderCostDOList.stream()
|
|
||||||
.filter(cost -> CostItemTypeEnum.ARTIFICIAL_TYPE.getType() == cost.getType().getType())
|
|
||||||
.map(cost -> {
|
|
||||||
if (cost.getPrice() != null && cost.getCount() != null) {
|
|
||||||
return cost.getPrice().multiply(BigDecimal.valueOf(cost.getCount()));
|
|
||||||
}
|
|
||||||
return BigDecimal.ZERO;
|
|
||||||
})
|
|
||||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
||||||
shipOrderDO.setLaborFee(totalLaborFee);
|
|
||||||
|
|
||||||
// 计算商标费总额
|
|
||||||
BigDecimal totalTrademarkFee = orderCostDOList.stream()
|
|
||||||
.filter(cost -> "商标费".equals(cost.getName()))
|
|
||||||
.map(cost -> {
|
|
||||||
if (cost.getPrice() != null && cost.getCount() != null) {
|
|
||||||
return cost.getPrice().multiply(BigDecimal.valueOf(cost.getCount()));
|
|
||||||
}
|
|
||||||
return BigDecimal.ZERO;
|
|
||||||
})
|
|
||||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
||||||
shipOrderDO.setTrademarkFee(totalTrademarkFee);
|
|
||||||
|
|
||||||
// 计算打码费总额
|
|
||||||
BigDecimal totalCodingFee = orderCostDOList.stream()
|
|
||||||
.filter(cost -> "打码费".equals(cost.getName()))
|
|
||||||
.map(cost -> {
|
|
||||||
if (cost.getPrice() != null && cost.getCount() != null) {
|
|
||||||
return cost.getPrice().multiply(BigDecimal.valueOf(cost.getCount()));
|
|
||||||
}
|
|
||||||
return BigDecimal.ZERO;
|
|
||||||
})
|
|
||||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
||||||
shipOrderDO.setTrademarkFee(totalCodingFee);
|
|
||||||
|
|
||||||
shipOrderDO.setCodingFee(totalTrademarkFee);
|
|
||||||
|
|
||||||
// 2. 处理包材信息,转换为发货单包材信息(按照boxCategoryId和boxProductName分组)
|
|
||||||
List<OrderSupplierDO> orderSupplierDOList = orderSupplierMapper.selectList(
|
|
||||||
Wrappers.lambdaQuery(OrderSupplierDO.class)
|
|
||||||
.eq(OrderSupplierDO::getOrderId, purchaseOrderDO.getOrderId())
|
|
||||||
);
|
|
||||||
|
|
||||||
// 收集所有的包材信息
|
|
||||||
List<Long> orderSupplierIdList = orderSupplierDOList.stream()
|
|
||||||
.map(OrderSupplierDO::getOrderSupplierId)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
List<OrderPackageDO> orderPackageDOList = new ArrayList<>();
|
|
||||||
if (CollUtil.isNotEmpty(orderSupplierIdList)) {
|
|
||||||
orderPackageDOList = orderPackageMapper.selectList(
|
|
||||||
Wrappers.lambdaQuery(OrderPackageDO.class)
|
|
||||||
.in(OrderPackageDO::getOrderSupplierId, orderSupplierIdList)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// 计算纸箱费总额
|
|
||||||
BigDecimal totalCartonFee = orderPackageDOList.stream()
|
|
||||||
.map(p -> {
|
|
||||||
if (p.getBoxSalePrice() != null && p.getBoxCount() != null) {
|
|
||||||
return p.getBoxSalePrice().multiply(BigDecimal.valueOf(p.getBoxCount()));
|
|
||||||
}
|
|
||||||
return BigDecimal.ZERO;
|
|
||||||
})
|
|
||||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
||||||
|
|
||||||
shipOrderDO.setCartonFee(totalCartonFee);
|
|
||||||
|
|
||||||
// 插入发货单
|
|
||||||
shipOrderMapper.insert(shipOrderDO);
|
|
||||||
|
|
||||||
// 按boxCategoryId和boxProductName分组包材信息
|
|
||||||
Map<String, List<OrderPackageDO>> packageGroupByKey = orderPackageDOList.stream()
|
|
||||||
.collect(Collectors.groupingBy(p -> p.getBoxSpecId() + "_" + p.getBoxProductName()));
|
|
||||||
|
|
||||||
// 创建发货单包材信息
|
|
||||||
for (Map.Entry<String, List<OrderPackageDO>> entry : packageGroupByKey.entrySet()) {
|
|
||||||
List<OrderPackageDO> packages = entry.getValue();
|
|
||||||
|
|
||||||
// 合并相同类型的包材信息
|
|
||||||
ShipOrderPackageDO shipOrderPackageDO = new ShipOrderPackageDO();
|
|
||||||
shipOrderPackageDO.setShipOrderId(shipOrderDO.getShipOrderId());
|
|
||||||
|
|
||||||
// 使用第一个包材的信息作为基础
|
|
||||||
OrderPackageDO firstPackage = packages.get(0);
|
|
||||||
shipOrderPackageDO.setBoxSpecId(firstPackage.getBoxSpecId());
|
|
||||||
shipOrderPackageDO.setBoxSpecName(firstPackage.getBoxSpecName());
|
|
||||||
shipOrderPackageDO.setBoxProduct(firstPackage.getBoxProductName());
|
|
||||||
|
|
||||||
// 使用第一个包材的销售价格作为单价
|
|
||||||
shipOrderPackageDO.setUnitPrice(firstPackage.getBoxSalePrice());
|
|
||||||
|
|
||||||
// 计算总数量
|
|
||||||
int totalQuantity = packages.stream()
|
|
||||||
.mapToInt(OrderPackageDO::getBoxCount)
|
|
||||||
.sum();
|
|
||||||
shipOrderPackageDO.setQuantity(totalQuantity);
|
|
||||||
|
|
||||||
// 计算总金额
|
|
||||||
BigDecimal unitPrice = firstPackage.getBoxSalePrice() != null ? firstPackage.getBoxSalePrice() : BigDecimal.ZERO;
|
|
||||||
BigDecimal totalAmount = unitPrice.multiply(BigDecimal.valueOf(totalQuantity));
|
|
||||||
shipOrderPackageDO.setItemAmount(totalAmount);
|
|
||||||
|
|
||||||
// 计算总重量(如果有)
|
|
||||||
BigDecimal totalWeight = packages.stream()
|
|
||||||
.map(p -> {
|
|
||||||
if (p.getBoxProductWeight() != null && p.getBoxCount() != null) {
|
|
||||||
return p.getBoxProductWeight().multiply(BigDecimal.valueOf(p.getBoxCount()));
|
|
||||||
}
|
|
||||||
return BigDecimal.ZERO;
|
|
||||||
})
|
|
||||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
||||||
shipOrderPackageDO.setTotalWeight(totalWeight);
|
|
||||||
|
|
||||||
shipOrderPackageDO.setSingleWeight(shipOrderPackageDO.getTotalWeight().divide(shipOrderPackageDO.getUnitPrice(), 2, RoundingMode.HALF_UP));
|
|
||||||
|
|
||||||
shipOrderPackageMapper.insert(shipOrderPackageDO);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 处理供应商信息,转换为发货单项信息(按照规格分组)
|
|
||||||
// 按销售单价分组供应商信息
|
|
||||||
Map<BigDecimal, List<OrderSupplierDO>> supplierGroupByPrice = orderSupplierDOList.stream()
|
|
||||||
.collect(Collectors.groupingBy(OrderSupplierDO::getSalePrice));
|
|
||||||
|
|
||||||
// 创建发货单项信息
|
|
||||||
for (Map.Entry<BigDecimal, List<OrderSupplierDO>> entry : supplierGroupByPrice.entrySet()) {
|
|
||||||
List<OrderSupplierDO> suppliers = entry.getValue();
|
|
||||||
|
|
||||||
// 合并相同价格的供应商信息
|
|
||||||
ShipOrderItemDO shipOrderItemDO = new ShipOrderItemDO();
|
|
||||||
shipOrderItemDO.setShipOrderId(shipOrderDO.getShipOrderId());
|
|
||||||
|
|
||||||
// 计算总毛重
|
|
||||||
BigDecimal totalGrossWeight = suppliers.stream()
|
|
||||||
.map(OrderSupplierDO::getGrossWeight)
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
||||||
shipOrderItemDO.setGrossWeight(totalGrossWeight);
|
|
||||||
|
|
||||||
// 计算总净重
|
|
||||||
BigDecimal totalNetWeight = suppliers.stream()
|
|
||||||
.map(OrderSupplierDO::getNetWeight)
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
||||||
shipOrderItemDO.setNetWeight(totalNetWeight);
|
|
||||||
|
|
||||||
shipOrderItemDO.setBoxWeight(totalGrossWeight.subtract(totalNetWeight));
|
|
||||||
|
|
||||||
// 计算总箱数
|
|
||||||
Integer totalBoxCount = orderPackageDOList.stream()
|
|
||||||
.filter(p -> suppliers.stream().map(OrderSupplierDO::getOrderSupplierId).toList().contains(p.getOrderSupplierId()))
|
|
||||||
.map(OrderPackageDO::getBoxCount)
|
|
||||||
.reduce(0, Integer::sum);
|
|
||||||
|
|
||||||
shipOrderItemDO.setBoxCount(totalBoxCount);
|
|
||||||
|
|
||||||
// 设置单价
|
|
||||||
BigDecimal salePrice = entry.getKey();
|
|
||||||
shipOrderItemDO.setUnitPrice(salePrice);
|
|
||||||
|
|
||||||
// 计算总金额
|
|
||||||
if (salePrice != null) {
|
|
||||||
BigDecimal totalAmount = salePrice.multiply(totalNetWeight);
|
|
||||||
shipOrderItemDO.setTotalAmount(totalAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
shipOrderItemMapper.insert(shipOrderItemDO);
|
|
||||||
}
|
|
||||||
|
|
||||||
purchaseOrderMapper.updateById(purchaseOrderDO);
|
purchaseOrderMapper.updateById(purchaseOrderDO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -807,6 +584,7 @@ public class PurchaseOrderGatewayImpl implements PurchaseOrderGateway {
|
|||||||
queryWrapper.last("limit 1");
|
queryWrapper.last("limit 1");
|
||||||
purchaseOrderDO = purchaseOrderMapper.selectOne(queryWrapper);
|
purchaseOrderDO = purchaseOrderMapper.selectOne(queryWrapper);
|
||||||
purchaseOrderDO.setActive(purchaseOrderStep1Cmd.getActive());
|
purchaseOrderDO.setActive(purchaseOrderStep1Cmd.getActive());
|
||||||
|
purchaseOrderDO.setState(PurchaseOrderStateEnum.DRAFT);
|
||||||
purchaseOrderMapper.updateById(purchaseOrderDO);
|
purchaseOrderMapper.updateById(purchaseOrderDO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -867,6 +645,7 @@ public class PurchaseOrderGatewayImpl implements PurchaseOrderGateway {
|
|||||||
queryWrapper.last("limit 1");
|
queryWrapper.last("limit 1");
|
||||||
PurchaseOrderDO purchaseOrderDO = purchaseOrderMapper.selectOne(queryWrapper);
|
PurchaseOrderDO purchaseOrderDO = purchaseOrderMapper.selectOne(queryWrapper);
|
||||||
purchaseOrderDO.setActive(purchaseOrderStep2Cmd.getActive());
|
purchaseOrderDO.setActive(purchaseOrderStep2Cmd.getActive());
|
||||||
|
purchaseOrderDO.setState(PurchaseOrderStateEnum.DRAFT);
|
||||||
purchaseOrderMapper.updateById(purchaseOrderDO);
|
purchaseOrderMapper.updateById(purchaseOrderDO);
|
||||||
|
|
||||||
// 更新供应商信息(精细化处理)
|
// 更新供应商信息(精细化处理)
|
||||||
@ -1019,6 +798,7 @@ public class PurchaseOrderGatewayImpl implements PurchaseOrderGateway {
|
|||||||
PurchaseOrderDO purchaseOrderDO = purchaseOrderMapper.selectOne(queryWrapper);
|
PurchaseOrderDO purchaseOrderDO = purchaseOrderMapper.selectOne(queryWrapper);
|
||||||
purchaseOrderDO.setActive(purchaseOrderStep3Cmd.getActive());
|
purchaseOrderDO.setActive(purchaseOrderStep3Cmd.getActive());
|
||||||
purchaseOrderDO.setForeman(purchaseOrderStep3Cmd.getForeman());
|
purchaseOrderDO.setForeman(purchaseOrderStep3Cmd.getForeman());
|
||||||
|
purchaseOrderDO.setState(PurchaseOrderStateEnum.DRAFT);
|
||||||
purchaseOrderMapper.updateById(purchaseOrderDO);
|
purchaseOrderMapper.updateById(purchaseOrderDO);
|
||||||
|
|
||||||
saveOrderCostItem(orderId, purchaseOrderStep3Cmd.getOrderCostItemList().stream().toList());
|
saveOrderCostItem(orderId, purchaseOrderStep3Cmd.getOrderCostItemList().stream().toList());
|
||||||
|
|||||||
@ -20,10 +20,13 @@
|
|||||||
<result property="grossWeight" column="gross_weight"/>
|
<result property="grossWeight" column="gross_weight"/>
|
||||||
<result property="netWeight" column="net_weight"/>
|
<result property="netWeight" column="net_weight"/>
|
||||||
<result property="purchasePrice" column="purchase_price"/>
|
<result property="purchasePrice" column="purchase_price"/>
|
||||||
|
<result property="packageUsage" column="package_usage"
|
||||||
|
typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/>
|
||||||
<result property="salePrice" column="sale_price"/>
|
<result property="salePrice" column="sale_price"/>
|
||||||
|
<result property="pricingMethod" column="pricing_method"/>
|
||||||
<result property="invoiceAmount" column="invoice_amount"/>
|
<result property="invoiceAmount" column="invoice_amount"/>
|
||||||
<result property="emptyWeightImg" column="empty_photo"/>
|
<result property="emptyWeightImg" column="empty_weight_img"/>
|
||||||
<result property="totalWeightImg" column="total_photo"/>
|
<result property="totalWeightImg" column="total_Weight_img"/>
|
||||||
<result property="invoiceImg" column="invoice_img"
|
<result property="invoiceImg" column="invoice_img"
|
||||||
typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/>
|
typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/>
|
||||||
<result property="contractImg" column="contract_img"
|
<result property="contractImg" column="contract_img"
|
||||||
|
|||||||
@ -10,17 +10,7 @@
|
|||||||
<result property="originPrincipal" column="origin_principal"/>
|
<result property="originPrincipal" column="origin_principal"/>
|
||||||
<result property="pricingMethod" column="pricing_method"/>
|
<result property="pricingMethod" column="pricing_method"/>
|
||||||
<result property="orderSn" column="order_sn"/>
|
<result property="orderSn" column="order_sn"/>
|
||||||
<result property="saleAmount" column="sale_amount"/>
|
|
||||||
<result property="packageFee" column="package_fee"/>
|
|
||||||
<result property="avgUnitPrice" column="avg_unit_price"/>
|
|
||||||
<result property="rebate" column="rebate"/>
|
|
||||||
<result property="grossWeight" column="gross_weight"/>
|
|
||||||
<result property="netWeight" column="net_weight"/>
|
|
||||||
<result property="totalCost" column="total_cost"/>
|
|
||||||
<result property="freightCharge" column="freight_charge"/>
|
|
||||||
<result property="supplierCount" column="supplier_count"/>
|
|
||||||
<result property="state" column="state"/>
|
<result property="state" column="state"/>
|
||||||
<result property="rejectReason" column="reject_reason"/>
|
|
||||||
<result property="foreman" column="foreman"/>
|
<result property="foreman" column="foreman"/>
|
||||||
<result property="remark" column="remark"/>
|
<result property="remark" column="remark"/>
|
||||||
<result property="createdBy" column="created_by"/>
|
<result property="createdBy" column="created_by"/>
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.xunhong.erp.turbo.infra.infrastructure.gateway;
|
package com.xunhong.erp.turbo.infra.infrastructure.gateway;
|
||||||
|
|
||||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||||
|
import com.xunhong.erp.turbo.base.utils.FileUploadUtil;
|
||||||
import com.xunhong.erp.turbo.file.FileService;
|
import com.xunhong.erp.turbo.file.FileService;
|
||||||
import com.xunhong.erp.turbo.file.config.OssProperties;
|
import com.xunhong.erp.turbo.file.config.OssProperties;
|
||||||
import com.xunhong.erp.turbo.infra.domain.entity.Credentials;
|
import com.xunhong.erp.turbo.infra.domain.entity.Credentials;
|
||||||
@ -37,7 +38,8 @@ public class OssGatewayImpl implements OssGateway {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String upload(MultipartFile file) {
|
public String upload(MultipartFile file) {
|
||||||
String objectName = "uploads/" + LocalDateTimeUtil.format(LocalDateTimeUtil.now(), "yyMM/dd") + "/" + file.getOriginalFilename();
|
// 使用FileUploadUtil安全处理文件名
|
||||||
|
String objectName = FileUploadUtil.generateObjectName(file);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fileService.upload(objectName, file.getInputStream());
|
fileService.upload(objectName, file.getInputStream());
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.xunhong.erp.turbo.api.biz.dto.cmd;
|
package com.xunhong.erp.turbo.api.biz.dto.cmd;
|
||||||
|
|
||||||
|
import com.xunhong.erp.turbo.api.biz.dto.common.UploadFileItem;
|
||||||
import com.xunhong.erp.turbo.base.dto.Command;
|
import com.xunhong.erp.turbo.base.dto.Command;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@ -29,7 +30,7 @@ public class OrderSupplierUpdateCmd extends Command {
|
|||||||
* 发票照片
|
* 发票照片
|
||||||
*/
|
*/
|
||||||
@Schema(title = "发票照片")
|
@Schema(title = "发票照片")
|
||||||
private List<String> invoiceImg;
|
private List<UploadFileItem> invoiceImg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否上传合同
|
* 是否上传合同
|
||||||
@ -41,7 +42,7 @@ public class OrderSupplierUpdateCmd extends Command {
|
|||||||
* 合同照片
|
* 合同照片
|
||||||
*/
|
*/
|
||||||
@Schema(title = "合同照片")
|
@Schema(title = "合同照片")
|
||||||
private List<String> contractImg;
|
private List<UploadFileItem> contractImg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否已付定金
|
* 是否已付定金
|
||||||
|
|||||||
@ -62,8 +62,8 @@ public class OrderPackage extends Command {
|
|||||||
/**
|
/**
|
||||||
* 箱子规格ID
|
* 箱子规格ID
|
||||||
*/
|
*/
|
||||||
@Schema(title = "箱子规格ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(title = "箱子规格ID", type = "string", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private String boxSpecId;
|
private Long boxSpecId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 箱子规格名称
|
* 箱子规格名称
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.xunhong.erp.turbo.api.biz.dto.common;
|
package com.xunhong.erp.turbo.api.biz.dto.common;
|
||||||
|
|
||||||
|
import com.xunhong.erp.turbo.api.biz.dto.enums.PurchaseOrderPricingMethodEnum;
|
||||||
import com.xunhong.erp.turbo.base.dto.Command;
|
import com.xunhong.erp.turbo.base.dto.Command;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@ -106,12 +107,24 @@ public class OrderSupplier extends Command {
|
|||||||
@Schema(title = "采购单价(元/斤)", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(title = "采购单价(元/斤)", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private BigDecimal purchasePrice;
|
private BigDecimal purchasePrice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 箱子类型
|
||||||
|
*/
|
||||||
|
@Schema(title = "箱子类型", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private List<SupplierPackageUsage> packageUsage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 销售单价(元/斤)
|
* 销售单价(元/斤)
|
||||||
*/
|
*/
|
||||||
@Schema(title = "销售单价(元/斤)", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(title = "销售单价(元/斤)", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private BigDecimal salePrice;
|
private BigDecimal salePrice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 报价方式:1_按毛重报价;2_按净重报价;
|
||||||
|
*/
|
||||||
|
@Schema(title = "报价方式:1_按毛重报价;2_按净重报价;")
|
||||||
|
private PurchaseOrderPricingMethodEnum pricingMethod;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发票金额
|
* 发票金额
|
||||||
*/
|
*/
|
||||||
@ -140,7 +153,7 @@ public class OrderSupplier extends Command {
|
|||||||
* 发票
|
* 发票
|
||||||
*/
|
*/
|
||||||
@Schema(title = "发票")
|
@Schema(title = "发票")
|
||||||
private List<String> invoiceImg;
|
private List<UploadFileItem> invoiceImg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否上传合同
|
* 是否上传合同
|
||||||
@ -152,7 +165,7 @@ public class OrderSupplier extends Command {
|
|||||||
* 合同
|
* 合同
|
||||||
*/
|
*/
|
||||||
@Schema(title = "合同")
|
@Schema(title = "合同")
|
||||||
private List<String> contractImg;
|
private List<UploadFileItem> contractImg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 产品ID
|
* 产品ID
|
||||||
|
|||||||
@ -31,7 +31,7 @@ public class ShipOrderPackage extends DTO {
|
|||||||
/**
|
/**
|
||||||
* 箱型ID
|
* 箱型ID
|
||||||
*/
|
*/
|
||||||
@Schema(title = "箱型ID")
|
@Schema(title = "箱型ID", type = "string", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private Long boxSpecId;
|
private Long boxSpecId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -0,0 +1,22 @@
|
|||||||
|
package com.xunhong.erp.turbo.api.biz.dto.common;
|
||||||
|
|
||||||
|
import com.alibaba.cola.dto.Command;
|
||||||
|
import com.xunhong.erp.turbo.api.biz.dto.enums.OrderPackageBoxTypeEnum;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author shenyifei
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(title = "采购订单供应商纸箱使用情况")
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class SupplierPackageUsage extends Command {
|
||||||
|
|
||||||
|
@Schema(title = "箱子类型:1_本次使用;2_额外运输;3_已使用额外运输;4_车上剩余;5_瓜农纸箱;6_空箱;")
|
||||||
|
private OrderPackageBoxTypeEnum boxType;
|
||||||
|
|
||||||
|
@Schema(title = "是否使用:0_未回答;1_使用;2_未使用")
|
||||||
|
private Integer isUsed;
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package com.xunhong.erp.turbo.api.biz.dto.common;
|
||||||
|
|
||||||
|
import com.alibaba.cola.dto.DTO;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author shenyifei
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(title = "文件上传")
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class UploadFileItem extends DTO {
|
||||||
|
@Schema(title = "文件名")
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
@Schema(title = "文件路径")
|
||||||
|
private String filePath;
|
||||||
|
|
||||||
|
@Schema(title = "文件大小")
|
||||||
|
private Long fileSize;
|
||||||
|
|
||||||
|
@Schema(title = "文件类型")
|
||||||
|
private String fileType;
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@ package com.xunhong.erp.turbo.api.biz.dto.vo;
|
|||||||
|
|
||||||
import com.alibaba.cola.dto.DTO;
|
import com.alibaba.cola.dto.DTO;
|
||||||
import com.xunhong.erp.turbo.api.biz.dto.common.OrderVehicle;
|
import com.xunhong.erp.turbo.api.biz.dto.common.OrderVehicle;
|
||||||
|
import com.xunhong.erp.turbo.api.biz.dto.common.UploadFileItem;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
@ -172,7 +173,7 @@ public class OrderSupplierVO extends DTO {
|
|||||||
* 发票照片
|
* 发票照片
|
||||||
*/
|
*/
|
||||||
@Schema(title = "发票照片")
|
@Schema(title = "发票照片")
|
||||||
private List<String> invoiceImg;
|
private List<UploadFileItem> invoiceImg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否上传合同
|
* 是否上传合同
|
||||||
@ -184,7 +185,7 @@ public class OrderSupplierVO extends DTO {
|
|||||||
* 合同照片
|
* 合同照片
|
||||||
*/
|
*/
|
||||||
@Schema(title = "合同照片")
|
@Schema(title = "合同照片")
|
||||||
private List<String> contractImg;
|
private List<UploadFileItem> contractImg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建时间
|
* 创建时间
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -63,60 +62,6 @@ public class PurchaseOrderVO extends DTO {
|
|||||||
@Schema(title = "报价方式:1_按毛重报价;2_按净重报价;")
|
@Schema(title = "报价方式:1_按毛重报价;2_按净重报价;")
|
||||||
private PurchaseOrderPricingMethodEnum pricingMethod;
|
private PurchaseOrderPricingMethodEnum pricingMethod;
|
||||||
|
|
||||||
/**
|
|
||||||
* 销售金额
|
|
||||||
*/
|
|
||||||
@Schema(title = "销售金额")
|
|
||||||
private BigDecimal saleAmount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 包装费
|
|
||||||
*/
|
|
||||||
@Schema(title = "包装费")
|
|
||||||
private BigDecimal packageFee;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 平均单价(元/斤)
|
|
||||||
*/
|
|
||||||
@Schema(title = "平均单价(元/斤)")
|
|
||||||
private BigDecimal avgUnitPrice;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否返点
|
|
||||||
*/
|
|
||||||
@Schema(title = "是否返点")
|
|
||||||
private Boolean rebate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 毛重(斤)
|
|
||||||
*/
|
|
||||||
@Schema(title = "毛重(斤)")
|
|
||||||
private BigDecimal grossWeight;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 净重(斤)
|
|
||||||
*/
|
|
||||||
@Schema(title = "净重(斤)")
|
|
||||||
private BigDecimal netWeight;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 成本合计
|
|
||||||
*/
|
|
||||||
@Schema(title = "成本合计")
|
|
||||||
private BigDecimal totalCost;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 运费
|
|
||||||
*/
|
|
||||||
@Schema(title = "运费")
|
|
||||||
private BigDecimal freightCharge;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 瓜农数量
|
|
||||||
*/
|
|
||||||
@Schema(title = "瓜农数量")
|
|
||||||
private Integer supplierCount;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 采购订单状态: 0_草稿;1_审核中;2_已完成;3_已驳回;4_已关闭;
|
* 采购订单状态: 0_草稿;1_审核中;2_已完成;3_已驳回;4_已关闭;
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -0,0 +1,177 @@
|
|||||||
|
package com.xunhong.erp.turbo.base.utils;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传工具类
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class FileUploadUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全获取文件名(处理后缀问题)
|
||||||
|
*
|
||||||
|
* @param file 上传的文件
|
||||||
|
* @return 处理后的安全文件名
|
||||||
|
*/
|
||||||
|
public static String getSafeFileName(MultipartFile file) {
|
||||||
|
String originalFilename = file.getOriginalFilename();
|
||||||
|
|
||||||
|
log.debug("原始文件名: {}, Content-Type: {}", originalFilename, file.getContentType());
|
||||||
|
|
||||||
|
// 如果原始文件名为空或无效,生成默认文件名
|
||||||
|
if (StringUtils.isBlank(originalFilename)) {
|
||||||
|
String generatedName = generateDefaultFileName(file);
|
||||||
|
log.warn("原始文件名为空,生成文件名: {}", generatedName);
|
||||||
|
return generatedName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件名是否包含后缀
|
||||||
|
if (!containsFileExtension(originalFilename)) {
|
||||||
|
// 尝试从Content-Type推断文件类型
|
||||||
|
String extension = getExtensionFromContentType(file.getContentType());
|
||||||
|
if (extension != null) {
|
||||||
|
String fileNameWithExt = originalFilename + "." + extension;
|
||||||
|
log.info("文件名缺少后缀,从Content-Type推断: {} -> {}", originalFilename, fileNameWithExt);
|
||||||
|
return fileNameWithExt;
|
||||||
|
} else {
|
||||||
|
log.warn("无法推断文件后缀,使用原始文件名: {}", originalFilename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return originalFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成唯一的文件对象名
|
||||||
|
*
|
||||||
|
* @param file 上传的文件
|
||||||
|
* @return 生成的对象名
|
||||||
|
*/
|
||||||
|
public static String generateObjectName(MultipartFile file) {
|
||||||
|
String safeFileName = getSafeFileName(file);
|
||||||
|
String datePath = LocalDateTimeUtil.format(LocalDateTime.now(), "yyMM/dd");
|
||||||
|
return "uploads/" + datePath + "/" + safeFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查文件名是否包含后缀
|
||||||
|
*
|
||||||
|
* @param filename 文件名
|
||||||
|
* @return 是否包含后缀
|
||||||
|
*/
|
||||||
|
private static boolean containsFileExtension(String filename) {
|
||||||
|
if (filename == null) return false;
|
||||||
|
int lastDotIndex = filename.lastIndexOf('.');
|
||||||
|
return lastDotIndex > 0 && lastDotIndex < filename.length() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从Content-Type推断文件扩展名
|
||||||
|
*
|
||||||
|
* @param contentType MIME类型
|
||||||
|
* @return 文件扩展名,如果无法推断则返回null
|
||||||
|
*/
|
||||||
|
private static String getExtensionFromContentType(String contentType) {
|
||||||
|
if (contentType == null) return null;
|
||||||
|
|
||||||
|
switch (contentType.toLowerCase()) {
|
||||||
|
// 图片类型
|
||||||
|
case "image/jpeg":
|
||||||
|
return "jpeg";
|
||||||
|
case "image/jpg":
|
||||||
|
return "jpg";
|
||||||
|
case "image/png":
|
||||||
|
return "png";
|
||||||
|
case "image/gif":
|
||||||
|
return "gif";
|
||||||
|
case "image/webp":
|
||||||
|
return "webp";
|
||||||
|
case "image/bmp":
|
||||||
|
return "bmp";
|
||||||
|
case "image/svg+xml":
|
||||||
|
return "svg";
|
||||||
|
|
||||||
|
// 文档类型
|
||||||
|
case "application/pdf":
|
||||||
|
return "pdf";
|
||||||
|
case "application/msword":
|
||||||
|
return "doc";
|
||||||
|
case "application/vnd.openxmlformats-officedocument.wordprocessingml.document":
|
||||||
|
return "docx";
|
||||||
|
case "application/vnd.ms-excel":
|
||||||
|
return "xls";
|
||||||
|
case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
|
||||||
|
return "xlsx";
|
||||||
|
|
||||||
|
// 文本类型
|
||||||
|
case "text/plain":
|
||||||
|
return "txt";
|
||||||
|
case "text/csv":
|
||||||
|
return "csv";
|
||||||
|
case "application/json":
|
||||||
|
return "json";
|
||||||
|
case "application/xml":
|
||||||
|
return "xml";
|
||||||
|
|
||||||
|
// 压缩文件
|
||||||
|
case "application/zip":
|
||||||
|
return "zip";
|
||||||
|
case "application/x-rar-compressed":
|
||||||
|
return "rar";
|
||||||
|
|
||||||
|
default:
|
||||||
|
log.debug("未知的Content-Type: {}", contentType);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成默认文件名
|
||||||
|
*
|
||||||
|
* @param file 上传的文件
|
||||||
|
* @return 生成的文件名
|
||||||
|
*/
|
||||||
|
private static String generateDefaultFileName(MultipartFile file) {
|
||||||
|
String timestamp = LocalDateTimeUtil.format(LocalDateTime.now(), "yyyyMMddHHmmss");
|
||||||
|
String randomId = UUID.randomUUID().toString().substring(0, 8);
|
||||||
|
String extension = getExtensionFromContentType(file.getContentType());
|
||||||
|
|
||||||
|
return extension != null ?
|
||||||
|
"file_" + timestamp + "_" + randomId + "." + extension :
|
||||||
|
"file_" + timestamp + "_" + randomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证文件类型是否允许上传
|
||||||
|
*
|
||||||
|
* @param file 上传的文件
|
||||||
|
* @param allowedExtensions 允许的文件扩展名数组
|
||||||
|
* @return 是否允许上传
|
||||||
|
*/
|
||||||
|
public static boolean isAllowedFileType(MultipartFile file, String[] allowedExtensions) {
|
||||||
|
String fileName = getSafeFileName(file);
|
||||||
|
if (fileName == null) return false;
|
||||||
|
|
||||||
|
int lastDotIndex = fileName.lastIndexOf('.');
|
||||||
|
if (lastDotIndex == -1) return false;
|
||||||
|
|
||||||
|
String extension = fileName.substring(lastDotIndex + 1).toLowerCase();
|
||||||
|
|
||||||
|
for (String allowedExt : allowedExtensions) {
|
||||||
|
if (allowedExt.toLowerCase().equals(extension)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user