|
|
@@ -0,0 +1,719 @@
|
|
|
+package org.jeecg.modules.bill.service.impl;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.commons.lang.StringUtils;
|
|
|
+import org.apache.shiro.SecurityUtils;
|
|
|
+import org.jeecg.common.system.vo.LoginUser;
|
|
|
+import org.jeecg.common.util.MinioUtil;
|
|
|
+import org.jeecg.common.util.oConvertUtils;
|
|
|
+import org.jeecg.modules.bill.entity.BillBatch;
|
|
|
+import org.jeecg.modules.bill.entity.BillCommonFile;
|
|
|
+import org.jeecg.modules.bill.entity.BillCustomer;
|
|
|
+import org.jeecg.modules.bill.entity.BillCustomerExcel;
|
|
|
+import org.jeecg.modules.bill.mapper.BillBatchMapper;
|
|
|
+import org.jeecg.modules.bill.mapper.BillCustomerMapper;
|
|
|
+import org.jeecg.modules.bill.service.IBillBatchService;
|
|
|
+import org.jeecg.modules.bill.service.IBillCommonFileService;
|
|
|
+import org.jeecg.modules.bill.service.IBillCustomerService;
|
|
|
+import org.jeecg.modules.bill.service.IBillEmailService;
|
|
|
+import org.jeecgframework.poi.excel.ExcelImportUtil;
|
|
|
+import org.jeecgframework.poi.excel.entity.ImportParams;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+import org.springframework.web.multipart.MultipartFile;
|
|
|
+
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
+import java.util.*;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @Description: 账单批次管理Service实现类
|
|
|
+ * @Author: jeecg-boot
|
|
|
+ * @Date: 2025-01-27
|
|
|
+ * @Version: V1.0
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+public class BillBatchServiceImpl extends ServiceImpl<BillBatchMapper, BillBatch> implements IBillBatchService {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IBillCustomerService billCustomerService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IBillCommonFileService billCommonFileService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private BillCustomerMapper billCustomerMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IBillEmailService billEmailService;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Map<String, Object> importExcelPreview(MultipartFile file, String batchId) {
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ List<BillCustomerExcel> customerList = new ArrayList<>();
|
|
|
+
|
|
|
+ // 用于校验文件内重复:Key = contractNo + "|" + email
|
|
|
+ Set<String> fileKeys = new HashSet<>();
|
|
|
+ // 用于校验数据库重复
|
|
|
+ Set<String> dbKeys = new HashSet<>();
|
|
|
+ if (StringUtils.isNotBlank(batchId)) {
|
|
|
+ List<BillCustomer> existedCustomers = billCustomerMapper.selectByBatchId(batchId);
|
|
|
+ for (BillCustomer c : existedCustomers) {
|
|
|
+ dbKeys.add((c.getContractNo() == null ? "" : c.getContractNo().trim()) + "|" + (c.getEmail() == null ? "" : c.getEmail().trim()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ ImportParams params = new ImportParams();
|
|
|
+ params.setTitleRows(0);
|
|
|
+ params.setHeadRows(1);
|
|
|
+ params.setNeedSave(false);
|
|
|
+
|
|
|
+ List<BillCustomerExcel> list = ExcelImportUtil.importExcel(file.getInputStream(), BillCustomerExcel.class, params);
|
|
|
+
|
|
|
+ int newCount = 0;
|
|
|
+ int coverCount = 0;
|
|
|
+ int dupCount = 0;
|
|
|
+
|
|
|
+ // 验证数据
|
|
|
+ for (int i = 0; i < list.size(); i++) {
|
|
|
+ BillCustomerExcel excel = list.get(i);
|
|
|
+ excel.setSerialNo(i + 1);
|
|
|
+
|
|
|
+ String key = (excel.getContractNo() == null ? "" : excel.getContractNo().trim()) + "|" + (excel.getEmail() == null ? "" : excel.getEmail().trim());
|
|
|
+
|
|
|
+ // 1. 基础验证
|
|
|
+ String validateResult = validateCustomerExcel(excel);
|
|
|
+ excel.setValidateResult(validateResult);
|
|
|
+ excel.setValid(StringUtils.isBlank(validateResult));
|
|
|
+
|
|
|
+ // 2. 状态标识逻辑
|
|
|
+ if (fileKeys.contains(key)) {
|
|
|
+ excel.setImportStatus(2); // 文件内重复
|
|
|
+ dupCount++;
|
|
|
+ if (excel.isValid()) {
|
|
|
+ excel.setValid(false);
|
|
|
+ excel.setValidateResult("邮箱+合同号重复");
|
|
|
+ }
|
|
|
+ } else if (dbKeys.contains(key)) {
|
|
|
+ excel.setImportStatus(1); // 覆盖
|
|
|
+ coverCount++;
|
|
|
+ fileKeys.add(key);
|
|
|
+ } else {
|
|
|
+ excel.setImportStatus(0); // 新增
|
|
|
+ newCount++;
|
|
|
+ fileKeys.add(key);
|
|
|
+ }
|
|
|
+
|
|
|
+ customerList.add(excel);
|
|
|
+ }
|
|
|
+
|
|
|
+ result.put("success", true);
|
|
|
+ result.put("data", customerList);
|
|
|
+ result.put("total", customerList.size());
|
|
|
+ result.put("validCount", customerList.stream().mapToInt(c -> c.isValid() ? 1 : 0).sum());
|
|
|
+ result.put("invalidCount", customerList.stream().mapToInt(c -> c.isValid() ? 0 : 1).sum());
|
|
|
+ // 统计各状态数量
|
|
|
+ result.put("newCount", newCount);
|
|
|
+ result.put("coverCount", coverCount);
|
|
|
+ result.put("dupCount", dupCount);
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("Excel导入预览失败", e);
|
|
|
+ result.put("success", false);
|
|
|
+ result.put("message", "Excel导入预览失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 验证客户Excel数据
|
|
|
+ */
|
|
|
+ private String validateCustomerExcel(BillCustomerExcel excel) {
|
|
|
+ List<String> errors = new ArrayList<>();
|
|
|
+
|
|
|
+ // 必填项:仅邮箱、合同号、账单种类
|
|
|
+ if (StringUtils.isBlank(excel.getEmail())) {
|
|
|
+ errors.add("邮箱不能为空");
|
|
|
+ } else if (!isValidEmail(excel.getEmail())) {
|
|
|
+ errors.add("邮箱格式不正确");
|
|
|
+ }
|
|
|
+ if (StringUtils.isBlank(excel.getContractNo())) {
|
|
|
+ errors.add("合同号不能为空");
|
|
|
+ }
|
|
|
+ if (StringUtils.isBlank(excel.getBillType())) {
|
|
|
+ errors.add("账单种类不能为空");
|
|
|
+ } else {
|
|
|
+ // 账单种类只能包含:A、B、D、移
|
|
|
+ String billType = excel.getBillType().trim();
|
|
|
+ for (int i = 0; i < billType.length(); i++) {
|
|
|
+ char c = billType.charAt(i);
|
|
|
+ if (c != 'A' && c != 'B' && c != 'D' && c != '移') {
|
|
|
+ errors.add("账单种类只能包含:A、B、D、移");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return errors.isEmpty() ? null : String.join("; ", errors);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 验证邮箱格式
|
|
|
+ */
|
|
|
+ private boolean isValidEmail(String email) {
|
|
|
+ if (StringUtils.isBlank(email)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return email.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public Map<String, Object> confirmImport(BillBatch batch, List<BillCustomerExcel> customerList) {
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ int successCount = 0;
|
|
|
+ int errorCount = 0;
|
|
|
+ List<String> errorMessages = new ArrayList<>();
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 获取当前登录用户
|
|
|
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
|
|
+ if (loginUser == null) {
|
|
|
+ throw new RuntimeException("未获取到当前登录用户");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (customerList == null) {
|
|
|
+ customerList = new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 新增批次:id 为空则创建批次;编辑批次:id 不为空则只更新规则
|
|
|
+ boolean isNewBatch = StringUtils.isBlank(batch.getId());
|
|
|
+ if (isNewBatch) {
|
|
|
+ // 验证用户邮箱
|
|
|
+ if (StringUtils.isBlank(loginUser.getEmail())) {
|
|
|
+ throw new RuntimeException("当前用户未绑定邮箱,无法创建批次");
|
|
|
+ }
|
|
|
+
|
|
|
+ String batchNo = generateBatchNo();
|
|
|
+ batch.setBatchNo(batchNo);
|
|
|
+ batch.setSenderEmail(loginUser.getEmail());
|
|
|
+ batch.setSenderUserId(loginUser.getId());
|
|
|
+ batch.setImportTime(new Date());
|
|
|
+ batch.setCustomerCount(0);
|
|
|
+ batch.setSentCount(0);
|
|
|
+ batch.setCreateTime(new Date());
|
|
|
+ this.save(batch);
|
|
|
+ } else {
|
|
|
+ BillBatch dbBatch = this.getById(batch.getId());
|
|
|
+ if (dbBatch == null) {
|
|
|
+ throw new RuntimeException("批次不存在");
|
|
|
+ }
|
|
|
+ dbBatch.setSendRuleType(batch.getSendRuleType());
|
|
|
+ dbBatch.setSendDate(batch.getSendDate());
|
|
|
+ if (batch.getSendPeriods() != null) {
|
|
|
+ dbBatch.setSendPeriods(batch.getSendPeriods());
|
|
|
+ }
|
|
|
+ dbBatch.setUpdateTime(new Date());
|
|
|
+ this.updateById(dbBatch);
|
|
|
+ batch = dbBatch;
|
|
|
+ }
|
|
|
+
|
|
|
+ Date sendDate = calculateSendDate(batch.getSendRuleType(), batch.getSendDate());
|
|
|
+
|
|
|
+ // Excel 内重复 key 以最后一条为准(覆盖)
|
|
|
+ Map<String, BillCustomerExcel> latestByKey = new LinkedHashMap<>();
|
|
|
+ for (BillCustomerExcel excel : customerList) {
|
|
|
+ if (excel == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ String key = (excel.getContractNo() == null ? "" : excel.getContractNo().trim()) + "|" + (excel.getEmail() == null ? "" : excel.getEmail().trim());
|
|
|
+ latestByKey.put(key, excel);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (BillCustomerExcel excel : latestByKey.values()) {
|
|
|
+ if (excel == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (!excel.isValid()) {
|
|
|
+ errorCount++;
|
|
|
+ errorMessages.add("第 " + excel.getSerialNo() + " 行:" + (excel.getValidateResult() == null ? "数据无效" : excel.getValidateResult()));
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 根据 batchId + contractNo + email 查找是否存在
|
|
|
+ BillCustomer existed = billCustomerMapper.selectByBatchAndKey(batch.getId(), excel.getContractNo(), excel.getEmail());
|
|
|
+
|
|
|
+ if (existed == null) {
|
|
|
+ BillCustomer customer = new BillCustomer();
|
|
|
+ customer.setBatchId(batch.getId());
|
|
|
+ customer.setSerialNo(excel.getSerialNo());
|
|
|
+ customer.setCustomerName(excel.getCustomerName());
|
|
|
+ customer.setEmail(excel.getEmail());
|
|
|
+ customer.setContractNo(excel.getContractNo());
|
|
|
+ customer.setDeveloperEmail(excel.getDeveloperEmail());
|
|
|
+ customer.setBillType(excel.getBillType());
|
|
|
+ customer.setNewManager(excel.getNewManager());
|
|
|
+ customer.setContactPerson(excel.getContactPerson());
|
|
|
+ customer.setContactPhone(excel.getContactPhone());
|
|
|
+ customer.setNewDeveloper(excel.getNewDeveloper());
|
|
|
+ customer.setSendDate(sendDate);
|
|
|
+ customer.setSendStatus(0);
|
|
|
+ customer.setCreateTime(new Date());
|
|
|
+ billCustomerService.save(customer);
|
|
|
+ } else {
|
|
|
+ // 覆盖其余字段(保留发送状态/发送时间/发送结果等发送过程信息)
|
|
|
+ existed.setSerialNo(excel.getSerialNo());
|
|
|
+ existed.setCustomerName(excel.getCustomerName());
|
|
|
+ existed.setDeveloperEmail(excel.getDeveloperEmail());
|
|
|
+ existed.setBillType(excel.getBillType());
|
|
|
+ existed.setNewManager(excel.getNewManager());
|
|
|
+ existed.setContactPerson(excel.getContactPerson());
|
|
|
+ existed.setContactPhone(excel.getContactPhone());
|
|
|
+ existed.setNewDeveloper(excel.getNewDeveloper());
|
|
|
+ existed.setSendDate(sendDate);
|
|
|
+ billCustomerService.updateById(existed);
|
|
|
+ }
|
|
|
+
|
|
|
+ successCount++;
|
|
|
+ } catch (Exception e) {
|
|
|
+ errorCount++;
|
|
|
+ errorMessages.add("第 " + excel.getSerialNo() + " 行:处理失败 - " + e.getMessage());
|
|
|
+ log.error("处理客户数据失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 重新统计并更新批次计数
|
|
|
+ int customerCount = billCustomerMapper.countByBatchId(batch.getId());
|
|
|
+ int sentCount = billCustomerMapper.countSentByBatchId(batch.getId());
|
|
|
+ batch.setCustomerCount(customerCount);
|
|
|
+ batch.setSentCount(sentCount);
|
|
|
+ batch.setUpdateTime(new Date());
|
|
|
+ this.updateById(batch);
|
|
|
+
|
|
|
+ result.put("success", true);
|
|
|
+ result.put("successCount", successCount);
|
|
|
+ result.put("errorCount", errorCount);
|
|
|
+ result.put("errorMessages", errorMessages);
|
|
|
+ result.put("batchId", batch.getId());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("确认导入失败", e);
|
|
|
+ result.put("success", false);
|
|
|
+ result.put("message", "确认导入失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public Map<String, Object> confirmImportWithFiles(BillBatch batch, List<BillCustomerExcel> customerList, MultipartFile[] files) {
|
|
|
+ boolean isNewBatch = org.apache.commons.lang.StringUtils.isBlank(batch.getId());
|
|
|
+ Map<String, Object> result = confirmImport(batch, customerList);
|
|
|
+ if (Boolean.TRUE.equals(result.get("success")) && isNewBatch && files != null && files.length > 0) {
|
|
|
+ String batchId = (String) result.get("batchId");
|
|
|
+ Map<String, Object> uploadResult = uploadContractFiles(files, batchId);
|
|
|
+ result.put("fileSuccessCount", uploadResult.get("successCount"));
|
|
|
+ result.put("fileCoverCount", uploadResult.get("coverCount"));
|
|
|
+ result.put("fileFailCount", uploadResult.get("failCount"));
|
|
|
+ result.put("fileErrorMessages", uploadResult.get("errorMessages"));
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Date calculateSendDate(Integer sendRuleType, String sendDate) {
|
|
|
+ if (sendRuleType == null || StringUtils.isBlank(sendDate)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ if (sendRuleType == 1) {
|
|
|
+ // 固定年月日
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
+ return sdf.parse(sendDate);
|
|
|
+ } else if (sendRuleType == 2) {
|
|
|
+ // 每月几日
|
|
|
+ int day = Integer.parseInt(sendDate);
|
|
|
+ Calendar cal = Calendar.getInstance();
|
|
|
+ int currentYear = cal.get(Calendar.YEAR);
|
|
|
+ int currentMonth = cal.get(Calendar.MONTH) + 1;
|
|
|
+ int maxDay = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
|
|
|
+ if (day > maxDay) {
|
|
|
+ day = maxDay;
|
|
|
+ }
|
|
|
+ cal.set(currentYear, currentMonth - 1, day, 0, 0, 0);
|
|
|
+ cal.set(Calendar.MILLISECOND, 0);
|
|
|
+ return cal.getTime();
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("计算发送日期失败", e);
|
|
|
+ }
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String generateBatchNo() {
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
|
|
|
+ String timestamp = sdf.format(new Date());
|
|
|
+ String random = String.valueOf((int)(Math.random() * 10000));
|
|
|
+ return "BILL" + timestamp + random;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Map<String, Object> statistics(String batchNo, Integer sendRuleType) {
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+
|
|
|
+ // 1. 根据条件查询符合条件的批次ID列表
|
|
|
+ LambdaQueryWrapper<BillBatch> batchWrapper = new LambdaQueryWrapper<>();
|
|
|
+ if (StringUtils.isNotBlank(batchNo)) {
|
|
|
+ batchWrapper.like(BillBatch::getBatchNo, batchNo);
|
|
|
+ }
|
|
|
+ if (sendRuleType != null) {
|
|
|
+ batchWrapper.eq(BillBatch::getSendRuleType, sendRuleType);
|
|
|
+ }
|
|
|
+ List<BillBatch> batches = this.list(batchWrapper);
|
|
|
+
|
|
|
+ if (batches.isEmpty()) {
|
|
|
+ result.put("totalCount", 0);
|
|
|
+ result.put("sentCount", 0);
|
|
|
+ result.put("pendingCount", 0);
|
|
|
+ result.put("failCount", 0);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ List<String> batchIds = batches.stream().map(BillBatch::getId).collect(Collectors.toList());
|
|
|
+
|
|
|
+ // 2. 统计客户明细
|
|
|
+ // 总记录数
|
|
|
+ LambdaQueryWrapper<BillCustomer> totalWrapper = new LambdaQueryWrapper<>();
|
|
|
+ totalWrapper.in(BillCustomer::getBatchId, batchIds);
|
|
|
+ int totalCount = (int) billCustomerService.count(totalWrapper);
|
|
|
+
|
|
|
+ // 已发送成功 (sendStatus = 1)
|
|
|
+ LambdaQueryWrapper<BillCustomer> sentWrapper = new LambdaQueryWrapper<>();
|
|
|
+ sentWrapper.in(BillCustomer::getBatchId, batchIds);
|
|
|
+ sentWrapper.eq(BillCustomer::getSendStatus, 1);
|
|
|
+ int sentCount = (int) billCustomerService.count(sentWrapper);
|
|
|
+
|
|
|
+ // 待发送 (sendStatus = 0 或 null)
|
|
|
+ LambdaQueryWrapper<BillCustomer> pendingWrapper = new LambdaQueryWrapper<>();
|
|
|
+ pendingWrapper.in(BillCustomer::getBatchId, batchIds);
|
|
|
+ pendingWrapper.and(w -> w.eq(BillCustomer::getSendStatus, 0).or().isNull(BillCustomer::getSendStatus));
|
|
|
+ int pendingCount = (int) billCustomerService.count(pendingWrapper);
|
|
|
+
|
|
|
+ // 发送失败 (sendStatus = 2)
|
|
|
+ LambdaQueryWrapper<BillCustomer> failWrapper = new LambdaQueryWrapper<>();
|
|
|
+ failWrapper.in(BillCustomer::getBatchId, batchIds);
|
|
|
+ failWrapper.eq(BillCustomer::getSendStatus, 2);
|
|
|
+ int failCount = (int) billCustomerService.count(failWrapper);
|
|
|
+
|
|
|
+ result.put("totalCount", totalCount);
|
|
|
+ result.put("sentCount", sentCount);
|
|
|
+ result.put("pendingCount", pendingCount);
|
|
|
+ result.put("failCount", failCount);
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public Map<String, Object> sendEmail(String customerId) {
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ try {
|
|
|
+ BillCustomer customer = billCustomerService.getById(customerId);
|
|
|
+ if (customer == null) {
|
|
|
+ result.put("success", false);
|
|
|
+ result.put("message", "客户信息不存在");
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 发送邮件
|
|
|
+ boolean success = billEmailService.sendBillEmail(customer);
|
|
|
+ if (success) {
|
|
|
+ // 更新发送状态
|
|
|
+ customer.setSendStatus(1);
|
|
|
+ customer.setSendTime(new Date());
|
|
|
+ customer.setSendResult("发送成功");
|
|
|
+ customer.setErrorMsg(null);
|
|
|
+ billCustomerService.updateById(customer);
|
|
|
+
|
|
|
+ // 更新批次已发送数量
|
|
|
+ updateBatchSentCount(customer.getBatchId());
|
|
|
+
|
|
|
+ result.put("success", true);
|
|
|
+ result.put("message", "邮件发送成功");
|
|
|
+ } else {
|
|
|
+ // 更新发送状态
|
|
|
+ customer.setSendStatus(2);
|
|
|
+ customer.setSendTime(new Date());
|
|
|
+ customer.setSendResult("发送失败");
|
|
|
+ customer.setErrorMsg("邮件服务器返回失败");
|
|
|
+ billCustomerService.updateById(customer);
|
|
|
+
|
|
|
+ result.put("success", false);
|
|
|
+ result.put("message", "邮件发送失败");
|
|
|
+ }
|
|
|
+ } catch (RuntimeException e) {
|
|
|
+ if ("无关联账单".equals(e.getMessage())) {
|
|
|
+ log.warn("客户 {} 无关联账单,标记为发送失败", customerId);
|
|
|
+ BillCustomer customer = billCustomerService.getById(customerId);
|
|
|
+ if (customer != null) {
|
|
|
+ customer.setSendStatus(2);
|
|
|
+ customer.setSendTime(new Date());
|
|
|
+ customer.setSendResult("无关联账单");
|
|
|
+ customer.setErrorMsg("未匹配到账单附件");
|
|
|
+ billCustomerService.updateById(customer);
|
|
|
+ }
|
|
|
+ result.put("success", false);
|
|
|
+ result.put("message", "无关联账单");
|
|
|
+ } else {
|
|
|
+ log.error("发送邮件异常", e);
|
|
|
+ result.put("success", false);
|
|
|
+ result.put("message", "发送邮件异常: " + e.getMessage());
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("发送邮件失败", e);
|
|
|
+ result.put("success", false);
|
|
|
+ result.put("message", "发送邮件异常: " + e.getMessage());
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public Map<String, Object> sendBatchEmails(String batchId) {
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ int successCount = 0;
|
|
|
+ int failCount = 0;
|
|
|
+ List<String> errorMessages = new ArrayList<>();
|
|
|
+
|
|
|
+ try {
|
|
|
+ BillBatch batch = this.getById(batchId);
|
|
|
+ if (batch == null) {
|
|
|
+ result.put("success", false);
|
|
|
+ result.put("message", "批次不存在");
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询未发送的客户
|
|
|
+ List<BillCustomer> customers = billCustomerMapper.selectByBatchId(batchId);
|
|
|
+ for (BillCustomer customer : customers) {
|
|
|
+ if (customer.getSendStatus() != null && customer.getSendStatus() == 1) {
|
|
|
+ // 已发送,跳过
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ Map<String, Object> sendResult = sendEmail(customer.getId());
|
|
|
+ if ((Boolean) sendResult.get("success")) {
|
|
|
+ successCount++;
|
|
|
+ } else {
|
|
|
+ failCount++;
|
|
|
+ errorMessages.add("客户 " + customer.getCustomerName() + "(" + customer.getEmail() + ") 发送失败: " + sendResult.get("message"));
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ failCount++;
|
|
|
+ errorMessages.add("客户 " + customer.getCustomerName() + "(" + customer.getEmail() + ") 发送异常: " + e.getMessage());
|
|
|
+ log.error("发送客户邮件失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ result.put("success", true);
|
|
|
+ result.put("successCount", successCount);
|
|
|
+ result.put("failCount", failCount);
|
|
|
+ result.put("errorMessages", errorMessages);
|
|
|
+ result.put("message", "批量发送完成,成功: " + successCount + ",失败: " + failCount);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("批量发送邮件失败", e);
|
|
|
+ result.put("success", false);
|
|
|
+ result.put("message", "批量发送邮件异常: " + e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public Map<String, Object> resendEmail(String customerId) {
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ try {
|
|
|
+ BillCustomer customer = billCustomerService.getById(customerId);
|
|
|
+ if (customer == null) {
|
|
|
+ result.put("success", false);
|
|
|
+ result.put("message", "客户信息不存在");
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 重置发送状态
|
|
|
+ customer.setSendStatus(0);
|
|
|
+ customer.setSendTime(null);
|
|
|
+ customer.setSendResult(null);
|
|
|
+ customer.setErrorMsg(null);
|
|
|
+ billCustomerService.updateById(customer);
|
|
|
+
|
|
|
+ // 重新发送
|
|
|
+ return sendEmail(customerId);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("重新发送邮件失败", e);
|
|
|
+ result.put("success", false);
|
|
|
+ result.put("message", "重新发送邮件异常: " + e.getMessage());
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新批次已发送数量
|
|
|
+ */
|
|
|
+ private void updateBatchSentCount(String batchId) {
|
|
|
+ try {
|
|
|
+ int sentCount = billCustomerMapper.countSentByBatchId(batchId);
|
|
|
+ BillBatch batch = this.getById(batchId);
|
|
|
+ if (batch != null) {
|
|
|
+ batch.setSentCount(sentCount);
|
|
|
+ this.updateById(batch);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("更新批次已发送数量失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public Map<String, Object> uploadContractFiles(MultipartFile[] files, String batchId) {
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ int successCount = 0;
|
|
|
+ int coverCount = 0;
|
|
|
+ int failCount = 0;
|
|
|
+ List<String> errorMessages = new ArrayList<>();
|
|
|
+ List<BillCommonFile> fileList = new ArrayList<>();
|
|
|
+
|
|
|
+ try {
|
|
|
+ for (MultipartFile file : files) {
|
|
|
+ try {
|
|
|
+ if (file.isEmpty()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ String fileName = file.getOriginalFilename();
|
|
|
+
|
|
|
+ // 检查是否已存在同名文件(同一批次下)
|
|
|
+ QueryWrapper<BillCommonFile> queryWrapper = new QueryWrapper<>();
|
|
|
+ queryWrapper.eq("file_name", fileName);
|
|
|
+ if (StringUtils.isNotBlank(batchId)) {
|
|
|
+ queryWrapper.eq("batch_id", batchId);
|
|
|
+ } else {
|
|
|
+ queryWrapper.isNull("batch_id");
|
|
|
+ }
|
|
|
+ BillCommonFile existFile = billCommonFileService.getOne(queryWrapper);
|
|
|
+
|
|
|
+ boolean isCover = false;
|
|
|
+ if (existFile != null) {
|
|
|
+ // 删除旧文件(使用Service的deleteFile方法)
|
|
|
+ billCommonFileService.deleteFile(existFile.getId());
|
|
|
+ isCover = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用billCommonFileService的uploadFile方法上传文件
|
|
|
+ BillCommonFile uploadedFile = billCommonFileService.uploadFile(file);
|
|
|
+
|
|
|
+ // 设置批次ID
|
|
|
+ if (uploadedFile != null) {
|
|
|
+ uploadedFile.setBatchId(batchId);
|
|
|
+ billCommonFileService.updateById(uploadedFile);
|
|
|
+ fileList.add(uploadedFile);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isCover) {
|
|
|
+ coverCount++;
|
|
|
+ }
|
|
|
+ successCount++;
|
|
|
+ } catch (Exception e) {
|
|
|
+ failCount++;
|
|
|
+ errorMessages.add(file.getOriginalFilename() + ": " + e.getMessage());
|
|
|
+ log.error("上传文件失败: " + file.getOriginalFilename(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ result.put("success", true);
|
|
|
+ result.put("successCount", successCount);
|
|
|
+ result.put("coverCount", coverCount);
|
|
|
+ result.put("failCount", failCount);
|
|
|
+ result.put("errorMessages", errorMessages);
|
|
|
+ result.put("fileList", fileList);
|
|
|
+ result.put("message", "上传完成:成功 " + successCount + " 个" + (coverCount > 0 ? ",覆盖 " + coverCount + " 个" : "") + (failCount > 0 ? ",失败 " + failCount + " 个" : ""));
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("批量上传文件失败", e);
|
|
|
+ result.put("success", false);
|
|
|
+ result.put("message", "批量上传文件失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<BillCommonFile> getContractFilesByBatchId(String batchId) {
|
|
|
+ QueryWrapper<BillCommonFile> queryWrapper = new QueryWrapper<>();
|
|
|
+ queryWrapper.eq("batch_id", batchId);
|
|
|
+ queryWrapper.orderByDesc("upload_time");
|
|
|
+ return billCommonFileService.list(queryWrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public boolean deleteContractFile(String fileId) {
|
|
|
+ try {
|
|
|
+ BillCommonFile file = billCommonFileService.getById(fileId);
|
|
|
+ if (file != null) {
|
|
|
+ // 删除物理文件
|
|
|
+ //deletePhysicalFile(file.getFilePath());
|
|
|
+ // 删除数据库记录
|
|
|
+ return billCommonFileService.removeById(fileId);
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("删除合同文件失败", e);
|
|
|
+ throw new RuntimeException("删除合同文件失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public boolean deleteContractFiles(List<String> fileIds) {
|
|
|
+ try {
|
|
|
+ for (String fileId : fileIds) {
|
|
|
+ deleteContractFile(fileId);
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("批量删除合同文件失败", e);
|
|
|
+ throw new RuntimeException("批量删除合同文件失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void deleteBatchCascade(String batchId) {
|
|
|
+ if (StringUtils.isBlank(batchId)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 1. 删除该批次下所有合同文件(含物理文件)
|
|
|
+ List<BillCommonFile> files = getContractFilesByBatchId(batchId);
|
|
|
+ for (BillCommonFile file : files) {
|
|
|
+ billCommonFileService.deleteFile(file.getId());
|
|
|
+ }
|
|
|
+ // 2. 删除该批次下所有客户
|
|
|
+ billCustomerMapper.deleteByBatchId(batchId);
|
|
|
+ // 3. 删除批次
|
|
|
+ this.removeById(batchId);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|