Commit bffc7f52 by jianan

保费到期提醒20-新单跟进导出

parent 5986f1a8
...@@ -520,11 +520,7 @@ public class ApiPolicyFollowController { ...@@ -520,11 +520,7 @@ public class ApiPolicyFollowController {
@Operation(summary = "导出新单跟进列表") @Operation(summary = "导出新单跟进列表")
public void exportPolicyFollow(@RequestBody PolicyFollowQueryRequest policyFollowQueryRequest, public void exportPolicyFollow(@RequestBody PolicyFollowQueryRequest policyFollowQueryRequest,
HttpServletResponse response) { HttpServletResponse response) {
try { apiPolicyFollowExportService.export(policyFollowQueryRequest, response);
apiPolicyFollowExportService.export(policyFollowQueryRequest, response);
} catch (Exception e) {
throw new RuntimeException("导出新单跟进清单失败", e);
}
} }
/** /**
......
...@@ -30,25 +30,15 @@ import java.util.Date; ...@@ -30,25 +30,15 @@ import java.util.Date;
import java.util.List; import java.util.List;
/** /**
* 新单跟进列表导出。 * 新单跟进列表导出:行集与分页页面查询同一筛选口径,列沿用保单清单 30 列模板,
* * R 列「保费到期日」按当天逐行实时计算。
* <p>行集与分页页面查询完全一致(复用 {@link PolicyFollowService#getQueryWrapper},含其固定的 id 倒序、
* 不过滤 is_deleted 口径);列映射沿用保单清单 30 列模板,R 列「保费到期日」按当天逐行实时计算。</p>
*
* <p>健壮性:先 COUNT 行数闸门(空结果/超上限在提交下载响应头之前报错,可正常返回 JSON),
* 再分页拉取、分批写入,避免全量进内存;纯只读,不加事务。</p>
*/ */
@Service @Service
@Slf4j @Slf4j
public class ApiPolicyFollowExportService { public class ApiPolicyFollowExportService {
/** /** 单次导出行数硬上限,超过需缩小筛选条件 */
* 单次导出行数硬上限,超过需缩小筛选条件
*/
private static final long MAX_EXPORT_ROWS = 50_000L; private static final long MAX_EXPORT_ROWS = 50_000L;
/**
* 分页拉取/分批写入大小,堆内只保留当前批
*/
private static final long PAGE_SIZE = 2_000L; private static final long PAGE_SIZE = 2_000L;
private static final String XLSX_CONTENT_TYPE = private static final String XLSX_CONTENT_TYPE =
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
...@@ -57,7 +47,7 @@ public class ApiPolicyFollowExportService { ...@@ -57,7 +47,7 @@ public class ApiPolicyFollowExportService {
@Resource @Resource
private PolicyFollowService policyFollowService; private PolicyFollowService policyFollowService;
public void export(PolicyFollowQueryRequest request, HttpServletResponse response) throws UnsupportedEncodingException { public void export(PolicyFollowQueryRequest request, HttpServletResponse response) {
QueryWrapper<PolicyFollow> wrapper = policyFollowService.getQueryWrapper(request); QueryWrapper<PolicyFollow> wrapper = policyFollowService.getQueryWrapper(request);
long total = policyFollowService.count(wrapper); long total = policyFollowService.count(wrapper);
...@@ -72,8 +62,6 @@ public class ApiPolicyFollowExportService { ...@@ -72,8 +62,6 @@ public class ApiPolicyFollowExportService {
long pages = (total + PAGE_SIZE - 1) / PAGE_SIZE; long pages = (total + PAGE_SIZE - 1) / PAGE_SIZE;
LocalDate today = LocalDate.now(); LocalDate today = LocalDate.now();
// 先取第一页,让主查询在提交下载响应头之前执行:
// 一旦查询阶段抛错,响应尚未 committed,全局异常处理器仍可返回 JSON 提示。
Page<PolicyFollow> firstPage = policyFollowService.page(new Page<>(1, PAGE_SIZE, false), wrapper); Page<PolicyFollow> firstPage = policyFollowService.page(new Page<>(1, PAGE_SIZE, false), wrapper);
setDownloadHeaders(response, buildFileName(today)); setDownloadHeaders(response, buildFileName(today));
...@@ -92,7 +80,6 @@ public class ApiPolicyFollowExportService { ...@@ -92,7 +80,6 @@ public class ApiPolicyFollowExportService {
writer.write(toRows(pageData.getRecords(), today), sheet); writer.write(toRows(pageData.getRecords(), today), sheet);
} }
} catch (IOException e) { } catch (IOException e) {
// 此时响应头已提交、无法再回 JSON,仅记录日志供排查
log.error("新单跟进导出写入失败,total={}", total, e); log.error("新单跟进导出写入失败,total={}", total, e);
throw new BusinessException("导出新单跟进清单失败"); throw new BusinessException("导出新单跟进清单失败");
} }
...@@ -114,9 +101,14 @@ public class ApiPolicyFollowExportService { ...@@ -114,9 +101,14 @@ public class ApiPolicyFollowExportService {
return rows; return rows;
} }
private String buildFileName(LocalDate today) throws UnsupportedEncodingException { private String buildFileName(LocalDate today) {
String raw = "新单跟进清单_" + today.format(DAY_FMT) + ".xlsx"; String raw = "新单跟进清单_" + today.format(DAY_FMT) + ".xlsx";
return URLEncoder.encode(raw, StandardCharsets.UTF_8.name()).replaceAll("\\+", "%20"); try {
return URLEncoder.encode(raw, StandardCharsets.UTF_8.name()).replaceAll("\\+", "%20");
} catch (UnsupportedEncodingException e) {
// UTF-8 为 JVM 规范保证支持,理论不可达
throw new IllegalStateException(e);
}
} }
private void setDownloadHeaders(HttpServletResponse response, String encodedFileName) { private void setDownloadHeaders(HttpServletResponse response, String encodedFileName) {
......
...@@ -4,6 +4,7 @@ import com.alibaba.excel.annotation.ExcelProperty; ...@@ -4,6 +4,7 @@ import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat; import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.yd.csf.service.model.PolicyFollow; import com.yd.csf.service.model.PolicyFollow;
import lombok.Data; import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
...@@ -112,7 +113,7 @@ public class RenewalPolicyExportDTO { ...@@ -112,7 +113,7 @@ public class RenewalPolicyExportDTO {
private String signLocation; private String signLocation;
/** /**
* 按 PolicyFollow 字段搬运生成一行;PI/受保人英文/总保费及征费无数据源留空,状态保留字典码。 * 按 PolicyFollow 字段搬运生成一行;PI/受保人英文无数据源留空,状态保留字典码。
* *
* @param p 保单记录 * @param p 保单记录
* @param dueDate 保费到期日(R 列),无下一期缴费日时传 null * @param dueDate 保费到期日(R 列),无下一期缴费日时传 null
...@@ -129,6 +130,7 @@ public class RenewalPolicyExportDTO { ...@@ -129,6 +130,7 @@ public class RenewalPolicyExportDTO {
row.setEachIssuePremium(p.getEachIssuePremium()); row.setEachIssuePremium(p.getEachIssuePremium());
row.setPolicyCurrency(p.getPolicyCurrency()); row.setPolicyCurrency(p.getPolicyCurrency());
row.setPolicyLevy(p.getPolicyLevy()); row.setPolicyLevy(p.getPolicyLevy());
row.setTotalPremiumAndLevy(buildTotalPremiumAndLevy(p));
row.setEffectiveDate(p.getEffectiveDate()); row.setEffectiveDate(p.getEffectiveDate());
row.setSignDate(p.getSignDate()); row.setSignDate(p.getSignDate());
row.setIssueDate(p.getIssueDate()); row.setIssueDate(p.getIssueDate());
...@@ -148,4 +150,27 @@ public class RenewalPolicyExportDTO { ...@@ -148,4 +150,27 @@ public class RenewalPolicyExportDTO {
row.setSignLocation(p.getSignLocation()); row.setSignLocation(p.getSignLocation());
return row; return row;
} }
/**
* 总保费及征费 = 期交保费 + 保单征费;两者均无值时留空,征费为空或非数字按 0 处理。
*/
private static String buildTotalPremiumAndLevy(PolicyFollow p) {
BigDecimal premium = p.getEachIssuePremium() == null ? BigDecimal.ZERO : p.getEachIssuePremium();
BigDecimal levy = parseAmount(p.getPolicyLevy());
if (p.getEachIssuePremium() == null && levy == null) {
return null;
}
return premium.add(levy == null ? BigDecimal.ZERO : levy).toPlainString();
}
private static BigDecimal parseAmount(String value) {
if (StringUtils.isBlank(value)) {
return null;
}
try {
return new BigDecimal(value.trim());
} catch (NumberFormatException e) {
return null;
}
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment