feat(quotation): 新增管理端客户报价单功能并完善相关接口

- 新增 AdminShoppingController 提供管理端报价单查询功能
- 添加 QuotationAdminSearchRequest 和 QuotationSearchRequest 请求参数类
- 创建 QuotationOrderInfoVO 和 QuotationSearchVO 返回结果对象
- 完善 IQuotationShoppingOrderService 服务接口及其实现
- 新增报价单搜索功能支持管理员端查询操作
- 添加购物车子项选择零件查询功能
- 完善用户方案默认配置和模型方案配置功能
- 修改价格配置和系数配置控制器路径规范
- 在购物车中集成方案系数计算逻辑
- 添加报价对象过滤条件确保只显示启用的客户
- 完善报价单状态判断逻辑和时间有效性验证
This commit is contained in:
曹鹏飞 2026-03-12 15:33:27 +08:00
parent 473c8f3240
commit 9e9635f709
32 changed files with 819 additions and 22 deletions

View File

@ -0,0 +1,74 @@
package com.nflg.mobilebroken.common.pojo.request;
import lombok.Data;
import java.time.LocalDate;
@Data
public class QuotationAdminSearchRequest extends PageRequest{
/**
* 型号
*/
private String no;
/**
* 模块ID
*/
private Integer moduleId;
/**
* 系列批次号
*/
private Long seriesNumber;
/**
* 类型批次号
*/
private Long typeNumber;
/**
* 客户名称
*/
private String customerName;
/**
* 报价对象
*/
private Long targetId;
/**
* 报价人代码
*/
private String quotationCode;
/**
* 配置ID
*/
private Long configId;
/**
* 价格ID
*/
private Long priceId;
/**
* 是否特价产品
*/
private Boolean hasDiscount;
/**
* 是否新品
*/
private Boolean recommend;
/**
* 报价时间-开始
*/
private LocalDate createTimeStart;
/**
* 报价时间-结束
*/
private LocalDate createTimeEnd;
}

View File

@ -0,0 +1,32 @@
package com.nflg.mobilebroken.common.pojo.request;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.nflg.mobilebroken.common.util.AppUserUtil;
import lombok.Data;
import java.time.LocalDate;
import java.util.List;
@Data
public class QuotationSearchRequest extends ModelConfigSearchRequest {
/**
* 报价时间-开始
*/
private LocalDate createTimeStart;
/**
* 报价时间-结束
*/
private LocalDate createTimeEnd;
@JsonIgnore
private List<Long> modelIds;
@JsonIgnore
private Integer userType = AppUserUtil.isAgent() ? 1 : 0;
@JsonIgnore
private Integer userId = AppUserUtil.getUserId();
}

View File

@ -0,0 +1,114 @@
package com.nflg.mobilebroken.common.pojo.vo;
import cn.hutool.core.util.StrUtil;
import com.nflg.mobilebroken.common.util.DateTimeUtil;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
public class QuotationSearchVO {
private Long id;
/**
* 报价编号
*/
private String no;
/**
* 客户名称
*/
private String customerName;
/**
* 报价人代码
*/
private String quotationCode;
/**
* 报价对象
*/
private Integer targetId;
/**
* 报价对象
*/
private String targetName;
/**
* 优惠金额
*/
private BigDecimal discount;
/**
* 总价
*/
private BigDecimal totalFee;
/**
* 实际总价
*/
private BigDecimal actualFee;
/**
* 报价总价汇率计算后的价格可能为空
*/
private BigDecimal exchangeFee;
/**
* 报价生效时间
*/
private String effectiveStartTime;
/**
* 报价失效时间
*/
private String effectiveEndTime;
/**
* 状态0失效1有效
*/
private Integer status;
public Integer getStatus() {
String date = DateTimeUtil.format(LocalDate.now(), "yyyy-MM-dd");
if (StrUtil.isNotBlank(effectiveStartTime)) {
if (StrUtil.isNotBlank(effectiveEndTime)) {
return effectiveStartTime.compareTo(date) <= 0 && effectiveEndTime.compareTo(date) >= 0 ? 1 : 0;
} else {
return effectiveStartTime.compareTo(date) <= 0 ? 1 : 0;
}
} else if (StrUtil.isNotBlank(effectiveEndTime)) {
return effectiveEndTime.compareTo(date) >= 0 ? 1 : 0;
}
return null;
}
/**
* 创建人类型0内部人员1代理商
*/
private Integer createByType;
/**
* 创建人id
*/
private Integer createById;
/**
* 创建人
*/
private String createBy;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 最后修改时间
*/
private LocalDateTime updateTime;
}

View File

