feat(quotation): 添加价格配置功能并优化模型配置管理

- 新增价格配置控制器,支持动态表头展示和价格区域配置
- 添加价格配置相关的VO、请求对象和数据库实体
- 实现价格配置的保存、发布和查询功能
- 重构模型配置控制器,移至admin包下并增加配置项字段
- 新增价格配置相关服务接口和实现类
- 添加DynamicHeaderVO用于动态表格展示
- 移除未使用的mybatis-plus-core依赖
- 优化代码格式和命名规范
This commit is contained in:
曹鹏飞 2026-02-24 18:04:42 +08:00
parent b63239be16
commit 101701f586
31 changed files with 1170 additions and 56 deletions

View File

@ -0,0 +1,22 @@
package com.nflg.mobilebroken.common.pojo.request;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
@Data
public class ModelPriceSaveAreaRequest {
/**
* 价格区域字典id
*/
@NotNull
private Long areaId;
/**
* 价格
*/
@NotNull
private BigDecimal amount;
}

View File

@ -0,0 +1,29 @@
package com.nflg.mobilebroken.common.pojo.request;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
@Data
public class ModelPriceSaveRequest {
/**
* 机型表batch_number
*/
@NotNull
private Long modelId;
/**
* 配置项唯一id
*/
@NotNull
private Long configItemUniqueId;
@Valid
@NotEmpty
private List<ModelPriceSaveAreaRequest> areas;
}

View File

@ -0,0 +1,38 @@
package com.nflg.mobilebroken.common.pojo.vo;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@Data
@Accessors(chain = true)
public class DynamicHeaderVO {
/**
* 属性名
*/
private String prop;
/**
* 显示名
*/
private String label;
/**
* 数据id
*/
private String dataId;
/**
* 是否显示
*/
private boolean show = true;
/**
* 是否是主键
*/
private boolean isKey = false;
private List<DynamicHeaderVO> children;
}

View File

@ -0,0 +1,30 @@
package com.nflg.mobilebroken.common.pojo.vo;
import lombok.Data;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
@Data
@Accessors(chain = true)
public class ModelPriceConfigAreaVO {
private Long id;
/**
* 价格区域字典id
*/
private Long areaId;
private String code;
/**
* 价格区域名称
*/
private String areaName;
/**
* 价格
*/
private BigDecimal amount;
}

View File

@ -0,0 +1,146 @@
package com.nflg.mobilebroken.common.pojo.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
@Data
@Accessors(chain = true)
public class ModelPriceConfigVO {
private Long id;
/**
* 机型表batch_number
*/
private Long modelId;
@JsonIgnore
private Long configId;
/**
* 配置项唯一id
*/
private Long configItemUniqueId;
@JsonIgnore
private Long priceId;
/**
* 部件或系统
*/
private String partName;
/**
* 单价版本号
*/
private String priceVersion;
/**
* 单价版本状态0草稿1已发布2已弃用
*/
@JsonIgnore
private Integer priceStatus;
/**
* 单价版本状态描述
*/
private String priceStatusDesc;
public String getPriceStatusDesc() {
if (Objects.isNull(priceStatus)){
return null;
}
switch (priceStatus) {
case 0:
return "草稿";
case 1:
return "已发布";
case 2:
return "已弃用";
default:
return "未知";
}
}
/**
* 配置版本号
*/
private String configVersion;
/**
* 类别0可选配置1标准配置
*/
@JsonIgnore
private Integer type;
/**
* 类别描述
*/
private String typeDesc;
public String getTypeDesc() {
if (Objects.isNull(type)){
return null;
}
switch (type) {
case 0:
return "可选配置";
case 1:
return "标准配置";
default:
return "未知";
}
}
/**
* 选配类别0新增可选1替换可选
*/
@JsonIgnore
private Integer optionalType;
/**
* 选配类别描述
*/
private String optionalTypeDesc;
public String getOptionalTypeDesc() {
if (Objects.isNull(optionalType)){
return null;
}
switch (optionalType) {
case 0:
return "实价";
case 1:
return "差价";
default:
return "未知";
}
}
/**
* 是否需要确认
*/
private Boolean needConfirm;
/**
* 更新人
*/
private String updateBy;
/**
* 更新时间
*/
private LocalDateTime updateTime;
/**
* 区域保护价
*/
private List<ModelPriceConfigAreaVO> areas;
private List<ModelPriceConfigVO> children;
}

View File

