diff --git a/api/erp-api/pom.xml b/api/erp-api/pom.xml index 37e28fb9..2feec11c 100644 --- a/api/erp-api/pom.xml +++ b/api/erp-api/pom.xml @@ -78,7 +78,11 @@ security 1.0 - + + com.squareup.okhttp3 + okhttp + 4.12.0 + diff --git a/api/erp-api/src/main/java/cn/qihangerp/erp/ErpApi.java b/api/erp-api/src/main/java/cn/qihangerp/erp/ErpApi.java index cd39f9c3..13766a53 100644 --- a/api/erp-api/src/main/java/cn/qihangerp/erp/ErpApi.java +++ b/api/erp-api/src/main/java/cn/qihangerp/erp/ErpApi.java @@ -21,6 +21,7 @@ public class ErpApi { { System.out.println( "Hello erp-api!" ); SpringApplication.run(ErpApi.class, args); + } @Bean @LoadBalanced diff --git a/api/erp-api/src/main/java/cn/qihangerp/erp/controller/ShopController.java b/api/erp-api/src/main/java/cn/qihangerp/erp/controller/ShopController.java index 17c2f3e7..a9e6b93a 100644 --- a/api/erp-api/src/main/java/cn/qihangerp/erp/controller/ShopController.java +++ b/api/erp-api/src/main/java/cn/qihangerp/erp/controller/ShopController.java @@ -6,6 +6,7 @@ import cn.qihangerp.module.service.OLogisticsCompanyService; import cn.qihangerp.module.service.OShopPlatformService; import cn.qihangerp.module.service.OShopService; import cn.qihangerp.erp.request.ShopBo; +import com.alibaba.fastjson2.JSONArray; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import cn.qihangerp.common.AjaxResult; import cn.qihangerp.common.TableDataInfo; @@ -14,7 +15,9 @@ import lombok.AllArgsConstructor; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * 店铺Controller @@ -30,6 +33,7 @@ public class ShopController extends BaseController { private final OShopService shopService; private final OShopPlatformService platformService; + /** * 查询店铺列表logistics */ diff --git a/api/erp-api/src/main/java/cn/qihangerp/erp/serviceImpl/DeepSeekService.java b/api/erp-api/src/main/java/cn/qihangerp/erp/serviceImpl/DeepSeekService.java new file mode 100644 index 00000000..4dddf36f --- /dev/null +++ b/api/erp-api/src/main/java/cn/qihangerp/erp/serviceImpl/DeepSeekService.java @@ -0,0 +1,365 @@ +//package cn.qihangerp.erp.serviceImpl; +// +//import com.fasterxml.jackson.databind.ObjectMapper; +//import jakarta.annotation.PostConstruct; +//import okhttp3.*; +//import org.slf4j.Logger; +//import org.slf4j.LoggerFactory; +//import org.springframework.beans.factory.annotation.Value; +//import org.springframework.stereotype.Service; +//import java.io.IOException; +//import java.util.*; +//import java.util.concurrent.TimeUnit; +// +//@Service +//public class DeepSeekService { +// +// private static final Logger log = LoggerFactory.getLogger(DeepSeekService.class); +// private static final MediaType JSON = MediaType.get("application/json; charset=utf-8"); +// +// @Value("${deepseek.api.key}") +// private String apiKey; +// +// @Value("${deepseek.api.endpoint:https://api.deepseek.com/v1/chat/completions}") +// private String apiEndpoint; +// +// @Value("${deepseek.api.model:deepseek-chat}") +// private String model; +// +// private OkHttpClient okHttpClient; +// private final ObjectMapper objectMapper; +// +// // 缓存最近一次成功的分析结果 +// private Map cachedAnalysis = new HashMap<>(); +// +// public DeepSeekService(ObjectMapper objectMapper) { +// this.objectMapper = objectMapper; +// } +// +// @PostConstruct +// public void init() { +// // 配置具有重试和连接池功能的OkHttpClient +// this.okHttpClient = new OkHttpClient.Builder() +// .connectTimeout(15, TimeUnit.SECONDS) // 连接超时 +// .readTimeout(30, TimeUnit.SECONDS) // 读取超时 +// .writeTimeout(15, TimeUnit.SECONDS) // 写入超时 +// .connectionPool(new ConnectionPool(5, 5, TimeUnit.MINUTES)) // 连接池 +// .addInterceptor(new RetryInterceptor(3)) // 自定义重试拦截器 +// .addInterceptor(new LoggingInterceptor()) // 日志拦截器 +// .build(); +// } +// +// /** +// * 调用DeepSeek API - 带有Spring Retry重试机制 +// */ +//// @Retryable( +//// value = {IOException.class, RuntimeException.class}, +//// maxAttempts = 3, +//// backoff = @Backoff(delay = 1000, multiplier = 2, maxDelay = 10000) +//// ) +// public Map analyzeData(Map formattedData, String analysisType) { +// String cacheKey = generateCacheKey(formattedData, analysisType); +// +// try { +// // 1. 构建请求体 +// String requestBody = buildRequestBody(formattedData, analysisType); +// RequestBody body = RequestBody.create(requestBody, JSON); +// +// // 2. 构建请求 +// Request request = new Request.Builder() +// .url(apiEndpoint) +// .header("Authorization", "Bearer " + apiKey) +// .header("Content-Type", "application/json") +// .post(body) +// .build(); +// +// // 3. 执行请求并处理响应 +// try (Response response = okHttpClient.newCall(request).execute()) { +// if (!response.isSuccessful()) { +// handleErrorResponse(response, cacheKey); +// } +// +// String responseBody = response.body().string(); +// Map result = parseResponse(responseBody, analysisType); +// +// // 缓存成功的结果 +// cacheSuccessfulResult(cacheKey, result); +// return result; +// } +// +// } catch (Exception e) { +// log.error("调用DeepSeek API失败,尝试使用缓存或降级方案", e); +// return getFallbackAnalysis(cacheKey, analysisType); +// } +// } +// +// /** +// * 为补货建议优化的专用方法 +// */ +// public Map generateReplenishmentSuggestions(Map inventoryData) { +// try { +// String prompt = buildReplenishmentPrompt(inventoryData); +// +// Map requestBody = Map.of( +// "model", model, +// "messages", List.of( +// Map.of("role", "system", "content", +// "你是一个经验丰富的电商库存管理专家,擅长制定补货策略。"), +// Map.of("role", "user", "content", prompt) +// ), +// "temperature", 0.2, +// "max_tokens", 1500, +// "response_format", Map.of("type", "json_object") +// ); +// +// String jsonBody = objectMapper.writeValueAsString(requestBody); +// +// Request request = new Request.Builder() +// .url(apiEndpoint) +// .header("Authorization", "Bearer " + apiKey) +// .post(RequestBody.create(jsonBody, JSON)) +// .build(); +// +// // 设置更短的超时时间,因为补货建议需要快速响应 +// OkHttpClient quickClient = okHttpClient.newBuilder() +// .readTimeout(15, TimeUnit.SECONDS) +// .build(); +// +// try (Response response = quickClient.newCall(request).execute()) { +// if (response.isSuccessful()) { +// String responseBody = response.body().string(); +// return parseReplenishmentResponse(responseBody); +// } else { +// // 如果API失败,使用本地算法生成补货建议 +// return generateLocalReplenishmentSuggestions(inventoryData); +// } +// } +// +// } catch (Exception e) { +// log.warn("AI补货建议失败,使用本地算法", e); +// return generateLocalReplenishmentSuggestions(inventoryData); +// } +// } +// +// /** +// * 本地补货算法 - 服务降级方案 +// */ +// private Map generateLocalReplenishmentSuggestions(Map inventoryData) { +// List> products = (List>) inventoryData.get("data"); +// List> suggestions = new ArrayList<>(); +// int totalQuantity = 0; +// double estimatedCost = 0.0; +// +// for (Map product : products) { +// String status = (String) product.get("inventoryStatus"); +// +// // 只处理需要补货的产品 +// if ("HEALTHY".equals(status) || "OVERSTOCK".equals(status)) { +// continue; +// } +// +// int currentStock = (int) product.getOrDefault("currentStock", 0); +// int safetyStock = (int) product.getOrDefault("safetyStock", 100); +// double avgDailySales = ((Integer) product.getOrDefault("avgDailySales", 10)).doubleValue(); +// double coverDays = (double) product.getOrDefault("coverDays", 0.0); +// +// Map suggestion = new HashMap<>(); +// suggestion.put("product_id", product.get("productId")); +// suggestion.put("product_name", product.get("productName")); +// +// // 本地补货逻辑 +// int suggestedQty; +// String urgency; +// +// if (currentStock <= 0) { +// suggestedQty = (int) (avgDailySales * 30); +// urgency = "紧急"; +// } else if (coverDays < 3) { +// suggestedQty = (int) (avgDailySales * 15 - currentStock); +// urgency = "高"; +// } else if (currentStock < safetyStock) { +// suggestedQty = safetyStock * 2 - currentStock; +// urgency = "中"; +// } else { +// suggestedQty = (int) (avgDailySales * 7); +// urgency = "低"; +// } +// +// suggestedQty = Math.max(suggestedQty, 10); +// totalQuantity += suggestedQty; +// +// suggestion.put("suggested_quantity", suggestedQty); +// suggestion.put("urgency", urgency); +// suggestion.put("reason", "本地算法计算"); +// suggestion.put("expected_cover_days", suggestedQty / Math.max(avgDailySales, 1)); +// +// suggestions.add(suggestion); +// } +// +// return Map.of( +// "success", true, +// "source", "local_algorithm", +// "replenishment_list", suggestions, +// "total_replenishment_quantity", totalQuantity, +// "analysis_summary", "基于本地规则生成的补货建议", +// "recommendations", List.of( +// "建议优先处理标记为'紧急'的商品", +// "此为本地降级方案,AI分析恢复后将提供更精确建议" +// ) +// ); +// } +// +// /** +// * 自定义重试拦截器 +// */ +// private static class RetryInterceptor implements Interceptor { +// private final int maxRetries; +// +// public RetryInterceptor(int maxRetries) { +// this.maxRetries = maxRetries; +// } +// +// @Override +// public Response intercept(Chain chain) throws IOException { +// Request request = chain.request(); +// Response response = null; +// IOException exception = null; +// +// // 重试逻辑 +// for (int retryCount = 0; retryCount <= maxRetries; retryCount++) { +// try { +// response = chain.proceed(request); +// +// // 只有服务器错误(5xx)或特定客户端错误才重试 +// if (response.isSuccessful() || +// (response.code() != 503 && response.code() != 429 && response.code() != 408)) { +// return response; +// } +// +// log.warn("API请求失败,状态码: {}, 重试: {}/{}", +// response.code(), retryCount, maxRetries); +// +// // 关闭响应体 +// response.close(); +// +// } catch (IOException e) { +// exception = e; +// log.warn("网络异常,重试: {}/{}", retryCount, maxRetries, e); +// } +// +// // 如果不是最后一次重试,等待一段时间 +// if (retryCount < maxRetries) { +// try { +// // 指数退避:1s, 2s, 4s... +// long waitTime = (long) Math.pow(2, retryCount) * 1000; +// Thread.sleep(waitTime); +// } catch (InterruptedException e) { +// Thread.currentThread().interrupt(); +// throw new IOException("重试被中断", e); +// } +// } +// } +// +// if (exception != null) { +// throw exception; +// } +// +// return response; +// } +// } +// +// /** +// * 日志拦截器 +// */ +// private static class LoggingInterceptor implements Interceptor { +// @Override +// public Response intercept(Chain chain) throws IOException { +// Request request = chain.request(); +// long startTime = System.currentTimeMillis(); +// +// log.debug("发送请求: {} {}", request.method(), request.url()); +// +// Response response; +// try { +// response = chain.proceed(request); +// } catch (IOException e) { +// long duration = System.currentTimeMillis() - startTime; +// log.error("请求失败: {} {} - 耗时: {}ms", +// request.method(), request.url(), duration, e); +// throw e; +// } +// +// long duration = System.currentTimeMillis() - startTime; +// log.info("收到响应: {} {} - 状态: {} - 耗时: {}ms", +// request.method(), request.url(), response.code(), duration); +// +// return response; +// } +// } +// +// /** +// * 错误处理 +// */ +// private void handleErrorResponse(Response response, String cacheKey) throws IOException { +// int code = response.code(); +// String errorBody = response.body() != null ? response.body().string() : "无错误详情"; +// +// log.error("DeepSeek API错误响应: 状态码={}, 错误信息={}", code, errorBody); +// +// // 根据错误类型处理 +// if (code == 401) { +// throw new RuntimeException("API密钥无效或已过期"); +// } else if (code == 429) { +// throw new RuntimeException("请求频率超限,请稍后重试"); +// } else if (code == 503) { +// // 服务不可用,尝试使用缓存 +// if (cachedAnalysis.containsKey(cacheKey)) { +// log.info("服务不可用,使用缓存结果"); +// throw new ServiceUnavailableException("服务不可用,已返回缓存结果"); +// } +// throw new RuntimeException("DeepSeek服务暂时不可用,请稍后重试"); +// } else { +// throw new RuntimeException(String.format("API请求失败: %d - %s", code, errorBody)); +// } +// } +// +// /** +// * 服务降级:获取缓存或基础分析 +// */ +// private Map getFallbackAnalysis(String cacheKey, String analysisType) { +// // 1. 首先尝试缓存 +// if (cachedAnalysis.containsKey(cacheKey)) { +// log.info("使用缓存的AI分析结果"); +// Map cached = (Map) cachedAnalysis.get(cacheKey); +// cached.put("source", "cached"); +// return cached; +// } +// +// // 2. 返回基础分析模板 +// log.info("返回基础分析模板"); +// return Map.of( +// "success", false, +// "source", "fallback_template", +// "message", "AI分析服务暂时不可用", +// "basic_analysis", Map.of( +// "suggestion", "建议检查库存水平,重点关注缺货商品", +// "generated_at", new Date() +// ), +// "recommendations", List.of( +// "1. 优先处理库存为0的商品", +// "2. 检查日销量高但库存低的商品", +// "3. AI服务恢复后重新获取详细分析" +// ) +// ); +// } +// +// // 其他辅助方法(buildRequestBody, parseResponse等)保持原有逻辑 +// // ... +//} +// +//// 自定义异常类 +//class ServiceUnavailableException extends RuntimeException { +// public ServiceUnavailableException(String message) { +// super(message); +// } +//} \ No newline at end of file diff --git a/api/erp-api/src/main/java/cn/qihangerp/erp/serviceImpl/InventorySalesAnalyzer.java b/api/erp-api/src/main/java/cn/qihangerp/erp/serviceImpl/InventorySalesAnalyzer.java new file mode 100644 index 00000000..ef4c5154 --- /dev/null +++ b/api/erp-api/src/main/java/cn/qihangerp/erp/serviceImpl/InventorySalesAnalyzer.java @@ -0,0 +1,273 @@ +package cn.qihangerp.erp.serviceImpl; + +import com.fasterxml.jackson.annotation.JsonProperty; +import okhttp3.*; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; + +public class InventorySalesAnalyzer { + + // 配置你的 DeepSeek API 信息 + private static final String API_KEY = "sk-e1f3aecc45e44eca9451d5a659a4bc91"; + private static final String API_URL = "https://api.deepseek.com/v1/chat/completions"; + + private static final MediaType JSON = MediaType.get("application/json; charset=utf-8"); + private static final OkHttpClient client = new OkHttpClient.Builder() + .connectTimeout(30, java.util.concurrent.TimeUnit.SECONDS) + .readTimeout(60, java.util.concurrent.TimeUnit.SECONDS) + .build(); + private static final ObjectMapper objectMapper = new ObjectMapper(); + + // 你的数据 + private static final String INVENTORY_JSON = "[{\"id\":1,\"goods_title\":\"雷士照明led吸顶灯灯芯替换圆形灯板节能灯芯冷光高显护眼健康\",\"sku_name\":\"白光12W\",\"stock_num\":12},{\"id\":2,\"goods_title\":\"雷士照明led吸顶灯灯芯替换圆形灯板节能灯芯冷光高显护眼健康\",\"sku_name\":\"白光18W\",\"stock_num\":12},{\"id\":3,\"goods_title\":\"雷士照明led吸顶灯灯芯替换圆形灯板节能灯芯冷光高显护眼健康\",\"sku_name\":\"白光24W\",\"stock_num\":12},{\"id\":4,\"goods_title\":\"雷士照明led吸顶灯灯芯替换圆形灯板节能灯芯冷光高显护眼健康\",\"sku_name\":\"双色36W\",\"stock_num\":12}]"; + + private static final String SALES_JSON = "[{\"order_num\":\"1\",\"sku_id\":1,\"count\":1,\"item_amount\":29.32,\"order_time\":\"2025-05-24 23:19:51\"},{\"order_num\":\"1\",\"sku_id\":3,\"count\":1,\"item_amount\":29.32,\"order_time\":\"2025-05-24 23:19:51\"},{\"order_num\":\"1\",\"sku_id\":1,\"count\":1,\"item_amount\":29.32,\"order_time\":\"2025-05-24 23:19:51\"},{\"order_num\":\"1\",\"sku_id\":2,\"count\":1,\"item_amount\":29.32,\"order_time\":\"2025-05-24 23:19:51\"},{\"order_num\":\"1\",\"sku_id\":4,\"count\":1,\"item_amount\":29.32,\"order_time\":\"2025-05-24 23:19:51\"},{\"order_num\":\"1\",\"sku_id\":1,\"count\":1,\"item_amount\":29.32,\"order_time\":\"2025-05-24 23:19:51\"},{\"order_num\":\"1\",\"sku_id\":1,\"count\":1,\"item_amount\":29.32,\"order_time\":\"2025-05-24 23:19:51\"},{\"order_num\":\"1\",\"sku_id\":3,\"count\":1,\"item_amount\":29.32,\"order_time\":\"2025-05-24 23:19:51\"},{\"order_num\":\"1\",\"sku_id\":1,\"count\":1,\"item_amount\":29.32,\"order_time\":\"2025-05-24 23:19:51\"},{\"order_num\":\"1\",\"sku_id\":1,\"count\":1,\"item_amount\":29.32,\"order_time\":\"2025-05-24 23:19:51\"},{\"order_num\":\"1\",\"sku_id\":2,\"count\":1,\"item_amount\":29.32,\"order_time\":\"2025-05-24 23:19:51\"}]"; + + public static void main(String[] args) { + try { + System.out.println("开始分析库存与销售数据...\n"); + + // 1. 解析数据 + List inventoryList = parseInventoryData(); + List salesList = parseSalesData(); + + // 2. 分析数据并生成报告 + String analysisResult = analyzeInventoryAndSales(inventoryList, salesList); + + System.out.println("=== AI 分析报告 ===\n"); + System.out.println(analysisResult); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 核心分析方法 + */ + public static String analyzeInventoryAndSales(List inventory, + List sales) throws IOException { + + // 1. 数据预处理:按 SKU ID 关联库存和销售数据 + Map analysisMap = new HashMap<>(); + + // 初始化库存数据 + for (InventoryItem item : inventory) { + SkuAnalysis analysis = new SkuAnalysis(); + analysis.id = item.id; + analysis.goodsTitle = item.goodsTitle; + analysis.skuName = item.skuName; + analysis.stockNum = item.stockNum; + analysisMap.put(item.id, analysis); + } + + // 统计销售数据 + for (SalesOrder order : sales) { + if (analysisMap.containsKey(order.skuId)) { + SkuAnalysis analysis = analysisMap.get(order.skuId); + analysis.totalSales += order.count; + analysis.totalRevenue += order.itemAmount; + analysis.orderCount++; + + // 记录销售时间(用于趋势分析) + analysis.salesTimes.add(order.orderTime); + } + } + + // 2. 计算关键指标 + for (SkuAnalysis analysis : analysisMap.values()) { + // 计算日均销量(假设数据是最近30天的) + analysis.dailyAvgSales = analysis.totalSales / 30.0; + + // 计算可售天数 + if (analysis.dailyAvgSales > 0) { + analysis.daysOfSupply = analysis.stockNum / analysis.dailyAvgSales; + } else { + analysis.daysOfSupply = 999; // 无销售 + } + + // 判断库存状态 + analysis.stockStatus = determineStockStatus(analysis.stockNum, analysis.dailyAvgSales); + } + + // 3. 构建 AI 分析提示词 + String prompt = buildAnalysisPrompt(analysisMap); + + // 4. 调用 DeepSeek API + return callDeepSeekAPI(prompt); + } + + /** + * 构建 AI 分析提示词 + */ + private static String buildAnalysisPrompt(Map analysisMap) { + StringBuilder prompt = new StringBuilder(); + + prompt.append("你是一名专业的电商库存管理专家。请分析以下 LED 灯具产品的库存与销售数据,并提供专业的分析报告和建议:\n\n"); + + prompt.append("=== 数据概览 ===\n"); + prompt.append("产品名称:雷士照明 LED 吸顶灯灯芯\n"); + prompt.append("分析时间:").append(new Date()).append("\n\n"); + + prompt.append("=== 详细数据 ===\n"); + prompt.append(String.format("%-8s %-12s %-8s %-8s %-12s %-10s %-15s\n", + "SKU ID", "规格", "库存量", "总销量", "总销售额", "可售天数", "库存状态")); + prompt.append("-".repeat(80)).append("\n"); + + for (SkuAnalysis analysis : analysisMap.values()) { + prompt.append(String.format("%-8d %-12s %-8d %-8d %-12.2f %-10.1f %-15s\n", + analysis.id, + analysis.skuName, + analysis.stockNum, + analysis.totalSales, + analysis.totalRevenue, + analysis.daysOfSupply, + analysis.stockStatus + )); + } + + prompt.append("\n=== 分析要求 ===\n"); + prompt.append("请基于以上数据,提供以下分析:\n"); + prompt.append("1. **库存健康度分析**:评估每个SKU的库存状况,识别缺货风险\n"); + prompt.append("2. **销售表现分析**:分析各规格产品的销售情况,找出畅销款和滞销款\n"); + prompt.append("3. **补货建议**:\n"); + prompt.append(" - 哪些SKU需要立即补货?建议补货数量?\n"); + prompt.append(" - 哪些SKU库存过多?建议如何清理?\n"); + prompt.append(" - 建议的安全库存水平\n"); + prompt.append("4. **运营建议**:基于销售模式,给出采购、促销或产品组合建议\n\n"); + + prompt.append("请以专业报告格式回复,包含具体数据和理由。"); + + return prompt.toString(); + } + + /** + * 调用 DeepSeek API + */ + private static String callDeepSeekAPI(String prompt) throws IOException { + // 构建请求体 + Map requestBody = new HashMap<>(); + requestBody.put("model", "deepseek-chat"); + requestBody.put("messages", Arrays.asList( + Map.of("role", "user", "content", prompt) + )); + requestBody.put("temperature", 0.3); // 降低随机性,使分析更稳定 + requestBody.put("max_tokens", 2000); + + String jsonBody = objectMapper.writeValueAsString(requestBody); + + // 创建请求 + Request request = new Request.Builder() + .url(API_URL) + .header("Authorization", "Bearer " + API_KEY) + .header("Content-Type", "application/json") + .post(RequestBody.create(jsonBody, JSON)) + .build(); + + // 发送请求(带重试机制) + for (int attempt = 0; attempt < 3; attempt++) { + try (Response response = client.newCall(request).execute()) { + if (response.isSuccessful()) { + String responseBody = response.body().string(); + return extractContentFromResponse(responseBody); + } else if (response.code() == 429 || response.code() >= 500) { + // 频率限制或服务器错误,等待后重试 + System.out.println("请求失败,状态码: " + response.code() + ",等待重试..."); + Thread.sleep(2000 * (attempt + 1)); + continue; + } else { + throw new IOException("API请求失败: " + response.code() + " - " + response.message()); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException("请求被中断", e); + } + } + + throw new IOException("API请求失败,已重试3次"); + } + + /** + * 从 API 响应中提取内容 + */ + private static String extractContentFromResponse(String responseBody) throws IOException { + Map responseMap = objectMapper.readValue(responseBody, + new TypeReference>() {}); + + List> choices = (List>) responseMap.get("choices"); + if (choices != null && !choices.isEmpty()) { + Map choice = choices.get(0); + Map message = (Map) choice.get("message"); + return (String) message.get("content"); + } + + return "未获取到有效回复"; + } + + /** + * 判断库存状态 + */ + private static String determineStockStatus(int stock, double dailySales) { + if (stock == 0) return "缺货"; + if (dailySales == 0) return "滞销"; + + double daysOfSupply = stock / dailySales; + + if (daysOfSupply < 7) return "急需补货"; + if (daysOfSupply < 14) return "需要补货"; + if (daysOfSupply < 30) return "库存正常"; + if (daysOfSupply < 60) return "库存偏高"; + return "库存积压"; + } + + // 数据解析方法 + private static List parseInventoryData() throws IOException { + return objectMapper.readValue(INVENTORY_JSON, + new TypeReference>() {}); + } + + private static List parseSalesData() throws IOException { + return objectMapper.readValue(SALES_JSON, + new TypeReference>() {}); + } + + // 数据类定义 + static class InventoryItem { + public int id; + @JsonProperty("goods_title") + public String goodsTitle; + @JsonProperty("sku_name") + public String skuName; + @JsonProperty("stock_num") + public int stockNum; + } + + static class SalesOrder { + @JsonProperty("order_num") + public String orderNum; + @JsonProperty("sku_id") + public int skuId; + public int count; + @JsonProperty("item_amount") + public double itemAmount; + @JsonProperty("order_time") + public String orderTime; + } + + static class SkuAnalysis { + public int id; + public String goodsTitle; + public String skuName; + public int stockNum; + public int totalSales = 0; + public double totalRevenue = 0.0; + public int orderCount = 0; + public double dailyAvgSales = 0.0; + public double daysOfSupply = 0.0; + public String stockStatus = "未知"; + public List salesTimes = new ArrayList<>(); + } +} \ No newline at end of file diff --git a/api/erp-api/src/main/resources/application.yml b/api/erp-api/src/main/resources/application.yml index 633e71fa..23ad8053 100644 --- a/api/erp-api/src/main/resources/application.yml +++ b/api/erp-api/src/main/resources/application.yml @@ -92,4 +92,10 @@ mybatis-plus: mapper-locations: classpath*:mapper/**/*Mapper.xml type-aliases-package: cn.qihangerp.oms.domain;cn.qihangerp.module.domain;cn.qihangerp.security.entity; # configuration: -# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开启sql日志 \ No newline at end of file +# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开启sql日志 + +deepseek: + api: + key: + endpoint: https://api.deepseek.com/chat/completions + model: deepseek-chat \ No newline at end of file diff --git a/api/oms-api/libs/open-sdk-2.1.11.jar b/api/oms-api/libs/open-sdk-2.1.11.jar deleted file mode 100644 index 1a298475..00000000 Binary files a/api/oms-api/libs/open-sdk-2.1.11.jar and /dev/null differ diff --git a/api/sys-api/src/main/java/cn/qihangerp/sys/controller/SysLoginController.java b/api/sys-api/src/main/java/cn/qihangerp/sys/controller/SysLoginController.java index 1e73e7bf..015b8724 100644 --- a/api/sys-api/src/main/java/cn/qihangerp/sys/controller/SysLoginController.java +++ b/api/sys-api/src/main/java/cn/qihangerp/sys/controller/SysLoginController.java @@ -51,7 +51,7 @@ public class SysLoginController ajax.put(Constants.TOKEN, token); return ajax; }catch (Exception e){ - return AjaxResult.error(e.getMessage()); + return AjaxResult.error(500,e.getMessage()); } }