refactor(quotation): 优化报价单及购物车相关功能与导出接口

- 优化PDF导出时随机配件与交付服务表格,避免空列表导致异常
- 新增根据机型批次号查询机型信息接口,并补充购物车机型相关字段
- 修改购物车服务代码,支持设置和显示机型描述及配件、服务、其他项列表
- 优化购物车目标名称显示,代理用户显示代理公司名,默认显示公司名
- 修改报价单调整相关字段,新增调价前后实际总价区分
- 调整报价价格更新请求参数校验提示信息,增加@NotNull与@NotBlank消息
- 优化数据查询排序逻辑,调整报价单商品调整按ID倒序排列
- 修改导出报价单接口路由名由/export改为/exportToPdf
- 新增字典项按编码与语言查询接口及对应Mapper实现
- 修改AdminMessageVO获取任务事项类型描述,防止空指针异常
- 删除TraceIdFilter中过长响应内容的截断,改为全量记录响应内容
This commit is contained in:
曹鹏飞 2026-05-22 11:23:35 +08:00
parent a46be04e40
commit 805f2474ba
20 changed files with 168 additions and 58 deletions

View File

@ -6,6 +6,7 @@ import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
import java.util.Optional;
@Data
@Accessors(chain = true)
@ -37,7 +38,7 @@ public class AdminMessageVO {
private String subTypeDesc;
public String getSubTypeDesc() {
return MessageSubType.findByValue(subType).getDescription();
return Optional.ofNullable(MessageSubType.findByValue(subType)).map(MessageSubType::getDescription).orElse("任务事项类型不存在");
}
/**

View File

@ -86,7 +86,7 @@ public class AdminShoppingController extends ControllerBase {
public ApiResult<List<ShoppingOrderAdjustVO>> getAdjusts(@RequestParam Long orderId) {
List<QuotationShoppingOrderAdjust> datas = shoppingOrderAdjustService.lambdaQuery()
.eq(QuotationShoppingOrderAdjust::getOrderId, orderId)
.orderByAsc(QuotationShoppingOrderAdjust::getId)
.orderByDesc(QuotationShoppingOrderAdjust::getId)
.list();
List<QuotationShoppingOrderAdjustItem> adjustItems = shoppingOrderAdjustItemService.lambdaQuery()
.eq(QuotationShoppingOrderAdjustItem::getOrderId, orderId)
@ -152,7 +152,7 @@ public class AdminShoppingController extends ControllerBase {
* 导出报价单
* @param id 报价单ID
*/
@GetMapping("/export")
@GetMapping("/exportToPdf")
public void exportPdf(HttpServletResponse response, @RequestParam Long id) throws IOException {
pdfExportService.export(id,response);
}

View File

@ -195,6 +195,7 @@ public class ShoppingController extends ControllerBase {
.throwMessage("该机型已禁售");
ModelPrice1VO modelPrice = priceService.getModelPrice(request.getModelId(), categoryId);
VUtils.trueThrowBusinessError(Objects.isNull(modelPrice)).throwMessage("该机型尚未设置价格");
DictionaryItem modelDescription = dictionaryItemService.getByCodeAndLanguage("ModelDescription", MultilingualUtil.getLanguage());
ShoppingCartVO vo = new ShoppingCartVO()
.setModelId(request.getModelId())
.setTargetId(request.getTargetId())
@ -202,7 +203,8 @@ public class ShoppingController extends ControllerBase {
.setPriceId(modelPrice.getPriceId())
.setConfigId(modelPrice.getConfigId())
.setTotalFee(modelPrice.getAmount())
.setActualFee(modelPrice.getAmount());
.setActualFee(modelPrice.getAmount())
.setModelDesc(Optional.ofNullable(modelDescription).map(DictionaryItem::getName).orElse(null));
log.debug("机型【{}】售价为{}", request.getModelNo(), modelPrice);
//系数
Pair<BigDecimal, BigDecimal> pair = getRatio(request.getModelId());
@ -452,6 +454,8 @@ public class ShoppingController extends ControllerBase {
if (Objects.nonNull(model)) {
vo.setModelKeyId(model.getId());
}
DictionaryItem modelDescription = dictionaryItemService.getByCodeAndLanguage("ModelDescription", MultilingualUtil.getLanguage());
vo.setModelDesc(modelDescription == null ? "" : modelDescription.getValue());
BigDecimal optionalRatio;
if (Objects.nonNull(cart.getPlanItemId())) {
QuotationUserPlanModelItem planItem = userPlanModelItemService.getById(cart.getPlanItemId());
@ -734,6 +738,14 @@ public class ShoppingController extends ControllerBase {
DictionaryItem currency = dictionaryItemService.getByIdAndLanguage(vo.getCurrency(), MultilingualUtil.getLanguage());
vo.setCurrencyName(currency.getName());
vo.setDeliveryMethods(shoppingOrderDeliveryMethodService.getList(order.getId()));
if (AppUserUtil.isAgent()) {
TBaseCustomer customer = customerService.lambdaQuery()
.eq(TBaseCustomer::getId, vo.getTargetId())
.one();
vo.setTargetName(Optional.ofNullable(customer).map(TBaseCustomer::getAgencyCompanyName).orElse("报价主体不存在"));
} else {
vo.setTargetName(Constant.COMPANY_NAME);
}
List<QuotationShoppingOrderItem> orderItems = shoppingOrderItemService.lambdaQuery()
.eq(QuotationShoppingOrderItem::getOrderId, id)
.list();
@ -743,6 +755,11 @@ public class ShoppingController extends ControllerBase {
List<QuotationShoppingOrderAdjustItem> adjustItems = shoppingOrderAdjustItemService.getParts(id);
carts.forEach(cart -> {
ShoppingCartVO cartVO = Convert.convert(ShoppingCartVO.class, cart);
ProductModel model = productModelService.getInfoByBatchNumber(cart.getModelId());
cartVO.setModelNo(model.getNo());
cartVO.setModelKeyId(model.getId());
DictionaryItem modelDescription = dictionaryItemService.getByCodeAndLanguage("ModelDescription", MultilingualUtil.getLanguage());
cartVO.setModelDesc(Optional.ofNullable(modelDescription).map(DictionaryItem::getName).orElse(null));
QuotationShoppingOrderAdjustItem adjustItem = adjustItems.stream()
.filter(item -> item.getCartId().equals(cart.getId()) && item.getConfigItemId().equals(0L))
.findFirst()
@ -787,6 +804,18 @@ public class ShoppingController extends ControllerBase {
}
).collect(Collectors.toList())
);
cartVO.setAccessories(shoppingCartAccessoryService.lambdaQuery()
.eq(QuotationShoppingCartAccessory::getCartId, cart.getId())
.list()
);
cartVO.setServices(shoppingCartServiceService.lambdaQuery()
.eq(QuotationShoppingCartService::getCartId, cart.getId())
.list()
);
cartVO.setOthers(shoppingCartOtherService.lambdaQuery()
.eq(QuotationShoppingCartOther::getCartId, cart.getId())
.list()
);
vo.getItems().add(cartVO);
});
return ApiResult.success(vo);
@ -817,7 +846,13 @@ public class ShoppingController extends ControllerBase {
VUtils.trueThrowBusinessError(Objects.isNull(order)).throwMessage("未找到报价单");
return ApiResult.success(shoppingOrderService.getCarts(orderId)
.stream()
.map(cart -> new CartsForCopyVO(cart.getId(), cart.getModelNo()))
.map(cart -> {
ProductModel model = productModelService.lambdaQuery()
.eq(ProductModel::getBatchNumber, cart.getModelId())
.eq(ProductModel::getState, 1)
.one();
return new CartsForCopyVO(cart.getId(), cart.getModelNo(), cart.getModelId(), model.getId());
})
.collect(Collectors.toList())
);
}
@ -851,6 +886,8 @@ public class ShoppingController extends ControllerBase {
.setPlanItemId(cart.getPlanItemId())
.setConfigId(cart.getConfigId())
.setDiscount(cart.getDiscount())
.setStandardFee(cart.getStandardFee())
.setOptionalFee(cart.getOptionalFee())
.setTotalFee(cart.getStandardFee().add(cart.getOptionalFee()))
.setActualFee(cart.getStandardFee().add(cart.getOptionalFee()));
// //系数
@ -966,7 +1003,8 @@ public class ShoppingController extends ControllerBase {
.setId(IdUtil.getId())
.setOrderId(order.getId())
.setDiscount(order.getActualFee().subtract(request.getActualFee()))
.setActualFee(request.getActualFee())
.setOldActualFee(order.getActualFee())
.setNewActualFee(request.getActualFee())
.setReason(request.getReason())
.setCreateTime(LocalDateTime.now());
order.setActualFee(request.getActualFee());
@ -1015,7 +1053,7 @@ public class ShoppingController extends ControllerBase {
* 报价单-导出报价单
* @param id 报价单ID
*/
@GetMapping("/export")
@GetMapping("/exportToPdf")
public void exportPdf(HttpServletResponse response, @RequestParam Long id) throws IOException {
pdfExportService.export(id, response);
}

View File

@ -17,12 +17,12 @@ public class QuotationPriceUpdateItemPartRequest {
/**
* 标准配置调价前价格
*/
@NotNull
@NotNull(message = "标准配置调价前价格不能为空")
private BigDecimal oldStandardFee;
/**
* 标准配置调价后价格
*/
@NotNull
@NotNull(message = "标准配置调价后价格不能为空")
private BigDecimal newStandardFee;
}

View File

@ -19,11 +19,13 @@ public class QuotationPriceUpdateItemRequest {
/**
* 标准配置调价前价格
*/
@NotNull(message = "标准配置调价前价格不能为空")
private BigDecimal oldStandardFee;
/**
* 标准配置调价后价格
*/
@NotNull(message = "标准配置调价后价格不能为空")
private BigDecimal newStandardFee;
/**

View File

@ -19,17 +19,19 @@ public class QuotationPriceUpdateRequest {
/**
* 调价后实际总价
*/
@NotNull(message = "调价后实际总价不能为空")
private BigDecimal actualFee;
/**
* 调价原因
*/
@NotBlank(message = "调价原因不能为空")
private String reason;
/**
* 报价失效时间
*/
@NotNull
@NotNull(message = "报价失效时间不能为空")
private LocalDate effectiveEndTime;
/**

View File

@ -16,4 +16,14 @@ public class CartsForCopyVO {
* 机型
*/
private String modelNo;
/**
* 机型batch_number
*/
private Long modelId;
/**
* 机型自增id
*/
private Integer modelKeyId;
}

View File

@ -28,6 +28,11 @@ public class QuotationOrderInfoVO {
*/
private Integer targetId;
/**
* 报价主体名称
*/
private String targetName;
/**
* 报价人代码
*/
@ -148,6 +153,11 @@ public class QuotationOrderInfoVO {
*/
private String contactCountry;
/**
* 报价公司地址
*/
private String address;
/**
* 交付方式列表
*/

View File

@ -21,11 +21,21 @@ public class ShoppingCartVO {
*/
private Long modelId;
/**
* 机型编号
*/
private String modelNo;
/**
* 机型自增id
*/
private Integer modelKeyId;
/**
* 机型描述
*/
private String modelDesc;
/**
* 价格id
*/

View File

@ -18,9 +18,14 @@ public class ShoppingOrderAdjustVO {
private BigDecimal discount;
/**
* 实际总价
* 调价前实际总价
*/
private BigDecimal actualFee;
private BigDecimal oldActualFee;
/**
* 调价后实际总价
*/
private BigDecimal newActualFee;
/**
* 调价原因

View File

@ -1,5 +1,6 @@
package com.nflg.mobilebroken.quotation.service;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.StrUtil;
import com.google.common.primitives.Floats;
@ -324,6 +325,7 @@ public class PdfExportService {
private void addRandomAccessories(Document document, List<QuotationShoppingCartAccessory> accessories
, List<WebComponentTranslateVO> translates) {
if (CollectionUtil.isNotEmpty(accessories)) {
document.add(new Paragraph(new Chunk(getText(translates, "RandomAccessories"), new Font(bfChinese, 13, Font.BOLD))));
PdfPTable table = new PdfPTable(5);
table.setWidthPercentage(100);
@ -344,8 +346,10 @@ public class PdfExportService {
});
document.add(table);
}
}
private void addJJFF(Document document, List<QuotationShoppingCartService> services, List<WebComponentTranslateVO> translates) {
if (CollectionUtil.isNotEmpty(services)) {
document.add(new Paragraph(new Chunk(getText(translates, "HandoverService"), new Font(bfChinese, 13, Font.BOLD))));
PdfPTable table = new PdfPTable(6);
table.setWidthPercentage(100);
@ -368,6 +372,7 @@ public class PdfExportService {
}
document.add(table);
}
}
private void addPriceInfo(Document document, List<OrderDeliveryMethodDTO> deliveryMethods
, List<ModelConfigEffectiveDTO> parts, QuotationShoppingOrder order, QuotationShoppingCartDTO cart

View File

@ -37,9 +37,14 @@ public class QuotationShoppingOrderAdjust implements Serializable {
private BigDecimal discount;
/**
* 实际总价
* 调价前实际总价
*/
private BigDecimal actualFee;
private BigDecimal oldActualFee;
/**
* 调价后实际总价
*/
private BigDecimal newActualFee;
/**
* 调价原因

View File

@ -29,4 +29,6 @@ public interface DictionaryItemMapper extends BaseMapper<DictionaryItem> {
List<DictionaryItem> getListByDictionaryCodeAndType(String dictionaryCode, String type);
DictionaryItem getByIdAndLanguage(Long id, String language);
DictionaryItem getByCodeAndLanguage(String code, String language);
}

View File

@ -33,4 +33,6 @@ public interface IDictionaryItemService extends IService<DictionaryItem> {
List<DictionaryItem> getListByDictionaryCodeAndType(String dictionaryCode,String type);
DictionaryItem getByIdAndLanguage(Long id, String language);
DictionaryItem getByCodeAndLanguage(String code, String language);
}

View File

@ -58,4 +58,6 @@ public interface IProductModelService extends IService<ProductModel> {
IPage<QuotationProductModelSearchVO> searchForQuotation(QuotationProductModelSearchRequest request, String language);
QuotationProductModelInfoVO getInfoForQuotation(Long id, String language);
ProductModel getInfoByBatchNumber(Long modelId);
}

View File

@ -158,4 +158,9 @@ public class DictionaryItemServiceImpl extends ServiceImpl<DictionaryItemMapper,
public DictionaryItem getByIdAndLanguage(Long id, String language) {
return baseMapper.getByIdAndLanguage(id, language);
}
@Override
public DictionaryItem getByCodeAndLanguage(String code, String language) {
return baseMapper.getByCodeAndLanguage(code, language);
}
}

View File

@ -420,6 +420,11 @@ public class ProductModelServiceImpl extends ServiceImpl<ProductModelMapper, Pro
return baseMapper.getInfoForQuotation(id,language);
}
@Override
public ProductModel getInfoByBatchNumber(Long modelId) {
return lambdaQuery().eq(ProductModel::getBatchNumber, modelId).eq(ProductModel::getState, 1).one();
}
private ProductModelCompareInfoVO getModelCompareInfo(Integer modelId, String language){
ProductModel productModel = getById(modelId);
VUtils.trueThrowBusinessError(Objects.isNull(productModel)).throwMessage("无效的数据");

View File

@ -37,4 +37,13 @@
inner join dictionary_item_translate dit on di.id = dit.dictionary_item_id
where di.id = #{id} and dit.language_code = #{language}
</select>
<select id="getByCodeAndLanguage" resultType="com.nflg.mobilebroken.repository.entity.DictionaryItem">
select dit.value as name,di.*
from dictionary_item di
inner join dictionary_item_translate dit on di.id = dit.dictionary_item_id
where di.code = #{code} and dit.language_code = #{language}
order by di.id desc
limit 1
</select>
</mapper>

View File

@ -24,5 +24,6 @@
<if test="request.no!=null and request.no!=''">
AND pm.`no` like concat('%', #{request.no}, '%')
</if>
order by qsc.id desc
</select>
</mapper>

View File

@ -72,11 +72,7 @@ public class TraceIdFilter extends OncePerRequestFilter {
}
byte[] buf = response.getContentAsByteArray();
if (buf.length > 0) {
if (buf.length<=1024) {
return new String(buf, StandardCharsets.UTF_8);
}else {
return "数据过长只记录前1024字节";
}
}
return "无数据";
}