feat(bid): 新增基于甲方报价快速创建RFQ功能

本次提交完成以下核心变更:
1. 新增RFQ编号自动生成逻辑,添加selectNextRfqNo方法获取月度递增的RFQ编号
2. 在biz_rfq表新增client_quote_id关联字段,添加索引并完善实体类映射
3. 实现基于甲方报价复制物料快速创建RFQ的业务逻辑,包括事务处理和明细复制
4. 新增RFQ列表页关联甲方报价展示,支持点击跳转查看甲方报价详情
5. 在RFQ编辑页新增甲方报价选择器,选中后自动填充对应物料和标题
6. 优化甲方报价单页面,新增生成RFQ按钮和已生成RFQ列表展示
7. 调整RFQ详情页,新增编辑模式支持草稿状态修改
8. 修复路由跳转路径,统一RFQ相关页面路由到/bid/rfq路径组
This commit is contained in:
2026-06-02 18:44:44 +08:00
parent a75589018f
commit 9db84336bc
12 changed files with 514 additions and 70 deletions

View File

@@ -13,6 +13,9 @@ public class BizRfq extends BaseEntity {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date deadline;
private String deliveryAddr;
private Long clientQuoteId;
private String clientQuoteNo;
private String clientName;
private String status;
private List<BizRfqItem> items;
private List<Long> supplierIds;
@@ -29,6 +32,12 @@ public class BizRfq extends BaseEntity {
public void setDeadline(Date deadline) { this.deadline = deadline; }
public String getDeliveryAddr() { return deliveryAddr; }
public void setDeliveryAddr(String deliveryAddr) { this.deliveryAddr = deliveryAddr; }
public Long getClientQuoteId() { return clientQuoteId; }
public void setClientQuoteId(Long clientQuoteId) { this.clientQuoteId = clientQuoteId; }
public String getClientQuoteNo() { return clientQuoteNo; }
public void setClientQuoteNo(String clientQuoteNo) { this.clientQuoteNo = clientQuoteNo; }
public String getClientName() { return clientName; }
public void setClientName(String clientName) { this.clientName = clientName; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public List<BizRfqItem> getItems() { return items; }

View File

@@ -9,5 +9,6 @@ public interface BizRfqMapper {
int insertBizRfq(BizRfq record);
int updateBizRfq(BizRfq record);
int deleteBizRfqById(Long id);
String selectNextRfqNo();
int deleteBizRfqByIds(Long[] ids);
}

View File

@@ -13,4 +13,6 @@ public interface IBizRfqService {
int deleteBizRfqByIds(Long[] ids);
int publishRfq(Long rfqId, Long[] supplierIds);
List<BizRfqItem> selectItemsByRfqId(Long rfqId);
/** 基于甲方报价快速创建 RFQ复制物料明细 */
BizRfq createRfqFromClientQuote(Long clientQuoteId, Long tenantId);
}

View File

@@ -1,20 +1,24 @@
package com.ruoyi.system.service.bid.impl;
import com.ruoyi.system.domain.bid.BizClientQuote;
import com.ruoyi.system.domain.bid.BizClientQuoteItem;
import com.ruoyi.system.domain.bid.BizRfq;
import com.ruoyi.system.domain.bid.BizRfqItem;
import com.ruoyi.system.mapper.bid.BizClientQuoteMapper;
import com.ruoyi.system.mapper.bid.BizRfqItemMapper;
import com.ruoyi.system.mapper.bid.BizRfqMapper;
import com.ruoyi.system.service.bid.IBizRfqService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class BizRfqServiceImpl implements IBizRfqService {
@Autowired private BizRfqMapper rfqMapper;
@Autowired private BizRfqItemMapper itemMapper;
@Autowired private BizClientQuoteMapper clientQuoteMapper;
@Override
public List<BizRfq> selectBizRfqList(BizRfq query) { return rfqMapper.selectBizRfqList(query); }
@@ -29,6 +33,9 @@ public class BizRfqServiceImpl implements IBizRfqService {
@Override
@Transactional
public int insertBizRfq(BizRfq rfq) {
if (rfq.getRfqNo() == null || rfq.getRfqNo().isBlank()) {
rfq.setRfqNo(rfqMapper.selectNextRfqNo());
}
rfq.setStatus("draft");
int rows = rfqMapper.insertBizRfq(rfq);
if (rfq.getItems() != null) {
@@ -71,4 +78,50 @@ public class BizRfqServiceImpl implements IBizRfqService {
public List<BizRfqItem> selectItemsByRfqId(Long rfqId) {
return itemMapper.selectItemsByRfqId(rfqId);
}
@Override
@Transactional
public BizRfq createRfqFromClientQuote(Long clientQuoteId, Long tenantId) {
// 加载甲方报价
BizClientQuote quote = clientQuoteMapper.selectClientQuoteById(clientQuoteId);
if (quote == null) {
throw new RuntimeException("甲方报价单不存在: " + clientQuoteId);
}
// 创建 RFQ
BizRfq rfq = new BizRfq();
rfq.setRfqNo(rfqMapper.selectNextRfqNo());
rfq.setTenantId(tenantId);
rfq.setRfqTitle("采购需求: " + quote.getClientName() + " - " + quote.getQuoteNo());
rfq.setClientQuoteId(quote.getQuoteId());
rfq.setDeliveryAddr("");
rfq.setStatus("draft");
rfq.setRemark("源自甲方报价单: " + quote.getQuoteNo());
// 复制物料明细
List<BizClientQuoteItem> sourceItems = clientQuoteMapper.selectItemsByQuoteId(clientQuoteId);
if (sourceItems != null && !sourceItems.isEmpty()) {
rfq.setItems(sourceItems.stream().map(src -> {
BizRfqItem item = new BizRfqItem();
item.setMaterialId(src.getMaterialId());
item.setMaterialName(src.getMaterialName());
item.setSpec(src.getSpec());
item.setUnit(src.getUnit());
item.setQuantity(src.getQuantity());
item.setExpectedPrice(src.getUnitPrice());
item.setRemark(src.getRemark());
return item;
}).collect(Collectors.toList()));
}
rfqMapper.insertBizRfq(rfq);
if (rfq.getItems() != null) {
for (BizRfqItem item : rfq.getItems()) {
item.setRfqId(rfq.getRfqId());
itemMapper.insertBizRfqItem(item);
}
}
return rfqMapper.selectBizRfqById(rfq.getRfqId());
}
}