#AI
zhou zhou
6 小时以前 51877df13075ad10ef51107f15bcd21f1661febe
rsf-server/src/main/java/com/vincent/rsf/server/ai/service/provider/AiWarehouseSummaryService.java
File was renamed from rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiWarehouseSummaryService.java
@@ -1,6 +1,7 @@
package com.vincent.rsf.server.ai.service;
package com.vincent.rsf.server.ai.service.provider;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.vincent.rsf.server.ai.model.AiDiagnosticToolResult;
import com.vincent.rsf.server.ai.model.AiPromptContext;
import com.vincent.rsf.server.manager.entity.Loc;
import com.vincent.rsf.server.manager.entity.LocItem;
@@ -13,7 +14,10 @@
import java.util.*;
@Service
public class AiWarehouseSummaryService implements AiPromptContextProvider {
public class AiWarehouseSummaryService implements AiDiagnosticDataProvider {
    private static final String TOOL_CODE = "warehouse_summary";
    private static final String TOOL_NAME = "库存摘要";
    private static final Map<String, String> LOC_STATUS_LABELS = new LinkedHashMap<>();
@@ -31,16 +35,54 @@
    @Resource
    private LocItemMapper locItemMapper;
    /**
     * 返回库存类内部工具的默认顺序。
     */
    @Override
    public boolean supports(AiPromptContext context) {
        return context != null && shouldSummarize(context.getQuestion());
    public int getOrder() {
        return 10;
    }
    /**
     * 返回库存工具编码。
     */
    @Override
    public String buildContext(AiPromptContext context) {
        if (!supports(context)) {
            return "";
    public String getToolCode() {
        return TOOL_CODE;
        }
    /**
     * 返回库存工具展示名。
     */
    @Override
    public String getToolName() {
        return TOOL_NAME;
    }
    /**
     * 返回库存工具默认说明。
     */
    @Override
    public String getDefaultToolPrompt() {
        return "结合库存摘要判断库位状态、库存结构与重点物料分布。";
    }
    /**
     * 汇总库位与库存明细,生成库存摘要工具结果。
     */
    @Override
    public AiDiagnosticToolResult buildDiagnosticData(AiPromptContext context) {
        return new AiDiagnosticToolResult()
                .setToolCode(getToolCode())
                .setToolName(getToolName())
                .setSeverity("INFO")
                .setSummaryText(buildWarehouseSummary(context));
    }
    /**
     * 基于 man_loc 和 man_loc_item 生成库存概览、库位状态分布和 TOP 统计。
     */
    private String buildWarehouseSummary(AiPromptContext context) {
        List<Loc> activeLocs = locMapper.selectList(new LambdaQueryWrapper<Loc>()
                .select(Loc::getUseStatus)
@@ -134,21 +176,9 @@
        return summary.toString();
    }
    private boolean shouldSummarize(String question) {
        if (question == null || question.trim().isEmpty()) {
            return false;
        }
        String normalized = question.toLowerCase(Locale.ROOT);
        return normalized.contains("loc")
                || normalized.contains("库位")
                || normalized.contains("货位")
                || normalized.contains("库区")
                || normalized.contains("库存")
                || normalized.contains("物料")
                || normalized.contains("巷道")
                || normalized.contains("储位");
    }
    /**
     * 将库位状态计数格式化为可读文本。
     */
    private String formatLocStatuses(Map<String, Long> counters) {
        if (counters == null || counters.isEmpty()) {
            return "暂无数据";
@@ -160,6 +190,9 @@
        return String.join(",", parts);
    }
    /**
     * 格式化库存最多的库位列表。
     */
    private String formatTopLocs(List<Map.Entry<String, LocAggregate>> rows) {
        List<String> parts = new ArrayList<>();
        for (Map.Entry<String, LocAggregate> row : rows) {
@@ -170,6 +203,9 @@
        return String.join(";", parts);
    }
    /**
     * 格式化库存最多的物料列表。
     */
    private String formatTopMaterials(List<MaterialAggregate> rows) {
        List<String> parts = new ArrayList<>();
        for (MaterialAggregate row : rows) {
@@ -183,11 +219,17 @@
        return String.join(";", parts);
    }
    /**
     * 统一格式化数量值。
     */
    private String formatDecimal(Object value) {
        BigDecimal decimal = toDecimal(value);
        return decimal.stripTrailingZeros().toPlainString();
    }
    /**
     * 将不同类型的数量字段统一转换为 BigDecimal。
     */
    private BigDecimal toDecimal(Object value) {
        if (value == null) {
            return BigDecimal.ZERO;
@@ -213,3 +255,6 @@
        private final Set<String> locCodes = new HashSet<>();
    }
}