新增按版本获取价格设置明细的接口,优化产品下拉选项查询,更新相关服务和前端调用逻辑。

This commit is contained in:
wuweihua
2026-10-29 16:13:18 +08:00
parent 59eb4b23cc
commit c3456ca92a
14 changed files with 218 additions and 29 deletions
@@ -30,6 +30,9 @@ public interface BmsProductMapper
public List<BmsProduct> selectBmsProductList(BmsProduct product);
/** Dropdown options: slim columns, no country/surcharge count subqueries */
public List<BmsProduct> selectBmsProductOptions(BmsProduct product);
public int insertBmsProduct(BmsProduct product);
public int updateBmsProduct(BmsProduct product);
@@ -19,6 +19,9 @@ public interface IBmsProductService
public List<BmsProduct> selectBmsProductList(BmsProduct product);
/** Product dropdown options (no status refresh, no count subqueries). */
public List<BmsProduct> selectBmsProductOptions(BmsProduct product);
public int insertBmsProduct(BmsProduct product);
public int updateBmsProduct(BmsProduct product);
@@ -71,6 +71,11 @@ public interface IPrcPriceSetService
*/
public List<PrcPriceSetDetail> selectDetailListByProduct(Long psvId, String productCode);
/**
* Load all detail rows for a version (no pagination). For batch prefetch vs N byProduct calls.
*/
public List<PrcPriceSetDetail> selectDetailListByVersion(Long psvId);
/**
* 版本明细搜索下拉选项:按版本去重后的 产品/国家/分区 列表。
* 返回 { products: [{code,name}], countries: [{code,name}], zones: [{code,name}] }
@@ -129,8 +129,7 @@ public class BmsProductServiceImpl implements IBmsProductService
@Override
public List<BmsProduct> selectBmsProductList(BmsProduct product)
{
productMapper.refreshStatusByDates();
countryMapper.refreshStatusByDates();
// Status refresh is done on activate / scheduled task; skip UPDATE on every list read.
if (product != null)
{
product.setStatus(normalizeStatusCode(product.getStatus()));
@@ -139,6 +138,17 @@ public class BmsProductServiceImpl implements IBmsProductService
return productMapper.selectBmsProductList(product);
}
@Override
public List<BmsProduct> selectBmsProductOptions(BmsProduct product)
{
if (product != null)
{
product.setStatus(normalizeStatusCode(product.getStatus()));
product.setEffectStatus(normalizeStatusCode(product.getEffectStatus()));
}
return productMapper.selectBmsProductOptions(product);
}
@Override
public int insertBmsProduct(BmsProduct product)
{
@@ -378,6 +388,7 @@ public class BmsProductServiceImpl implements IBmsProductService
{
throw new ServiceException(MessageUtils.message("price.product.import.sheetNotFound"));
}
assertNotSingleCountryQuoteForMultiImport(workbook);
List<BmsProduct> products = parseCatalog(catalog);
if (products.isEmpty())
@@ -678,6 +689,7 @@ public class BmsProductServiceImpl implements IBmsProductService
{
throw new ServiceException(MessageUtils.message("price.product.import.sheetNotFound"));
}
assertNotSingleCountryQuoteForMultiImport(workbook);
List<BmsProduct> products = dedupeCatalogProducts(parseCatalog(catalog));
if (products.isEmpty())
{
@@ -763,6 +775,7 @@ public class BmsProductServiceImpl implements IBmsProductService
}
if (out.isEmpty())
{
assertNotSingleCountryQuoteForMultiImport(workbook);
throw new ServiceException(MessageUtils.message("price.set.import.noPriceRows"));
}
QuoteWorkbook parsed = new QuoteWorkbook();
@@ -778,6 +791,80 @@ public class BmsProductServiceImpl implements IBmsProductService
private Map<String, List<BmsProductCountry>> byCode;
}
/**
* Multi-country import expects price sheets with a "country" column.
* Single-country dedicated-line sheets use product-name + weight blocks instead.
*/
private void assertNotSingleCountryQuoteForMultiImport(Workbook workbook)
{
if (looksLikeSingleCountryQuote(workbook))
{
throw new ServiceException(MessageUtils.message("price.product.import.singleCountryAsMulti"));
}
}
/** Detect single-country cost-sheet layout (no country column on price sheets). */
private boolean looksLikeSingleCountryQuote(Workbook workbook)
{
if (workbook == null)
{
return false;
}
boolean sawCountryCol = false;
boolean sawCountryPriceBlock = false;
for (int i = 0; i < workbook.getNumberOfSheets(); i++)
{
String sheetName = workbook.getSheetName(i);
if (shouldSkipCountryPriceSheet(sheetName))
{
continue;
}
Sheet sheet = workbook.getSheetAt(i);
if (sheet == null)
{
continue;
}
if (findHeaderCell(sheet, "\u56fd\u5bb6") != null)
{
sawCountryCol = true;
break;
}
int[] nameHeader = findHeaderCell(sheet, "\u4ea7\u54c1\u540d\u79f0");
if (nameHeader == null)
{
continue;
}
Row head = sheet.getRow(nameHeader[0]);
if (head == null)
{
continue;
}
if (findColByHeader(head, "\u91cd\u91cf", 0) >= 0)
{
sawCountryPriceBlock = true;
}
}
if (sawCountryCol)
{
return false;
}
if (sawCountryPriceBlock)
{
return true;
}
Sheet catalog = workbook.getSheet(CATALOG_SHEET);
if (catalog == null)
{
return false;
}
if (findHeaderCell(catalog, "\u5bf9\u63a5\u4ee3\u7801") != null)
{
return false;
}
return findHeaderCell(catalog, "\u4ea7\u54c1\u540d\u79f0") != null
|| findHeaderCell(catalog, "\u4ea7\u54c1ID") != null;
}
@Override
public Map<String, List<BmsProductCountry>> parseCountryQuoteExcelByProduct(byte[] bytes, String filename,
String countryCode) throws Exception
@@ -881,6 +881,18 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
return selectDetailList(q);
}
@Override
public List<PrcPriceSetDetail> selectDetailListByVersion(Long psvId)
{
if (psvId == null)
{
return new ArrayList<PrcPriceSetDetail>();
}
PrcPriceSetDetail q = new PrcPriceSetDetail();
q.setPsvId(psvId);
return selectDetailList(q);
}
@Override
public Map<String, Object> selectDetailOptions(Long psvId)
{
@@ -45,14 +45,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
ifnull(p.is_del, '0') as is_del,
p.source_sheet, null as remark, null as tms_id,
v.created_by, p.create_time, v.updated_by, p.update_time,
(select count(distinct ifnull(c.country_code, c.country_name)) from prc_price_set_detail c
where ifnull(c.is_del, '0') = '0'
and (c.psp_id = p.psp_id or (c.psv_id = p.psv_id and c.product_code = p.product_code))
) as country_count,
(select count(*) from prc_surcharge_rel r
inner join prc_surcharge s on s.sc_id = r.sc_id and ifnull(s.is_del, '0') = '0'
where ifnull(r.is_del, '0') = '0'
and r.product_id = p.psp_id and r.price_type = 'PRODUCT') as surcharge_count
null as country_count, null as surcharge_count
from prc_price_set_product p
inner join prc_price_set_version v on v.psv_id = p.psv_id
</sql>
@@ -176,6 +169,66 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</choose>
</select>
<!-- Dropdown: same filters as list, without per-row country/surcharge count scans -->
<select id="selectBmsProductOptions" parameterType="BmsProduct" resultMap="BmsProductResult">
select p.psp_id as product_id, p.psv_id, p.price_type, p.product_code, p.product_name, p.service_level, p.cargo_attr,
p.dest_region, p.tax_mode,
p.effective_time as effective_date, p.expire_time as expire_date, p.status,
ifnull(p.is_del, '0') as is_del,
null as update_note, null as source_sheet, null as remark, null as tms_id,
null as created_by, p.create_time, null as updated_by, p.update_time,
null as country_count, null as surcharge_count
from prc_price_set_product p
inner join prc_price_set_version v on v.psv_id = p.psv_id
<where>
and p.price_type = 'PRODUCT'
and ifnull(p.is_del, '0') = '0'
<choose>
<when test="psvId != null">
AND p.psv_id = #{psvId}
</when>
<when test="statusGroup != null and statusGroup == 'effect'">
AND v.price_set_id is null AND v.status = '1'
</when>
<when test="statusGroup != null and statusGroup == 'pending'">
AND v.price_set_id is null AND v.status in ('0', '3')
</when>
<when test="statusGroup != null and statusGroup == 'expired'">
AND v.status = '2'
</when>
<otherwise>
AND p.psv_id = <include refid="latestProductPsv"/>
</otherwise>
</choose>
<if test="productCode != null and productCode != ''">
AND p.product_code like concat('%', #{productCode}, '%')
</if>
<if test="productName != null and productName != ''">
AND p.product_name like concat('%', #{productName}, '%')
</if>
<if test="taxMode != null and taxMode != ''">
AND p.tax_mode = #{taxMode}
</if>
<if test="status != null and status != ''">
AND v.status = #{status}
</if>
<if test="effectStatus != null and effectStatus != ''">
AND v.status = #{effectStatus}
</if>
<if test="statusGroup != null and statusGroup == 'active'">
AND v.status in ('0', '1')
</if>
<if test="cargoAttr != null and cargoAttr != ''">
AND p.cargo_attr like concat('%', #{cargoAttr}, '%')
</if>
<if test="params != null and params.keyword != null and params.keyword != ''">
AND (p.product_code like concat('%', #{params.keyword}, '%')
OR p.product_name like concat('%', #{params.keyword}, '%'))
</if>
</where>
order by p.product_code asc, p.psp_id desc
</select>
<insert id="insertBmsProduct" parameterType="BmsProduct" useGeneratedKeys="true" keyProperty="productId">
insert into prc_price_set_product (
price_type, psv_id, product_code, product_name, service_level, cargo_attr,