@ -0,0 +1,70 @@
package com.nflg.mobilebroken.quotation.controller.admin;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nflg.mobilebroken.common.pojo.ApiResult;
import com.nflg.mobilebroken.common.pojo.PageData;
import com.nflg.mobilebroken.common.pojo.request.QuotationAdminSearchRequest;
import com.nflg.mobilebroken.common.pojo.request.QuotationSearchRequest;
import com.nflg.mobilebroken.common.pojo.vo.QuotationSearchVO;
import com.nflg.mobilebroken.common.util.AppUserUtil;
import com.nflg.mobilebroken.quotation.controller.ControllerBase;
import com.nflg.mobilebroken.repository.entity.ProductModel;
import com.nflg.mobilebroken.repository.entity.TBaseCustomer;
import com.nflg.mobilebroken.repository.service.IQuotationShoppingOrderService;
import com.nflg.mobilebroken.repository.service.ITBaseCustomerService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import java.util.stream.Collectors;
/**
* (管理端)客户报价单
*/
@RestController
@RequestMapping("/shopping")
public class AdminShoppingController extends ControllerBase {
@Resource
private IQuotationShoppingOrderService shoppingOrderService;
@Resource
private ITBaseCustomerService customerService;
/**
* 查询报价单
*/
@PostMapping("/search")
public ApiResult<PageData<QuotationSearchVO>> searchQuotation(@Valid @RequestBody QuotationAdminSearchRequest request) {
IPage<QuotationSearchVO> datas = shoppingOrderService.searchFromAdmin(request);
boolean isAgent = AppUserUtil.isAgent();
List<TBaseCustomer> customers;
if (isAgent) {
customers = customerService.lambdaQuery()
.in(TBaseCustomer::getId, datas.getRecords().stream().map(QuotationSearchVO::getTargetId).collect(Collectors.toList()))
.list();
} else {
customers = null;
}
return ApiResult.success(datas, data -> {
if (isAgent) {
data.setTargetName(
customers.stream()
.filter(item -> item.getId().equals(data.getTargetId()))
.map(TBaseCustomer::getAgencyCompanyName)
.findFirst()
.orElse("")
);
} else {
data.setTargetName(AppUserUtil.getUserName());
}
//TODO 设置汇率价格
return data;
});
}
}

View File

