feat(quotation): 重构报价生成并支持多交货方式

- 新增QuotationGenerateRequest请求类,支持传入多个购物车id和交货方式信息
- 新增QuotationShoppingOrderDeliveryMethod实体及相关服务和Mapper,管理报价单交货方式
- 购物车实体QuotationShoppingCart去除单一交货方式与支付信息字段,新增额外费用字段
- 报价单实体QuotationShoppingOrder增加支付方式、币种、汇率、补充说明及备注字段
- 购物车服务实体QuotationShoppingCartService新增地址及备注字段
- 修改ShoppingController中报价生成接口,改为接收QuotationGenerateRequest参数,校验更严格
- 更新购物保存请求类,加强必填字段校验,新增质保服务及随机配件相关费用字段
- 购物保存服务请求类新增地址及备注字段支持
- 提升了报价单多交货方式的支持能力及数据结构的灵活性和完整性
This commit is contained in:
曹鹏飞 2026-05-14 14:13:31 +08:00
parent 0d79babfac
commit 1316c2786c
12 changed files with 314 additions and 128 deletions

View File

@ -523,23 +523,19 @@ public class ShoppingController extends ControllerBase {
*/
@Transactional
@PostMapping("/quotation/generate")
public ApiResult<Long> generateQuotation(@RequestBody @NotEmpty List<Long> cartIds) {
public ApiResult<Long> generateQuotation(@Valid @RequestBody QuotationGenerateRequest request) {
List<QuotationShoppingCart> carts = shoppingCartService.lambdaQuery()
.eq(QuotationShoppingCart::getStatus, 0)
.eq(QuotationShoppingCart::getCreateByType, AppUserUtil.isAgent() ? 1 : 0)
.eq(QuotationShoppingCart::getCreateById, AppUserUtil.getUserId())
.in(QuotationShoppingCart::getId, cartIds)
.in(QuotationShoppingCart::getId, request.getCartIds())
.list();
VUtils.trueThrowBusinessError(CollectionUtil.isEmpty(carts)).throwMessage("未找到购物车信息");
VUtils.trueThrowBusinessError(cartIds.size() != carts.size()).throwMessage("数据无效");
VUtils.trueThrowBusinessError(request.getCartIds().size() != carts.size()).throwMessage("数据无效");
VUtils.trueThrowBusinessError(carts.stream().map(QuotationShoppingCart::getCustomerName).collect(Collectors.toSet()).size() > 1)
.throwMessage("客户名称不一致");
VUtils.trueThrowBusinessError(carts.stream().map(QuotationShoppingCart::getTargetId).collect(Collectors.toSet()).size() > 1)
.throwMessage("报价主体不一致");
VUtils.trueThrowBusinessError(carts.stream().map(QuotationShoppingCart::getCurrency).collect(Collectors.toSet()).size() > 1)
.throwMessage("币种不一致");
VUtils.trueThrowBusinessError(carts.stream().map(QuotationShoppingCart::getExchangeRate).collect(Collectors.toSet()).size() > 1)
.throwMessage("汇率不一致");
QuotationShoppingOrder order = new QuotationShoppingOrder()
.setCreateByType(AppUserUtil.isAgent() ? 1 : 0)
.setCreateBy(AppUserUtil.getUserName())
@ -553,7 +549,7 @@ public class ShoppingController extends ControllerBase {
.setTargetId(carts.get(0).getTargetId())
.setTotalFee(carts.stream().map(QuotationShoppingCart::getActualFee).reduce(BigDecimal::add).get());
order.setActualFee(order.getTotalFee());
order.setExchangeFee(NumberUtil.multiply(order.getActualFee(), carts.get(0).getExchangeRate()));
order.setExchangeFee(NumberUtil.multiply(order.getActualFee(), request.getExchangeRate()));
shoppingOrderItemService.saveBatch(
carts.stream()
.map(cart -> new QuotationShoppingOrderItem()
@ -566,7 +562,7 @@ public class ShoppingController extends ControllerBase {
shoppingCartService.lambdaUpdate()
.set(QuotationShoppingCart::getStatus, 1)
.eq(QuotationShoppingCart::getStatus, 0)
.in(QuotationShoppingCart::getId, cartIds)
.in(QuotationShoppingCart::getId, request.getCartIds())
.update();
return ApiResult.success(order.getId());
}

View File

@ -0,0 +1,60 @@
package com.nflg.mobilebroken.quotation.pojo.request;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import java.math.BigDecimal;
import java.util.List;
@Data
public class QuotationGenerateRequest {
/**
* 购物车id列表
*/
@NotEmpty
private List<Long> cartIds;
/**
* 交货方式列表
*/
@Valid
@NotEmpty
private List<QuotationShoppingOrderDeliveryMethodRequest> deliveryMethods;
/**
* 付款方式
*/
@NotBlank(message = "付款方式不能为空")
private String paymentMethod;
/**
* 付款方式产生的费用
*/
@NotBlank(message = "付款方式产生的费用不能为空")
private BigDecimal paymentFee;
/**
* 币种字典id
*/
@NotBlank(message = "币种不能为空")
private Long currency;
/**
* 汇率
*/
@NotBlank(message = "汇率不能为空")
private BigDecimal exchangeRate;
/**
* 补充说明
*/
private String additionalNotes;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,35 @@
package com.nflg.mobilebroken.quotation.pojo.request;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
@Data
public class QuotationShoppingOrderDeliveryMethodRequest {
/**
* 交货方式字典id
*/
@NotNull(message = "交货方式不能为空")
private Long deliveryMethod;
/**
* 交货时间
*/
@NotBlank(message = "交货时间不能为空")
private String deliveryDate;
/**
* 交货地点
*/
@NotBlank(message = "交货地点不能为空")
private String deliveryAddress;
/**
* 运费
*/
@NotNull(message = "运费不能为空")
private BigDecimal deliveryFee;
}

View File

@ -19,19 +19,19 @@ public class ShoppingSaveRequest {
/**
* 机型batch_number
*/
@NotNull
@NotNull(message = "机型不能为空")
private Long modelId;
/**
* 价格id
*/
@NotNull
@NotNull(message = "价格id不能为空")
private Long priceId;
/**
* 配置id
*/
@NotNull
@NotNull(message = "配置id不能为空")
private Long configId;
/**
@ -52,52 +52,43 @@ public class ShoppingSaveRequest {
/**
* 客户名称
*/
@NotBlank
@NotBlank(message = "客户名称不能为空")
private String customerName;
/**
* 报价主体代理商时为公司id内部用户时为其用户id
*/
@NotNull
@NotNull(message = "报价主体不能为空")
private Integer targetId;
/**
* 油漆要求
* 其他要求总费用
*/
private String paintRequirements;
/**
* 其他要求
*/
private String otherRequirements;
/**
* 其他要求费用
*/
private BigDecimal otherFee;
@NotNull(message = "其他要求总费用不能为空")
private BigDecimal otherRequirementsFee;
/**
* 标配价格
*/
@NotNull
@NotNull(message = "标配价格不能为空")
private BigDecimal standardFee;
/**
* 选配价格
*/
@NotNull
@NotNull(message = "选配价格不能为空")
private BigDecimal optionalFee;
/**
* 总价
*/
@NotNull
@NotNull(message = "总价不能为空")
private BigDecimal totalFee;
/**
* 实际总价
*/
@NotNull
@NotNull(message = "实际总价不能为空")
private BigDecimal actualFee;
/**
@ -110,57 +101,35 @@ public class ShoppingSaveRequest {
*/
private BigDecimal serviceFee = BigDecimal.ZERO;
/**
* 交货方式字典id
*/
@NotNull
private Long deliveryMethod;
/**
* 交货时间
*/
@NotBlank
private String deliveryDate;
/**
* 交货地点
*/
@NotBlank
private String deliveryAddress;
/**
* 运费
*/
@NotNull
private BigDecimal deliveryFee;
/**
* 付款方式
*/
@NotBlank
private String paymentMethod;
/**
* 付款方式产生的费用
*/
private BigDecimal paymentFee;
/**
* 币种字典id
*/
@NotNull
private Long currency;
/**
* 汇率
*/
private BigDecimal exchangeRate;
/**
* 优惠金额
*/
private BigDecimal discount;
/**
* 质保服务描述
*/
@NotBlank(message = "质保服务描述不能为空")
private String warrantyServiceDesc;
/**
* 质保服务费用
*/
@NotNull(message = "质保服务费用不能为空")
private BigDecimal warrantyServiceFee;
/**
* 随机配件包装费
*/
@NotNull(message = "随机配件包装费不能为空")
private BigDecimal accessoryPackagingFee;
/**
* 随机配件运费
*/
@NotNull(message = "随机配件运费不能为空")
private BigDecimal accessoryShippingFee;
/**
* 部件列表
*/

View File

@ -31,4 +31,14 @@ public class ShoppingSaveServiceRequest {
*/
@NotNull
private BigDecimal totalFee;
/**
* 地点
*/
private String address;
/**
* 备注
*/
private String remark;
}

View File

@ -67,19 +67,9 @@ public class QuotationShoppingCart implements Serializable {
private Integer targetId;
/**
* 油漆要求
* 其他要求总费用
*/
private String paintRequirements;
/**
* 其他要求
*/
private String otherRequirements;
/**
* 其他要求费用
*/
private BigDecimal otherFee;
private BigDecimal otherRequirementsFee;
/**
* 标配价格
@ -111,46 +101,6 @@ public class QuotationShoppingCart implements Serializable {
*/
private BigDecimal serviceFee;
/**
* 交货方式字典id
*/
private Long deliveryMethod;
/**
* 交货时间
*/
private String deliveryDate;
/**
* 交货地点
*/
private String deliveryAddress;
/**
* 运费
*/
private BigDecimal deliveryFee;
/**
* 付款方式
*/
private String paymentMethod;
/**
* 付款方式产生的费用
*/
private BigDecimal paymentFee;
/**
* 币种字典id
*/
private Long currency;
/**
* 汇率
*/
private BigDecimal exchangeRate;
/**
* 创建人类型0内部人员1代理商
*/
@ -185,4 +135,24 @@ public class QuotationShoppingCart implements Serializable {
* 状态0未生成报价单1已生成报价单
*/
private Integer status;
/**
* 质保服务描述
*/
private String warrantyServiceDesc;
/**
* 质保服务费用
*/
private BigDecimal warrantyServiceFee;
/**
* 随机配件包装费
*/
private BigDecimal accessoryPackagingFee;
/**
* 随机配件运费
*/
private BigDecimal accessoryShippingFee;
}

View File

@ -54,4 +54,14 @@ public class QuotationShoppingCartService implements Serializable {
* 服务总费用
*/
private BigDecimal totalFee;
/**
* 地点
*/
private String address;
/**
* 备注
*/
private String remark;
}

View File

@ -105,4 +105,34 @@ public class QuotationShoppingOrder implements Serializable {
* 最后修改时间
*/
private LocalDateTime updateTime;
/**
* 付款方式
*/
private String paymentMethod;
/**
* 付款方式产生的费用
*/
private BigDecimal paymentFee;
/**
* 币种字典id
*/
private Long currency;
/**
* 汇率
*/
private BigDecimal exchangeRate;
/**
* 补充说明
*/
private String additionalNotes;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,54 @@
package com.nflg.mobilebroken.repository.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* <p>
* 报价-报价单-交货方式
* </p>
*
* @author 代码生成器生成
* @since 2026
*/
@Getter
@Setter
@Accessors(chain = true)
@TableName("quotation_shopping_order_delivery_method")
public class QuotationShoppingOrderDeliveryMethod implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
/**
* 报价单id
*/
private Long orderId;
/**
* 交货方式字典id
*/
private Long deliveryMethod;
/**
* 交货时间
*/
private String deliveryDate;
/**
* 交货地点
*/
private String deliveryAddress;
/**
* 运费
*/
private BigDecimal deliveryFee;
}

View File

@ -0,0 +1,16 @@
package com.nflg.mobilebroken.repository.mapper;
import com.nflg.mobilebroken.repository.entity.QuotationShoppingOrderDeliveryMethod;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 报价-报价单-交货方式 Mapper 接口
* </p>
*
* @author 代码生成器生成
* @since 2026
*/
public interface QuotationShoppingOrderDeliveryMethodMapper extends BaseMapper<QuotationShoppingOrderDeliveryMethod> {
}

View File

@ -0,0 +1,16 @@
package com.nflg.mobilebroken.repository.service;
import com.nflg.mobilebroken.repository.entity.QuotationShoppingOrderDeliveryMethod;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 报价-报价单-交货方式 服务类
* </p>
*
* @author 代码生成器生成
* @since 2026
*/
public interface IQuotationShoppingOrderDeliveryMethodService extends IService<QuotationShoppingOrderDeliveryMethod> {
}

View File

@ -0,0 +1,20 @@
package com.nflg.mobilebroken.repository.service.impl;
import com.nflg.mobilebroken.repository.entity.QuotationShoppingOrderDeliveryMethod;
import com.nflg.mobilebroken.repository.mapper.QuotationShoppingOrderDeliveryMethodMapper;
import com.nflg.mobilebroken.repository.service.IQuotationShoppingOrderDeliveryMethodService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 报价-报价单-交货方式 服务实现类
* </p>
*
* @author 代码生成器生成
* @since 2026
*/
@Service
public class QuotationShoppingOrderDeliveryMethodServiceImpl extends ServiceImpl<QuotationShoppingOrderDeliveryMethodMapper, QuotationShoppingOrderDeliveryMethod> implements IQuotationShoppingOrderDeliveryMethodService {
}