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;
|
|
/**
|
* 各模块的 AI Prompt 模板
|
*/
|
public final class MockDataPrompt {
|
|
private MockDataPrompt() {}
|
|
public static String buildSystemPrompt() {
|
return """
|
你是一个企业ERP系统的数据模拟专家。
|
|
核心规则(必须严格遵守):
|
1. 输出必须是纯JSON数组,不要用markdown代码块包裹,不要有任何其他文字
|
2. 每个JSON对象必须包含 "entity" 字段,值为实体类型名
|
3. 数据内容要符合指定行业的特征
|
4. 金额、数量等数值字段要合理
|
5. 同一模块内的实体之间要有引用关系
|
6. 不要使用emoji或特殊unicode字符
|
7. 最重要:日期只从"指定时间范围"中取,绝对不要用其他年份!
|
8. 人员字段(如entryPerson、salesman等)必须从"系统用户列表"中选取,根据角色和部门匹配对应模块,不要编造人名!
|
""";
|
}
|
|
public static String buildUserMessage(List<String> modules, List<String> industries,
|
int countMin, int countMax,
|
String dateStart, String dateEnd,
|
String additionalInfo,
|
Map<Long, String> productModelIdToCategory,
|
List<SysUser> systemUsers,
|
List<String> existingCustomerNames,
|
List<String> existingSupplierNames) {
|
StringBuilder sb = new StringBuilder();
|
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("\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("=== 产品规格列表 ===\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, existingCustomerNames));
|
}
|
if (modules.contains("purchase")) {
|
sb.append(buildPurchasePrompt(countMin, countMax, dateStart, dateEnd, existingSupplierNames));
|
}
|
if (modules.contains("quality")) {
|
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, dateStart));
|
}
|
|
sb.append("\n请直接输出JSON数组:");
|
return sb.toString();
|
}
|
|
/**
|
* 将顶级产品名称映射为分类标签
|
*/
|
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 """
|
销售模块格式(所有日期必须在 %s ~ %s 范围内):
|
%s注意:不要生成customer实体,只生成salesLedger!
|
productData中的productModelId必须选【成品类】规格ID!
|
entryPerson和salesman必须从系统用户列表中角色或部门含"销售"的用户中选取!
|
{
|
"entity": "salesLedger",
|
"customerName": "引用上面生成的客户名称",
|
"salesContractNo": "XS-YYYYMMDD-001",
|
"projectName": "XX项目",
|
"entryDate": "%s",
|
"entryPerson": "从用户列表中选",
|
"salesman": "从用户列表中选",
|
"contractAmount": 50000.00,
|
"paymentMethod": "月结30天",
|
"executionDate": "%s",
|
"deliveryDate": "%s",
|
"type": 1,
|
"productData": [{
|
"productId": 1, "productModelId": 1, "quantity": 100,
|
"taxInclusiveUnitPrice": 500.00, "taxInclusiveTotalPrice": 50000.00,
|
"taxExclusiveTotalPrice": 44247.79, "taxRate": 13.00, "unit": "件", "type": 1
|
}]
|
}
|
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,
|
List<String> existingSupplierNames) {
|
String namesHint = existingSupplierNames.isEmpty() ? ""
|
: "supplierName必须从以下已有供应商中选择: " + String.join("、", existingSupplierNames) + "\n";
|
return """
|
采购模块格式(所有日期必须在 %s ~ %s 范围内):
|
%s注意:不要生成supplier实体,只生成purchaseLedger!
|
productData中的productModelId必须选【原材料类】规格ID!
|
人员字段必须从系统用户列表中角色或部门含"采购"的用户中选取!
|
{
|
"entity": "purchaseLedger",
|
"supplierName": "从已有供应商中选择",
|
"purchaseContractNumber": "CG-YYYYMMDD-001",
|
"projectName": "XX采购项目",
|
"entryDate": "%s",
|
"contractAmount": 30000.00,
|
"paymentMethod": "货到付款",
|
"executionDate": "%s",
|
"productData": [{
|
"productId": 1, "productModelId": 1, "quantity": 50,
|
"taxInclusiveUnitPrice": 600.00, "taxInclusiveTotalPrice": 30000.00,
|
"taxExclusiveTotalPrice": 26548.67, "taxRate": 13.00, "unit": "件", "type": 2
|
}]
|
}
|
purchaseContractNumber中YYYYMMDD替换为entryDate的日期。%d-%d条。
|
""".formatted(dateStart, dateEnd, namesHint, dateStart, dateStart, min, max);
|
}
|
|
private static String buildQualityPrompt(int min, int max, String dateStart) {
|
return """
|
质量模块格式:
|
{
|
"entity": "qualityTestStandard",
|
"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(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": "%s",
|
"source": "销售",
|
"promisedDeliveryDate": "%s",
|
"remark": "XX客户订单需求"
|
}
|
{
|
"entity": "productionOrder",
|
"productModelId": 1,
|
"quantity": 200,
|
"planCompleteTime": "%s",
|
"remark": "根据生产计划XX生成"
|
}
|
source可选"销售"或"内部"。%d-%d条。
|
""".formatted(dateStart, dateEnd, dateStart, dateEnd, dateEnd, min, max);
|
}
|
|
private static String buildStockPrompt(int min, int max, String dateStart) {
|
return """
|
库存模块格式:
|
{
|
"entity": "stockInventory",
|
"productModelId": 1,
|
"qualitity": 500,
|
"batchNo": "BATCH-YYYYMMDD-001",
|
"warnNum": 50,
|
"remark": "安全库存"
|
}
|
batchNo中YYYYMMDD替换为 %s。%d-%d条。
|
""".formatted(dateStart.replace("-", ""), min, max);
|
}
|
}
|