This commit is contained in:
van
2026-03-24 16:23:57 +08:00
parent 318cef274e
commit 066ab35a17
10 changed files with 429 additions and 8 deletions

View File

@@ -20,6 +20,9 @@ public class RebateRemarkItem {
/** 写入时间戳(毫秒) */
private Long uploadTime;
/** 对应 jd_group_rebate_excel_upload.id用于删除上传记录时精确撤销本条备注 */
private Long uploadRecordId;
/**
* 是否判定为异常(空「是否返现」、待补/下次做表等关键词)
* 便于列表筛选与着色

View File

@@ -8,6 +8,10 @@ public interface GroupRebateExcelUploadMapper {
int insertGroupRebateExcelUpload(GroupRebateExcelUpload row);
int updateGroupRebateExcelUpload(GroupRebateExcelUpload row);
int deleteGroupRebateExcelUploadById(Long id);
GroupRebateExcelUpload selectGroupRebateExcelUploadById(Long id);
List<GroupRebateExcelUpload> selectGroupRebateExcelUploadList(GroupRebateExcelUpload query);

View File

@@ -46,6 +46,11 @@ public interface JDOrderMapper {
*/
JDOrder selectJDOrderByThirdPartyOrderNo(String thirdPartyOrderNo);
/**
* 后返备注 JSON 中含指定 uploadRecordId 的订单主键(撤销导入时用)
*/
List<Long> selectOrderIdsByRebateRemarkUploadRecordId(Long uploadRecordId);
/**
* 批量删除根据主键ID
*/

View File

@@ -11,6 +11,8 @@ public interface IGroupRebateExcelUploadService {
*/
void saveRecordInNewTransaction(GroupRebateExcelUpload row);
void updateRecordInNewTransaction(GroupRebateExcelUpload row);
GroupRebateExcelUpload selectGroupRebateExcelUploadById(Long id);
List<GroupRebateExcelUpload> selectGroupRebateExcelUploadList(GroupRebateExcelUpload query);

View File

@@ -7,9 +7,11 @@ import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.jarvis.domain.GroupRebateExcelUpload;
import com.ruoyi.jarvis.domain.JDOrder;
import com.ruoyi.jarvis.domain.dto.RebateRemarkItem;
import com.ruoyi.jarvis.mapper.GroupRebateExcelUploadMapper;
import com.ruoyi.jarvis.mapper.JDOrderMapper;
import com.ruoyi.jarvis.service.IGroupRebateExcelUploadService;
import org.apache.poi.ss.usermodel.Cell;
@@ -38,6 +40,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Pattern;
@@ -62,6 +65,9 @@ public class GroupRebateExcelImportService {
@Resource
private JDOrderMapper jdOrderMapper;
@Resource
private GroupRebateExcelUploadMapper groupRebateExcelUploadMapper;
@Resource
private IGroupRebateExcelUploadService groupRebateExcelUploadService;
@@ -104,9 +110,10 @@ public class GroupRebateExcelImportService {
}
File diskFile = resolveDiskFile(webPath);
Long recordId = insertInitialUploadRecord(originalFilename, webPath, fileSize, title);
Map<String, Object> result;
try {
result = self.processWorkbookTransactional(diskFile, title);
result = self.processWorkbookTransactional(diskFile, title, recordId);
} catch (Exception e) {
log.error("后返表导入处理异常", e);
result = new HashMap<>();
@@ -115,13 +122,136 @@ public class GroupRebateExcelImportService {
result.put("documentTitle", title);
}
result.put("savedFilePath", webPath);
Long recordId = persistUploadRecord(originalFilename, webPath, fileSize, title, result);
finalizeUploadRecord(recordId, originalFilename, webPath, fileSize, title, result);
result.put("uploadRecordId", recordId);
return result;
}
/**
* 批量导入:按顺序逐个调用 {@link #importExcel},在同一请求内由后台依次处理完成。
*
* @param documentTitle 仅当恰好 1 个有效文件时作为文档标题;多文件时忽略,各文件用各自文件名。
*/
public Map<String, Object> importExcelBatch(MultipartFile[] files, String documentTitle) {
if (files == null || files.length == 0) {
Map<String, Object> empty = new HashMap<>();
empty.put("success", false);
empty.put("message", "请选择文件");
return empty;
}
List<MultipartFile> list = new ArrayList<>();
for (MultipartFile f : files) {
if (f != null && !f.isEmpty()) {
list.add(f);
}
}
if (list.isEmpty()) {
Map<String, Object> empty = new HashMap<>();
empty.put("success", false);
empty.put("message", "没有有效文件");
return empty;
}
boolean single = list.size() == 1;
String sharedTitle = (single && StringUtils.isNotEmpty(documentTitle)) ? documentTitle.trim() : null;
int ok = 0;
int fail = 0;
List<Map<String, Object>> failures = new ArrayList<>();
for (MultipartFile file : list) {
try {
Map<String, Object> r = importExcel(file, sharedTitle);
if (Boolean.TRUE.equals(r.get("success"))) {
ok++;
} else {
fail++;
addBatchFailureBrief(failures, file.getOriginalFilename(), r.get("message"));
}
} catch (Exception e) {
log.warn("批量后返表:单文件导入失败 {}", file.getOriginalFilename(), e);
fail++;
addBatchFailureBrief(failures, file.getOriginalFilename(), e.getMessage());
}
}
Map<String, Object> out = new HashMap<>();
out.put("success", true);
out.put("totalFiles", list.size());
out.put("successCount", ok);
out.put("failCount", fail);
out.put("message", String.format(Locale.ROOT,
"已处理 %d 个文件:成功 %d失败 %d。明细可在「后返上传记录」查看。",
list.size(), ok, fail));
if (!failures.isEmpty()) {
out.put("failures", failures);
}
return out;
}
private static void addBatchFailureBrief(List<Map<String, Object>> failures, String filename, Object message) {
if (failures.size() >= 15) {
return;
}
Map<String, Object> fb = new HashMap<>(2);
fb.put("filename", filename);
fb.put("message", message != null ? message.toString() : "");
failures.add(fb);
}
/**
* 删除一条上传记录:从订单 rebate_remark_json 中移除本次导入写入的条目,并删除数据库记录与磁盘文件。
*/
public Map<String, Object> deleteUploadRecord(Long id) {
if (id == null) {
throw new ServiceException("记录 ID 无效");
}
GroupRebateExcelUpload rec = groupRebateExcelUploadService.selectGroupRebateExcelUploadById(id);
if (rec == null) {
throw new ServiceException("上传记录不存在");
}
String filePath = rec.getFilePath();
Map<String, Object> data = self.deleteUploadRecordTransactional(id, rec);
deleteUploadDiskFileIfPresent(filePath);
return data;
}
@Transactional(rollbackFor = Exception.class)
public Map<String, Object> processWorkbookTransactional(File diskFile, String title) throws Exception {
public Map<String, Object> deleteUploadRecordTransactional(Long id, GroupRebateExcelUpload rec) {
Set<Long> orderIds = new LinkedHashSet<>();
List<Long> fromDetail = parseAffectedOrderIds(rec.getResultDetailJson());
if (fromDetail != null) {
orderIds.addAll(fromDetail);
}
if (orderIds.isEmpty()) {
List<Long> fromJson = jdOrderMapper.selectOrderIdsByRebateRemarkUploadRecordId(id);
if (fromJson != null) {
orderIds.addAll(fromJson);
}
}
int ordersUpdated = 0;
for (Long oid : orderIds) {
if (stripRebateRemarkItemsByUploadRecordId(oid, id)) {
ordersUpdated++;
}
}
int deleted = groupRebateExcelUploadMapper.deleteGroupRebateExcelUploadById(id);
if (deleted == 0) {
throw new ServiceException("删除上传记录失败");
}
Map<String, Object> data = new HashMap<>();
data.put("ordersScanned", orderIds.size());
data.put("ordersRemarkUpdated", ordersUpdated);
data.put("revertedByUploadRecordId", id);
if (orderIds.isEmpty()) {
data.put("message", "已删除上传记录;未找到可回滚的订单后返备注(多为本功能上线前的历史导入,请按需手工改订单)");
} else {
data.put("message", String.format(Locale.ROOT,
"已删除上传记录;已扫描 %d 个订单,从 %d 个订单移除了本次导入对应的后返备注",
orderIds.size(), ordersUpdated));
}
return data;
}
@Transactional(rollbackFor = Exception.class)
public Map<String, Object> processWorkbookTransactional(File diskFile, String title, Long uploadRecordId) throws Exception {
Map<String, Object> result = new HashMap<>();
try (InputStream is = new FileInputStream(diskFile); Workbook wb = WorkbookFactory.create(is)) {
Sheet sheet = wb.getNumberOfSheets() > 0 ? wb.getSheetAt(0) : null;
@@ -155,8 +285,40 @@ public class GroupRebateExcelImportService {
return result;
}
if (hm.whetherRebateCol >= 0) {
int orderDataRows = 0;
boolean anyWhetherFilled = false;
for (int r = headerRowIndex + 1; r <= sheet.getLastRowNum(); r++) {
Row scanRow = sheet.getRow(r);
if (scanRow == null) {
continue;
}
String orderNoScan = normalizeOrderNo(getCellText(scanRow, hm.orderCol));
if (orderNoScan.isEmpty()) {
continue;
}
orderDataRows++;
String w = trimToNull(getCellText(scanRow, hm.whetherRebateCol));
if (w != null) {
anyWhetherFilled = true;
break;
}
}
if (orderDataRows > 0 && !anyWhetherFilled) {
result.put("success", false);
result.put("message", "「是否返现」列全部为空,未写入任何订单,请补全该列后重新上传。");
result.put("documentTitle", title);
result.put("dataRows", orderDataRows);
result.put("updatedOrders", 0);
result.put("notFoundOrderNos", new ArrayList<>());
result.put("errors", new ArrayList<>());
return result;
}
}
int dataRows = 0;
int updatedOrders = 0;
Set<Long> affectedOrderIds = new LinkedHashSet<>();
Set<String> notFound = new LinkedHashSet<>();
List<String> errors = new ArrayList<>();
@@ -194,7 +356,8 @@ public class GroupRebateExcelImportService {
}
try {
appendRebateRemark(order, title, whether, amountStr);
appendRebateRemark(order, title, whether, amountStr, uploadRecordId);
affectedOrderIds.add(order.getId());
updatedOrders++;
} catch (Exception e) {
log.warn("写入后返备注失败 orderNo={} id={}", orderNo, order.getId(), e);
@@ -208,6 +371,7 @@ public class GroupRebateExcelImportService {
result.put("updatedOrders", updatedOrders);
result.put("notFoundOrderNos", new ArrayList<>(notFound));
result.put("errors", errors);
result.put("affectedOrderIds", new ArrayList<>(affectedOrderIds));
result.put("message", String.format(Locale.ROOT,
"解析完成:有效数据行 %d匹配并更新订单 %d未找到订单 %d 条",
dataRows, updatedOrders, notFound.size()));
@@ -215,18 +379,46 @@ public class GroupRebateExcelImportService {
}
}
private Long persistUploadRecord(String originalFilename, String webPath, long fileSize,
String title, Map<String, Object> procResult) {
private Long insertInitialUploadRecord(String originalFilename, String webPath, long fileSize, String title) {
GroupRebateExcelUpload row = new GroupRebateExcelUpload();
row.setOriginalFilename(originalFilename);
row.setFilePath(StringUtils.isNotEmpty(webPath) ? webPath : "");
row.setFilePath(webPath);
row.setFileSize(fileSize);
row.setDocumentTitle(title);
row.setImportStatus(1);
row.setDataRows(0);
row.setUpdatedOrders(0);
row.setNotFoundCount(0);
JSONObject d = new JSONObject();
d.put("message", "解析中");
d.put("documentTitle", title);
row.setResultDetailJson(d.toJSONString());
try {
row.setCreateBy(SecurityUtils.getUsername());
} catch (Exception e) {
row.setCreateBy("");
}
groupRebateExcelUploadService.saveRecordInNewTransaction(row);
return row.getId();
}
private void finalizeUploadRecord(Long recordId, String originalFilename, String webPath, long fileSize,
String title, Map<String, Object> procResult) {
GroupRebateExcelUpload row = new GroupRebateExcelUpload();
row.setId(recordId);
row.setOriginalFilename(originalFilename);
row.setFilePath(StringUtils.isNotEmpty(webPath) ? webPath : "");
row.setFileSize(fileSize);
row.setDocumentTitle(title);
applyProcResultToRow(row, procResult);
try {
groupRebateExcelUploadService.updateRecordInNewTransaction(row);
} catch (Exception e) {
log.error("更新后返上传记录失败 id={}", recordId, e);
}
}
private void applyProcResultToRow(GroupRebateExcelUpload row, Map<String, Object> procResult) {
boolean ok = Boolean.TRUE.equals(procResult.get("success"));
row.setImportStatus(ok ? 2 : 1);
row.setDataRows(asInt(procResult.get("dataRows")));
@@ -247,7 +439,25 @@ public class GroupRebateExcelImportService {
if (procResult.containsKey("savedFilePath")) {
detail.put("savedFilePath", procResult.get("savedFilePath"));
}
if (procResult.containsKey("affectedOrderIds")) {
detail.put("affectedOrderIds", procResult.get("affectedOrderIds"));
}
row.setResultDetailJson(detail.toJSONString());
}
private Long persistUploadRecord(String originalFilename, String webPath, long fileSize,
String title, Map<String, Object> procResult) {
GroupRebateExcelUpload row = new GroupRebateExcelUpload();
row.setOriginalFilename(originalFilename);
row.setFilePath(StringUtils.isNotEmpty(webPath) ? webPath : "");
row.setFileSize(fileSize);
row.setDocumentTitle(title);
try {
row.setCreateBy(SecurityUtils.getUsername());
} catch (Exception e) {
row.setCreateBy("");
}
applyProcResultToRow(row, procResult);
try {
groupRebateExcelUploadService.saveRecordInNewTransaction(row);
@@ -258,6 +468,69 @@ public class GroupRebateExcelImportService {
}
}
private static List<Long> parseAffectedOrderIds(String resultDetailJson) {
if (StringUtils.isEmpty(resultDetailJson)) {
return null;
}
try {
JSONObject o = JSON.parseObject(resultDetailJson);
if (o == null || !o.containsKey("affectedOrderIds")) {
return null;
}
return o.getList("affectedOrderIds", Long.class);
} catch (Exception e) {
return null;
}
}
private boolean stripRebateRemarkItemsByUploadRecordId(Long orderId, Long uploadRecordId) {
JDOrder order = jdOrderMapper.selectJDOrderById(orderId);
if (order == null) {
return false;
}
List<RebateRemarkItem> list = new ArrayList<>();
String existing = order.getRebateRemarkJson();
if (existing != null && !existing.trim().isEmpty()) {
try {
list.addAll(JSON.parseArray(existing, RebateRemarkItem.class));
} catch (Exception e) {
return false;
}
}
int before = list.size();
list.removeIf(it -> Objects.equals(uploadRecordId, it.getUploadRecordId()));
if (list.size() == before) {
return false;
}
boolean hasAbnormal = false;
for (RebateRemarkItem it : list) {
if (Boolean.TRUE.equals(it.getAbnormal())) {
hasAbnormal = true;
break;
}
}
JDOrder upd = new JDOrder();
upd.setId(order.getId());
upd.setRebateRemarkJson(list.isEmpty() ? "[]" : JSON.toJSONString(list));
upd.setRebateRemarkHasAbnormal(hasAbnormal ? 1 : 0);
jdOrderMapper.updateJDOrder(upd);
return true;
}
private void deleteUploadDiskFileIfPresent(String webPath) {
if (StringUtils.isEmpty(webPath)) {
return;
}
try {
File f = resolveDiskFile(webPath);
if (f.isFile() && !f.delete()) {
log.warn("删除后返上传文件失败: {}", f.getAbsolutePath());
}
} catch (Exception e) {
log.warn("删除后返上传文件异常 path={}", webPath, e);
}
}
private static Integer asInt(Object o) {
if (o == null) {
return null;
@@ -299,7 +572,8 @@ public class GroupRebateExcelImportService {
return new File(RuoYiConfig.getProfile(), sub);
}
private void appendRebateRemark(JDOrder order, String documentTitle, String whetherRebate, String rebateAmount) {
private void appendRebateRemark(JDOrder order, String documentTitle, String whetherRebate, String rebateAmount,
Long uploadRecordId) {
List<RebateRemarkItem> list = new ArrayList<>();
String existing = order.getRebateRemarkJson();
if (existing != null && !existing.trim().isEmpty()) {
@@ -317,6 +591,7 @@ public class GroupRebateExcelImportService {
item.setRebateAmount(rebateAmount);
item.setUploadTime(System.currentTimeMillis());
item.setAbnormal(isAbnormalWhetherRebate(whetherRebate));
item.setUploadRecordId(uploadRecordId);
list.add(item);
boolean hasAbnormal = false;

View File

@@ -22,6 +22,12 @@ public class GroupRebateExcelUploadServiceImpl implements IGroupRebateExcelUploa
groupRebateExcelUploadMapper.insertGroupRebateExcelUpload(row);
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void updateRecordInNewTransaction(GroupRebateExcelUpload row) {
groupRebateExcelUploadMapper.updateGroupRebateExcelUpload(row);
}
@Override
public GroupRebateExcelUpload selectGroupRebateExcelUploadById(Long id) {
return groupRebateExcelUploadMapper.selectGroupRebateExcelUploadById(id);

View File

@@ -33,6 +33,24 @@
)
</insert>
<update id="updateGroupRebateExcelUpload" parameterType="GroupRebateExcelUpload">
update jd_group_rebate_excel_upload
set document_title = #{documentTitle},
original_filename = #{originalFilename},
file_path = #{filePath},
file_size = #{fileSize},
import_status = #{importStatus},
data_rows = #{dataRows},
updated_orders = #{updatedOrders},
not_found_count = #{notFoundCount},
result_detail_json = #{resultDetailJson}
where id = #{id}
</update>
<delete id="deleteGroupRebateExcelUploadById" parameterType="long">
delete from jd_group_rebate_excel_upload where id = #{id}
</delete>
<select id="selectGroupRebateExcelUploadById" parameterType="long" resultMap="GroupRebateExcelUploadResult">
<include refid="selectCols"/>
where id = #{id}

View File

@@ -233,6 +233,16 @@
limit 1
</select>
<select id="selectOrderIdsByRebateRemarkUploadRecordId" parameterType="long" resultType="long">
select id from jd_order
where rebate_remark_json is not null
and char_length(trim(rebate_remark_json)) &gt; 2
and (
rebate_remark_json like concat('%"uploadRecordId":', #{uploadRecordId}, ',%')
or rebate_remark_json like concat('%"uploadRecordId":', #{uploadRecordId}, '}%')
)
</select>
<delete id="deleteJDOrderByIds" parameterType="long">
delete from jd_order where id in
<foreach collection="array" item="id" open="(" separator="," close=")">