feat(bid): 完成物料管理模块全功能开发
1. 新增物料详情页路由、菜单与接口,支持查看物料报价与信息 2. 重构物料列表页面,新增品牌筛选、表格样式优化与详情跳转 3. 扩展物料实体与数据库字段,新增材质、用途、性能参数等字段 4. 新增供应商/甲方报价查询、批量对比、同名称物料匹配接口 5. 新增物料详情组件,包含基础信息、供应商报价、甲方报价标签页 6. 修复比价路由跳转路径错误,调整数据库密码配置 7. 新增物料相关SQL脚本与初始化数据
This commit is contained in:
@@ -9,7 +9,6 @@ public class BizMaterial extends BaseEntity {
|
||||
private String materialCode;
|
||||
private String materialName;
|
||||
private String spec;
|
||||
private String modelNo;
|
||||
private String unit;
|
||||
private String brand;
|
||||
private String description;
|
||||
@@ -17,6 +16,12 @@ public class BizMaterial extends BaseEntity {
|
||||
// search helper
|
||||
private String categoryName;
|
||||
|
||||
// 新增字段
|
||||
private String performanceParams;
|
||||
private String material;
|
||||
private String purpose;
|
||||
private String imageUrl;
|
||||
|
||||
public Long getMaterialId() { return materialId; }
|
||||
public void setMaterialId(Long materialId) { this.materialId = materialId; }
|
||||
public Long getTenantId() { return tenantId; }
|
||||
@@ -29,8 +34,6 @@ public class BizMaterial extends BaseEntity {
|
||||
public void setMaterialName(String materialName) { this.materialName = materialName; }
|
||||
public String getSpec() { return spec; }
|
||||
public void setSpec(String spec) { this.spec = spec; }
|
||||
public String getModelNo() { return modelNo; }
|
||||
public void setModelNo(String modelNo) { this.modelNo = modelNo; }
|
||||
public String getUnit() { return unit; }
|
||||
public void setUnit(String unit) { this.unit = unit; }
|
||||
public String getBrand() { return brand; }
|
||||
@@ -41,4 +44,12 @@ public class BizMaterial extends BaseEntity {
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public String getCategoryName() { return categoryName; }
|
||||
public void setCategoryName(String categoryName) { this.categoryName = categoryName; }
|
||||
public String getPerformanceParams() { return performanceParams; }
|
||||
public void setPerformanceParams(String performanceParams) { this.performanceParams = performanceParams; }
|
||||
public String getMaterial() { return material; }
|
||||
public void setMaterial(String material) { this.material = material; }
|
||||
public String getPurpose() { return purpose; }
|
||||
public void setPurpose(String purpose) { this.purpose = purpose; }
|
||||
public String getImageUrl() { return imageUrl; }
|
||||
public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.ruoyi.system.mapper.bid;
|
||||
|
||||
import com.ruoyi.system.domain.bid.BizMaterial;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface BizMaterialMapper {
|
||||
List<BizMaterial> selectBizMaterialList(BizMaterial query);
|
||||
@@ -10,4 +12,21 @@ public interface BizMaterialMapper {
|
||||
int updateBizMaterial(BizMaterial record);
|
||||
int deleteBizMaterialById(Long id);
|
||||
int deleteBizMaterialByIds(Long[] ids);
|
||||
|
||||
// 物料详情页
|
||||
List<Map<String, Object>> selectSupplierQuotesByMaterialId(Long materialId);
|
||||
|
||||
// 批量物料对比
|
||||
List<BizMaterial> selectBizMaterialByIds(List<Long> materialIds);
|
||||
List<Map<String, Object>> selectSupplierPriceSummaryByMaterialIds(List<Long> materialIds);
|
||||
List<Map<String, Object>> selectBestSupplierOfferByMaterialIds(List<Long> materialIds);
|
||||
List<Map<String, Object>> selectClientQuotesByMaterialId(Long materialId);
|
||||
List<String> selectManufacturerList();
|
||||
|
||||
// 同类型物料横向对比
|
||||
List<BizMaterial> selectMaterialsWithSupplierQuotes();
|
||||
List<Map<String, Object>> selectSupplierQuoteComparison(List<Long> materialIds);
|
||||
|
||||
// 根据物料名称精确匹配(同名称不同规格/品牌对比)
|
||||
List<BizMaterial> selectMaterialsByExactName(@Param("materialName") String materialName, @Param("excludeId") Long excludeId);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.ruoyi.system.service.bid;
|
||||
|
||||
import com.ruoyi.system.domain.bid.BizMaterial;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface IBizMaterialService {
|
||||
List<BizMaterial> selectBizMaterialList(BizMaterial query);
|
||||
@@ -10,4 +11,18 @@ public interface IBizMaterialService {
|
||||
int updateBizMaterial(BizMaterial record);
|
||||
int deleteBizMaterialById(Long id);
|
||||
int deleteBizMaterialByIds(Long[] ids);
|
||||
|
||||
List<Map<String, Object>> selectSupplierQuotesByMaterialId(Long materialId);
|
||||
List<Map<String, Object>> selectClientQuotesByMaterialId(Long materialId);
|
||||
List<String> selectManufacturerList();
|
||||
|
||||
// 批量物料对比
|
||||
Map<String, Object> compareMaterials(List<Long> materialIds);
|
||||
|
||||
// 同类型物料横向对比
|
||||
List<BizMaterial> selectMaterialsWithSupplierQuotes();
|
||||
List<Map<String, Object>> selectSupplierQuoteComparison(List<Long> materialIds);
|
||||
|
||||
// 根据物料名称精确匹配(同名称不同规格/品牌对比)
|
||||
List<BizMaterial> selectMaterialsByExactName(String materialName, Long excludeId);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ import com.ruoyi.system.mapper.bid.BizMaterialMapper;
|
||||
import com.ruoyi.system.service.bid.IBizMaterialService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class BizMaterialServiceImpl implements IBizMaterialService {
|
||||
@@ -41,4 +42,70 @@ public class BizMaterialServiceImpl implements IBizMaterialService {
|
||||
public int deleteBizMaterialByIds(Long[] ids) {
|
||||
return mapper.deleteBizMaterialByIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> selectSupplierQuotesByMaterialId(Long materialId) {
|
||||
return mapper.selectSupplierQuotesByMaterialId(materialId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> selectClientQuotesByMaterialId(Long materialId) {
|
||||
return mapper.selectClientQuotesByMaterialId(materialId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectManufacturerList() {
|
||||
return mapper.selectManufacturerList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> compareMaterials(List<Long> materialIds) {
|
||||
if (materialIds == null || materialIds.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
// 1. 获取物料基本信息
|
||||
List<BizMaterial> materials = mapper.selectBizMaterialByIds(materialIds);
|
||||
|
||||
// 2. 获取供应商报价汇总
|
||||
List<Map<String, Object>> priceSummaries = mapper.selectSupplierPriceSummaryByMaterialIds(materialIds);
|
||||
Map<Long, Map<String, Object>> priceMap = new HashMap<>();
|
||||
for (Map<String, Object> row : priceSummaries) {
|
||||
Long mid = ((Number) row.get("material_id")).longValue();
|
||||
priceMap.put(mid, row);
|
||||
}
|
||||
|
||||
// 3. 获取最优报价详情
|
||||
List<Map<String, Object>> bestOffers = mapper.selectBestSupplierOfferByMaterialIds(materialIds);
|
||||
Map<Long, Map<String, Object>> bestOfferMap = new HashMap<>();
|
||||
for (Map<String, Object> row : bestOffers) {
|
||||
Long mid = ((Number) row.get("material_id")).longValue();
|
||||
bestOfferMap.put(mid, row);
|
||||
}
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("materials", materials);
|
||||
result.put("priceMap", priceMap);
|
||||
result.put("bestOfferMap", bestOfferMap);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BizMaterial> selectMaterialsWithSupplierQuotes() {
|
||||
return mapper.selectMaterialsWithSupplierQuotes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> selectSupplierQuoteComparison(List<Long> materialIds) {
|
||||
if (materialIds == null || materialIds.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return mapper.selectSupplierQuoteComparison(materialIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BizMaterial> selectMaterialsByExactName(String materialName, Long excludeId) {
|
||||
return mapper.selectMaterialsByExactName(materialName, excludeId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
<result property="materialCode" column="material_code"/>
|
||||
<result property="materialName" column="material_name"/>
|
||||
<result property="spec" column="spec"/>
|
||||
<result property="modelNo" column="model_no"/>
|
||||
<result property="unit" column="unit"/>
|
||||
<result property="brand" column="brand"/>
|
||||
<result property="description" column="description"/>
|
||||
@@ -18,27 +17,40 @@
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
<result property="performanceParams" column="performance_params"/>
|
||||
<result property="material" column="material"/>
|
||||
<result property="purpose" column="purpose"/>
|
||||
<result property="imageUrl" column="image_url"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectBizMaterialList" resultMap="BaseRM">
|
||||
SELECT m.* FROM biz_material m
|
||||
SELECT m.*, c.category_name AS category_name
|
||||
FROM biz_material m
|
||||
LEFT JOIN biz_material_category c ON m.category_id = c.category_id
|
||||
<where>
|
||||
<if test="tenantId != null"> AND m.tenant_id = #{tenantId}</if>
|
||||
<if test="categoryId != null"> AND m.category_id = #{categoryId}</if>
|
||||
<if test="materialCode != null and materialCode != ''"> AND m.material_code LIKE CONCAT('%',#{materialCode},'%')</if>
|
||||
<if test="materialName != null and materialName != ''"> AND m.material_name LIKE CONCAT('%',#{materialName},'%')</if>
|
||||
<if test="status != null and status != ''"> AND m.status = #{status}</if>
|
||||
<if test="materialCode != null and materialCode != ''"> AND m.material_code LIKE CONCAT('%',#{materialCode},'%')</if>
|
||||
<if test="materialName != null and materialName != ''"> AND m.material_name LIKE CONCAT('%',#{materialName},'%')</if>
|
||||
<if test="brand != null and brand != ''"> AND m.brand LIKE CONCAT('%',#{brand},'%')</if>
|
||||
<if test="spec != null and spec != ''"> AND m.spec LIKE CONCAT('%',#{spec},'%')</if>
|
||||
<if test="status != null and status != ''"> AND m.status = #{status}</if>
|
||||
</where>
|
||||
ORDER BY m.material_id DESC
|
||||
</select>
|
||||
|
||||
<select id="selectBizMaterialById" resultMap="BaseRM">
|
||||
SELECT * FROM biz_material WHERE material_id=#{id}
|
||||
SELECT m.*, c.category_name AS category_name
|
||||
FROM biz_material m
|
||||
LEFT JOIN biz_material_category c ON m.category_id = c.category_id
|
||||
WHERE m.material_id=#{id}
|
||||
</select>
|
||||
|
||||
<insert id="insertBizMaterial" useGeneratedKeys="true" keyProperty="materialId">
|
||||
INSERT INTO biz_material(tenant_id,category_id,material_code,material_name,spec,model_no,unit,brand,description,status,create_by,create_time)
|
||||
VALUES(#{tenantId},#{categoryId},#{materialCode},#{materialName},#{spec},#{modelNo},#{unit},#{brand},#{description},#{status},#{createBy},NOW())
|
||||
INSERT INTO biz_material(tenant_id,category_id,material_code,material_name,spec,unit,brand,
|
||||
description,status,performance_params,material,purpose,image_url,create_by,create_time)
|
||||
VALUES(#{tenantId},#{categoryId},#{materialCode},#{materialName},#{spec},#{unit},#{brand},
|
||||
#{description},#{status},#{performanceParams},#{material},#{purpose},#{imageUrl},#{createBy},NOW())
|
||||
</insert>
|
||||
|
||||
<update id="updateBizMaterial">
|
||||
@@ -47,12 +59,15 @@
|
||||
<if test="materialName != null">material_name=#{materialName},</if>
|
||||
<if test="materialCode != null">material_code=#{materialCode},</if>
|
||||
<if test="spec != null">spec=#{spec},</if>
|
||||
<if test="modelNo != null">model_no=#{modelNo},</if>
|
||||
<if test="unit != null">unit=#{unit},</if>
|
||||
<if test="brand != null">brand=#{brand},</if>
|
||||
<if test="description != null">description=#{description},</if>
|
||||
<if test="categoryId != null">category_id=#{categoryId},</if>
|
||||
<if test="status != null">status=#{status},</if>
|
||||
<if test="performanceParams != null">performance_params=#{performanceParams},</if>
|
||||
<if test="material != null">material=#{material},</if>
|
||||
<if test="purpose != null">purpose=#{purpose},</if>
|
||||
<if test="imageUrl != null">image_url=#{imageUrl},</if>
|
||||
update_by=#{updateBy}, update_time=NOW()
|
||||
</set>
|
||||
WHERE material_id=#{materialId}
|
||||
@@ -63,4 +78,129 @@
|
||||
DELETE FROM biz_material WHERE material_id IN
|
||||
<foreach collection="array" item="id" open="(" separator="," close=")">#{id}</foreach>
|
||||
</delete>
|
||||
|
||||
<!-- 供应商报价历史:通过 rfq_item 关联物料 -->
|
||||
<select id="selectSupplierQuotesByMaterialId" resultType="java.util.Map">
|
||||
SELECT qi.item_id, qi.unit_price, qi.total_price, qi.delivery_days,
|
||||
q.quotation_id, q.quote_no, q.submit_time, q.status AS quote_status,
|
||||
s.supplier_id, s.supplier_name
|
||||
FROM biz_quotation_item qi
|
||||
JOIN biz_quotation q ON qi.quotation_id = q.quotation_id
|
||||
JOIN biz_supplier s ON q.supplier_id = s.supplier_id
|
||||
JOIN biz_rfq_item ri ON qi.rfq_item_id = ri.item_id
|
||||
WHERE ri.material_id = #{materialId}
|
||||
ORDER BY q.submit_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 甲方报价历史:通过物料名称关联 -->
|
||||
<select id="selectClientQuotesByMaterialId" resultType="java.util.Map">
|
||||
SELECT cqi.item_id, cqi.spec, cqi.model_no, cqi.unit, cqi.quantity,
|
||||
cqi.cost_price, cqi.unit_price, cqi.total_price, cqi.delivery_days,
|
||||
cq.quote_id, cq.quote_no, cq.client_name, cq.status AS quote_status, cq.create_time
|
||||
FROM biz_client_quote_item cqi
|
||||
JOIN biz_client_quote cq ON cqi.quote_id = cq.quote_id
|
||||
WHERE cqi.material_name = (SELECT material_name FROM biz_material WHERE material_id = #{materialId})
|
||||
ORDER BY cq.create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- ========== 批量物料对比 ========== -->
|
||||
<select id="selectBizMaterialByIds" resultMap="BaseRM">
|
||||
SELECT m.*, c.category_name AS category_name
|
||||
FROM biz_material m
|
||||
LEFT JOIN biz_material_category c ON m.category_id = c.category_id
|
||||
WHERE m.material_id IN
|
||||
<foreach collection="list" item="id" open="(" separator="," close=")">#{id}</foreach>
|
||||
ORDER BY m.material_id
|
||||
</select>
|
||||
|
||||
<!-- 按物料分组统计供应商报价汇总 -->
|
||||
<select id="selectSupplierPriceSummaryByMaterialIds" resultType="java.util.Map">
|
||||
SELECT ri.material_id,
|
||||
MIN(qi.unit_price) AS min_price,
|
||||
MAX(qi.unit_price) AS max_price,
|
||||
ROUND(AVG(qi.unit_price), 2) AS avg_price,
|
||||
COUNT(DISTINCT q.supplier_id) AS supplier_count
|
||||
FROM biz_quotation_item qi
|
||||
JOIN biz_quotation q ON qi.quotation_id = q.quotation_id
|
||||
JOIN biz_rfq_item ri ON qi.rfq_item_id = ri.item_id
|
||||
WHERE ri.material_id IN
|
||||
<foreach collection="list" item="id" open="(" separator="," close=")">#{id}</foreach>
|
||||
GROUP BY ri.material_id
|
||||
</select>
|
||||
|
||||
<!-- 每个物料最低价对应的供应商详情 -->
|
||||
<select id="selectBestSupplierOfferByMaterialIds" resultType="java.util.Map">
|
||||
SELECT t.material_id, t.unit_price, t.delivery_days, s.supplier_name, s.contact, s.phone
|
||||
FROM (
|
||||
SELECT ri.material_id, qi.unit_price, qi.delivery_days, qi.quotation_id,
|
||||
ROW_NUMBER() OVER (PARTITION BY ri.material_id ORDER BY qi.unit_price ASC, qi.delivery_days ASC) AS rn
|
||||
FROM biz_quotation_item qi
|
||||
JOIN biz_quotation q ON qi.quotation_id = q.quotation_id
|
||||
JOIN biz_rfq_item ri ON qi.rfq_item_id = ri.item_id
|
||||
WHERE ri.material_id IN
|
||||
<foreach collection="list" item="id" open="(" separator="," close=")">#{id}</foreach>
|
||||
AND qi.unit_price IS NOT NULL
|
||||
) t
|
||||
JOIN biz_quotation q ON t.quotation_id = q.quotation_id
|
||||
JOIN biz_supplier s ON q.supplier_id = s.supplier_id
|
||||
WHERE t.rn = 1
|
||||
</select>
|
||||
|
||||
<!-- 获取厂家/品牌列表(去重) -->
|
||||
<select id="selectManufacturerList" resultType="java.lang.String">
|
||||
SELECT DISTINCT brand FROM biz_material
|
||||
WHERE brand IS NOT NULL AND brand != ''
|
||||
ORDER BY brand
|
||||
</select>
|
||||
|
||||
<!-- 根据物料名称精确匹配查询(用于同名称不同规格/品牌对比) -->
|
||||
<select id="selectMaterialsByExactName" resultMap="BaseRM">
|
||||
SELECT m.*, c.category_name AS category_name
|
||||
FROM biz_material m
|
||||
LEFT JOIN biz_material_category c ON m.category_id = c.category_id
|
||||
WHERE m.material_name = #{materialName}
|
||||
AND m.material_id != #{excludeId}
|
||||
ORDER BY m.material_id DESC
|
||||
</select>
|
||||
|
||||
<!-- ========== 同类型物料横向对比 ========== -->
|
||||
|
||||
<!-- 获取所有有供应商报价记录的物料列表(用于对比选择框) -->
|
||||
<select id="selectMaterialsWithSupplierQuotes" resultMap="BaseRM">
|
||||
SELECT DISTINCT m.*, c.category_name AS category_name
|
||||
FROM biz_material m
|
||||
LEFT JOIN biz_material_category c ON m.category_id = c.category_id
|
||||
WHERE EXISTS (
|
||||
SELECT 1 FROM biz_quotation_item qi
|
||||
JOIN biz_quotation q ON qi.quotation_id = q.quotation_id
|
||||
JOIN biz_rfq_item ri ON qi.rfq_item_id = ri.item_id
|
||||
WHERE ri.material_id = m.material_id
|
||||
)
|
||||
ORDER BY m.material_name, m.material_id DESC
|
||||
</select>
|
||||
|
||||
<!-- 根据物料ID列表获取各供应商的详细报价明细(用于对比展示) -->
|
||||
<select id="selectSupplierQuoteComparison" resultType="java.util.Map">
|
||||
SELECT
|
||||
ri.material_id,
|
||||
qi.unit_price,
|
||||
qi.total_price,
|
||||
qi.delivery_days,
|
||||
q.quotation_id,
|
||||
q.quote_no,
|
||||
q.submit_time,
|
||||
q.status AS quote_status,
|
||||
s.supplier_id,
|
||||
s.supplier_name,
|
||||
s.contact AS supplier_contact,
|
||||
s.phone AS supplier_phone
|
||||
FROM biz_quotation_item qi
|
||||
JOIN biz_quotation q ON qi.quotation_id = q.quotation_id
|
||||
JOIN biz_supplier s ON q.supplier_id = s.supplier_id
|
||||
JOIN biz_rfq_item ri ON qi.rfq_item_id = ri.item_id
|
||||
WHERE ri.material_id IN
|
||||
<foreach collection="list" item="id" open="(" separator="," close=")">#{id}</foreach>
|
||||
ORDER BY ri.material_id, qi.unit_price ASC, q.submit_time DESC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
Reference in New Issue
Block a user