| File was renamed from rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiWarehouseSummaryService.java |
| | |
| | | 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; |
| | |
| | | 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<>(); |
| | | |
| | |
| | | @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) |
| | |
| | | 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 "暂无数据"; |
| | |
| | | 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) { |
| | |
| | | return String.join(";", parts); |
| | | } |
| | | |
| | | /** |
| | | * 格式化库存最多的物料列表。 |
| | | */ |
| | | private String formatTopMaterials(List<MaterialAggregate> rows) { |
| | | List<String> parts = new ArrayList<>(); |
| | | for (MaterialAggregate row : rows) { |
| | |
| | | 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; |
| | |
| | | private final Set<String> locCodes = new HashSet<>(); |
| | | } |
| | | } |
| | | |
| | | |
| | | |