feat(inventory): 添加批量查询出库单条码和价格详情功能

- 新增OutboundOrderInventoryInfoToolProvider工具类,支持通过出库单号批量查询条码和价格详情
- 扩展IInventoryInfoService接口及其实现类InventoryInfoServiceImpl,添加按出库单号列表查询库存信息的方法
- 更新InventoryOuter实体类,添加outerCodeList属性
- 修改InventoryOuterMapper.xml,支持按出库单号列表查询
- 更新InventoryInfoMapper.java及InventoryInfoMapper.xml,支持按出库单号列表查询库存信息
dev_1.0.2
chenhao 2026-03-23 17:36:07 +08:00
parent 487bf5e0c6
commit a7e0598951
7 changed files with 240 additions and 1 deletions

View File

@ -31,6 +31,7 @@ public class InventoryOuter extends BaseEntity
/** 出库单号 */
@Excel(name = "出库单号")
private String outerCode;
private List<String> outerCodeList;
/** 发货时间 */
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")

View File

@ -0,0 +1,217 @@
package com.ruoyi.sip.llm.tools;
import com.ruoyi.sip.domain.InventoryInfo;
import com.ruoyi.sip.domain.InventoryOuter;
import com.ruoyi.sip.domain.ProductInfo;
import com.ruoyi.sip.llm.tools.support.AbstractMcpToolProvider;
import com.ruoyi.sip.service.IInventoryInfoService;
import com.ruoyi.sip.service.IInventoryOuterService;
import com.ruoyi.sip.service.IProductInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@Component
public class OutboundOrderInventoryInfoToolProvider extends AbstractMcpToolProvider {
@Autowired
private IInventoryInfoService inventoryInfoService;
@Autowired
private IInventoryOuterService inventoryOuterService;
@Autowired
private IProductInfoService productInfoService;
@Override
protected String getToolName() {
return "outbound_order_inventory_info";
}
@Override
protected String getToolDescription() {
return "Batch query barcode and price details by outbound order codes.";
}
@Override
protected Map<String, Object> buildInputSchema() {
Map<String, Object> outerCodeListProperty = new LinkedHashMap<>();
outerCodeListProperty.put("type", "array");
outerCodeListProperty.put("description", "Outbound order code list.");
outerCodeListProperty.put("items", stringProperty("Outbound order code"));
outerCodeListProperty.put("minItems", 1);
Map<String, Object> properties = new LinkedHashMap<>();
properties.put("outer_code_list", outerCodeListProperty);
return objectSchema(properties, "outer_code_list");
}
@Override
protected Object handle(Map<String, Object> params) {
List<String> outerCodeList = getStringList(params, "outer_code_list");
if (outerCodeList.isEmpty()) {
throw new RuntimeException("outer_code_list is required");
}
List<InventoryOuter> inventoryOuters = loadInventoryOuters(outerCodeList);
Map<String, String> productNameMap = loadProductNameMap(inventoryOuters);
Map<String, List<InventoryInfo>> inventoryInfoMap = loadInventoryInfoMap(outerCodeList);
List<Map<String, Object>> items = new ArrayList<>();
for (InventoryOuter inventoryOuter : inventoryOuters) {
items.add(toOutboundOrderItem(inventoryOuter, productNameMap, inventoryInfoMap));
}
Map<String, Object> query = new LinkedHashMap<>();
query.put("outer_code_list", outerCodeList);
Map<String, Object> data = new LinkedHashMap<>();
data.put("total", items.size());
data.put("items", items);
Map<String, Object> metadata = new LinkedHashMap<>();
metadata.put("tool", getToolName());
metadata.put("description", "Batch query outbound-order barcode details from oms_inventory_info.");
metadata.put("query_fields", mapOf(
"outer_code_list", "Outbound order code list"
));
metadata.put("data_fields", mapOf(
"total", "Matched outbound orders count",
"items", "Outbound order detail list"
));
metadata.put("item_fields", mapOf(
"outboundOrderCode", "Outbound order code",
"orderCode", "Order code",
"productCode", "Product code",
"productName", "Product name",
"quantity", "Outbound quantity",
"inventoryCount", "Barcode detail count",
"inventoryInfos", "Barcode detail list"
));
metadata.put("inventory_info_fields", mapOf(
"productSn", "Barcode / serial number",
"innerPrice", "Inner price",
"outerPrice", "Outer price",
"taxRate", "Tax rate"
));
return response(metadata, query, data);
}
private List<InventoryOuter> loadInventoryOuters(List<String> outerCodeList) {
InventoryOuter query = new InventoryOuter();
query.setOuterCodeList(outerCodeList);
List<InventoryOuter> inventoryOuters = inventoryOuterService.selectInventoryOuterList(query);
if (inventoryOuters == null || inventoryOuters.isEmpty()) {
return Collections.emptyList();
}
Map<String, InventoryOuter> outerMap = inventoryOuters.stream().collect(Collectors.toMap(
InventoryOuter::getOuterCode,
item -> item,
(first, second) -> first,
LinkedHashMap::new
));
List<InventoryOuter> ordered = new ArrayList<>();
for (String outerCode : outerCodeList) {
InventoryOuter inventoryOuter = outerMap.get(outerCode);
if (inventoryOuter != null) {
ordered.add(inventoryOuter);
}
}
return ordered;
}
private Map<String, String> loadProductNameMap(List<InventoryOuter> inventoryOuters) {
if (inventoryOuters == null || inventoryOuters.isEmpty()) {
return Collections.emptyMap();
}
Set<String> productCodes = inventoryOuters.stream()
.map(InventoryOuter::getProductCode)
.filter(code -> !isBlank(code))
.collect(Collectors.toCollection(LinkedHashSet::new));
if (productCodes.isEmpty()) {
return Collections.emptyMap();
}
List<ProductInfo> productInfos = productInfoService.selectProductInfoByCodeList(new ArrayList<>(productCodes));
if (productInfos == null || productInfos.isEmpty()) {
return Collections.emptyMap();
}
return productInfos.stream().collect(Collectors.toMap(ProductInfo::getProductCode, ProductInfo::getProductName, (a, b) -> a));
}
private Map<String, List<InventoryInfo>> loadInventoryInfoMap(List<String> outerCodeList) {
List<InventoryInfo> inventoryInfos = inventoryInfoService.selectInventoryInfoByOuterCodeList(outerCodeList);
if (inventoryInfos == null || inventoryInfos.isEmpty()) {
return Collections.emptyMap();
}
return inventoryInfos.stream()
.filter(item -> !isBlank(item.getOuterCode()))
.collect(Collectors.groupingBy(InventoryInfo::getOuterCode, LinkedHashMap::new, Collectors.toList()));
}
private Map<String, Object> toOutboundOrderItem(InventoryOuter inventoryOuter,
Map<String, String> productNameMap,
Map<String, List<InventoryInfo>> inventoryInfoMap) {
List<InventoryInfo> inventoryInfos = inventoryInfoMap.get(inventoryOuter.getOuterCode());
List<Map<String, Object>> inventoryInfoItems = new ArrayList<>();
if (inventoryInfos != null) {
for (InventoryInfo inventoryInfo : inventoryInfos) {
inventoryInfoItems.add(toInventoryInfoItem(inventoryInfo));
}
}
Map<String, Object> item = new LinkedHashMap<>();
item.put("outboundOrderCode", inventoryOuter.getOuterCode());
item.put("orderCode", inventoryOuter.getOrderCode());
item.put("productCode", inventoryOuter.getProductCode());
item.put("productName", productNameMap.get(inventoryOuter.getProductCode()));
item.put("quantity", inventoryOuter.getQuantity());
item.put("inventoryCount", inventoryInfoItems.size());
item.put("inventoryInfos", inventoryInfoItems);
return item;
}
private Map<String, Object> toInventoryInfoItem(InventoryInfo inventoryInfo) {
Map<String, Object> item = new LinkedHashMap<>();
item.put("productSn", inventoryInfo.getProductSn());
item.put("innerPrice", inventoryInfo.getInnerPrice());
item.put("outerPrice", inventoryInfo.getOuterPrice());
item.put("taxRate", inventoryInfo.getTaxRate());
return item;
}
private List<String> getStringList(Map<String, Object> params, String key) {
if (params == null) {
return Collections.emptyList();
}
Object value = params.get(key);
if (!(value instanceof List)) {
return Collections.emptyList();
}
List<?> rawList = (List<?>) value;
List<String> result = new ArrayList<>();
for (Object raw : rawList) {
if (raw == null) {
continue;
}
String item = String.valueOf(raw).trim();
if (!item.isEmpty()) {
result.add(item);
}
}
return result;
}
}

