Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yd-csf
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
xingmin
yd-csf
Commits
5986f1a8
Commit
5986f1a8
authored
Sep 15, 2026
by
jianan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
保费到期提醒19-新单跟进导出
parent
9c0b5ddd
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
189 additions
and
34 deletions
+189
-34
yd-csf-api/src/main/java/com/yd/csf/api/controller/ApiPolicyFollowController.java
+18
-0
yd-csf-api/src/main/java/com/yd/csf/api/service/ApiPolicyFollowExportService.java
+127
-0
yd-csf-service/src/main/java/com/yd/csf/service/component/RenewalPolicyReportService.java
+3
-33
yd-csf-service/src/main/java/com/yd/csf/service/vo/RenewalPolicyExportDTO.java
+41
-1
No files found.
yd-csf-api/src/main/java/com/yd/csf/api/controller/ApiPolicyFollowController.java
View file @
5986f1a8
...
@@ -26,6 +26,7 @@ import com.yd.csf.api.listener.PolicyDataListener;
...
@@ -26,6 +26,7 @@ import com.yd.csf.api.listener.PolicyDataListener;
import
com.yd.csf.api.service.ApiCommissionConditionService
;
import
com.yd.csf.api.service.ApiCommissionConditionService
;
import
com.yd.csf.api.service.ApiExpectedFortuneService
;
import
com.yd.csf.api.service.ApiExpectedFortuneService
;
import
com.yd.csf.api.service.ApiPolicyFollowService
;
import
com.yd.csf.api.service.ApiPolicyFollowService
;
import
com.yd.csf.api.service.ApiPolicyFollowExportService
;
import
com.yd.csf.feign.request.expectedfortune.ApiGenerateExpectedFortuneRequest
;
import
com.yd.csf.feign.request.expectedfortune.ApiGenerateExpectedFortuneRequest
;
import
com.yd.csf.feign.response.appointment.ApiAppointmentDetailResponse
;
import
com.yd.csf.feign.response.appointment.ApiAppointmentDetailResponse
;
import
com.yd.csf.feign.response.expectedfortune.ApiGenerateExpectedFortuneResponse
;
import
com.yd.csf.feign.response.expectedfortune.ApiGenerateExpectedFortuneResponse
;
...
@@ -67,6 +68,7 @@ import javax.servlet.ServletOutputStream;
...
@@ -67,6 +68,7 @@ import javax.servlet.ServletOutputStream;
import
javax.servlet.http.HttpServletRequest
;
import
javax.servlet.http.HttpServletRequest
;
import
javax.servlet.http.HttpServletResponse
;
import
javax.servlet.http.HttpServletResponse
;
import
javax.validation.constraints.NotBlank
;
import
javax.validation.constraints.NotBlank
;
import
java.io.UnsupportedEncodingException
;
import
java.math.BigDecimal
;
import
java.math.BigDecimal
;
import
java.sql.SQLIntegrityConstraintViolationException
;
import
java.sql.SQLIntegrityConstraintViolationException
;
import
java.text.ParseException
;
import
java.text.ParseException
;
...
@@ -112,6 +114,8 @@ public class ApiPolicyFollowController {
...
@@ -112,6 +114,8 @@ public class ApiPolicyFollowController {
private
FeignResultHelper
feignResultHelper
;
private
FeignResultHelper
feignResultHelper
;
@Resource
@Resource
private
ApiExpectedFortuneAsyncService
apiExpectedFortuneAsyncService
;
private
ApiExpectedFortuneAsyncService
apiExpectedFortuneAsyncService
;
@Resource
private
ApiPolicyFollowExportService
apiPolicyFollowExportService
;
@Autowired
@Autowired
private
ApiCommissionConditionService
apiCommissionConditionService
;
private
ApiCommissionConditionService
apiCommissionConditionService
;
...
@@ -510,6 +514,20 @@ public class ApiPolicyFollowController {
...
@@ -510,6 +514,20 @@ public class ApiPolicyFollowController {
}
}
/**
/**
* 按页面查询条件导出新单跟进清单(与列表查询同一筛选口径,30 列模板,R 列为实时计算的保费到期日)
*/
@PostMapping
(
"/export"
)
@Operation
(
summary
=
"导出新单跟进列表"
)
public
void
exportPolicyFollow
(
@RequestBody
PolicyFollowQueryRequest
policyFollowQueryRequest
,
HttpServletResponse
response
)
{
try
{
apiPolicyFollowExportService
.
export
(
policyFollowQueryRequest
,
response
);
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
"导出新单跟进清单失败"
,
e
);
}
}
/**
* 修改跟进状态
* 修改跟进状态
*
*
* @param changePolicyFollowStatusRequest
* @param changePolicyFollowStatusRequest
...
...
yd-csf-api/src/main/java/com/yd/csf/api/service/ApiPolicyFollowExportService.java
0 → 100644
View file @
5986f1a8
package
com
.
yd
.
csf
.
api
.
service
;
import
com.alibaba.excel.EasyExcel
;
import
com.alibaba.excel.ExcelWriter
;
import
com.alibaba.excel.write.metadata.WriteSheet
;
import
com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
;
import
com.baomidou.mybatisplus.extension.plugins.pagination.Page
;
import
com.yd.common.exception.BusinessException
;
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.utils.SimpleDateUtils
;
import
com.yd.csf.service.vo.RenewalPolicyExportDTO
;
import
lombok.extern.slf4j.Slf4j
;
import
org.apache.commons.collections4.CollectionUtils
;
import
org.springframework.stereotype.Service
;
import
javax.annotation.Resource
;
import
javax.servlet.http.HttpServletResponse
;
import
java.io.IOException
;
import
java.io.UnsupportedEncodingException
;
import
java.net.URLEncoder
;
import
java.nio.charset.StandardCharsets
;
import
java.time.LocalDate
;
import
java.time.format.DateTimeFormatter
;
import
java.util.ArrayList
;
import
java.util.Date
;
import
java.util.List
;
/**
* 新单跟进列表导出。
*
* <p>行集与分页页面查询完全一致(复用 {@link PolicyFollowService#getQueryWrapper},含其固定的 id 倒序、
* 不过滤 is_deleted 口径);列映射沿用保单清单 30 列模板,R 列「保费到期日」按当天逐行实时计算。</p>
*
* <p>健壮性:先 COUNT 行数闸门(空结果/超上限在提交下载响应头之前报错,可正常返回 JSON),
* 再分页拉取、分批写入,避免全量进内存;纯只读,不加事务。</p>
*/
@Service
@Slf4j
public
class
ApiPolicyFollowExportService
{
/**
* 单次导出行数硬上限,超过需缩小筛选条件
*/
private
static
final
long
MAX_EXPORT_ROWS
=
50_000L
;
/**
* 分页拉取/分批写入大小,堆内只保留当前批
*/
private
static
final
long
PAGE_SIZE
=
2_000L
;
private
static
final
String
XLSX_CONTENT_TYPE
=
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
;
private
static
final
DateTimeFormatter
DAY_FMT
=
DateTimeFormatter
.
ofPattern
(
"yyyyMMdd"
);
@Resource
private
PolicyFollowService
policyFollowService
;
public
void
export
(
PolicyFollowQueryRequest
request
,
HttpServletResponse
response
)
throws
UnsupportedEncodingException
{
QueryWrapper
<
PolicyFollow
>
wrapper
=
policyFollowService
.
getQueryWrapper
(
request
);
long
total
=
policyFollowService
.
count
(
wrapper
);
if
(
total
==
0
)
{
throw
new
BusinessException
(
"无导出数据"
);
}
if
(
total
>
MAX_EXPORT_ROWS
)
{
throw
new
BusinessException
(
String
.
format
(
"导出数据量 %d 条超过上限 %d 条,请缩小筛选条件后再导出"
,
total
,
MAX_EXPORT_ROWS
));
}
long
pages
=
(
total
+
PAGE_SIZE
-
1
)
/
PAGE_SIZE
;
LocalDate
today
=
LocalDate
.
now
();
// 先取第一页,让主查询在提交下载响应头之前执行:
// 一旦查询阶段抛错,响应尚未 committed,全局异常处理器仍可返回 JSON 提示。
Page
<
PolicyFollow
>
firstPage
=
policyFollowService
.
page
(
new
Page
<>(
1
,
PAGE_SIZE
,
false
),
wrapper
);
setDownloadHeaders
(
response
,
buildFileName
(
today
));
try
(
ExcelWriter
writer
=
EasyExcel
.
write
(
response
.
getOutputStream
(),
RenewalPolicyExportDTO
.
class
)
.
useDefaultStyle
(
false
)
.
registerWriteHandler
(
new
TemplateStyleStrategy
())
.
build
())
{
WriteSheet
sheet
=
EasyExcel
.
writerSheet
(
"新单跟进"
).
build
();
writer
.
write
(
toRows
(
firstPage
.
getRecords
(),
today
),
sheet
);
for
(
long
pageNo
=
2
;
pageNo
<=
pages
;
pageNo
++)
{
Page
<
PolicyFollow
>
pageData
=
policyFollowService
.
page
(
new
Page
<>(
pageNo
,
PAGE_SIZE
,
false
),
wrapper
);
if
(
CollectionUtils
.
isEmpty
(
pageData
.
getRecords
()))
{
break
;
}
writer
.
write
(
toRows
(
pageData
.
getRecords
(),
today
),
sheet
);
}
}
catch
(
IOException
e
)
{
// 此时响应头已提交、无法再回 JSON,仅记录日志供排查
log
.
error
(
"新单跟进导出写入失败,total={}"
,
total
,
e
);
throw
new
BusinessException
(
"导出新单跟进清单失败"
);
}
}
private
List
<
RenewalPolicyExportDTO
>
toRows
(
List
<
PolicyFollow
>
policies
,
LocalDate
today
)
{
List
<
RenewalPolicyExportDTO
>
rows
=
new
ArrayList
<>(
policies
.
size
());
for
(
PolicyFollow
p
:
policies
)
{
RenewalDueDateCalculator
.
Result
result
=
RenewalDueDateCalculator
.
calculate
(
SimpleDateUtils
.
toLocalDate
(
p
.
getEffectiveDate
()),
p
.
getPaymentFrequency
(),
p
.
getIssueNumber
(),
today
);
Date
dueDate
=
result
.
isIncluded
()
?
Date
.
from
(
result
.
getDueDate
().
atStartOfDay
(
java
.
time
.
ZoneId
.
systemDefault
()).
toInstant
())
:
null
;
rows
.
add
(
RenewalPolicyExportDTO
.
toRow
(
p
,
dueDate
));
}
return
rows
;
}
private
String
buildFileName
(
LocalDate
today
)
throws
UnsupportedEncodingException
{
String
raw
=
"新单跟进清单_"
+
today
.
format
(
DAY_FMT
)
+
".xlsx"
;
return
URLEncoder
.
encode
(
raw
,
StandardCharsets
.
UTF_8
.
name
()).
replaceAll
(
"\\+"
,
"%20"
);
}
private
void
setDownloadHeaders
(
HttpServletResponse
response
,
String
encodedFileName
)
{
response
.
setContentType
(
XLSX_CONTENT_TYPE
);
response
.
setCharacterEncoding
(
StandardCharsets
.
UTF_8
.
name
());
response
.
setHeader
(
"Content-disposition"
,
"attachment;filename*=utf-8''"
+
encodedFileName
);
}
}
yd-csf-service/src/main/java/com/yd/csf/service/component/RenewalPolicyReportService.java
View file @
5986f1a8
...
@@ -3,7 +3,6 @@ package com.yd.csf.service.component;
...
@@ -3,7 +3,6 @@ package com.yd.csf.service.component;
import
com.alibaba.excel.EasyExcel
;
import
com.alibaba.excel.EasyExcel
;
import
com.yd.csf.service.dto.RenewalDuePolicy
;
import
com.yd.csf.service.dto.RenewalDuePolicy
;
import
com.yd.csf.service.dto.RenewalReminderData
;
import
com.yd.csf.service.dto.RenewalReminderData
;
import
com.yd.csf.service.model.PolicyFollow
;
import
com.yd.csf.service.vo.RenewalPolicyExportDTO
;
import
com.yd.csf.service.vo.RenewalPolicyExportDTO
;
import
org.apache.commons.collections4.CollectionUtils
;
import
org.apache.commons.collections4.CollectionUtils
;
import
org.springframework.stereotype.Component
;
import
org.springframework.stereotype.Component
;
...
@@ -14,7 +13,6 @@ import java.time.LocalDate;
...
@@ -14,7 +13,6 @@ import java.time.LocalDate;
import
java.time.format.DateTimeFormatter
;
import
java.time.format.DateTimeFormatter
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Objects
;
/**
/**
* 续期保单清单 Excel 生成(30 列,对齐 policy报表模版.xlsx)
* 续期保单清单 Excel 生成(30 列,对齐 policy报表模版.xlsx)
...
@@ -59,37 +57,9 @@ public class RenewalPolicyReportService {
...
@@ -59,37 +57,9 @@ public class RenewalPolicyReportService {
return
rows
;
return
rows
;
}
}
for
(
RenewalDuePolicy
item
:
data
.
getDuePolicies
())
{
for
(
RenewalDuePolicy
item
:
data
.
getDuePolicies
())
{
PolicyFollow
p
=
item
.
getPolicyFollow
();
java
.
util
.
Date
dueDate
=
java
.
util
.
Date
.
from
(
item
.
getDueDate
()
RenewalPolicyExportDTO
row
=
new
RenewalPolicyExportDTO
();
.
atStartOfDay
(
java
.
time
.
ZoneId
.
systemDefault
()).
toInstant
());
row
.
setId
(
p
.
getId
());
rows
.
add
(
RenewalPolicyExportDTO
.
toRow
(
item
.
getPolicyFollow
(),
dueDate
));
row
.
setPolicyNo
(
p
.
getPolicyNo
());
row
.
setProductName
(
p
.
getProductName
());
row
.
setPolicyHolder
(
p
.
getPolicyHolder
());
row
.
setPolicyHolderEn
(
p
.
getPolicyHolderEn
());
row
.
setInsured
(
p
.
getInsured
());
row
.
setStatus
(
p
.
getStatus
());
row
.
setEachIssuePremium
(
p
.
getEachIssuePremium
());
row
.
setPolicyCurrency
(
p
.
getPolicyCurrency
());
row
.
setPolicyLevy
(
p
.
getPolicyLevy
());
row
.
setEffectiveDate
(
p
.
getEffectiveDate
());
row
.
setSignDate
(
p
.
getSignDate
());
row
.
setIssueDate
(
p
.
getIssueDate
());
row
.
setCoolingOffEndDate
(
p
.
getCoolingOffEndDate
());
row
.
setDueDate
(
java
.
util
.
Date
.
from
(
item
.
getDueDate
()
.
atStartOfDay
(
java
.
time
.
ZoneId
.
systemDefault
()).
toInstant
()));
row
.
setIssueNumber
(
Objects
.
toString
(
p
.
getIssueNumber
(),
null
));
row
.
setPaymentFrequency
(
p
.
getPaymentFrequency
());
row
.
setIsPrepay
(
p
.
getIsPrepay
());
row
.
setPrepaidTerm
(
p
.
getPrepaidTerm
());
row
.
setProductCate
(
p
.
getProductCate
());
row
.
setInsuranceCompany
(
p
.
getInsuranceCompany
());
row
.
setReconciliationCompany
(
p
.
getReconciliationCompany
());
row
.
setFirstBroker
(
p
.
getFirstBroker
());
row
.
setSigner
(
p
.
getSigner
());
row
.
setPracticeCode
(
p
.
getPracticeCode
());
row
.
setAppointmentNo
(
p
.
getAppointmentNo
());
row
.
setSignLocation
(
p
.
getSignLocation
());
rows
.
add
(
row
);
}
}
return
rows
;
return
rows
;
}
}
...
...
yd-csf-service/src/main/java/com/yd/csf/service/vo/RenewalPolicyExportDTO.java
View file @
5986f1a8
...
@@ -2,13 +2,15 @@ package com.yd.csf.service.vo;
...
@@ -2,13 +2,15 @@ package com.yd.csf.service.vo;
import
com.alibaba.excel.annotation.ExcelProperty
;
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
lombok.Data
;
import
lombok.Data
;
import
java.math.BigDecimal
;
import
java.math.BigDecimal
;
import
java.util.Date
;
import
java.util.Date
;
import
java.util.Objects
;
/**
/**
*
续期保单清单导出 DTO
,列顺序与表头严格对齐 policy报表模版.xlsx(30 列)。
*
保单清单导出 DTO(续期跑批与新单跟进导出共用)
,列顺序与表头严格对齐 policy报表模版.xlsx(30 列)。
* 字体/边框/对齐/列宽/冻结统一由 {@link com.yd.csf.service.component.TemplateStyleStrategy} 复刻模板。
* 字体/边框/对齐/列宽/冻结统一由 {@link com.yd.csf.service.component.TemplateStyleStrategy} 复刻模板。
*/
*/
@Data
@Data
...
@@ -108,4 +110,42 @@ public class RenewalPolicyExportDTO {
...
@@ -108,4 +110,42 @@ public class RenewalPolicyExportDTO {
@ExcelProperty
(
"签单地点"
)
@ExcelProperty
(
"签单地点"
)
private
String
signLocation
;
private
String
signLocation
;
/**
* 按 PolicyFollow 字段搬运生成一行;PI/受保人英文/总保费及征费无数据源留空,状态保留字典码。
*
* @param p 保单记录
* @param dueDate 保费到期日(R 列),无下一期缴费日时传 null
*/
public
static
RenewalPolicyExportDTO
toRow
(
PolicyFollow
p
,
Date
dueDate
)
{
RenewalPolicyExportDTO
row
=
new
RenewalPolicyExportDTO
();
row
.
setId
(
p
.
getId
());
row
.
setPolicyNo
(
p
.
getPolicyNo
());
row
.
setProductName
(
p
.
getProductName
());
row
.
setPolicyHolder
(
p
.
getPolicyHolder
());
row
.
setPolicyHolderEn
(
p
.
getPolicyHolderEn
());
row
.
setInsured
(
p
.
getInsured
());
row
.
setStatus
(
p
.
getStatus
());
row
.
setEachIssuePremium
(
p
.
getEachIssuePremium
());
row
.
setPolicyCurrency
(
p
.
getPolicyCurrency
());
row
.
setPolicyLevy
(
p
.
getPolicyLevy
());
row
.
setEffectiveDate
(
p
.
getEffectiveDate
());
row
.
setSignDate
(
p
.
getSignDate
());
row
.
setIssueDate
(
p
.
getIssueDate
());
row
.
setCoolingOffEndDate
(
p
.
getCoolingOffEndDate
());
row
.
setDueDate
(
dueDate
);
row
.
setIssueNumber
(
Objects
.
toString
(
p
.
getIssueNumber
(),
null
));
row
.
setPaymentFrequency
(
p
.
getPaymentFrequency
());
row
.
setIsPrepay
(
p
.
getIsPrepay
());
row
.
setPrepaidTerm
(
p
.
getPrepaidTerm
());
row
.
setProductCate
(
p
.
getProductCate
());
row
.
setInsuranceCompany
(
p
.
getInsuranceCompany
());
row
.
setReconciliationCompany
(
p
.
getReconciliationCompany
());
row
.
setFirstBroker
(
p
.
getFirstBroker
());
row
.
setSigner
(
p
.
getSigner
());
row
.
setPracticeCode
(
p
.
getPracticeCode
());
row
.
setAppointmentNo
(
p
.
getAppointmentNo
());
row
.
setSignLocation
(
p
.
getSignLocation
());
return
row
;
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment