| | |
| | | package com.ruoyi.mock.prompt; |
| | | |
| | | import com.ruoyi.project.system.domain.SysUser; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | |
| | | |
| | | public static String buildSystemPrompt() { |
| | | return """ |
| | | 你是一个企业ERP系统的数据模拟专家。你需要根据用户提供的行业、数量、时间范围等信息, |
| | | 生成符合业务逻辑的模拟数据。 |
| | | 你是一个企业ERP系统的数据模拟专家。 |
| | | |
| | | 要求: |
| | | 核心规则(必须严格遵守): |
| | | 1. 输出必须是纯JSON数组,不要用markdown代码块包裹,不要有任何其他文字 |
| | | 2. 每个JSON对象必须包含 "entity" 字段,值为实体类型名 |
| | | 3. 数据内容要符合指定行业的特征(公司名称、产品名称、联系人等要像该行业的) |
| | | 4. 日期字段在指定的时间范围内随机分布 |
| | | 5. 金额、数量等数值字段要合理 |
| | | 6. 同一模块内的实体之间要有引用关系(如销售台账引用客户名称) |
| | | 7. 所有字符串字段不要使用emoji或特殊unicode字符 |
| | | 3. 数据内容要符合指定行业的特征 |
| | | 4. 金额、数量等数值字段要合理 |
| | | 5. 同一模块内的实体之间要有引用关系 |
| | | 6. 不要使用emoji或特殊unicode字符 |
| | | 7. 最重要:日期只从"指定时间范围"中取,绝对不要用其他年份! |
| | | 8. 人员字段(如entryPerson、salesman等)必须从"系统用户列表"中选取,根据角色和部门匹配对应模块,不要编造人名! |
| | | """; |
| | | } |
| | | |
| | |
| | | int countMin, int countMax, |
| | | String dateStart, String dateEnd, |
| | | String additionalInfo, |
| | | List<Long> productIds, |
| | | List<Long> productModelIds) { |
| | | Map<Long, String> productModelIdToCategory, |
| | | List<SysUser> systemUsers, |
| | | List<String> existingCustomerNames, |
| | | List<String> existingSupplierNames) { |
| | | StringBuilder sb = new StringBuilder(); |
| | | sb.append("请为以下行业生成模拟ERP业务数据:\n"); |
| | | sb.append("- 行业: ").append(String.join("、", industries)).append("\n"); |
| | | sb.append("- 数据条数: 每种实体 ").append(countMin).append("-").append(countMax).append(" 条\n"); |
| | | sb.append("- 时间范围: ").append(dateStart).append(" ~ ").append(dateEnd).append("\n"); |
| | | sb.append("指定行业: ").append(String.join("、", industries)).append("\n"); |
| | | sb.append("每种实体生成: ").append(countMin).append("-").append(countMax).append(" 条\n"); |
| | | sb.append("指定时间范围: ").append(dateStart).append(" 到 ").append(dateEnd); |
| | | sb.append("(所有日期字段必须在这个范围内!禁止使用2023、2024、2025等任何超出范围的年份!)\n"); |
| | | if (additionalInfo != null && !additionalInfo.isBlank()) { |
| | | sb.append("- 补充信息: ").append(additionalInfo).append("\n"); |
| | | sb.append("补充信息: ").append(additionalInfo).append("\n"); |
| | | } |
| | | sb.append("\n"); |
| | | |
| | | sb.append("可用的产品ID列表: ").append(productIds.stream().map(String::valueOf).collect(Collectors.joining(","))).append("\n"); |
| | | sb.append("可用的产品规格ID列表: ").append(productModelIds.stream().map(String::valueOf).collect(Collectors.joining(","))).append("\n"); |
| | | sb.append("\n"); |
| | | // 按顶级产品分类(成品/原材料/半成品)分组列出产品规格 |
| | | Map<String, List<Map.Entry<Long, String>>> grouped = productModelIdToCategory.entrySet().stream() |
| | | .collect(Collectors.groupingBy( |
| | | e -> categorize(e.getValue()), |
| | | java.util.LinkedHashMap::new, |
| | | Collectors.toList())); |
| | | |
| | | sb.append("需要生成的模块: ").append(String.join("、", modules)).append("\n\n"); |
| | | sb.append("=== 产品规格列表 ===\n"); |
| | | for (Map.Entry<String, List<Map.Entry<Long, String>>> entry : grouped.entrySet()) { |
| | | String label = entry.getKey(); |
| | | List<Map.Entry<Long, String>> items = entry.getValue(); |
| | | sb.append("【").append(label).append("】: "); |
| | | sb.append(items.stream() |
| | | .map(e -> e.getKey() + "(" + e.getValue() + ")") |
| | | .collect(Collectors.joining(", "))).append("\n"); |
| | | } |
| | | sb.append("销售台账的productData只能选【成品类】规格,采购台账只能选【原材料类】规格。\n\n"); |
| | | |
| | | // 系统用户列表(含角色和部门) |
| | | sb.append("=== 系统用户列表(人员字段必须从中选取,不要编造人名)===\n"); |
| | | for (SysUser u : systemUsers) { |
| | | String roleNames = u.getRoleNames() != null && !u.getRoleNames().isBlank() ? u.getRoleNames() : "无角色"; |
| | | String deptNames = u.getDeptNames() != null && !u.getDeptNames().isBlank() ? u.getDeptNames() : "无部门"; |
| | | sb.append(u.getNickName()).append("(角色: ").append(roleNames).append(", 部门: ").append(deptNames).append(")\n"); |
| | | } |
| | | sb.append("选择规则:\n"); |
| | | sb.append("- 销售模块的人员(entryPerson、salesman)选角色或部门含\"销售\"的用户\n"); |
| | | sb.append("- 采购模块的人员选角色或部门含\"采购\"的用户\n"); |
| | | sb.append("- 生产模块的人员选角色或部门含\"生产\"的用户\n"); |
| | | sb.append("- 质量模块的人员选角色或部门含\"质量\"或\"质检\"的用户\n"); |
| | | sb.append("- 仓库模块的人员选角色或部门含\"仓库\"或\"库存\"的用户\n\n"); |
| | | |
| | | if (!existingCustomerNames.isEmpty()) { |
| | | sb.append("=== 已有客户(只能引用,不要生成新customer实体)===\n"); |
| | | sb.append(String.join("、", existingCustomerNames)).append("\n\n"); |
| | | } |
| | | if (!existingSupplierNames.isEmpty()) { |
| | | sb.append("=== 已有供应商(只能引用,不要生成新supplier实体)===\n"); |
| | | sb.append(String.join("、", existingSupplierNames)).append("\n\n"); |
| | | } |
| | | |
| | | sb.append("数据模块: ").append(String.join("、", modules)).append("\n\n"); |
| | | |
| | | if (modules.contains("sales")) { |
| | | sb.append(buildSalesPrompt(countMin, countMax, dateStart, dateEnd)); |
| | | sb.append(buildSalesPrompt(countMin, countMax, dateStart, dateEnd, existingCustomerNames)); |
| | | } |
| | | if (modules.contains("purchase")) { |
| | | sb.append(buildPurchasePrompt(countMin, countMax, dateStart, dateEnd)); |
| | | sb.append(buildPurchasePrompt(countMin, countMax, dateStart, dateEnd, existingSupplierNames)); |
| | | } |
| | | if (modules.contains("quality")) { |
| | | sb.append(buildQualityPrompt(countMin, countMax)); |
| | | sb.append(buildQualityPrompt(countMin, countMax, dateStart)); |
| | | } |
| | | if (modules.contains("production")) { |
| | | sb.append(buildProductionPrompt(countMin, countMax, dateStart, dateEnd)); |
| | | } |
| | | if (modules.contains("stock")) { |
| | | sb.append(buildStockPrompt(countMin, countMax)); |
| | | sb.append(buildStockPrompt(countMin, countMax, dateStart)); |
| | | } |
| | | |
| | | sb.append("\n请直接输出JSON数组,不要有任何其他内容:"); |
| | | sb.append("\n请直接输出JSON数组:"); |
| | | return sb.toString(); |
| | | } |
| | | |
| | | private static String buildSalesPrompt(int min, int max, String dateStart, String dateEnd) { |
| | | /** |
| | | * 将顶级产品名称映射为分类标签 |
| | | */ |
| | | private static String categorize(String rootProductName) { |
| | | if (rootProductName == null) return "其他类"; |
| | | if (rootProductName.contains("成品") || rootProductName.contains("产成品")) return "成品类-销售优先选这些"; |
| | | if (rootProductName.contains("原料") || rootProductName.contains("原材料") || rootProductName.contains("材料")) return "原材料类-采购优先选这些"; |
| | | if (rootProductName.contains("半成品")) return "半成品类"; |
| | | return "其他类"; |
| | | } |
| | | |
| | | private static String buildSalesPrompt(int min, int max, String dateStart, String dateEnd, |
| | | List<String> existingCustomerNames) { |
| | | String namesHint = existingCustomerNames.isEmpty() ? "" |
| | | : "customerName必须从以下已有客户中选择: " + String.join("、", existingCustomerNames) + "\n"; |
| | | return """ |
| | | 销售模块 - 按以下格式生成: |
| | | { |
| | | "entity": "customer", |
| | | "customerName": "XX科技有限公司", |
| | | "customerType": "企业客户", |
| | | "contactPerson": "张三", |
| | | "contactPhone": "13800138000", |
| | | "companyAddress": "XX省XX市XX区XX路XX号", |
| | | "taxpayerIdentificationNumber": "91110108XXXXXXXXXX" |
| | | } |
| | | 销售模块格式(所有日期必须在 %s ~ %s 范围内): |
| | | %s注意:不要生成customer实体,只生成salesLedger! |
| | | productData中的productModelId必须选【成品类】规格ID! |
| | | entryPerson和salesman必须从系统用户列表中角色或部门含"销售"的用户中选取! |
| | | { |
| | | "entity": "salesLedger", |
| | | "customerName": "引用上面生成的客户名称", |
| | | "salesContractNo": "XS-20260601-001", |
| | | "salesContractNo": "XS-YYYYMMDD-001", |
| | | "projectName": "XX项目", |
| | | "entryDate": "2026-06-01", |
| | | "salesman": "销售员姓名", |
| | | "entryDate": "%s", |
| | | "entryPerson": "从用户列表中选", |
| | | "salesman": "从用户列表中选", |
| | | "contractAmount": 50000.00, |
| | | "paymentMethod": "月结30天", |
| | | "executionDate": "2026-06-01", |
| | | "deliveryDate": "2026-07-01", |
| | | "executionDate": "%s", |
| | | "deliveryDate": "%s", |
| | | "type": 1, |
| | | "productData": [ |
| | | { |
| | | "productId": 1, |
| | | "productModelId": 1, |
| | | "quantity": 100, |
| | | "taxInclusiveUnitPrice": 500.00, |
| | | "taxInclusiveTotalPrice": 50000.00, |
| | | "taxRate": 13.00, |
| | | "unit": "件", |
| | | "type": 1 |
| | | } |
| | | ] |
| | | "productData": [{ |
| | | "productId": 1, "productModelId": 1, "quantity": 100, |
| | | "taxInclusiveUnitPrice": 500.00, "taxInclusiveTotalPrice": 50000.00, |
| | | "taxExclusiveTotalPrice": 44247.79, "taxRate": 13.00, "unit": "件", "type": 1 |
| | | }] |
| | | } |
| | | 客户名称要像指定行业的公司,合同编号格式XS-年月日-序号,金额合理。 |
| | | 日期范围: %s ~ %s,每种实体生成%d-%d条。 |
| | | """.formatted(dateStart, dateEnd, min, max); |
| | | salesContractNo中YYYYMMDD替换为entryDate的日期。%d-%d条。 |
| | | """.formatted(dateStart, dateEnd, namesHint, dateStart, dateStart, dateEnd, min, max); |
| | | } |
| | | |
| | | private static String buildPurchasePrompt(int min, int max, String dateStart, String dateEnd) { |
| | | private static String buildPurchasePrompt(int min, int max, String dateStart, String dateEnd, |
| | | List<String> existingSupplierNames) { |
| | | String namesHint = existingSupplierNames.isEmpty() ? "" |
| | | : "supplierName必须从以下已有供应商中选择: " + String.join("、", existingSupplierNames) + "\n"; |
| | | return """ |
| | | 采购模块 - 按以下格式生成: |
| | | { |
| | | "entity": "supplier", |
| | | "supplierName": "XX原材料有限公司", |
| | | "supplierType": "原材料供应商", |
| | | "contactUserName": "李四", |
| | | "contactUserPhone": "13900139000", |
| | | "companyAddress": "XX省XX市XX区XX路XX号", |
| | | "taxpayerIdentificationNum": "91110108XXXXXXXXXX", |
| | | "bankAccountName": "XX银行XX支行", |
| | | "bankAccountNum": "622202XXXXXXXXXXXX", |
| | | "isWhite": 0 |
| | | } |
| | | 采购模块格式(所有日期必须在 %s ~ %s 范围内): |
| | | %s注意:不要生成supplier实体,只生成purchaseLedger! |
| | | productData中的productModelId必须选【原材料类】规格ID! |
| | | 人员字段必须从系统用户列表中角色或部门含"采购"的用户中选取! |
| | | { |
| | | "entity": "purchaseLedger", |
| | | "supplierName": "引用上面生成的供应商名称", |
| | | "purchaseContractNumber": "CG-20260601-001", |
| | | "supplierName": "从已有供应商中选择", |
| | | "purchaseContractNumber": "CG-YYYYMMDD-001", |
| | | "projectName": "XX采购项目", |
| | | "entryDate": "2026-06-01", |
| | | "entryDate": "%s", |
| | | "contractAmount": 30000.00, |
| | | "paymentMethod": "货到付款", |
| | | "executionDate": "2026-06-01", |
| | | "productData": [ |
| | | { |
| | | "productId": 1, |
| | | "productModelId": 1, |
| | | "quantity": 50, |
| | | "taxInclusiveUnitPrice": 600.00, |
| | | "taxInclusiveTotalPrice": 30000.00, |
| | | "taxRate": 13.00, |
| | | "unit": "件", |
| | | "type": 2 |
| | | } |
| | | ] |
| | | "executionDate": "%s", |
| | | "productData": [{ |
| | | "productId": 1, "productModelId": 1, "quantity": 50, |
| | | "taxInclusiveUnitPrice": 600.00, "taxInclusiveTotalPrice": 30000.00, |
| | | "taxExclusiveTotalPrice": 26548.67, "taxRate": 13.00, "unit": "件", "type": 2 |
| | | }] |
| | | } |
| | | 供应商名称要像指定行业的供应商,合同编号格式CG-年月日-序号。 |
| | | 日期范围: %s ~ %s,每种实体生成%d-%d条。 |
| | | """.formatted(dateStart, dateEnd, min, max); |
| | | purchaseContractNumber中YYYYMMDD替换为entryDate的日期。%d-%d条。 |
| | | """.formatted(dateStart, dateEnd, namesHint, dateStart, dateStart, min, max); |
| | | } |
| | | |
| | | private static String buildQualityPrompt(int min, int max) { |
| | | private static String buildQualityPrompt(int min, int max, String dateStart) { |
| | | return """ |
| | | 质量模块 - 按以下格式生成: |
| | | 质量模块格式: |
| | | { |
| | | "entity": "qualityTestStandard", |
| | | "standardNo": "QTS-20260601-001", |
| | | "standardNo": "QTS-YYYYMMDD-001", |
| | | "standardName": "XX产品检验标准", |
| | | "inspectType": 0, |
| | | "remark": "适用于XX行业的质量检验标准" |
| | | } |
| | | inspectType: 0=原材料检验, 1=过程检验, 2=出厂检验。三种类型都要覆盖。 |
| | | standardNo中YYYYMMDD替换为 %s。%d-%d条。 |
| | | { |
| | | "entity": "qualityTestStandardBinding", |
| | | "productId": 1, |
| | | "testStandardId": 1 |
| | | } |
| | | 每种实体生成%d-%d条。 |
| | | """.formatted(min, max); |
| | | %d-%d条。 |
| | | """.formatted(dateStart.replace("-", ""), min, max, min, max); |
| | | } |
| | | |
| | | private static String buildProductionPrompt(int min, int max, String dateStart, String dateEnd) { |
| | | return """ |
| | | 生产模块 - 按以下格式生成: |
| | | 生产模块格式(所有日期必须在 %s ~ %s 范围内): |
| | | { |
| | | "entity": "productionPlan", |
| | | "productModelId": 1, |
| | | "qtyRequired": 200, |
| | | "requiredDate": "2026-06-15", |
| | | "requiredDate": "%s", |
| | | "source": "销售", |
| | | "promisedDeliveryDate": "2026-07-01", |
| | | "promisedDeliveryDate": "%s", |
| | | "remark": "XX客户订单需求" |
| | | } |
| | | { |
| | | "entity": "productionOrder", |
| | | "productModelId": 1, |
| | | "quantity": 200, |
| | | "planCompleteTime": "2026-06-30", |
| | | "planCompleteTime": "%s", |
| | | "remark": "根据生产计划XX生成" |
| | | } |
| | | productionPlan的source可选"销售"或"内部"。 |
| | | 日期范围: %s ~ %s,每种实体生成%d-%d条。 |
| | | """.formatted(dateStart, dateEnd, min, max); |
| | | source可选"销售"或"内部"。%d-%d条。 |
| | | """.formatted(dateStart, dateEnd, dateStart, dateEnd, dateEnd, min, max); |
| | | } |
| | | |
| | | private static String buildStockPrompt(int min, int max) { |
| | | private static String buildStockPrompt(int min, int max, String dateStart) { |
| | | return """ |
| | | 库存模块 - 按以下格式生成: |
| | | 库存模块格式: |
| | | { |
| | | "entity": "stockInventory", |
| | | "productModelId": 1, |
| | | "qualitity": 500, |
| | | "batchNo": "BATCH-20260601-001", |
| | | "batchNo": "BATCH-YYYYMMDD-001", |
| | | "warnNum": 50, |
| | | "remark": "安全库存" |
| | | } |
| | | 每种实体生成%d-%d条。batchNo格式BATCH-年月日-序号。 |
| | | """.formatted(min, max); |
| | | batchNo中YYYYMMDD替换为 %s。%d-%d条。 |
| | | """.formatted(dateStart.replace("-", ""), min, max); |
| | | } |
| | | } |