Commit bf7d94c2 by jianan

保费到期提醒23-保单邮件逐条通知

parent 15a970a0
......@@ -10,7 +10,7 @@ import com.yd.csf.service.component.TemplateStyleStrategy;
import com.yd.csf.service.dto.PolicyFollowQueryRequest;
import com.yd.csf.service.model.PolicyFollow;
import com.yd.csf.service.service.PolicyFollowService;
import com.yd.csf.service.utils.RenewalDueDateCalculator;
import com.yd.csf.service.component.renewal.RenewalDueDateCalculator;
import com.yd.csf.service.utils.SimpleDateUtils;
import com.yd.csf.service.vo.RenewalPolicyExportDTO;
import lombok.extern.slf4j.Slf4j;
......
......@@ -4,9 +4,10 @@ import com.yd.common.exception.BusinessException;
import com.yd.common.utils.RandomStringGenerator;
import com.yd.csf.api.service.RenewalDispatchService;
import com.yd.csf.api.service.RenewalMailService;
import com.yd.csf.service.component.RenewalPolicyReportService;
import com.yd.csf.service.dto.RenewalDuePolicy;
import com.yd.csf.service.dto.RenewalReminderData;
import com.yd.csf.service.component.renewal.RenewalPolicyMailBuilder;
import com.yd.csf.service.component.renewal.RenewalPolicyReportService;
import com.yd.csf.service.component.renewal.RenewalDuePolicy;
import com.yd.csf.service.component.renewal.RenewalReminderData;
import com.yd.csf.service.enums.RenewalMailTypeEnum;
import com.yd.csf.service.enums.RenewalRemindStatusEnum;
import com.yd.csf.service.model.RenewalRemindBatch;
......
package com.yd.csf.service.utils;
package com.yd.csf.service.component.renewal;
import com.yd.csf.feign.enums.PaymentFrequencyEnum;
import lombok.Getter;
......
package com.yd.csf.service.dto;
package com.yd.csf.service.component.renewal;
import com.yd.csf.service.model.PolicyFollow;
import lombok.AllArgsConstructor;
......
package com.yd.csf.api.service.impl;
package com.yd.csf.service.component.renewal;
import com.yd.csf.service.dto.RenewalDuePolicy;
import com.yd.csf.service.enums.RenewalMailTypeEnum;
import com.yd.csf.service.model.PolicyFollow;
import com.yd.csf.service.utils.RenewalPremiumUtils;
import com.yd.csf.service.utils.SimpleDateUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
......@@ -19,6 +17,7 @@ import java.util.concurrent.atomic.AtomicInteger;
/**
* 逐单续期保费通知邮件构建器:简繁双语 HTML(内联 CSS、无图片无外链)。
* 版式按需求稿:字段单列、开场句日期加粗下划线、应缴总保费与备注红色、底部日期加粗下划线。
* 静态标签简体段/繁体段各一份,数据值原样填充不做简繁转换,全部做 HTML 转义。
*/
@Component
......@@ -29,6 +28,14 @@ public class RenewalPolicyMailBuilder {
private static final String FONT = "'Microsoft YaHei','PingFang SC',Arial,sans-serif";
private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
private static final DecimalFormat MONEY_FMT = new DecimalFormat("#,##0.00");
/**
* 需求稿强调色:应缴总保费、备注整行红色
*/
private static final String EMPHASIS_RED = "#FF0000";
/**
* 标签对齐宽度(全角字符数,含冒号);最长标签「保单持有人:」为 6
*/
private static final int LABEL_WIDTH = 6;
/**
* grace_period 缺失时括注中的默认天数,与筛选侧同源配置
......@@ -72,15 +79,15 @@ public class RenewalPolicyMailBuilder {
String effectiveText = effectiveDate == null ? null : effectiveDate.format(DATE_FMT);
String graceNoteSc = item.isGraceFallback()
? "(宽限天数待核对(按默认 " + resolveDefaultGraceDays() + " 天估算))"
: "(保费到期日起计 " + item.getGraceDays() + " 天)";
: "(获取保费到期日起计" + item.getGraceDays() + "天)";
String graceNoteTc = item.isGraceFallback()
? "(寬限天數待核對(按預設 " + resolveDefaultGraceDays() + " 天估算))"
: "(保費到期日起計 " + item.getGraceDays() + " 天)";
: "(获取保费到期日起计" + item.getGraceDays() + "天)";
String subject = (type == RenewalMailTypeEnum.R2 ? "【宽限期跟进】" : "")
+ "续期保费通知-" + policyNo;
+ "续期保费通知-【" + policyNo + "】";
String html = "<div style=\"font-family:" + FONT + ";font-size:14px;color:#333333;line-height:1.9;\">"
String html = "<div style=\"font-family:" + FONT + ";font-size:14px;color:#000000;line-height:1.9;\">"
+ segment(Labels.SC, MISSING_SC, type, company, product, esc(policyNo), holder, insured,
money, effectiveText, dueDateText, graceEndText, graceNoteSc)
+ "<hr style=\"border:none;border-top:1px solid #dddddd;margin:18px 0;\">"
......@@ -104,55 +111,63 @@ public class RenewalPolicyMailBuilder {
String money, String effectiveText, String dueDateText,
String graceEndText, String graceNote) {
String opening;
String boldDue = boldUnderline(dueDateText);
if (type == RenewalMailTypeEnum.R2) {
opening = l.openingR2Prefix + dueDateText + l.openingR2Middle + graceEndText + l.openingR2Suffix;
opening = l.openingR2Prefix + boldDue + l.openingR2Middle
+ boldUnderline(graceEndText) + l.openingR2Suffix;
} else {
opening = l.openingR1Prefix + dueDateText + l.openingR1Suffix;
opening = l.openingR1Prefix + boldDue + l.openingR1Suffix;
}
String companyV = markOrValue(company, missingMark);
String productV = markOrValue(product, missingMark);
String holderV = markOrValue(holder, missingMark);
String insuredV = markOrValue(insured, missingMark);
String moneyV = markOrValue(money, missingMark);
String effectiveV = markOrValue(effectiveText, missingMark);
StringBuilder sb = new StringBuilder();
sb.append("<p style=\"margin:0 0 6px 0;font-weight:bold;\">").append(l.segmentTitle).append("</p>");
sb.append("<p style=\"margin:0 0 12px 0;\">").append(opening).append("</p>");
sb.append("<table cellspacing=\"0\" cellpadding=\"4\" style=\"border-collapse:collapse;font-size:14px;\">");
sb.append(row4(l.company, companyV, l.product, productV));
sb.append(row4(l.policyNo, policyNo, l.effectiveDate, effectiveV));
sb.append(row4(l.holder, holderV, l.insured, insuredV));
sb.append(row1(l.premiumAndLevy, moneyV, false));
sb.append(row1(l.totalPremium, moneyV, true));
sb.append("</table>");
sb.append("<p style=\"margin:10px 0 0 0;\">").append(l.remark).append("</p>");
sb.append("<p style=\"margin:6px 0 0 0;\">")
.append(l.dueDateLabel).append(":<span style=\"font-weight:bold;text-decoration:underline;\">")
.append(dueDateText).append("</span></p>");
sb.append("<p style=\"margin:6px 0 0 0;\">")
.append(l.graceEndLabel).append(":<span style=\"font-weight:bold;text-decoration:underline;\">")
.append(graceEndText).append("</span>").append(graceNote).append("</p>");
sb.append("<p style=\"margin:0 0 8px 0;font-weight:bold;\">").append(l.segmentTitle).append("</p>");
sb.append("<p style=\"margin:0 0 10px 0;\">").append(opening).append("</p>");
// 保单资料:每字段单独一行,标签全角补齐对齐
sb.append("<div style=\"line-height:2;\">");
sb.append(fieldLine(l.company, markOrValue(company, missingMark)));
sb.append(fieldLine(l.product, markOrValue(product, missingMark)));
sb.append(fieldLine(l.policyNo, policyNo));
sb.append(fieldLine(l.holder, markOrValue(holder, missingMark)));
sb.append(fieldLine(l.insured, markOrValue(insured, missingMark)));
sb.append(fieldLine(l.premiumAndLevy, markOrValue(money, missingMark)));
sb.append(fieldLine(l.effectiveDate, markOrValue(effectiveText, missingMark)));
sb.append("</div>");
// 应缴总保费:标签+金额整行红色加粗
sb.append("<div style=\"margin-top:14px;color:").append(EMPHASIS_RED)
.append(";font-weight:bold;\">")
.append(l.totalPremium).append(":").append(markOrValue(money, missingMark))
.append("</div>");
// 备注:整行红色、常规字重(繁体段文案按需求稿原文)
sb.append("<div style=\"margin-top:4px;color:").append(EMPHASIS_RED).append(";\">")
.append(l.remark).append("</div>");
// 到期日区域与备注间空一行
sb.append("<div style=\"margin-top:22px;\">")
.append(l.dueDateLabel).append(":").append(boldDue)
.append("</div>");
sb.append("<div style=\"margin-top:6px;\">")
.append(l.graceEndLabel).append(":").append(boldUnderline(graceEndText))
.append("&nbsp;&nbsp;").append(graceNote)
.append("</div>");
return sb.toString();
}
private String row4(String label1, String value1, String label2, String value2) {
return "<tr>"
+ "<td style=\"white-space:nowrap;color:#666666;\">" + label1 + ":</td>"
+ "<td style=\"padding-right:24px;\">" + value1 + "</td>"
+ "<td style=\"white-space:nowrap;color:#666666;\">" + label2 + ":</td>"
+ "<td>" + value2 + "</td>"
+ "</tr>";
private String fieldLine(String label, String value) {
return "<div>" + padLabel(label) + value + "</div>";
}
private String boldUnderline(String text) {
return "<span style=\"font-weight:bold;text-decoration:underline;\">" + text + "</span>";
}
private String row1(String label, String value, boolean total) {
String valueStyle = total
? "<span style=\"color:#D90000;font-weight:bold;\">" + value + "</span>"
: value;
return "<tr>"
+ "<td style=\"white-space:nowrap;color:#666666;\">" + label + ":</td>"
+ "<td colspan=\"3\">" + valueStyle + "</td>"
+ "</tr>";
/**
* 标签(含全角冒号)右侧补全角空格到固定宽度,使各字段值纵向对齐
*/
private String padLabel(String labelWithColon) {
StringBuilder sb = new StringBuilder(labelWithColon);
while (sb.length() < LABEL_WIDTH) {
sb.append(' ');
}
return sb.toString();
}
/**
......@@ -284,19 +299,20 @@ public class RenewalPolicyMailBuilder {
}
private static final Labels SC = new Labels(
"【简体】", "产品公司", "计划名称", "保单号码", "保单生效日",
"保单持有人", "受保人", "保费及征费", "应缴总保费",
"【简体】", "产品公司:", "计划名称:", "保单号码:", "保单生效日:",
"保单持有人:", "受保人:", "保费及征费:", "应缴总保费",
"备注:此金额未计入保费优惠,保费优惠待确认后通知客户。",
"保费到期日", "宽限期到期日",
"以下保单的保费将于 ", " 到期,请在此日期前缴付应缴保费。",
"以下保单的保费已于 ", " 到期,宽限期将于 ", " 届满,请尽快跟进确认缴费情况。");
"以下保单的保费将于", "到期,请在此日期前缴付应缴保费。保单资料如下:",
"以下保单的保费已于", "到期,宽限期将于", "届满,请尽快跟进确认缴费情况。保单资料如下:");
private static final Labels TC = new Labels(
"【繁體】", "產品公司", "計劃名稱", "保單號碼", "保單生效日",
"保單持有人", "受保人", "保費及征費", "應繳總保費",
"備註:此金額未計入保費優惠,保費優惠待確認後通知客戶。",
"【繁體】", "產品公司:", "計劃名稱:", "保單號碼:", "保單生效日:",
"保單持有人:", "受保人:", "保費及征費:", "應繳總保費",
// 繁体段备注按需求稿原文(「备注」「客户」保留稿中用字)
"备注:此金額未計入保費優惠,保費優惠待確認後通知客户。",
"保費到期日", "寬限期到期日",
"以下保單的保費將於 ", " 到期,請在此日期前繳付應繳保費。",
"以下保單的保費已於 ", " 到期,寬限期將於 ", " 屆滿,請盡快跟進確認繳費情況。");
"以下保單的保費將於", "到期,請在此日期前繳付應繳保費。保單資料如下:",
"以下保單的保費已於", "到期,寬限期將於", "屆滿,請盡快跟進確認繳費情況。保單資料如下:");
}
}
package com.yd.csf.service.component;
package com.yd.csf.service.component.renewal;
import com.alibaba.excel.EasyExcel;
import com.yd.csf.service.dto.RenewalDuePolicy;
import com.yd.csf.service.dto.RenewalReminderData;
import com.yd.csf.service.component.TemplateStyleStrategy;
import com.yd.csf.service.vo.RenewalPolicyExportDTO;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;
......
package com.yd.csf.service.utils;
package com.yd.csf.service.component.renewal;
import com.yd.csf.service.model.PolicyFollow;
import org.apache.commons.lang3.StringUtils;
......
package com.yd.csf.service.dto;
package com.yd.csf.service.component.renewal;
import lombok.AllArgsConstructor;
import lombok.Data;
......
package com.yd.csf.service.service;
import com.yd.csf.service.dto.RenewalReminderData;
import com.yd.csf.service.component.renewal.RenewalReminderData;
import java.time.LocalDate;
import java.time.YearMonth;
......
......@@ -2,12 +2,12 @@ package com.yd.csf.service.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yd.csf.service.dao.PolicyFollowMapper;
import com.yd.csf.service.dto.RenewalDuePolicy;
import com.yd.csf.service.dto.RenewalReminderData;
import com.yd.csf.service.component.renewal.RenewalDuePolicy;
import com.yd.csf.service.component.renewal.RenewalReminderData;
import com.yd.csf.service.enums.PolicyFollowStatusEnum;
import com.yd.csf.service.model.PolicyFollow;
import com.yd.csf.service.service.RenewalReminderQueryService;
import com.yd.csf.service.utils.RenewalDueDateCalculator;
import com.yd.csf.service.component.renewal.RenewalDueDateCalculator;
import com.yd.csf.service.utils.SimpleDateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
......
......@@ -2,6 +2,7 @@ package com.yd.csf.service.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.yd.csf.service.component.renewal.RenewalPremiumUtils;
import com.yd.csf.service.model.PolicyFollow;
import lombok.Data;
......@@ -152,10 +153,10 @@ public class RenewalPolicyExportDTO {
/**
* 总保费及征费 = 期交保费 + 保单征费;两者均无值时留空,征费为空或非数字按 0 处理。
* 口径与逐单邮件共用 {@link com.yd.csf.service.utils.RenewalPremiumUtils}。
* 口径与逐单邮件共用 {@link RenewalPremiumUtils}。
*/
private static String buildTotalPremiumAndLevy(PolicyFollow p) {
BigDecimal total = com.yd.csf.service.utils.RenewalPremiumUtils.totalPremiumAndLevy(p);
BigDecimal total = RenewalPremiumUtils.totalPremiumAndLevy(p);
return total == null ? null : total.toPlainString();
}
}
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