@ -37,9 +37,6 @@ import java.util.stream.Collectors;
@RequestMapping("/forbid/config")
public class ForbidConfigController extends ControllerBase {
@Resource
private ObjectMapper objectMapper;
@Resource
private IDictionaryItemService dictionaryItemService;
@ -60,6 +57,7 @@ public class ForbidConfigController extends ControllerBase {
List<TBaseCustomer> customers = customerService.lambdaQuery()
.eq(TBaseCustomer::getDelIs, 0)
.eq(TBaseCustomer::getEnableState, 1)
.eq(TBaseCustomer::getQuotationUse, true)
.isNotNull(TBaseCustomer::getCategoryId)
.orderByAsc(TBaseCustomer::getId)
.list();
@ -93,6 +91,7 @@ public class ForbidConfigController extends ControllerBase {
List<TBaseCustomer> customers = customerService.lambdaQuery()
.eq(TBaseCustomer::getDelIs, 0)
.eq(TBaseCustomer::getEnableState, 1)
.eq(TBaseCustomer::getQuotationUse, true)
.isNotNull(TBaseCustomer::getCategoryId)
.list();
return ApiResult.success(pdatas, data -> {
@ -128,6 +127,7 @@ public class ForbidConfigController extends ControllerBase {
List<TBaseCustomer> customers = customerService.lambdaQuery()
.eq(TBaseCustomer::getDelIs, 0)
.eq(TBaseCustomer::getEnableState, 1)
.eq(TBaseCustomer::getQuotationUse, true)
.isNotNull(TBaseCustomer::getCategoryId)
.list();
List<QuotationModelForbid> forbidForAdd = new ArrayList<>();

View File

@ -99,6 +99,7 @@ public class PriceConfigController extends ControllerBase {
return ApiResult.success(priceService.search(request), data -> {
Map<String, Object> map = objectMapper.convertValue(data, new TypeReference<>() {
});
map.put("key", IdUtil.getSnowflakeNextIdStr());
if (CollectionUtil.isNotEmpty(data.getAreas())) {
data.getAreas().forEach(area -> {
map.put(area.getCode(), NumberUtil.format(area.getAmount()));
@ -113,6 +114,7 @@ public class PriceConfigController extends ControllerBase {
public void convert(Map<String, Object> map, ModelPriceConfigVO vo) {
Map<String, Object> md = objectMapper.convertValue(vo, new TypeReference<>() {
});
md.put("key", IdUtil.getSnowflakeNextIdStr());
if (CollectionUtil.isNotEmpty(vo.getAreas())) {
vo.getAreas().forEach(area -> {
md.put(area.getCode(), NumberUtil.format(area.getAmount()));

View File

@ -72,6 +72,7 @@ public class RatioAgentConfigController extends ControllerBase {
.select(TBaseCustomer::getId, TBaseCustomer::getCategoryId)
.eq(TBaseCustomer::getDelIs, 0)
.eq(TBaseCustomer::getEnableState, 1)
.eq(TBaseCustomer::getQuotationUse, true)
.list();
List<DictionaryItem> categories = dictionaryItemService.getListByDictionaryCode(Constant.DICTIONARY_DIRECT_SALES_CATEGORY);
List<AppUser> users = appUserService.lambdaQuery()
@ -132,6 +133,7 @@ public class RatioAgentConfigController extends ControllerBase {
.select(TBaseCustomer::getId, TBaseCustomer::getCategoryId)
.eq(TBaseCustomer::getDelIs, 0)
.eq(TBaseCustomer::getEnableState, 1)
.eq(TBaseCustomer::getQuotationUse, true)
.list();
List<DictionaryItem> categories = dictionaryItemService.getListByDictionaryCode(Constant.DICTIONARY_DIRECT_SALES_CATEGORY);
List<AppUser> users = appUserService.lambdaQuery()

View File

@ -126,7 +126,6 @@ public class RatioConfigController extends ControllerBase {
List<QuotationModelRatioAgentItemDTO> agentItems = ratioAgentItemService.getEffectives();
if (CollectionUtil.isNotEmpty(agentItems)) {
List<AppUser> users = appUserService.listByIds(agentItems.stream().map(QuotationModelRatioAgentItemDTO::getUserId).collect(Collectors.toList()));
vo.getChildren().add(voc1);
agentItems.stream()
.filter(QuotationModelRatioAgentItemDTO::getIsPrimary)
.forEach(ai -> {

View File

@ -33,7 +33,7 @@ import java.util.stream.Collectors;
* (用户端)定价系数
*/
@RestController
@RequestMapping("app/ratio/agent")
@RequestMapping("/app/ratio/agent")
public class AppRatioAgentConfigController extends ControllerBase {
@Resource

View File

@ -30,7 +30,7 @@ import java.util.stream.Collectors;
* (用户端)报价项目管理
*/
@RestController
@RequestMapping("/plan")
@RequestMapping("/app/plan")
public class PlanController extends ControllerBase {
@Resource

View File

@ -2,19 +2,27 @@ package com.nflg.mobilebroken.quotation.controller.app;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nflg.mobilebroken.common.pojo.ApiResult;
import com.nflg.mobilebroken.common.pojo.PageData;
import com.nflg.mobilebroken.common.pojo.dto.ModelConfigEffectiveDTO;
import com.nflg.mobilebroken.common.pojo.dto.QuotationDiscountDTO;
import com.nflg.mobilebroken.common.pojo.dto.QuotationModelRatioDirectItemDTO;
import com.nflg.mobilebroken.common.pojo.request.QuotationSearchRequest;
import com.nflg.mobilebroken.common.pojo.request.ShoppingSearchRequest;
import com.nflg.mobilebroken.common.pojo.vo.ModelPrice1VO;
import com.nflg.mobilebroken.common.pojo.vo.QuotationSearchVO;
import com.nflg.mobilebroken.common.pojo.vo.ShoppingSearchVO;
import com.nflg.mobilebroken.common.pojo.vo.SimpleUserVO;
import com.nflg.mobilebroken.common.util.*;
import com.nflg.mobilebroken.quotation.controller.ControllerBase;
import com.nflg.mobilebroken.quotation.pojo.request.QuotationInfoUpdateRequest;
import com.nflg.mobilebroken.quotation.pojo.request.QuotationPriceUpdateRequest;
import com.nflg.mobilebroken.quotation.pojo.request.ShoppingInitRequest;
import com.nflg.mobilebroken.quotation.pojo.request.ShoppingSaveRequest;
import com.nflg.mobilebroken.quotation.pojo.vo.QuotationOrderInfoVO;
import com.nflg.mobilebroken.quotation.pojo.vo.ShoppingCartPartVO;
import com.nflg.mobilebroken.quotation.pojo.vo.ShoppingCartVO;
import com.nflg.mobilebroken.repository.entity.*;
@ -28,11 +36,9 @@ import javax.annotation.Resource;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.*;
import java.util.stream.Collectors;
/**
@ -40,7 +46,7 @@ import java.util.stream.Collectors;
*/
@Slf4j
@RestController
@RequestMapping("/shopping")
@RequestMapping("/app/shopping")
public class ShoppingController extends ControllerBase {
@Resource
@ -64,6 +70,12 @@ public class ShoppingController extends ControllerBase {
@Resource
private IQuotationModelRatioDirectItemService ratioDirectItemService;
@Resource
private IQuotationUserPlanModelItemService userPlanModelItemService;
@Resource
private IQuotationUserPlanDefaultService userPlanDefaultService;
@Resource
private IQuotationShoppingCartService shoppingCartService;
@ -76,8 +88,23 @@ public class ShoppingController extends ControllerBase {
@Resource
private IQuotationShoppingCartServiceService shoppingCartServiceService;
@Resource
private IQuotationShoppingOrderService shoppingOrderService;
@Resource
private IQuotationShoppingOrderAdjustService shoppingOrderAdjustService;
@Resource
private IQuotationShoppingOrderItemService shoppingOrderItemService;
@Resource
private IProductModelService productModelService;
@Resource
private ITBaseCustomerService customerService;
/**
* 获取报价对象
* 购物车-获取报价对象
*/
@GetMapping("/cart/getTargets")
public ApiResult<List<SimpleUserVO>> getTargets() {
@ -101,7 +128,7 @@ public class ShoppingController extends ControllerBase {
}
/**
* 根据机型查询售价
* 购物车-根据机型查询售价
*/
@PostMapping("/cart/init")
public ApiResult<ShoppingCartVO> init(@Valid @RequestBody ShoppingInitRequest request) {
@ -131,7 +158,22 @@ public class ShoppingController extends ControllerBase {
Pair<BigDecimal, BigDecimal> pair = getRatio(request.getModelId());
BigDecimal standardRatio = pair.getLeft(), optionalRatio = pair.getRight();
log.debug("机型【{}】标准配件系数为{},可选配件系数为{}", request.getModelNo(), standardRatio, optionalRatio);
vo.setActualFee(vo.getActualFee().multiply(standardRatio));
//方案
QuotationUserPlanModelItem planModelItem = userPlanModelItemService.getEffectiveForUser(request.getModelId(), AppUserUtil.isAgent() ? 1 : 0, AppUserUtil.getUserId());
if (Objects.nonNull(planModelItem)) {
log.debug("机型【{}】方案为{},系数:{}", request.getModelNo(), planModelItem.getName(), planModelItem.getRatio());
standardRatio = NumberUtil.multiply(standardRatio, planModelItem.getRatio());
log.debug("机型【{}】标准配件系数为{},可选配件系数为{}", request.getModelNo(), standardRatio, optionalRatio);
vo.setPlanItemId(planModelItem.getId());
} else {
QuotationUserPlanDefault userPlanDefault = userPlanDefaultService.getEffectiveForUser(AppUserUtil.isAgent() ? 1 : 0, AppUserUtil.getUserId());
if (Objects.nonNull(userPlanDefault)) {
log.debug("用户方案默认系数为{}", userPlanDefault.getRatio());
standardRatio = NumberUtil.multiply(standardRatio, userPlanDefault.getRatio());
log.debug("机型【{}】标准配件系数为{},可选配件系数为{}", request.getModelNo(), standardRatio, optionalRatio);
}
}
vo.setActualFee(vo.getTotalFee().subtract(vo.getDiscount()).multiply(standardRatio));
log.debug("机型【{}】价格为{},优惠{}", request.getModelNo(), vo.getActualFee(), vo.getDiscount());
//获取部件配置
List<ModelConfigEffectiveDTO> parts = modelConfigService.getEffectives(modelPrice.getPriceId(), categoryId, MultilingualUtil.getLanguage());
@ -169,7 +211,7 @@ public class ShoppingController extends ControllerBase {
}
/**
* 保存购物车
* 购物车-保存购物车
*/
@Transactional
@PostMapping("/cart/save")
@ -236,7 +278,7 @@ public class ShoppingController extends ControllerBase {
}
/**
* 查询购物车
* 购物车-查询购物车
*/
@PostMapping("/cart/search")
public ApiResult<PageData<ShoppingSearchVO>> search(@Valid @RequestBody ShoppingSearchRequest request) {
@ -244,7 +286,7 @@ public class ShoppingController extends ControllerBase {
}
/**
* 删除购物车
* 购物车-删除购物车
*/
@Transactional
@PostMapping("/cart/delete")
@ -266,10 +308,11 @@ public class ShoppingController extends ControllerBase {
}
/**
* 生成报价单
* 报价单-生成报价单
*/
@Transactional
@PostMapping("/quotation/generate")
private ApiResult<Void> generate(@RequestBody @NotEmpty List<Long> cartIds) {
public ApiResult<Void> generateQuotation(@RequestBody @NotEmpty List<Long> cartIds) {
List<QuotationShoppingCart> carts = shoppingCartService.lambdaQuery()
.eq(QuotationShoppingCart::getStatus, 0)
.eq(QuotationShoppingCart::getCreateByType, AppUserUtil.isAgent() ? 1 : 0)
@ -281,7 +324,193 @@ public class ShoppingController extends ControllerBase {
.throwMessage("客户名称不一致");
VUtils.trueThrowBusinessError(carts.stream().map(QuotationShoppingCart::getTargetId).collect(Collectors.toSet()).size() > 1)
.throwMessage("报价对象不一致");
//TODO
QuotationShoppingOrder order = new QuotationShoppingOrder()
.setCreateByType(AppUserUtil.isAgent() ? 1 : 0)
.setCreateBy(AppUserUtil.getUserName())
.setCreateById(AppUserUtil.getUserId())
.setCreateTime(LocalDateTime.now())
.setId(IdUtil.getId())
.setCustomerName(carts.get(0).getCustomerName())
.setTargetId(carts.get(0).getTargetId())
.setTotalFee(carts.stream().map(QuotationShoppingCart::getActualFee).reduce(BigDecimal::add).get());
order.setActualFee(order.getTotalFee());
shoppingOrderItemService.saveBatch(
carts.stream()
.map(cart -> new QuotationShoppingOrderItem()
.setOrderId(order.getId())
.setCartId(cart.getId()))
.collect(Collectors.toList())
);
QuotationShoppingOrder last = CollectionUtil.get(shoppingOrderService.lambdaQuery().orderByDesc(QuotationShoppingOrder::getId).list(), 0);
if (Objects.isNull(last)) {
order.setNo("QO" + DateTimeUtil.format(LocalDate.now(), "yyyyMMdd") + "001");
} else {
String date = StrUtil.subWithLength(last.getNo(), 2, 8);
if (date.equals(DateTimeUtil.format(LocalDate.now(), "yyyyMMdd"))) {
order.setNo("QO" + date + StrUtil.padPre(String.valueOf(last.getId() + 1), 3, '0'));
} else {
order.setNo("QO" + DateTimeUtil.format(LocalDate.now(), "yyyyMMdd") + "001");
}
}
shoppingOrderService.save(order);
return ApiResult.success();
}
/**
* 报价单-查询报价单
*/
@PostMapping("/quotation/search")
public ApiResult<PageData<QuotationSearchVO>> searchQuotation(@Valid @RequestBody QuotationSearchRequest request) {
Integer page = request.getPage(), pageSize = request.getPageSize();
request.setPage(1);
request.setPageSize(Integer.MAX_VALUE);
Page<ProductModel> models = productModelService.searchForQuotation(request);
request.setModelIds(
models.getRecords().stream()
.map(ProductModel::getBatchNumber)
.collect(Collectors.toList())
);
request.setPage(page);
request.setPageSize(pageSize);
IPage<QuotationSearchVO> datas = shoppingOrderService.search(request);
boolean isAgent = AppUserUtil.isAgent();
List<TBaseCustomer> customers;
if (isAgent) {
customers = customerService.lambdaQuery()
.in(TBaseCustomer::getId, datas.getRecords().stream().map(QuotationSearchVO::getTargetId).collect(Collectors.toList()))
.list();
} else {
customers = null;
}
return ApiResult.success(datas, data -> {
if (isAgent) {
data.setTargetName(
customers.stream()
.filter(item -> item.getId().equals(data.getTargetId()))
.map(TBaseCustomer::getAgencyCompanyName)
.findFirst()
.orElse("")
);
} else {
data.setTargetName(AppUserUtil.getUserName());
}
//TODO 设置汇率价格
return data;
});
}
/**
* 报价单-获取报价单详情
*/
@GetMapping("/quotation/getInfo")
public ApiResult<QuotationOrderInfoVO> getQuotationInfo(@RequestParam Long id) {
QuotationShoppingOrder order = shoppingOrderService.getById(id);
VUtils.trueThrowBusinessError(Objects.isNull(order)).throwMessage("未找到报价单");
QuotationOrderInfoVO vo = Convert.convert(QuotationOrderInfoVO.class, order);
List<QuotationShoppingOrderItem> orderItems = shoppingOrderItemService.lambdaQuery()
.eq(QuotationShoppingOrderItem::getOrderId, id)
.list();
List<QuotationShoppingCart> carts = shoppingCartService.lambdaQuery()
.in(QuotationShoppingCart::getId, orderItems.stream().map(QuotationShoppingOrderItem::getCartId).collect(Collectors.toList()))
.list();
carts.forEach(cart -> {
ShoppingCartVO cartVO = Convert.convert(ShoppingCartVO.class, cart);
QuotationShoppingOrderItem orderItem = orderItems.stream()
.filter(item -> item.getCartId().equals(cart.getId()))
.findFirst()
.orElse(null);
if (Objects.nonNull(orderItem)) {
cartVO.setStandardFee(orderItem.getStandardFee());
}
//获取部件配置
List<ModelConfigEffectiveDTO> parts = shoppingCartItemService.getSelectedParts(cart.getId(), MultilingualUtil.getLanguage());
cartVO.setStandardParts(parts.stream()
.filter(part -> part.getParentId() == 0L && part.getType() == 1)
.map(part -> {
ShoppingCartPartVO vi = Convert.convert(ShoppingCartPartVO.class, part);
vi.setAmount(
vi.getAmount().add(
parts.stream()
.filter(pi -> pi.getParentId().equals(part.getId()) && pi.getType() == 0)
.map(ModelConfigEffectiveDTO::getAmount)
.reduce(BigDecimal::add)
.orElse(BigDecimal.ZERO)
)
);
return vi;
}
).collect(Collectors.toList())
);
cartVO.setOptionalParts(parts.stream()
.filter(part -> part.getParentId() == 0L && part.getType() == 0)
.map(part -> {
ShoppingCartPartVO vi = Convert.convert(ShoppingCartPartVO.class, part);
vi.setAmount(
vi.getAmount().add(
parts.stream()
.filter(pi -> pi.getParentId().equals(part.getId()) && pi.getType() == 0)
.map(ModelConfigEffectiveDTO::getAmount)
.reduce(BigDecimal::add)
.orElse(BigDecimal.ZERO)
)
);
return vi;
}
).collect(Collectors.toList())
);
vo.getItems().add(cartVO);
});
return ApiResult.success(vo);
}
/**
* 报价单-设置报价有效期
*/
@PostMapping("/quotation/update")
public ApiResult<Void> updateQuotationInfo(@Valid @RequestBody QuotationInfoUpdateRequest request) {
QuotationShoppingOrder order = shoppingOrderService.getById(request.getId());
VUtils.trueThrowBusinessError(Objects.isNull(order)).throwMessage("未找到报价单");
shoppingOrderService.lambdaUpdate()
.set(QuotationShoppingOrder::getEffectiveStartTime, request.getEffectiveStartTime())
.set(QuotationShoppingOrder::getEffectiveEndTime, request.getEffectiveEndTime())
.set(QuotationShoppingOrder::getUpdateTime, LocalDateTime.now())
.eq(QuotationShoppingOrder::getId, request.getId())
.update();
return ApiResult.success();
}
/**
* 报价单-调价
*/
@Transactional
@PostMapping("/quotation/updatePrice")
public ApiResult<Void> updateQuotationPrice(@Valid @RequestBody QuotationPriceUpdateRequest request) {
QuotationShoppingOrder order = shoppingOrderService.getById(request.getId());
VUtils.trueThrowBusinessError(Objects.isNull(order)).throwMessage("未找到报价单");
request.getItems().forEach(item -> {
shoppingOrderItemService.lambdaUpdate()
.set(QuotationShoppingOrderItem::getStandardFee, item.getNewStandardFee())
.eq(QuotationShoppingOrderItem::getCartId, item.getCartId())
.eq(QuotationShoppingOrderItem::getOrderId, request.getId())
.update();
});
shoppingOrderAdjustService.save(new QuotationShoppingOrderAdjust()
.setOrderId(request.getId())
.setDiscount(request.getItems()
.stream()
.map(it -> it.getOldStandardFee().subtract(it.getNewStandardFee()))
.reduce(BigDecimal::add)
.get()
)
.setActualFee(request.getActualFee())
.setCreateTime(LocalDateTime.now())
);
shoppingOrderService.lambdaUpdate()
.set(QuotationShoppingOrder::getActualFee, request.getActualFee())
.set(QuotationShoppingOrder::getDiscount, order.getTotalFee().subtract(request.getActualFee()))
.set(QuotationShoppingOrder::getUpdateTime, LocalDateTime.now())
.eq(QuotationShoppingOrder::getId, request.getId())
.update();
return ApiResult.success();
}

View File

@ -0,0 +1,25 @@
package com.nflg.mobilebroken.quotation.pojo.request;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@Data
public class QuotationInfoUpdateRequest {
@NotNull
private Long id;
/**
* 报价生效时间
*/
@NotBlank
private String effectiveStartTime;
/**
* 报价失效时间
*/
@NotBlank
private String effectiveEndTime;
}

View File

@ -0,0 +1,28 @@
package com.nflg.mobilebroken.quotation.pojo.request;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
@Data
public class QuotationPriceUpdateItemRequest {
/**
* 购物车id
*/
@NotNull
private Long cartId;
/**
* 调价前价格
*/
@NotNull
private BigDecimal oldStandardFee;
/**
* 调价后价格
*/
@NotNull
private BigDecimal newStandardFee;
}

View File

@ -0,0 +1,23 @@
package com.nflg.mobilebroken.quotation.pojo.request;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.List;
@Data
public class QuotationPriceUpdateRequest {
@NotNull
private Long id;
/**
* 调价后实际总价
*/
private BigDecimal actualFee;
@NotEmpty
private List<QuotationPriceUpdateItemRequest> items;
}

View File

@ -0,0 +1,84 @@
package com.nflg.mobilebroken.quotation.pojo.vo;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Data
public class QuotationOrderInfoVO {
private Long id;
/**
* 报价编号
*/
private String no;
/**
* 客户名称
*/
private String customerName;
/**
* 报价对象
*/
private Integer targetId;
/**
* 优惠金额
*/
private BigDecimal discount;
/**
* 总价
*/
private BigDecimal totalFee;
/**
* 实际总价
*/
private BigDecimal actualFee;
/**
* 报价生效时间
*/
private String effectiveStartTime;
/**
* 报价失效时间
*/
private String effectiveEndTime;
/**
* 创建人类型0内部人员1代理商
*/
private Integer createByType;
/**
* 创建人id
*/
private Integer createById;
/**
* 创建人
*/
private String createBy;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 最后修改时间
*/
private LocalDateTime updateTime;
/**
* 购物车列表
*/
private List<ShoppingCartVO> items=new ArrayList<>();
}

View File

@ -1,5 +1,6 @@
package com.nflg.mobilebroken.quotation.pojo.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.experimental.Accessors;
@ -42,5 +43,11 @@ public class ShoppingCartPartVO {
*/
private Integer optionalType;
/**
* 是否已选择
*/
@JsonProperty("hasSelect")
private Boolean hasSelect;
private List<ShoppingCartPartVO> children;
}

View File

@ -166,7 +166,7 @@ public class ShoppingCartVO {
/**
* 优惠金额
*/
private BigDecimal discount;
private BigDecimal discount = BigDecimal.ZERO;
/**
* 状态0未生成报价单1已生成报价单

View File

@ -25,6 +25,11 @@ public class QuotationShoppingOrderItem implements Serializable {
private Long id;
/**
* 报价单id
*/
private Long orderId;
/**
* 购物车id
*/

View File

@ -1,8 +1,11 @@
package com.nflg.mobilebroken.repository.mapper;
import com.nflg.mobilebroken.common.pojo.dto.ModelConfigEffectiveDTO;
import com.nflg.mobilebroken.repository.entity.QuotationShoppingCartItem;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
/**
* <p>
* 报价-购物车-子项 Mapper 接口
@ -13,4 +16,5 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
*/
public interface QuotationShoppingCartItemMapper extends BaseMapper<QuotationShoppingCartItem> {
List<ModelConfigEffectiveDTO> getSelectedParts(Long id,String language);
}

View File

@ -1,5 +1,10 @@
package com.nflg.mobilebroken.repository.mapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nflg.mobilebroken.common.pojo.request.QuotationAdminSearchRequest;
import com.nflg.mobilebroken.common.pojo.request.QuotationSearchRequest;
import com.nflg.mobilebroken.common.pojo.vo.QuotationSearchVO;
import com.nflg.mobilebroken.repository.entity.QuotationShoppingOrder;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
@ -13,4 +18,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
*/
public interface QuotationShoppingOrderMapper extends BaseMapper<QuotationShoppingOrder> {
IPage<QuotationSearchVO> search(QuotationSearchRequest request, Page<?> page);
IPage<QuotationSearchVO> searchFromAdmin(QuotationAdminSearchRequest request, Page<?> page);
}

View File

@ -21,4 +21,6 @@ public interface QuotationUserPlanModelItemMapper extends BaseMapper<QuotationUs
List<QuotationUserPlanModelItemDTO> getEffectivesForAgent();
List<PlanSearchItemVO> search(Long id, ModelConfigSearchRequest request);
QuotationUserPlanModelItem getEffectiveForUser(Long modelId,Integer userType, Integer userId);
}

View File

@ -1,8 +1,11 @@
package com.nflg.mobilebroken.repository.service;
import com.nflg.mobilebroken.common.pojo.dto.ModelConfigEffectiveDTO;
import com.nflg.mobilebroken.repository.entity.QuotationShoppingCartItem;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
/**
* <p>
* 报价-购物车-子项 服务类
@ -13,4 +16,5 @@ import com.baomidou.mybatisplus.extension.service.IService;
*/
public interface IQuotationShoppingCartItemService extends IService<QuotationShoppingCartItem> {
List<ModelConfigEffectiveDTO> getSelectedParts(Long id,String language);
}

View File

@ -1,8 +1,14 @@
package com.nflg.mobilebroken.repository.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.nflg.mobilebroken.common.pojo.request.QuotationAdminSearchRequest;
import com.nflg.mobilebroken.common.pojo.request.QuotationSearchRequest;
import com.nflg.mobilebroken.common.pojo.vo.QuotationSearchVO;
import com.nflg.mobilebroken.repository.entity.QuotationShoppingOrder;
import com.baomidou.mybatisplus.extension.service.IService;
import javax.validation.Valid;
/**
* <p>
* 报价-报价单 服务类
@ -13,4 +19,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
*/
public interface IQuotationShoppingOrderService extends IService<QuotationShoppingOrder> {
IPage<QuotationSearchVO> search(QuotationSearchRequest request);
IPage<QuotationSearchVO> searchFromAdmin(QuotationAdminSearchRequest request);
}

View File

@ -13,4 +13,5 @@ import com.baomidou.mybatisplus.extension.service.IService;
*/
public interface IQuotationUserPlanDefaultService extends IService<QuotationUserPlanDefault> {
QuotationUserPlanDefault getEffectiveForUser(int userType, Integer userId);
}

View File

@ -22,4 +22,6 @@ public interface IQuotationUserPlanModelItemService extends IService<QuotationUs
List<QuotationUserPlanModelItemDTO> getEffectivesForAgent();
List<PlanSearchItemVO> search(Long id,ModelConfigSearchRequest request);
QuotationUserPlanModelItem getEffectiveForUser(Long modelId,Integer userType, Integer userId);
}

View File

@ -1,11 +1,14 @@
package com.nflg.mobilebroken.repository.service.impl;
import com.nflg.mobilebroken.common.pojo.dto.ModelConfigEffectiveDTO;
import com.nflg.mobilebroken.repository.entity.QuotationShoppingCartItem;
import com.nflg.mobilebroken.repository.mapper.QuotationShoppingCartItemMapper;
import com.nflg.mobilebroken.repository.service.IQuotationShoppingCartItemService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* <p>
* 报价-购物车-子项 服务实现类
@ -17,4 +20,8 @@ import org.springframework.stereotype.Service;
@Service
public class QuotationShoppingCartItemServiceImpl extends ServiceImpl<QuotationShoppingCartItemMapper, QuotationShoppingCartItem> implements IQuotationShoppingCartItemService {
@Override
public List<ModelConfigEffectiveDTO> getSelectedParts(Long id,String language) {
return baseMapper.getSelectedParts(id,language);
}
}

View File

@ -1,5 +1,10 @@
package com.nflg.mobilebroken.repository.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nflg.mobilebroken.common.pojo.request.QuotationAdminSearchRequest;
import com.nflg.mobilebroken.common.pojo.request.QuotationSearchRequest;
import com.nflg.mobilebroken.common.pojo.vo.QuotationSearchVO;
import com.nflg.mobilebroken.repository.entity.QuotationShoppingOrder;
import com.nflg.mobilebroken.repository.mapper.QuotationShoppingOrderMapper;
import com.nflg.mobilebroken.repository.service.IQuotationShoppingOrderService;
@ -17,4 +22,13 @@ import org.springframework.stereotype.Service;
@Service
public class QuotationShoppingOrderServiceImpl extends ServiceImpl<QuotationShoppingOrderMapper, QuotationShoppingOrder> implements IQuotationShoppingOrderService {
@Override
public IPage<QuotationSearchVO> search(QuotationSearchRequest request) {
return baseMapper.search(request, new Page<>(request.getPage(), request.getPageSize()));
}
@Override
public IPage<QuotationSearchVO> searchFromAdmin(QuotationAdminSearchRequest request) {
return baseMapper.searchFromAdmin(request, new Page<>(request.getPage(), request.getPageSize()));
}
}

View File

@ -17,4 +17,11 @@ import org.springframework.stereotype.Service;
@Service
public class QuotationUserPlanDefaultServiceImpl extends ServiceImpl<QuotationUserPlanDefaultMapper, QuotationUserPlanDefault> implements IQuotationUserPlanDefaultService {
@Override
public QuotationUserPlanDefault getEffectiveForUser(int userType, Integer userId) {
return lambdaQuery()
.eq(QuotationUserPlanDefault::getCreateByType, userType)
.eq(QuotationUserPlanDefault::getCreateById, userId)
.one();
}
}

View File

@ -31,4 +31,9 @@ public class QuotationUserPlanModelItemServiceImpl extends ServiceImpl<Quotation
public List<PlanSearchItemVO> search(Long id, ModelConfigSearchRequest request) {
return baseMapper.search(id, request);
}
@Override
public QuotationUserPlanModelItem getEffectiveForUser(Long modelId,Integer userType, Integer userId) {
return baseMapper.getEffectiveForUser(modelId,userType, userId);
}
}

View File

@ -2,4 +2,12 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.nflg.mobilebroken.repository.mapper.QuotationShoppingCartItemMapper">
<select id="getSelectedParts" resultType="com.nflg.mobilebroken.common.pojo.dto.ModelConfigEffectiveDTO">
SELECT qmci.id,qmci.parent_id,qmcil.part_name,qmcil.part_remark,qmci.image_url,qsci.amount,qmci.type,qmci.optional_type
FROM quotation_shopping_cart_item qsci
INNER JOIN quotation_model_config_item qmci ON qmci.id=qsci.config_item_id
INNER JOIN quotation_model_config_item_language qmcil ON qmcil.config_item_id=qmci.id
INNER JOIN `language` l ON l.id=qmcil.language_id
WHERE qsci.cart_id=#{id} AND qsci.has_select=1 AND l.`code`=#{language}
</select>
</mapper>

View File

@ -2,4 +2,28 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.nflg.mobilebroken.repository.mapper.QuotationShoppingOrderMapper">
</mapper>
<select id="search" resultType="com.nflg.mobilebroken.common.pojo.vo.QuotationSearchVO">
SELECT distinct qso.*
FROM quotation_shopping_order qso
INNER JOIN quotation_shopping_order_item qsoi ON qsoi.order_id=qso.id
INNER join quotation_shopping_cart cart on cart.id=qsoi.cart_id
where qso.create_by_type=#{request.userType} and qso.create_by_id=#{request.userId}
<if test="request.modelIds!=null and request.modelIds.size>0">
and cart.model_id in
<foreach item="item" collection="request.modelIds" separator="," open="(" close=")">
#{item}
</foreach>
</if>
<if test="request.createTimeStart!=null and request.createTimeStart!=''">
and qso.create_time>=#{request.createTimeStart}
</if>
<if test="request.createTimeEnd!=null and request.createTimeEnd!=''">
and qso.create_time &lt;= #{request.createTimeEnd}
</if>
order by qso.id desc
</select>
<select id="searchFromAdmin" resultType="com.nflg.mobilebroken.common.pojo.vo.QuotationSearchVO">
</select>
</mapper>

View File

@ -18,4 +18,12 @@
and pm.no like CONCAT('%', #{request.no}, '%')
</if>
</select>
<select id="getEffectiveForUser" resultType="com.nflg.mobilebroken.repository.entity.QuotationUserPlanModelItem">
SELECT qupmi.*
FROM quotation_user_plan_model qupm
INNER JOIN quotation_user_plan_model_item qupmi ON qupm.id=qupmi.plan_id
WHERE qupm.`status`=1 AND qupm.create_by_type=#{userType} AND qupm.create_by_id=#{userId} AND qupmi.is_default=1
AND qupmi.model_id=#{modelId}
</select>
</mapper>