View File

@ -79,4 +79,6 @@ public interface InventoryInfoMapper
List<InventoryInfo> listByDeliveryId(Long id);
List<InventoryInfo> selectInventoryInfoByOrderCode(List<String> strings);
List<InventoryInfo> selectInventoryInfoByOuterCodeList(List<String> outerCodeList);
}

View File

@ -89,4 +89,6 @@ public interface IInventoryInfoService
List<InventoryInfo> listByDeliveryId(Long id);
List<InventoryInfo> selectInventoryInfoByOrderCode(List<String> strings);
List<InventoryInfo> selectInventoryInfoByOuterCodeList(List<String> outerCodeList);
}

View File

@ -217,5 +217,10 @@ public class InventoryInfoServiceImpl implements IInventoryInfoService {
return inventoryInfoMapper.selectInventoryInfoByOrderCode(strings);
}
@Override
public List<InventoryInfo> selectInventoryInfoByOuterCodeList(List<String> outerCodeList) {
return inventoryInfoMapper.selectInventoryInfoByOuterCodeList(outerCodeList);
}
}

View File

@ -111,6 +111,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
)
</select>
<select id="selectInventoryInfoByOuterCodeList" resultType="com.ruoyi.sip.domain.InventoryInfo">
<include refid="selectInventoryInfoVo"/>
where t1.outer_code in
<foreach item="item" index="index" collection="list" separator="," open="(" close=")">
#{item}
</foreach>
</select>
<insert id="insertInventoryInfo" parameterType="InventoryInfo" useGeneratedKeys="true" keyProperty="id">
@ -213,4 +220,4 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
)
</delete>
</mapper>
</mapper>

View File

@ -77,6 +77,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<include refid="selectInventoryOuterRelationVo"/>
<where>
<if test="outerCode != null and outerCode != ''">and t1.outer_code = #{outerCode}</if>
<if test="outerCodeList != null and outerCodeList.size>0">and t1.outer_code in
<foreach item="item" index="index" collection="outerCodeList" separator="," open="(" close=")">
#{item}
</foreach>
</if>
<if test="productCode != null and productCode != ''">and t1.product_code = #{productCode}</if>
<if test="productCodeList != null and productCodeList.size>0">and t1.product_code in
<foreach item="item" index="index" collection="productCodeList" separator="," open="(" close=")">