@ -1,4 +1,4 @@
package com.nflg.mobilebroken.quotation.controller;
package com.nflg.mobilebroken.quotation.controller.admin;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
@ -11,7 +11,9 @@ import com.nflg.mobilebroken.common.pojo.request.ModelConfigSearchRequest;
import com.nflg.mobilebroken.common.pojo.vo.ModelConfigVO;
import com.nflg.mobilebroken.common.pojo.vo.quotation.ModelConfigItemLanguageVO;
import com.nflg.mobilebroken.common.util.AdminUserUtil;
import com.nflg.mobilebroken.common.util.DateTimeUtil;
import com.nflg.mobilebroken.common.util.VUtils;
import com.nflg.mobilebroken.quotation.controller.ControllerBase;
import com.nflg.mobilebroken.quotation.pojo.request.ModelConfigItemAddRequest;
import com.nflg.mobilebroken.quotation.pojo.request.ModelConfigItemUpdateRequest;
import com.nflg.mobilebroken.repository.entity.Language;
@ -155,7 +157,11 @@ public class ModelConfigController extends ControllerBase {
.setConfigId(request.getConfigId())
.setType(request.getType())
.setOptionalType(request.getOptionalType())
.setParentId(request.getParentId());
.setPartName(request.getPartName())
.setPartRemark(request.getPartRemark())
.setParentId(request.getParentId())
.setImageUrl(request.getImageUrl())
.setUniqueId(IdUtil.getSnowflakeNextId());
modelConfigItemService.save(item);
List<QuotationModelConfigItemLanguage> itemLanguages = new ArrayList<>();
QuotationModelConfigItemLanguage cnItem = new QuotationModelConfigItemLanguage()
@ -216,6 +222,9 @@ public class ModelConfigController extends ControllerBase {
it.setType(request.getType());
it.setOptionalType(request.getOptionalType());
it.setParentId(request.getParentId());
it.setPartName(request.getPartName());
it.setPartRemark(request.getPartRemark());
it.setImageUrl(request.getImageUrl());
}
});
modelConfigItemService.saveBatch(items);
@ -362,6 +371,7 @@ public class ModelConfigController extends ControllerBase {
.eq(QuotationModelConfig::getConfigStatus, 1)
.update();
config.setConfigStatus(1);
config.setConfigVersion("MC" + DateTimeUtil.format(LocalDateTime.now(), "yyMMddHHmm"));
config.setUpdateById(AdminUserUtil.getUserId());
config.setUpdateBy(AdminUserUtil.getUserName());
config.setUpdateTime(LocalDateTime.now());

View File

@ -0,0 +1,364 @@
package com.nflg.mobilebroken.quotation.controller.admin;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.nflg.mobilebroken.common.constant.STATE;
import com.nflg.mobilebroken.common.pojo.ApiResult;
import com.nflg.mobilebroken.common.pojo.PageData;
import com.nflg.mobilebroken.common.pojo.request.ModelConfigSearchRequest;
import com.nflg.mobilebroken.common.pojo.request.ModelPriceSaveAreaRequest;
import com.nflg.mobilebroken.common.pojo.request.ModelPriceSaveRequest;
import com.nflg.mobilebroken.common.pojo.vo.DynamicHeaderVO;
import com.nflg.mobilebroken.common.pojo.vo.ModelPriceConfigVO;
import com.nflg.mobilebroken.common.util.AdminUserUtil;
import com.nflg.mobilebroken.common.util.DateTimeUtil;
import com.nflg.mobilebroken.quotation.controller.ControllerBase;
import com.nflg.mobilebroken.repository.entity.*;
import com.nflg.mobilebroken.repository.service.*;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* 价格配置
*/
@RestController
@RequestMapping("/price/config")
public class PriceConfigController extends ControllerBase {
// private static final ObjectMapper objectMapper = new ObjectMapper()
// .registerModule(new JavaTimeModule())
// .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
@Resource
private ObjectMapper objectMapper;
@Resource
private IQuotationModelPriceService priceService;
@Resource
private IQuotationModelPriceItemService priceItemService;
@Resource
private IQuotationModelPriceItemAreaService priceItemAreaService;
@Resource
private IDictionaryItemService dictionaryItemService;
@Resource
private IQuotationModelConfigService configService;
/**
* 获取动态表头
*/
@GetMapping("/headers")
public ApiResult<List<DynamicHeaderVO>> getHeaders() {
List<DynamicHeaderVO> vos = new ArrayList<>();
vos.add(new DynamicHeaderVO().setProp("id").setLabel("id").setKey(true).setShow(false));
vos.add(new DynamicHeaderVO().setProp("modelId").setLabel("机型ID").setShow(false));
vos.add(new DynamicHeaderVO().setProp("configItemUniqueId").setLabel("配置项唯一id").setShow(false));
vos.add(new DynamicHeaderVO().setProp("partName").setLabel("部件或系统"));
vos.add(new DynamicHeaderVO().setProp("priceVersion").setLabel("单价版本号"));
vos.add(new DynamicHeaderVO().setProp("priceStatusDesc").setLabel("单价版本状态"));
vos.add(new DynamicHeaderVO().setProp("typeDesc").setLabel("配置类别"));
vos.add(new DynamicHeaderVO().setProp("optionalTypeDesc").setLabel("配置单价类型"));
List<DictionaryItem> areas = dictionaryItemService.getListByDictionaryCode("DispatchCategory");
vos.add(new DynamicHeaderVO().setProp("").setLabel("保护价(¥)")
.setChildren(
areas.stream()
.map(area -> new DynamicHeaderVO()
.setProp(area.getCode())
.setLabel(area.getName())
.setDataId(area.getCode() + "Id")
)
.collect(Collectors.toList())
)
);
vos.add(new DynamicHeaderVO().setProp("updateBy").setLabel("设置人"));
vos.add(new DynamicHeaderVO().setProp("updateTime").setLabel("设置时间"));
return ApiResult.success(vos);
}
/**
* 获取机型价格配置列表
*/
@PostMapping("/search")
public ApiResult<PageData<Map<String, Object>>> search(@Valid @RequestBody ModelConfigSearchRequest request) {
IPage<ModelPriceConfigVO> pdata = priceService.search(request);
ApiResult<PageData<Map<String, Object>>> vo = new ApiResult<>();
PageData<Map<String, Object>> pageData = new PageData<>();
pageData.setPage((int) pdata.getCurrent());
pageData.setPageSize((int) pdata.getSize());
pageData.setTotal((int) pdata.getTotal());
pageData.setItems(pdata.getRecords().stream()
.map(it -> {
Map<String, Object> map = objectMapper.convertValue(it, new TypeReference<>() {
});
if (CollectionUtil.isNotEmpty(it.getAreas())) {
it.getAreas().forEach(area -> {
map.put(area.getCode(), area.getCode());
map.put(area.getCode() + "Id", area.getAreaId());
});
}
return map;
})
.collect(Collectors.toList())
);
vo.setCode(STATE.Success.getState());
vo.setType(STATE.Success.getType());
vo.setResult(pageData);
return vo;
}
/**
* 保存机型价格配置
*/
@Transactional
@PostMapping("/save")
public ApiResult<Void> save(@Valid @RequestBody List<ModelPriceSaveRequest> datas) {
Map<Long, List<ModelPriceSaveRequest>> group = datas.stream().collect(Collectors.groupingBy(ModelPriceSaveRequest::getModelId));
group.forEach((modelId, itemList) -> {
QuotationModelPrice price = priceService.lambdaQuery()
.eq(QuotationModelPrice::getModelId, modelId)
.orderByDesc(QuotationModelPrice::getId)
.last("limit 1")
.one();
if (Objects.isNull(price)) {
//新增
price = new QuotationModelPrice()
.setId(IdUtil.getSnowflakeNextId())
.setModelId(modelId)
.setCreateById(AdminUserUtil.getUserId())
.setCreateBy(AdminUserUtil.getUserName())
.setCreateTime(LocalDateTime.now())
.setUpdateById(AdminUserUtil.getUserId())
.setUpdateBy(AdminUserUtil.getUserName())
.setUpdateTime(LocalDateTime.now());
priceService.save(price);
List<QuotationModelPriceItem> items = new ArrayList<>();
List<QuotationModelPriceItemArea> areas = new ArrayList<>();
for (ModelPriceSaveRequest it : itemList) {
QuotationModelPriceItem item = new QuotationModelPriceItem()
.setPriceId(price.getId())
.setId(IdUtil.getSnowflakeNextId())
.setConfigItemUniqueId(it.getConfigItemUniqueId())
.setUpdateById(AdminUserUtil.getUserId())
.setUpdateBy(AdminUserUtil.getUserName())
.setUpdateTime(LocalDateTime.now());
items.add(item);
for (ModelPriceSaveAreaRequest ait : it.getAreas()) {
QuotationModelPriceItemArea area = new QuotationModelPriceItemArea()
.setAreaId(ait.getAreaId())
.setPriceId(price.getId())
.setPriceItemId(item.getId())
.setAmount(ait.getAmount());
areas.add(area);
}
}
priceItemService.saveBatch(items);
priceItemAreaService.saveBatch(areas);
} else if (price.getPriceStatus() == 0) {
//草稿
List<QuotationModelPriceItem> items = priceItemService.lambdaQuery()
.eq(QuotationModelPriceItem::getPriceId, price.getId())
.in(QuotationModelPriceItem::getConfigItemUniqueId, itemList.stream()
.map(ModelPriceSaveRequest::getConfigItemUniqueId)
.collect(Collectors.toList())
)
.list();
List<QuotationModelPriceItemArea> areas = priceItemAreaService.lambdaQuery()
.eq(QuotationModelPriceItemArea::getPriceId, price.getId())
.in(QuotationModelPriceItemArea::getPriceItemId, items.stream()
.map(QuotationModelPriceItem::getId)
.collect(Collectors.toList())
)
.list();
List<QuotationModelPriceItem> itemsForAdd = new ArrayList<>();
List<QuotationModelPriceItem> itemsForUpdate = new ArrayList<>();
List<QuotationModelPriceItemArea> areasForAdd = new ArrayList<>();
List<QuotationModelPriceItemArea> areasForUpdate = new ArrayList<>();
for (ModelPriceSaveRequest sit : itemList) {
QuotationModelPriceItem item = items.stream()
.filter(it -> sit.getConfigItemUniqueId().equals(it.getConfigItemUniqueId()))
.findFirst()
.orElse(null);
if (Objects.isNull(item)) {
item = new QuotationModelPriceItem()
.setId(IdUtil.getSnowflakeNextId())
.setPriceId(price.getId())
.setConfigItemUniqueId(sit.getConfigItemUniqueId())
.setUpdateById(AdminUserUtil.getUserId())
.setUpdateBy(AdminUserUtil.getUserName())
.setUpdateTime(LocalDateTime.now());
itemsForAdd.add(item);
for (ModelPriceSaveAreaRequest ait : sit.getAreas()) {
areasForAdd.add(new QuotationModelPriceItemArea()
.setAreaId(ait.getAreaId())
.setPriceId(price.getId())
.setPriceItemId(item.getId())
.setAmount(ait.getAmount()));
}
} else {
item.setUpdateById(AdminUserUtil.getUserId());
item.setUpdateBy(AdminUserUtil.getUserName());
item.setUpdateTime(LocalDateTime.now());
itemsForUpdate.add(item);
QuotationModelPriceItem finalItem = item;
for (ModelPriceSaveAreaRequest ait : sit.getAreas()) {
QuotationModelPriceItemArea area = areas.stream()
.filter(it -> it.getPriceItemId().equals(finalItem.getId())
&& it.getAreaId().equals(ait.getAreaId())
)
.findFirst()
.orElse(null);
if (Objects.isNull(area)) {
areasForAdd.add(new QuotationModelPriceItemArea()
.setAreaId(ait.getAreaId())
.setPriceId(price.getId())
.setPriceItemId(item.getId())
.setAmount(ait.getAmount()));
} else {
area.setAmount(ait.getAmount());
areasForUpdate.add(area);
}
}
}
}
price.setUpdateById(AdminUserUtil.getUserId());
price.setUpdateBy(AdminUserUtil.getUserName());
price.setUpdateTime(LocalDateTime.now());
priceService.updateById(price);
if (CollectionUtil.isNotEmpty(itemsForAdd)) {
priceItemService.saveBatch(itemsForAdd);
}
if (CollectionUtil.isNotEmpty(itemsForUpdate)) {
priceItemService.updateBatchById(itemsForUpdate);
}
if (CollectionUtil.isNotEmpty(areasForAdd)) {
priceItemAreaService.saveBatch(areasForAdd);
}
if (CollectionUtil.isNotEmpty(areasForUpdate)) {
priceItemAreaService.updateBatchById(areasForUpdate);
}
} else {
//已发布
List<QuotationModelPriceItem> dbItems = priceItemService.lambdaQuery()
.eq(QuotationModelPriceItem::getPriceId, price.getId())
.list();
List<QuotationModelPriceItemArea> areasForAdd = priceItemAreaService.lambdaQuery()
.eq(QuotationModelPriceItemArea::getPriceId, price.getId())
.list();
List<QuotationModelPriceItem> itemsForAdd = new ArrayList<>();
price.setId(IdUtil.getSnowflakeNextId());
price.setConfigId(null);
price.setUpdateById(AdminUserUtil.getUserId());
price.setUpdateBy(AdminUserUtil.getUserName());
price.setUpdateTime(LocalDateTime.now());
for (ModelPriceSaveRequest sit : itemList) {
QuotationModelPriceItem dbItem = dbItems.stream()
.filter(ait -> ait.getConfigItemUniqueId().equals(sit.getConfigItemUniqueId()))
.findFirst()
.orElse(null);
if (Objects.nonNull(dbItem)) {
List<QuotationModelPriceItemArea> areas = areasForAdd.stream()
.filter(area -> area.getPriceItemId().equals(dbItem.getId()))
.collect(Collectors.toList());
for (ModelPriceSaveAreaRequest ait : sit.getAreas()) {
QuotationModelPriceItemArea area = areas.stream()
.filter(it -> it.getAreaId().equals(ait.getAreaId()))
.findFirst()
.orElse(null);
if (Objects.nonNull(area)) {
area.setId(IdUtil.getSnowflakeNextId());
area.setPriceId(price.getId());
area.setAmount(ait.getAmount());
areasForAdd.add(area);
} else {
areasForAdd.add(new QuotationModelPriceItemArea()
.setAreaId(ait.getAreaId())
.setPriceId(price.getId())
.setPriceItemId(dbItem.getId())
.setAmount(ait.getAmount()));
}
}
} else {
QuotationModelPriceItem item = new QuotationModelPriceItem()
.setId(IdUtil.getSnowflakeNextId())
.setPriceId(price.getId())
.setConfigItemUniqueId(sit.getConfigItemUniqueId())
.setUpdateById(AdminUserUtil.getUserId())
.setUpdateBy(AdminUserUtil.getUserName())
.setUpdateTime(LocalDateTime.now());
itemsForAdd.add(item);
for (ModelPriceSaveAreaRequest ait : sit.getAreas()) {
areasForAdd.add(new QuotationModelPriceItemArea()
.setAreaId(ait.getAreaId())
.setPriceId(price.getId())
.setPriceItemId(item.getId())
.setAmount(ait.getAmount()));
}
}
}
for (QuotationModelPriceItem item : dbItems) {
item.setUpdateById(AdminUserUtil.getUserId());
item.setUpdateBy(AdminUserUtil.getUserName());
item.setUpdateTime(LocalDateTime.now());
item.setId(IdUtil.getSnowflakeNextId());
item.setPriceId(price.getId());
areasForAdd.stream()
.filter(area -> area.getPriceItemId().equals(item.getId()))
.forEach(area -> area.setPriceItemId(item.getId()));
}
priceService.save(price);
if (CollectionUtil.isNotEmpty(itemsForAdd)) {
priceItemService.saveBatch(itemsForAdd);
}
if (CollectionUtil.isNotEmpty(dbItems)) {
priceItemService.saveBatch(dbItems);
}
if (CollectionUtil.isNotEmpty(areasForAdd)) {
priceItemAreaService.saveBatch(areasForAdd);
}
}
});
return ApiResult.success();
}
/**
* 发布
*/
@Transactional
@PostMapping("/publish")
public ApiResult<Void> publish(@RequestBody @NotEmpty List<Long> ids) {
ids.forEach(id -> {
QuotationModelPrice price = priceService.getById(id);
if (Objects.nonNull(price) && price.getPriceStatus() == 0) {
QuotationModelConfig config = configService.lambdaQuery()
.eq(QuotationModelConfig::getModelId, price.getModelId())
.eq(QuotationModelConfig::getConfigStatus, 1)
.one();
if (Objects.nonNull(config)) {
price.setPriceStatus(1);
price.setConfigId(config.getId());
price.setPriceVersion("MP" + DateTimeUtil.format(LocalDateTime.now(), "yyMMddHHmm"));
priceService.updateById(price);
}
}
});
return ApiResult.success();
}
}

View File

@ -47,6 +47,4 @@ public class ModelConfigItemUpdateRequest{
* 图片
*/
private String imageUrl;
}

View File

@ -45,20 +45,20 @@ public class AliYunTranslate implements ITranslate {
@PostConstruct
public void init() throws Exception {
Config credentialConfig = new Config();
credentialConfig.type="access_key";
credentialConfig.accessKeyId=accessKeyId;
credentialConfig.accessKeySecret=accessKeySecret;
credentialConfig.type = "access_key";
credentialConfig.accessKeyId = accessKeyId;
credentialConfig.accessKeySecret = accessKeySecret;
Client credentialClient = new Client(credentialConfig);
com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
.setCredential(credentialClient);
// Endpoint 请参考 https://api.aliyun.com/product/alimt
config.endpoint = endpoint;
client=new com.aliyun.alimt20181012.Client(config);
client = new com.aliyun.alimt20181012.Client(config);
}
@Override
public String translateWord(String text, String sourceLanguage, String targetLanguage, String formatType) {
if (isOnlyDigitsAndSymbols(text)){
if (isOnlyDigitsAndSymbols(text)) {
return text;
}
com.aliyun.alimt20181012.models.TranslateGeneralRequest request = new com.aliyun.alimt20181012.models.TranslateGeneralRequest()

View File

@ -1,13 +1,4 @@
# Nacos 地址
nacos.server-addr=${NACOS_SERVER_ADDR:192.168.0.194:8848}
#nacos.server-addr=192.168.0.194:8848
#spring.cloud.nacos.discovery.username=nacos
#spring.cloud.nacos.discovery.password=ZLQ8vgmjoJ4?EPJ4]fs_
#spring.config.activate.on-profile=dev
logging.level.com.nflg=trace
logging.level.com.alibaba.cloud.nacos.config=DEBUG
#spring.datasource.url=jdbc:mysql://112.74.186.154:13151/mobilebroken?useUnicode=true&characterEncoding=utf8mb4&tinyInt1isBit=false&useSSL=false&rewriteBatchedStatements=true&allowMultiQueries=true&serverTimezone=GMT%2B8
#spring.datasource.username=nflg
#spring.datasource.password=Aciga@2022
#spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
logging.config=classpath:logback-sit.xml

View File

@ -1,13 +1,4 @@
# Nacos 地址
nacos.server-addr=${NACOS_SERVER_ADDR:192.168.0.194:8848}
#nacos.server-addr=192.168.0.194:8848
#spring.cloud.nacos.discovery.username=nacos
#spring.cloud.nacos.discovery.password=ZLQ8vgmjoJ4?EPJ4]fs_
#spring.config.activate.on-profile=dev
logging.level.com.nflg=DEBUG
logging.level.com.alibaba.cloud.nacos.config=DEBUG
#spring.datasource.url=jdbc:mysql://112.74.186.154:13151/mobilebroken?useUnicode=true&characterEncoding=utf8mb4&tinyInt1isBit=false&useSSL=false&rewriteBatchedStatements=true&allowMultiQueries=true&serverTimezone=GMT%2B8
#spring.datasource.username=nflg
#spring.datasource.password=Aciga@2022
#spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
logging.config=classpath:logback-sit.xml

View File

@ -6,14 +6,7 @@ spring.servlet.multipart.max-file-size=1024MB
spring.servlet.multipart.max-request-size=1024MB
spring.servlet.multipart.file-size-threshold=10MB
spring.servlet.multipart.location=/tmp
#spring.config.import=classpath:application-${spring.profiles.active}.properties,nacos:
#spring.config.import=nacos:
logging.level.root=info
#logging.level.com.alibaba.nacos.client=DEBUG
#logging.level.org.springframework.boot.context.config=DEBUG
#logging.config=classpath:logback-sit.xml
# 启用服务发现自动路由
spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.nacos.config.namespace=mobilebroken
spring.cloud.nacos.config.group=${spring.profiles.active}
spring.cloud.nacos.config.server-addr=${nacos.server-addr}
@ -21,9 +14,5 @@ spring.cloud.nacos.config.file-extension=properties
spring.cloud.nacos.config.extension-configs[0].data-id=shared.properties
spring.cloud.nacos.config.extension-configs[0].group=${spring.profiles.active}
spring.cloud.nacos.config.extension-configs[0].refresh=true
spring.cloud.nacos.discovery.server-addr=${nacos.server-addr}
spring.cloud.nacos.discovery.namespace=mobilebroken
spring.cloud.nacos.discovery.group=${spring.profiles.active}
spring.cloud.nacos.discovery.metadata.env=${spring.profiles.active}
spring.thymeleaf.encoding=UTF-8
#spring.thymeleaf.cache=true

View File

@ -34,15 +34,15 @@ public class QuotationModelConfigItem implements Serializable {
*/
private Long parentId;
// /**
// * 部件或系统
// */
// private String partName;
/**
* 部件或系统
*/
private String partName;
// /**
// * 参数/描述
// */
// private String partRemark;
/**
* 参数/描述
*/
private String partRemark;
/**
* 类别0可选配置1标准配置
@ -59,8 +59,13 @@ public class QuotationModelConfigItem implements Serializable {
*/
private Boolean enable;
// /**
// * 图片
// */
// private String imageUrl;
/**
* 图片
*/
private String imageUrl;
/**
* 唯一id不随版本变化
*/
private Long uniqueId;
}

View File

@ -0,0 +1,82 @@
package com.nflg.mobilebroken.repository.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.time.LocalDateTime;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
/**
* <p>
* 报价-机型价格
* </p>
*
* @author 代码生成器生成
* @since 2026
*/
@Getter
@Setter
@Accessors(chain = true)
@TableName("quotation_model_price")
public class QuotationModelPrice implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
/**
* 配置id
*/
private Long configId;
/**
* 机型表batch_number
*/
private Long modelId;
/**
* 版本号
*/
private String priceVersion;
/**
* 版本状态0草稿1已发布2已弃用
*/
private Integer priceStatus;
/**
* 是否需要确认
*/
private Boolean needConfirm;
/**
* 新增人id
*/
private Integer createById;
/**
* 创建人
*/
private String createBy;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 修改人id
*/
private Integer updateById;
/**
* 更新人
*/
private String updateBy;
/**
* 更新时间
*/
private LocalDateTime updateTime;
}

View File

@ -0,0 +1,52 @@
package com.nflg.mobilebroken.repository.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.time.LocalDateTime;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
/**
* <p>
* 报价-机型价格-子项
* </p>
*
* @author 代码生成器生成
* @since 2026
*/
@Getter
@Setter
@Accessors(chain = true)
@TableName("quotation_model_price_item")
public class QuotationModelPriceItem implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
/**
* 价格id
*/
private Long priceId;
/**
* 配置项唯一id
*/
private Long configItemUniqueId;
/**
* 修改人id
*/
private Integer updateById;
/**
* 更新人
*/
private String updateBy;
/**
* 更新时间
*/
private LocalDateTime updateTime;
}

View File

@ -0,0 +1,47 @@
package com.nflg.mobilebroken.repository.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.math.BigDecimal;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
/**
* <p>
* 报价-机型价格-子项区域价格
* </p>
*
* @author 代码生成器生成
* @since 2026
*/
@Getter
@Setter
@Accessors(chain = true)
@TableName("quotation_model_price_item_area")
public class QuotationModelPriceItemArea implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
/**
* 价格id
*/
private Long priceId;
/**
* 价格项id
*/
private Long priceItemId;
/**
* 价格区域字典id
*/
private Long areaId;
/**
* 价格单位
*/
private BigDecimal amount;
}

View File

@ -0,0 +1,16 @@
package com.nflg.mobilebroken.repository.mapper;
import com.nflg.mobilebroken.repository.entity.QuotationModelPriceItemArea;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 报价-机型价格-子项区域价格 Mapper 接口
* </p>
*
* @author 代码生成器生成
* @since 2026
*/
public interface QuotationModelPriceItemAreaMapper extends BaseMapper<QuotationModelPriceItemArea> {
}

View File

@ -0,0 +1,16 @@
package com.nflg.mobilebroken.repository.mapper;
import com.nflg.mobilebroken.repository.entity.QuotationModelPriceItem;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 报价-机型价格-子项 Mapper 接口
* </p>
*
* @author 代码生成器生成
* @since 2026
*/
public interface QuotationModelPriceItemMapper extends BaseMapper<QuotationModelPriceItem> {
}

View File

@ -0,0 +1,21 @@
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.ModelConfigSearchRequest;
import com.nflg.mobilebroken.common.pojo.vo.ModelPriceConfigVO;
import com.nflg.mobilebroken.repository.entity.QuotationModelPrice;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 报价-机型价格 Mapper 接口
* </p>
*
* @author 代码生成器生成
* @since 2026
*/
public interface QuotationModelPriceMapper extends BaseMapper<QuotationModelPrice> {
IPage<ModelPriceConfigVO> search(ModelConfigSearchRequest request, Page<?> page);
}

View File

@ -0,0 +1,16 @@
package com.nflg.mobilebroken.repository.service;
import com.nflg.mobilebroken.repository.entity.QuotationModelPriceItemArea;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 报价-机型价格-子项区域价格 服务类
* </p>
*
* @author 代码生成器生成
* @since 2026
*/
public interface IQuotationModelPriceItemAreaService extends IService<QuotationModelPriceItemArea> {
}

View File

@ -0,0 +1,16 @@
package com.nflg.mobilebroken.repository.service;
import com.nflg.mobilebroken.repository.entity.QuotationModelPriceItem;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 报价-机型价格-子项 服务类
* </p>
*
* @author 代码生成器生成
* @since 2026
*/
public interface IQuotationModelPriceItemService extends IService<QuotationModelPriceItem> {
}

View File

@ -0,0 +1,22 @@
package com.nflg.mobilebroken.repository.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.nflg.mobilebroken.common.pojo.request.ModelConfigSearchRequest;
import com.nflg.mobilebroken.common.pojo.vo.ModelPriceConfigVO;
import com.nflg.mobilebroken.repository.entity.QuotationModelPrice;
import com.baomidou.mybatisplus.extension.service.IService;
import javax.validation.Valid;
/**
* <p>
* 报价-机型价格 服务类
* </p>
*
* @author 代码生成器生成
* @since 2026
*/
public interface IQuotationModelPriceService extends IService<QuotationModelPrice> {
IPage<ModelPriceConfigVO> search(ModelConfigSearchRequest request);
}

View File

@ -0,0 +1,20 @@
package com.nflg.mobilebroken.repository.service.impl;
import com.nflg.mobilebroken.repository.entity.QuotationModelPriceItemArea;
import com.nflg.mobilebroken.repository.mapper.QuotationModelPriceItemAreaMapper;
import com.nflg.mobilebroken.repository.service.IQuotationModelPriceItemAreaService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 报价-机型价格-子项区域价格 服务实现类
* </p>
*
* @author 代码生成器生成
* @since 2026
*/
@Service
public class QuotationModelPriceItemAreaServiceImpl extends ServiceImpl<QuotationModelPriceItemAreaMapper, QuotationModelPriceItemArea> implements IQuotationModelPriceItemAreaService {
}

View File

@ -0,0 +1,20 @@
package com.nflg.mobilebroken.repository.service.impl;
import com.nflg.mobilebroken.repository.entity.QuotationModelPriceItem;
import com.nflg.mobilebroken.repository.mapper.QuotationModelPriceItemMapper;
import com.nflg.mobilebroken.repository.service.IQuotationModelPriceItemService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 报价-机型价格-子项 服务实现类
* </p>
*
* @author 代码生成器生成
* @since 2026
*/
@Service
public class QuotationModelPriceItemServiceImpl extends ServiceImpl<QuotationModelPriceItemMapper, QuotationModelPriceItem> implements IQuotationModelPriceItemService {
}

View File

@ -0,0 +1,128 @@
package com.nflg.mobilebroken.repository.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nflg.mobilebroken.common.pojo.request.ModelConfigSearchRequest;
import com.nflg.mobilebroken.common.pojo.vo.ModelPriceConfigAreaVO;
import com.nflg.mobilebroken.common.pojo.vo.ModelPriceConfigVO;
import com.nflg.mobilebroken.repository.entity.*;
import com.nflg.mobilebroken.repository.mapper.QuotationModelPriceMapper;
import com.nflg.mobilebroken.repository.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* <p>
* 报价-机型价格 服务实现类
* </p>
* @author 代码生成器生成
* @since 2026
*/
@Service
public class QuotationModelPriceServiceImpl extends ServiceImpl<QuotationModelPriceMapper, QuotationModelPrice> implements IQuotationModelPriceService {
@Resource
private IQuotationModelConfigItemService configItemService;
@Resource
private IQuotationModelPriceItemService priceItemService;
@Resource
private IDictionaryItemService dictionaryItemService;
@Resource
private IQuotationModelPriceItemAreaService priceItemAreaService;
@Override
public IPage<ModelPriceConfigVO> search(ModelConfigSearchRequest request) {
IPage<ModelPriceConfigVO> pdata = baseMapper.search(request, new Page<>(request.getPage(), request.getPageSize()));
if (CollectionUtil.isEmpty(pdata.getRecords())) {
return pdata;
}
List<Long> configIds = pdata.getRecords().stream()
.map(ModelPriceConfigVO::getConfigId)
.filter(Objects::nonNull)
.collect(Collectors.toList());
if (CollectionUtil.isEmpty(configIds)) {
return pdata;
}
List<QuotationModelConfigItem> configItems = configItemService.lambdaQuery()
.in(QuotationModelConfigItem::getConfigId, configIds)
.list();
List<Long> priceIds = pdata.getRecords().stream()
.map(ModelPriceConfigVO::getPriceId)
.filter(Objects::nonNull)
.collect(Collectors.toList());
List<QuotationModelPriceItem> priceItems = CollectionUtil.isEmpty(priceIds) ? new ArrayList<>() : priceItemService.lambdaQuery()
.in(QuotationModelPriceItem::getPriceId, priceIds)
.list();
List<DictionaryItem> areas = dictionaryItemService.getListByDictionaryCode("DispatchCategory");
pdata.getRecords().forEach(data -> {
if (Objects.nonNull(data.getConfigId())) {
List<QuotationModelConfigItem> items = configItems.stream()
.filter(item -> item.getConfigId().equals(data.getConfigId()))
.collect(Collectors.toList());
if (CollectionUtil.isNotEmpty(items)) {
List<QuotationModelPriceItemArea> areaPrices = Objects.isNull(data.getPriceId()) ? new ArrayList<>()
: priceItemAreaService.lambdaQuery()
.in(QuotationModelPriceItemArea::getPriceId, data.getPriceId())
.list();
data.setChildren(items.stream()
.filter(item -> item.getParentId() == 0L)
.map(item -> generateVO(item, items, priceItems, areaPrices, areas))
.collect(Collectors.toList())
);
}
}
});
return pdata;
}
private ModelPriceConfigVO generateVO(QuotationModelConfigItem configItem, List<QuotationModelConfigItem> items
, List<QuotationModelPriceItem> priceItems, List<QuotationModelPriceItemArea> areaPrices, List<DictionaryItem> areas) {
ModelPriceConfigVO vo = new ModelPriceConfigVO()
.setPartName(configItem.getPartName())
.setType(configItem.getType())
.setConfigItemUniqueId(configItem.getUniqueId())
.setOptionalType(configItem.getOptionalType());
QuotationModelPriceItem priceItem = priceItems.stream()
.filter(pit -> pit.getConfigItemUniqueId().equals(configItem.getUniqueId()))
.findFirst()
.orElse(null);
if (Objects.nonNull(priceItem)) {
vo.setUpdateBy(priceItem.getUpdateBy());
vo.setUpdateTime(priceItem.getUpdateTime());
vo.setAreas(
areaPrices.stream()
.filter(area -> area.getPriceItemId().equals(priceItem.getId()))
.map(area -> {
DictionaryItem aa = areas.stream()
.filter(a -> a.getId().equals(area.getAreaId()))
.findFirst()
.orElse(null);
return new ModelPriceConfigAreaVO()
.setId(area.getId())
.setAmount(area.getAmount())
.setAreaId(area.getAreaId())
.setCode(Objects.isNull(aa) ? "" : aa.getCode())
.setAreaName(Objects.isNull(aa) ? "" : aa.getName());
})
.collect(Collectors.toList())
);
}
vo.setChildren(
items.stream()
.filter(cit -> Objects.equals(cit.getParentId(), configItem.getId()))
.map(cit -> generateVO(cit, items, priceItems, areaPrices, areas))
.collect(Collectors.toList())
);
return vo;
}
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!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.QuotationModelPriceItemAreaMapper">
</mapper>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!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.QuotationModelPriceItemMapper">
</mapper>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!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.QuotationModelPriceMapper">
<select id="search" resultType="com.nflg.mobilebroken.common.pojo.vo.ModelPriceConfigVO">
SELECT qmp.id,pm.batch_number as 'modelId',qmp.id as 'priceId',pm.`no` as 'partName',qmp.price_version
,qmp.price_status,qmp.update_by,qmp.update_time
,if(qmp.price_status=1,qmc2.config_version,qmc1.config_version) as 'configVersion'
,if(qmp.price_status=1,qmc2.id,qmc1.id) as 'configId'
FROM quotation_model_config qmc1
INNER JOIN product_model pm ON qmc1.model_id=pm.batch_number
LEFT JOIN product_type pt on pm.type_number=pt.batch_number AND pt.state=1
LEFT JOIN product_series ps ON pm.series_number=ps.batch_number AND ps.state=1
LEFT JOIN dictionary_item di ON di.id=pm.module_id
LEFT JOIN v_quotation_model_price qmp ON pm.batch_number=qmp.model_id
LEFT JOIN quotation_model_config qmc2 ON qmc2.id=qmp.config_id
WHERE pm.state=1 AND qmc1.config_status=1
<where>
<if test="request.moduleId!=null">
AND pm.module_id=#{request.moduleId}
</if>
<if test="request.seriesNumber!=null">
AND pm.series_number=#{request.seriesNumber}
</if>
<if test="request.typeNumber!=null">
AND pm.type_number=#{request.typeNumber}
</if>
<if test="request.no!=null and request.no!=''">
AND pm.`no` like concat('%', #{request.no}, '%')
</if>
</where>
order by qmp.price_status,qmp.update_time desc,pm.id
</select>
</mapper>

View File

@ -33,7 +33,7 @@ public class CodeGeneratorTest {
, Paths.get(System.getProperty("user.dir")) + "/src/main/resources/mapper"))
)
.strategyConfig(builder -> {
builder.addInclude("quotation_model_config_item_language") //只生成指定表
builder.addInclude("quotation_model_price_item_area") //只生成指定表
.entityBuilder()
.enableLombok()
.enableChainModel()

View File

@ -9,6 +9,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
@Configuration
@ConditionalOnProperty(name = "file.upload.type", havingValue = "oss")

10
pom.xml
View File

@ -115,11 +115,11 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-core</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.baomidou</groupId>-->
<!-- <artifactId>mybatis-plus-core</artifactId>-->
<!-- <version>${mybatis-plus.version}</version>-->
<!-- </dependency>-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>