feat: 重写insertBatchDataToTarget方法,参考Salesforce数据插入逻辑
- 重写DataiIntegrationBatchServiceImpl中的insertBatchDataToTarget方法 - 参考CommonServiceImpl.java中的Salesforce数据插入逻辑 - 优化字段映射结构,使用List<Map<String, Object>>存储字段映射 - 增加字段过滤,排除is_dump和is_upload等标记字段 - 添加线程休眠机制,避免API限流 - 改进错误处理,增加完整异常堆栈信息 - 添加工作流提示词文档(03-01, 03-02, 03-03) - 添加Salesforce参考代码(CommonServiceImpl.java, DataImportNewServiceImpl.java)
This commit is contained in:
parent
19b1d1a41a
commit
f6a97dbf7f
@ -13,7 +13,7 @@ import com.datai.common.utils.SecurityUtils;
|
||||
import com.datai.integration.model.param.DataiSyncParam;
|
||||
import com.datai.salesforce.common.utils.SoqlBuilder;
|
||||
import com.datai.integration.util.ConvertUtil;
|
||||
import com.sforce.soap.partner.PartnerConnection;
|
||||
import com.sforce.soap.partner.*;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -27,10 +27,7 @@ import com.datai.integration.service.IDataiIntegrationFieldService;
|
||||
import com.datai.integration.service.IDataiIntegrationSyncLogService;
|
||||
import com.datai.integration.mapper.CustomMapper;
|
||||
import com.datai.integration.factory.impl.SOAPConnectionFactory;
|
||||
import com.sforce.soap.partner.DescribeSObjectResult;
|
||||
import com.sforce.soap.partner.Field;
|
||||
import com.datai.integration.core.IPartnerV1Connection;
|
||||
import com.sforce.soap.partner.QueryResult;
|
||||
import com.sforce.soap.partner.sobject.SObject;
|
||||
import com.sforce.ws.ConnectionException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -998,197 +995,202 @@ public class DataiIntegrationBatchServiceImpl implements IDataiIntegrationBatchS
|
||||
public Map<String, Object> insertBatchDataToTarget(Integer id, String targetOrgType) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
log.info("开始插入批次数据到目标系统,批次ID: {}, 目标ORG类型: {}", id, targetOrgType);
|
||||
|
||||
if (id == null) {
|
||||
log.error("批次ID不能为空");
|
||||
result.put("success", false);
|
||||
result.put("message", "批次ID不能为空");
|
||||
return result;
|
||||
}
|
||||
|
||||
if (targetOrgType == null || targetOrgType.trim().isEmpty()) {
|
||||
log.error("目标ORG类型不能为空");
|
||||
result.put("success", false);
|
||||
result.put("message", "目标ORG类型不能为空");
|
||||
return result;
|
||||
}
|
||||
|
||||
DataiIntegrationBatch batch = selectDataiIntegrationBatchById(id);
|
||||
if (batch == null) {
|
||||
log.error("批次不存在,批次ID: {}", id);
|
||||
result.put("success", false);
|
||||
result.put("message", "批次不存在");
|
||||
return result;
|
||||
}
|
||||
|
||||
String objectApi = batch.getApi();
|
||||
if (objectApi == null || objectApi.trim().isEmpty()) {
|
||||
log.error("批次对象API为空,批次ID: {}", id);
|
||||
result.put("success", false);
|
||||
result.put("message", "批次对象API不能为空");
|
||||
return result;
|
||||
}
|
||||
|
||||
String tableName = objectApi.trim();
|
||||
if (customMapper.checkTable(tableName) == null) {
|
||||
log.error("本地表不存在,表名: {}", tableName);
|
||||
result.put("success", false);
|
||||
result.put("message", "本地表不存在: " + tableName);
|
||||
return result;
|
||||
}
|
||||
|
||||
log.info("准备插入批次 {} 的对象 {} 数据到目标系统", id, objectApi);
|
||||
|
||||
IPartnerV1Connection targetConnection = null;
|
||||
try {
|
||||
log.info("开始插入批次数据到目标系统,批次ID: {}, 目标ORG类型: {}", id, targetOrgType);
|
||||
|
||||
if (id == null) {
|
||||
log.error("批次ID不能为空");
|
||||
targetConnection = soapConnectionFactory.getConnection(targetOrgType);
|
||||
if (targetConnection == null) {
|
||||
log.error("无法获取目标系统连接,ORG类型: {}", targetOrgType);
|
||||
result.put("success", false);
|
||||
result.put("message", "批次ID不能为空");
|
||||
result.put("message", "无法获取目标系统连接");
|
||||
return result;
|
||||
}
|
||||
|
||||
if (targetOrgType == null || targetOrgType.trim().isEmpty()) {
|
||||
log.error("目标ORG类型不能为空");
|
||||
DescribeSObjectResult objDetail = targetConnection.describeSObject(objectApi);
|
||||
if (objDetail == null) {
|
||||
log.error("目标系统不存在该对象,对象API: {}", objectApi);
|
||||
result.put("success", false);
|
||||
result.put("message", "目标ORG类型不能为空");
|
||||
result.put("message", "目标系统不存在该对象: " + objectApi);
|
||||
return result;
|
||||
}
|
||||
|
||||
DataiIntegrationBatch batch = selectDataiIntegrationBatchById(id);
|
||||
if (batch == null) {
|
||||
log.error("批次不存在,批次ID: {}", id);
|
||||
String batchField = batch.getBatchField();
|
||||
String whereClause = "";
|
||||
if (batchField != null && !batchField.trim().isEmpty()) {
|
||||
whereClause = "WHERE " + batchField.trim();
|
||||
}
|
||||
|
||||
log.info("开始查询本地批次数据,表名: {}, 条件: {}", tableName, whereClause);
|
||||
List<Map<String, Object>> dataList = customMapper.list("*", tableName, whereClause);
|
||||
|
||||
if (dataList == null || dataList.isEmpty()) {
|
||||
log.warn("本地批次没有数据,表名: {}, 条件: {}", tableName, whereClause);
|
||||
result.put("success", false);
|
||||
result.put("message", "批次不存在");
|
||||
result.put("message", "本地批次没有数据");
|
||||
return result;
|
||||
}
|
||||
|
||||
String objectApi = batch.getApi();
|
||||
if (objectApi == null || objectApi.trim().isEmpty()) {
|
||||
log.error("批次对象API为空,批次ID: {}", id);
|
||||
result.put("success", false);
|
||||
result.put("message", "批次对象API不能为空");
|
||||
return result;
|
||||
}
|
||||
log.info("查询到 {} 条本地批次数据,准备插入到目标系统", dataList.size());
|
||||
|
||||
String tableName = objectApi.trim();
|
||||
if (customMapper.checkTable(tableName) == null) {
|
||||
log.error("本地表不存在,表名: {}", tableName);
|
||||
result.put("success", false);
|
||||
result.put("message", "本地表不存在: " + tableName);
|
||||
return result;
|
||||
}
|
||||
int successCount = 0;
|
||||
int failureCount = 0;
|
||||
List<Map<String, Object>> failedRecords = new ArrayList<>();
|
||||
|
||||
log.info("准备插入批次 {} 的对象 {} 数据到目标系统", id, objectApi);
|
||||
|
||||
IPartnerV1Connection targetConnection = null;
|
||||
try {
|
||||
targetConnection = soapConnectionFactory.getConnection(targetOrgType);
|
||||
if (targetConnection == null) {
|
||||
log.error("无法获取目标系统连接,ORG类型: {}", targetOrgType);
|
||||
result.put("success", false);
|
||||
result.put("message", "无法获取目标系统连接");
|
||||
return result;
|
||||
}
|
||||
|
||||
DescribeSObjectResult objDetail = targetConnection.describeSObject(objectApi);
|
||||
if (objDetail == null) {
|
||||
log.error("目标系统不存在该对象,对象API: {}", objectApi);
|
||||
result.put("success", false);
|
||||
result.put("message", "目标系统不存在该对象: " + objectApi);
|
||||
return result;
|
||||
}
|
||||
|
||||
String batchField = batch.getBatchField();
|
||||
String whereClause = "";
|
||||
if (batchField != null && !batchField.trim().isEmpty()) {
|
||||
whereClause = "WHERE " + batchField.trim() + " = '" + batch.getBatchValue() + "'";
|
||||
}
|
||||
|
||||
log.info("开始查询本地批次数据,表名: {}, 条件: {}", tableName, whereClause);
|
||||
List<Map<String, Object>> dataList = customMapper.list("*", tableName, whereClause);
|
||||
|
||||
if (dataList == null || dataList.isEmpty()) {
|
||||
log.warn("本地批次没有数据,表名: {}, 条件: {}", tableName, whereClause);
|
||||
result.put("success", false);
|
||||
result.put("message", "本地批次没有数据");
|
||||
return result;
|
||||
}
|
||||
|
||||
log.info("查询到 {} 条本地批次数据,准备插入到目标系统", dataList.size());
|
||||
|
||||
int successCount = 0;
|
||||
int failureCount = 0;
|
||||
List<Map<String, Object>> failedRecords = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < dataList.size(); i++) {
|
||||
Map<String, Object> dataMap = dataList.get(i);
|
||||
try {
|
||||
String localId = (String) dataMap.get("id");
|
||||
if (localId == null || localId.isEmpty()) {
|
||||
log.warn("记录ID为空,跳过该记录,索引: {}", i);
|
||||
failureCount++;
|
||||
Map<String, Object> failedRecord = new HashMap<>();
|
||||
failedRecord.put("index", i);
|
||||
failedRecord.put("reason", "记录ID为空");
|
||||
failedRecords.add(failedRecord);
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<String, Object> fieldsToInsert = new HashMap<>();
|
||||
for (String key : dataMap.keySet()) {
|
||||
if (!"id".equalsIgnoreCase(key) && !"new_id".equalsIgnoreCase(key) &&
|
||||
!"is_insert".equalsIgnoreCase(key) && !"is_update".equalsIgnoreCase(key) &&
|
||||
dataMap.get(key) != null) {
|
||||
fieldsToInsert.put(key, dataMap.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldsToInsert.isEmpty()) {
|
||||
log.warn("记录没有需要插入的字段,跳过该记录,ID: {}", localId);
|
||||
continue;
|
||||
}
|
||||
|
||||
SObject sObject = new SObject();
|
||||
sObject.setType(objectApi);
|
||||
for (Map.Entry<String, Object> entry : fieldsToInsert.entrySet()) {
|
||||
sObject.setField(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
SaveResult[] saveResults = targetConnection.create(new SObject[]{sObject});
|
||||
if (saveResults != null && saveResults.length > 0 && saveResults[0].isSuccess()) {
|
||||
String newId = saveResults[0].getId();
|
||||
|
||||
List<Map<String, Object>> updateMaps = new ArrayList<>();
|
||||
Map<String, Object> newIdMap = new HashMap<>();
|
||||
newIdMap.put("key", "new_id");
|
||||
newIdMap.put("value", newId);
|
||||
updateMaps.add(newIdMap);
|
||||
|
||||
Map<String, Object> isInsertMap = new HashMap<>();
|
||||
isInsertMap.put("key", "is_insert");
|
||||
isInsertMap.put("value", 1);
|
||||
updateMaps.add(isInsertMap);
|
||||
|
||||
customMapper.updateById(tableName, updateMaps, localId);
|
||||
|
||||
successCount++;
|
||||
log.debug("记录插入成功,本地ID: {}, 新ID: {}", localId, newId);
|
||||
} else {
|
||||
String errorMsg = saveResults != null && saveResults.length > 0 ?
|
||||
saveResults[0].getErrors()[0].getMessage() : "未知错误";
|
||||
log.error("记录插入失败,本地ID: {}, 错误: {}", localId, errorMsg);
|
||||
failureCount++;
|
||||
Map<String, Object> failedRecord = new HashMap<>();
|
||||
failedRecord.put("index", i);
|
||||
failedRecord.put("localId", localId);
|
||||
failedRecord.put("reason", errorMsg);
|
||||
failedRecords.add(failedRecord);
|
||||
}
|
||||
|
||||
if ((i + 1) % 100 == 0) {
|
||||
log.info("已插入 {}/{} 条记录", i + 1, dataList.size());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("插入记录失败,索引: {}, 错误: {}", i, e.getMessage());
|
||||
for (int i = 0; i < dataList.size(); i++) {
|
||||
Map<String, Object> dataMap = dataList.get(i);
|
||||
try {
|
||||
String localId = (String) dataMap.get("id");
|
||||
if (localId == null || localId.isEmpty()) {
|
||||
log.warn("记录ID为空,跳过该记录,索引: {}", i);
|
||||
failureCount++;
|
||||
Map<String, Object> failedRecord = new HashMap<>();
|
||||
failedRecord.put("index", i);
|
||||
failedRecord.put("reason", e.getMessage());
|
||||
failedRecord.put("reason", "记录ID为空");
|
||||
failedRecords.add(failedRecord);
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Map<String, Object>> maps = new ArrayList<>();
|
||||
for (String key : dataMap.keySet()) {
|
||||
Object value = dataMap.get(key);
|
||||
if (value != null && !"id".equalsIgnoreCase(key) &&
|
||||
!"new_id".equalsIgnoreCase(key) &&
|
||||
!"is_insert".equalsIgnoreCase(key) &&
|
||||
!"is_update".equalsIgnoreCase(key) &&
|
||||
!"is_dump".equalsIgnoreCase(key) &&
|
||||
!"is_upload".equalsIgnoreCase(key)) {
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("key", key);
|
||||
paramMap.put("value", value);
|
||||
maps.add(paramMap);
|
||||
}
|
||||
}
|
||||
|
||||
if (maps.isEmpty()) {
|
||||
log.warn("记录没有需要插入的字段,跳过该记录,ID: {}", localId);
|
||||
continue;
|
||||
}
|
||||
|
||||
SObject sObject = new SObject();
|
||||
sObject.setType(objectApi);
|
||||
for (Map<String, Object> paramMap : maps) {
|
||||
String key = (String) paramMap.get("key");
|
||||
Object value = paramMap.get("value");
|
||||
sObject.setField(key, value);
|
||||
}
|
||||
|
||||
SaveResult[] saveResults = targetConnection.create(new SObject[]{sObject});
|
||||
if (saveResults != null && saveResults.length > 0 && saveResults[0].isSuccess()) {
|
||||
String newId = saveResults[0].getId();
|
||||
|
||||
List<Map<String, Object>> updateMaps = new ArrayList<>();
|
||||
Map<String, Object> newIdMap = new HashMap<>();
|
||||
newIdMap.put("key", "new_id");
|
||||
newIdMap.put("value", newId);
|
||||
updateMaps.add(newIdMap);
|
||||
|
||||
Map<String, Object> isInsertMap = new HashMap<>();
|
||||
isInsertMap.put("key", "is_insert");
|
||||
isInsertMap.put("value", 1);
|
||||
updateMaps.add(isInsertMap);
|
||||
|
||||
customMapper.updateById(tableName, updateMaps, localId);
|
||||
|
||||
successCount++;
|
||||
log.debug("记录插入成功,本地ID: {}, 新ID: {}", localId, newId);
|
||||
} else {
|
||||
String errorMsg = saveResults != null && saveResults.length > 0 ?
|
||||
saveResults[0].getErrors()[0].getMessage() : "未知错误";
|
||||
log.error("记录插入失败,本地ID: {}, 错误: {}", localId, errorMsg);
|
||||
failureCount++;
|
||||
Map<String, Object> failedRecord = new HashMap<>();
|
||||
failedRecord.put("index", i);
|
||||
failedRecord.put("localId", localId);
|
||||
failedRecord.put("reason", errorMsg);
|
||||
failedRecords.add(failedRecord);
|
||||
}
|
||||
}
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
long duration = endTime - startTime;
|
||||
|
||||
result.put("success", true);
|
||||
result.put("message", "批次数据插入完成");
|
||||
result.put("batchId", id);
|
||||
result.put("api", objectApi);
|
||||
result.put("label", batch.getLabel());
|
||||
result.put("targetOrgType", targetOrgType);
|
||||
result.put("totalCount", dataList.size());
|
||||
result.put("successCount", successCount);
|
||||
result.put("failureCount", failureCount);
|
||||
result.put("duration", duration);
|
||||
result.put("failedRecords", failedRecords);
|
||||
|
||||
log.info("批次数据插入完成,批次ID: {}, 对象API: {}, 总数: {}, 成功: {}, 失败: {}, 耗时: {}ms",
|
||||
id, objectApi, dataList.size(), successCount, failureCount, duration);
|
||||
|
||||
} finally {
|
||||
if (targetConnection != null) {
|
||||
try {
|
||||
targetConnection.logout();
|
||||
} catch (Exception e) {
|
||||
log.error("关闭目标系统连接失败", e);
|
||||
if ((i + 1) % 100 == 0) {
|
||||
log.info("已插入 {}/{} 条记录", i + 1, dataList.size());
|
||||
}
|
||||
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("线程休眠被中断", e);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("插入记录失败,索引: {}, 错误: {}", i, e.getMessage(), e);
|
||||
failureCount++;
|
||||
Map<String, Object> failedRecord = new HashMap<>();
|
||||
failedRecord.put("index", i);
|
||||
failedRecord.put("reason", e.getMessage());
|
||||
failedRecords.add(failedRecord);
|
||||
}
|
||||
}
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
long duration = endTime - startTime;
|
||||
|
||||
result.put("success", true);
|
||||
result.put("message", "批次数据插入完成");
|
||||
result.put("batchId", id);
|
||||
result.put("api", objectApi);
|
||||
result.put("label", batch.getLabel());
|
||||
result.put("targetOrgType", targetOrgType);
|
||||
result.put("totalCount", dataList.size());
|
||||
result.put("successCount", successCount);
|
||||
result.put("failureCount", failureCount);
|
||||
result.put("duration", duration);
|
||||
result.put("failedRecords", failedRecords);
|
||||
|
||||
log.info("批次数据插入完成,批次ID: {}, 对象API: {}, 总数: {}, 成功: {}, 失败: {}, 耗时: {}ms",
|
||||
id, objectApi, dataList.size(), successCount, failureCount, duration);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("插入批次数据到目标系统失败,批次ID: {}", id, e);
|
||||
result.put("success", false);
|
||||
@ -1201,186 +1203,175 @@ public class DataiIntegrationBatchServiceImpl implements IDataiIntegrationBatchS
|
||||
public Map<String, Object> updateBatchDataToTarget(Integer id, String targetOrgType) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
|
||||
log.info("开始更新批次数据到目标系统,批次ID: {}, 目标ORG类型: {}", id, targetOrgType);
|
||||
|
||||
if (id == null) {
|
||||
log.error("批次ID不能为空");
|
||||
result.put("success", false);
|
||||
result.put("message", "批次ID不能为空");
|
||||
return result;
|
||||
}
|
||||
|
||||
if (targetOrgType == null || targetOrgType.trim().isEmpty()) {
|
||||
log.error("目标ORG类型不能为空");
|
||||
result.put("success", false);
|
||||
result.put("message", "目标ORG类型不能为空");
|
||||
return result;
|
||||
}
|
||||
|
||||
DataiIntegrationBatch batch = selectDataiIntegrationBatchById(id);
|
||||
if (batch == null) {
|
||||
log.error("批次不存在,批次ID: {}", id);
|
||||
result.put("success", false);
|
||||
result.put("message", "批次不存在");
|
||||
return result;
|
||||
}
|
||||
|
||||
String objectApi = batch.getApi();
|
||||
if (objectApi == null || objectApi.trim().isEmpty()) {
|
||||
log.error("批次对象API为空,批次ID: {}", id);
|
||||
result.put("success", false);
|
||||
result.put("message", "批次对象API不能为空");
|
||||
return result;
|
||||
}
|
||||
|
||||
String tableName = objectApi.trim();
|
||||
if (customMapper.checkTable(tableName) == null) {
|
||||
log.error("本地表不存在,表名: {}", tableName);
|
||||
result.put("success", false);
|
||||
result.put("message", "本地表不存在: " + tableName);
|
||||
return result;
|
||||
}
|
||||
|
||||
log.info("准备更新批次 {} 的对象 {} 数据到目标系统", id, objectApi);
|
||||
|
||||
IPartnerV1Connection targetConnection = null;
|
||||
try {
|
||||
log.info("开始更新批次数据到目标系统,批次ID: {}, 目标ORG类型: {}", id, targetOrgType);
|
||||
|
||||
if (id == null) {
|
||||
log.error("批次ID不能为空");
|
||||
targetConnection = soapConnectionFactory.getConnection(targetOrgType);
|
||||
if (targetConnection == null) {
|
||||
log.error("无法获取目标系统连接,ORG类型: {}", targetOrgType);
|
||||
result.put("success", false);
|
||||
result.put("message", "批次ID不能为空");
|
||||
result.put("message", "无法获取目标系统连接");
|
||||
return result;
|
||||
}
|
||||
|
||||
if (targetOrgType == null || targetOrgType.trim().isEmpty()) {
|
||||
log.error("目标ORG类型不能为空");
|
||||
DescribeSObjectResult objDetail = targetConnection.describeSObject(objectApi);
|
||||
if (objDetail == null) {
|
||||
log.error("目标系统不存在该对象,对象API: {}", objectApi);
|
||||
result.put("success", false);
|
||||
result.put("message", "目标ORG类型不能为空");
|
||||
result.put("message", "目标系统不存在该对象: " + objectApi);
|
||||
return result;
|
||||
}
|
||||
|
||||
DataiIntegrationBatch batch = selectDataiIntegrationBatchById(id);
|
||||
if (batch == null) {
|
||||
log.error("批次不存在,批次ID: {}", id);
|
||||
String batchField = batch.getBatchField();
|
||||
String whereClause = "";
|
||||
if (batchField != null && !batchField.trim().isEmpty()) {
|
||||
whereClause = "WHERE " + batchField.trim();
|
||||
}
|
||||
|
||||
log.info("开始查询本地批次数据,表名: {}, 条件: {}", tableName, whereClause);
|
||||
List<Map<String, Object>> dataList = customMapper.list("*", tableName, whereClause);
|
||||
|
||||
if (dataList == null || dataList.isEmpty()) {
|
||||
log.warn("本地批次没有数据,表名: {}, 条件: {}", tableName, whereClause);
|
||||
result.put("success", false);
|
||||
result.put("message", "批次不存在");
|
||||
result.put("message", "本地批次没有数据");
|
||||
return result;
|
||||
}
|
||||
|
||||
String objectApi = batch.getApi();
|
||||
if (objectApi == null || objectApi.trim().isEmpty()) {
|
||||
log.error("批次对象API为空,批次ID: {}", id);
|
||||
result.put("success", false);
|
||||
result.put("message", "批次对象API不能为空");
|
||||
return result;
|
||||
}
|
||||
log.info("查询到 {} 条本地批次数据,准备更新到目标系统", dataList.size());
|
||||
|
||||
String tableName = objectApi.trim();
|
||||
if (customMapper.checkTable(tableName) == null) {
|
||||
log.error("本地表不存在,表名: {}", tableName);
|
||||
result.put("success", false);
|
||||
result.put("message", "本地表不存在: " + tableName);
|
||||
return result;
|
||||
}
|
||||
int successCount = 0;
|
||||
int failureCount = 0;
|
||||
List<Map<String, Object>> failedRecords = new ArrayList<>();
|
||||
|
||||
log.info("准备更新批次 {} 的对象 {} 数据到目标系统", id, objectApi);
|
||||
for (int i = 0; i < dataList.size(); i++) {
|
||||
Map<String, Object> dataMap = dataList.get(i);
|
||||
try {
|
||||
String localId = (String) dataMap.get("id");
|
||||
String newId = (String) dataMap.get("new_id");
|
||||
|
||||
IPartnerV1Connection targetConnection = null;
|
||||
try {
|
||||
targetConnection = soapConnectionFactory.getConnection(targetOrgType);
|
||||
if (targetConnection == null) {
|
||||
log.error("无法获取目标系统连接,ORG类型: {}", targetOrgType);
|
||||
result.put("success", false);
|
||||
result.put("message", "无法获取目标系统连接");
|
||||
return result;
|
||||
}
|
||||
|
||||
DescribeSObjectResult objDetail = targetConnection.describeSObject(objectApi);
|
||||
if (objDetail == null) {
|
||||
log.error("目标系统不存在该对象,对象API: {}", objectApi);
|
||||
result.put("success", false);
|
||||
result.put("message", "目标系统不存在该对象: " + objectApi);
|
||||
return result;
|
||||
}
|
||||
|
||||
String batchField = batch.getBatchField();
|
||||
String whereClause = "";
|
||||
if (batchField != null && !batchField.trim().isEmpty()) {
|
||||
whereClause = "WHERE " + batchField.trim() + " = '" + batch.getBatchValue() + "'";
|
||||
}
|
||||
|
||||
log.info("开始查询本地批次数据,表名: {}, 条件: {}", tableName, whereClause);
|
||||
List<Map<String, Object>> dataList = customMapper.list("*", tableName, whereClause);
|
||||
|
||||
if (dataList == null || dataList.isEmpty()) {
|
||||
log.warn("本地批次没有数据,表名: {}, 条件: {}", tableName, whereClause);
|
||||
result.put("success", false);
|
||||
result.put("message", "本地批次没有数据");
|
||||
return result;
|
||||
}
|
||||
|
||||
log.info("查询到 {} 条本地批次数据,准备更新到目标系统", dataList.size());
|
||||
|
||||
int successCount = 0;
|
||||
int failureCount = 0;
|
||||
List<Map<String, Object>> failedRecords = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < dataList.size(); i++) {
|
||||
Map<String, Object> dataMap = dataList.get(i);
|
||||
try {
|
||||
String localId = (String) dataMap.get("id");
|
||||
String newId = (String) dataMap.get("new_id");
|
||||
|
||||
if (localId == null || localId.isEmpty()) {
|
||||
log.warn("记录ID为空,跳过该记录,索引: {}", i);
|
||||
failureCount++;
|
||||
Map<String, Object> failedRecord = new HashMap<>();
|
||||
failedRecord.put("index", i);
|
||||
failedRecord.put("reason", "记录ID为空");
|
||||
failedRecords.add(failedRecord);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (newId == null || newId.isEmpty()) {
|
||||
log.warn("记录new_id为空,跳过该记录,本地ID: {}", localId);
|
||||
failureCount++;
|
||||
Map<String, Object> failedRecord = new HashMap<>();
|
||||
failedRecord.put("index", i);
|
||||
failedRecord.put("localId", localId);
|
||||
failedRecord.put("reason", "new_id为空,请先执行插入操作");
|
||||
failedRecords.add(failedRecord);
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<String, Object> fieldsToUpdate = new HashMap<>();
|
||||
for (String key : dataMap.keySet()) {
|
||||
if (!"id".equalsIgnoreCase(key) && !"new_id".equalsIgnoreCase(key) &&
|
||||
!"is_insert".equalsIgnoreCase(key) && !"is_update".equalsIgnoreCase(key) &&
|
||||
dataMap.get(key) != null) {
|
||||
fieldsToUpdate.put(key, dataMap.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldsToUpdate.isEmpty()) {
|
||||
log.warn("记录没有需要更新的字段,跳过该记录,本地ID: {}", localId);
|
||||
continue;
|
||||
}
|
||||
|
||||
targetConnection.update(objectApi, newId, fieldsToUpdate);
|
||||
|
||||
List<Map<String, Object>> updateMaps = new ArrayList<>();
|
||||
Map<String, Object> isUpdateMap = new HashMap<>();
|
||||
isUpdateMap.put("key", "is_update");
|
||||
isUpdateMap.put("value", 1);
|
||||
updateMaps.add(isUpdateMap);
|
||||
|
||||
customMapper.updateById(tableName, updateMaps, localId);
|
||||
|
||||
successCount++;
|
||||
log.debug("记录更新成功,本地ID: {}, 目标ID: {}", localId, newId);
|
||||
|
||||
if ((i + 1) % 100 == 0) {
|
||||
log.info("已更新 {}/{} 条记录", i + 1, dataList.size());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("更新记录失败,索引: {}, 错误: {}", i, e.getMessage());
|
||||
if (localId == null || localId.isEmpty()) {
|
||||
log.warn("记录ID为空,跳过该记录,索引: {}", i);
|
||||
failureCount++;
|
||||
Map<String, Object> failedRecord = new HashMap<>();
|
||||
failedRecord.put("index", i);
|
||||
failedRecord.put("reason", e.getMessage());
|
||||
failedRecord.put("reason", "记录ID为空");
|
||||
failedRecords.add(failedRecord);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
long duration = endTime - startTime;
|
||||
|
||||
result.put("success", true);
|
||||
result.put("message", "批次数据更新完成");
|
||||
result.put("batchId", id);
|
||||
result.put("api", objectApi);
|
||||
result.put("label", batch.getLabel());
|
||||
result.put("targetOrgType", targetOrgType);
|
||||
result.put("totalCount", dataList.size());
|
||||
result.put("successCount", successCount);
|
||||
result.put("failureCount", failureCount);
|
||||
result.put("duration", duration);
|
||||
result.put("failedRecords", failedRecords);
|
||||
|
||||
log.info("批次数据更新完成,批次ID: {}, 对象API: {}, 总数: {}, 成功: {}, 失败: {}, 耗时: {}ms",
|
||||
id, objectApi, dataList.size(), successCount, failureCount, duration);
|
||||
|
||||
} finally {
|
||||
if (targetConnection != null) {
|
||||
try {
|
||||
targetConnection.logout();
|
||||
} catch (Exception e) {
|
||||
log.error("关闭目标系统连接失败", e);
|
||||
if (newId == null || newId.isEmpty()) {
|
||||
log.warn("记录new_id为空,跳过该记录,本地ID: {}", localId);
|
||||
failureCount++;
|
||||
Map<String, Object> failedRecord = new HashMap<>();
|
||||
failedRecord.put("index", i);
|
||||
failedRecord.put("localId", localId);
|
||||
failedRecord.put("reason", "new_id为空,请先执行插入操作");
|
||||
failedRecords.add(failedRecord);
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<String, Object> fieldsToUpdate = new HashMap<>();
|
||||
for (String key : dataMap.keySet()) {
|
||||
if (!"id".equalsIgnoreCase(key) && !"new_id".equalsIgnoreCase(key) &&
|
||||
!"is_insert".equalsIgnoreCase(key) && !"is_update".equalsIgnoreCase(key) &&
|
||||
dataMap.get(key) != null) {
|
||||
fieldsToUpdate.put(key, dataMap.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldsToUpdate.isEmpty()) {
|
||||
log.warn("记录没有需要更新的字段,跳过该记录,本地ID: {}", localId);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
List<Map<String, Object>> updateMaps = new ArrayList<>();
|
||||
Map<String, Object> isUpdateMap = new HashMap<>();
|
||||
isUpdateMap.put("key", "is_update");
|
||||
isUpdateMap.put("value", 1);
|
||||
updateMaps.add(isUpdateMap);
|
||||
|
||||
customMapper.updateById(tableName, updateMaps, localId);
|
||||
|
||||
successCount++;
|
||||
log.debug("记录更新成功,本地ID: {}, 目标ID: {}", localId, newId);
|
||||
|
||||
if ((i + 1) % 100 == 0) {
|
||||
log.info("已更新 {}/{} 条记录", i + 1, dataList.size());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("更新记录失败,索引: {}, 错误: {}", i, e.getMessage());
|
||||
failureCount++;
|
||||
Map<String, Object> failedRecord = new HashMap<>();
|
||||
failedRecord.put("index", i);
|
||||
failedRecord.put("reason", e.getMessage());
|
||||
failedRecords.add(failedRecord);
|
||||
}
|
||||
}
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
long duration = endTime - startTime;
|
||||
|
||||
result.put("success", true);
|
||||
result.put("message", "批次数据更新完成");
|
||||
result.put("batchId", id);
|
||||
result.put("api", objectApi);
|
||||
result.put("label", batch.getLabel());
|
||||
result.put("targetOrgType", targetOrgType);
|
||||
result.put("totalCount", dataList.size());
|
||||
result.put("successCount", successCount);
|
||||
result.put("failureCount", failureCount);
|
||||
result.put("duration", duration);
|
||||
result.put("failedRecords", failedRecords);
|
||||
|
||||
log.info("批次数据更新完成,批次ID: {}, 对象API: {}, 总数: {}, 成功: {}, 失败: {}, 耗时: {}ms",
|
||||
id, objectApi, dataList.size(), successCount, failureCount, duration);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("更新批次数据到目标系统失败,批次ID: {}", id, e);
|
||||
result.put("success", false);
|
||||
|
||||
@ -0,0 +1,194 @@
|
||||
#### 角色设定 (Role Definition)
|
||||
|
||||
> 你是项目的 **"SSOT 架构师" (Single Source of Truth Architect)**。你的核心职责是维护项目文档的唯一性、准确性和可追溯性。你拒绝在没有文档依据的情况下编写代码。你严格遵守以下七步工作流。
|
||||
|
||||
### 核心原则:无文档不开发
|
||||
|
||||
> **规则:** AI 严禁在未更新 SSOT 文档的情况下生成业务代码。所有操作必须遵循“需求 -> 决策 -> 提示词 -> 执行 -> 复盘”的单向链条。
|
||||
|
||||
#### 优化后的七步工作流 (Refined Workflow)
|
||||
|
||||
**1. 初始化协作基线 (Bootstrap)**
|
||||
|
||||
- **操作**:在根目录创建 `README.md` (项目蓝图)、`CONTRIBUTING.md` (AI与人类的协作协议)、`CHANGELOG.md`。
|
||||
|
||||
- **约束**:`README.md` 必须包含指向 `docs/index.md` 的显著链接。
|
||||
|
||||
|
||||
**2. 建立真源中心 (SSOT Hub)**
|
||||
|
||||
- **操作**:创建 `docs/index.md`。
|
||||
|
||||
- **规则**:这是项目的**唯一**入口。它不仅是列表,更是导航图。它必须包含对 Requirements, Design, ADRs, Prompts 的动态索引。**严禁出现“孤儿文件” (即未被 index 索引的文件)。**
|
||||
|
||||
|
||||
**3. 落地信息架构 (Information Architecture)**
|
||||
|
||||
- **操作**:创建标准目录结构:
|
||||
|
||||
- `docs/requirements/` (需求)
|
||||
|
||||
- `docs/design/` (系统设计)
|
||||
|
||||
- `docs/decisions/adr/` (架构决策记录)
|
||||
|
||||
- `docs/prompts/` (提示词资产 - **Treat Prompts as Code**)
|
||||
|
||||
- `docs/sessions/` (AI 会话快照)
|
||||
|
||||
- `docs/retros/` (迭代复盘)
|
||||
|
||||
|
||||
**4. 注入智能模板:ADR (架构决策)**
|
||||
|
||||
- **文件**:`docs/decisions/adr/0000-template.md`
|
||||
|
||||
- **优化结构**:
|
||||
|
||||
- `Title`: 决策标题
|
||||
|
||||
- `Status`: [Draft | Accepted | Deprecated]
|
||||
|
||||
- `Context`: 为什么要做这个决策?
|
||||
|
||||
- `Decision`: 我们决定做什么?
|
||||
|
||||
- `Consequences`: 好的后果与坏的后果 (Trade-offs)
|
||||
|
||||
- `Compliance`: 如何验证决策已被执行?
|
||||
|
||||
|
||||
**5. 注入智能模板:Prompt First (提示词资产)**
|
||||
|
||||
- **文件**:`docs/prompts/0000-template.md`
|
||||
|
||||
- **优化结构**:
|
||||
|
||||
- `Goal`: 该提示词解决什么具体问题?
|
||||
|
||||
- `Context Links`: 引用了哪些 docs (必须是相对链接)?
|
||||
|
||||
- `Inputs`: 需要用户提供哪些变量?
|
||||
|
||||
- `Prompt Content`: 实际的提示词内容 (Markdown Code Block)。
|
||||
|
||||
- `Output Definition`: 期望的输出格式 (JSON/Markdown/Code)。
|
||||
|
||||
- `Version`: 提示词的版本号。
|
||||
|
||||
|
||||
**6. 注入智能模板:Session Logs (会话上下文)**
|
||||
|
||||
- **文件**:`docs/sessions/YYYYMMDD-TaskName.md`
|
||||
|
||||
- **关键补充**:记录不仅仅是“聊天记录”,而是“状态恢复点”。
|
||||
|
||||
- `Trigger`: 触发本次会话的需求或 Bug ID。
|
||||
|
||||
- `Used Prompts`: 链接到使用了 `docs/prompts/` 下的哪个文件。
|
||||
|
||||
- `Outcome`: 最终产生了哪些代码变更 (Commit Hash)。
|
||||
|
||||
|
||||
**7. 注入智能模板:Retro & Loop (闭环复盘)**
|
||||
|
||||
- **文件**:`docs/retros/YYYYMMDD-Review.md`
|
||||
|
||||
- **强制闭环规则**:
|
||||
|
||||
- 如果发现 Prompt 效果不好 -> **必须**修改 `docs/prompts/` 下的对应文件。
|
||||
|
||||
- 如果发现流程问题 -> **必须**更新 `CONTRIBUTING.md`。
|
||||
|
||||
- 不要只记录问题,要产生 Action Item。
|
||||
|
||||
### 执行阶段与具体逻辑
|
||||
|
||||
#### 阶段 1:需求定义与入库 (Requirements)
|
||||
|
||||
- **动作**:在 `docs/requirements/` 创建新需求文档(如 `REQ-001.md`)。
|
||||
|
||||
- **逻辑**:
|
||||
|
||||
1. 解析用户原始需求,将其拆解为:用户故事、验收标准 (AC)。
|
||||
|
||||
2. 同步更新 `docs/index.md`,将该需求挂载到任务清单中。
|
||||
|
||||
|
||||
#### 阶段 2:方案决策 (Architecture Decision)
|
||||
|
||||
- **动作**:基于 `docs/decisions/adr/0000-template.md` 创建决策记录。
|
||||
|
||||
- **逻辑**:
|
||||
|
||||
1. 针对该需求,分析至少两种技术方案。
|
||||
|
||||
2. 记录选定方案的理由、潜在风险及回滚策略。
|
||||
|
||||
3. **强制校验**:检查方案是否冲突于已有的 ADR 记录。
|
||||
|
||||
|
||||
#### 阶段 3:提示词资产化 (Prompt Engineering)
|
||||
|
||||
- **动作**:基于 `docs/prompts/0000-template.md` 编写执行提示词。
|
||||
|
||||
- **逻辑**:
|
||||
|
||||
1. **引用真源**:提示词开头必须引用 `docs/requirements/` 和 `docs/design/` 的文件链接。
|
||||
|
||||
2. 定义输出格式(如:必须包含单元测试,必须符合某设计模式)。
|
||||
|
||||
3. 将此 Prompt 存入版本库,作为可重复调用的“代码”。
|
||||
|
||||
|
||||
#### 阶段 4:执行会话与代码生成 (Session Execution)
|
||||
|
||||
- **动作**:基于 `docs/sessions/YYYYMMDD-template.md` 记录执行过程。
|
||||
|
||||
- **逻辑**:
|
||||
|
||||
1. **首句申明**:明确当前“现状”与“目标”。
|
||||
|
||||
2. 加载阶段 3 准备的 Prompt,观察输出。
|
||||
|
||||
3. 记录 AI 的质疑、替代方案以及最终的复现步骤。
|
||||
|
||||
|
||||
#### 阶段 5:变更记录与归档 (Changelog)
|
||||
|
||||
- **动作**:更新 `CHANGELOG.md` 和 `docs/changelog/`。
|
||||
|
||||
- **逻辑**:
|
||||
|
||||
1. 简述本次变更对项目的影响。
|
||||
|
||||
2. 在 `docs/index.md` 中标注该需求已完成。
|
||||
|
||||
|
||||
#### 阶段 6:闭环复盘 (Retrospective)
|
||||
|
||||
- **动作**:基于 `docs/retros/YYYYMMDD-template.md` 进行总结。
|
||||
|
||||
- **逻辑**:
|
||||
|
||||
1. 对比目标与实际产出。
|
||||
|
||||
2. **提取模式**:记录 3 条有效的 Prompt 技巧和 3 个避免的坑。
|
||||
|
||||
3. **模板迭代**:如果发现模板不适用,立即执行一次“模板更新”。
|
||||
|
||||
|
||||
---
|
||||
|
||||
### 自动化约束检查(插入到提示词末尾)
|
||||
|
||||
> **执行顺序自检清单:**
|
||||
>
|
||||
> 1. 我是否已经为这个任务创建了需求文档?
|
||||
>
|
||||
> 2. 我是否已经根据 ADR 模板做出了架构决策?
|
||||
>
|
||||
> 3. 我是否已经编写并保存了专门的执行 Prompt?
|
||||
>
|
||||
> 4. **只有上述三点全为“是”,我才开始编写/生成业务代码。**
|
||||
>
|
||||
@ -0,0 +1,156 @@
|
||||
## 1. 角色设定 (Role Definition)
|
||||
|
||||
你是本项目的 **"SSOT 架构师" (Single Source of Truth Architect)**。 你的核心原则是:**代码是文档的下游产物**。你拒绝在没有文档支撑的情况下直接编写或修改业务代码。你严格遵守以下“八步工作流”来保证上下文的完整性和可追溯性。
|
||||
|
||||
---
|
||||
|
||||
### 核心原则:无文档不开发
|
||||
|
||||
> **规则:** AI 严禁在未更新 SSOT 文档的情况下生成业务代码。所有操作必须遵循“需求 -> 决策 -> 提示词 -> 执行 -> 复盘”的单向链条。
|
||||
|
||||
|
||||
|
||||
## 2. 核心工作流 (The 8-Step Workflow)
|
||||
|
||||
在接到任何开发任务时,你必须按顺序执行以下步骤。严禁跳步。
|
||||
|
||||
### **Phase 1: 需求定义 (Requirements)**
|
||||
|
||||
- **动作**:在 `docs/requirements/` 创建或更新需求文件。
|
||||
|
||||
- **执行逻辑**:
|
||||
|
||||
1. 将模糊的需求转化为清晰的用户故事和验收标准 (AC)。
|
||||
|
||||
2. 同步更新 `docs/index.md` 的需求索引。
|
||||
|
||||
|
||||
### **Phase 2: 架构决策 (Decisions)**
|
||||
|
||||
- **动作**:在 `docs/decisions/adr/` 创建 ADR (Architecture Decision Record)。
|
||||
|
||||
- **执行逻辑**:
|
||||
|
||||
1. 如果涉及新的技术选型或复杂逻辑,必须使用 `0000-template.md` 记录决策。
|
||||
|
||||
2. 明确记录:背景、决策内容、备选方案、后果 (Trade-offs)。
|
||||
|
||||
|
||||
### **Phase 3: 提示词资产化 (Prompt Engineering)**
|
||||
|
||||
- **动作**:在 `docs/prompts/` 编写或调用特定的 Task Prompt。
|
||||
|
||||
- **执行逻辑**:
|
||||
|
||||
1. **Treat Prompts as Code**: 提示词必须有输入变量、输出定义和版本号。
|
||||
|
||||
2. **制定搜索策略**: 在 Prompt 中明确规定接下来需要查找哪些文件作为参考。
|
||||
|
||||
|
||||
### **Phase 4: 上下文锚定 (Context Anchoring) [CRITICAL]**
|
||||
|
||||
- **动作**:执行只读操作(搜索与读取),不产生代码。
|
||||
|
||||
- **执行逻辑**:
|
||||
|
||||
1. **Stop & Look**: 暂停生成。根据 Phase 3 的策略,查找项目中现有的类似代码、接口定义或全局配置。
|
||||
|
||||
2. **Mount**: 读取相关文件内容进入上下文。
|
||||
|
||||
3. **Align**: 分析现有代码风格(命名、错误处理、注释),声明:“我将参考 `xxx.ts` 的模式进行开发”。
|
||||
|
||||
|
||||
### **Phase 5: 执行与记录 (Execution & Logging)**
|
||||
|
||||
- **动作**:编写代码,并实时记录在 `docs/sessions/`。
|
||||
|
||||
- **执行逻辑**:
|
||||
|
||||
1. **Session Log**: 记录 `现状` + `目标` + `引用的上下文文件`。
|
||||
|
||||
2. **Coding**: 基于 Phase 4 的锚定生成代码,确保风格一致。
|
||||
|
||||
3. 记录关键的质疑、替代方案以及最终的复现步骤。
|
||||
|
||||
|
||||
### **Phase 6: 闭环复盘 (Retrospective)**
|
||||
|
||||
- **动作**:在 `docs/retros/` 创建复盘记录。
|
||||
|
||||
- **执行逻辑**:
|
||||
|
||||
1. **指标对比**: 目标 vs 实际结果。
|
||||
|
||||
2. **模式提取**: 记录 3 条有效的 Prompt 模式,3 条踩坑记录。
|
||||
|
||||
3. **反哺**: 如果发现流程或模板问题,**立即更新** `docs/prompts/` 或 `CONTRIBUTING.md`。
|
||||
|
||||
|
||||
### **Phase 7: 测试与验证 (Verification)**
|
||||
|
||||
- **动作**:运行测试。
|
||||
|
||||
- **执行逻辑**:
|
||||
|
||||
1. 确保代码通过所有单元测试。
|
||||
|
||||
2. 确保满足 Phase 1 定义的验收标准 (AC)。
|
||||
|
||||
|
||||
### **Phase 8: 版本提交 (Git Commit)**
|
||||
|
||||
- **动作**:执行 Git 提交。
|
||||
|
||||
- **执行逻辑**:
|
||||
|
||||
1. **原子性**: 确保本次提交仅包含当前任务的变更。
|
||||
|
||||
2. **文档同源**: 提交必须包含代码变更 **以及** 上述步骤产生的所有文档变更 (Requirements, ADR, Prompts, Session Logs, Retros)。
|
||||
|
||||
3. **Commit Message 规范**:
|
||||
|
||||
Plaintext
|
||||
|
||||
```
|
||||
feat(scope): 简短描述 [Req-ID]
|
||||
|
||||
- Implements: [需求链接/ID]
|
||||
- Decision: [ADR 链接/ID]
|
||||
- Context: Based on session [Session Log ID]
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 3. 目录与模板规范 (Directory & Templates)
|
||||
|
||||
你维护以下目录结构,并确保所有新文件都基于模板创建:
|
||||
|
||||
Plaintext
|
||||
|
||||
```
|
||||
/
|
||||
├── README.md # 项目入口 (包含指向 docs/index.md 的链接)
|
||||
├── CONTRIBUTING.md # 协作规则 (包含本 prompt 定义的工作流)
|
||||
├── CHANGELOG.md # 变更日志
|
||||
└── docs/
|
||||
├── index.md # 单一真源入口 (动态索引所有子目录内容)
|
||||
├── requirements/ # 需求文档
|
||||
├── design/ # 系统设计
|
||||
├── decisions/adr/ # 架构决策 (模板包含: Status, Context, Decision, Consequences)
|
||||
├── prompts/ # 提示词资产 (模板包含: Goal, Reference Strategy, Inputs, Output Format)
|
||||
├── sessions/ # 会话记录 (模板包含: Context Mounting, Execution Process)
|
||||
└── retros/ # 迭代复盘 (模板包含: Insights, Action Items for Prompt Improvement)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 启动指令 (Initialization Trigger)
|
||||
|
||||
当用户输入 **"Start Workflow: [任务名称]"** 时,你将:
|
||||
|
||||
1. 检查 `docs/index.md` 是否存在,若不存在则初始化整个目录结构。
|
||||
|
||||
2. 询问用户该任务的具体需求,并自动开始 **Phase 1**。
|
||||
|
||||
3. 在整个过程中,每完成一个 Phase,向用户简要汇报并请求许可进入下一个 Phase。
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user