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
0ac1d533
Commit
0ac1d533
authored
Oct 23, 2026
by
jianan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
转介人门户:OIDC 登录与行级隔离,新增仪表盘/保单/在途佣金/月结单/待办接口
parent
b81c107f
Hide whitespace changes
Inline
Side-by-side
Showing
27 changed files
with
1823 additions
and
0 deletions
+1823
-0
yd-csf-api/src/main/java/com/yd/csf/api/controller/ReferrerDashboardController.java
+34
-0
yd-csf-api/src/main/java/com/yd/csf/api/controller/ReferrerPipelineController.java
+36
-0
yd-csf-api/src/main/java/com/yd/csf/api/controller/ReferrerPolicyController.java
+46
-0
yd-csf-api/src/main/java/com/yd/csf/api/controller/ReferrerStatementController.java
+47
-0
yd-csf-api/src/main/java/com/yd/csf/api/controller/ReferrerTodoController.java
+35
-0
yd-csf-api/src/main/java/com/yd/csf/api/dto/ReferrerTodoNotifyRequest.java
+19
-0
yd-csf-api/src/main/java/com/yd/csf/api/security/ReferrerIdentityContext.java
+57
-0
yd-csf-api/src/main/java/com/yd/csf/api/security/ReferrerIdentityFilter.java
+145
-0
yd-csf-api/src/main/java/com/yd/csf/api/security/ReferrerOidcProperties.java
+53
-0
yd-csf-api/src/main/java/com/yd/csf/api/security/ReferrerPortalSecurityConfig.java
+94
-0
yd-csf-service/src/main/java/com/yd/csf/service/dao/ReferrerTodoMapper.java
+11
-0
yd-csf-service/src/main/java/com/yd/csf/service/enums/ReferrerCommissionStage.java
+38
-0
yd-csf-service/src/main/java/com/yd/csf/service/model/ReferrerTodo.java
+47
-0
yd-csf-service/src/main/java/com/yd/csf/service/service/IReferrerPortalService.java
+50
-0
yd-csf-service/src/main/java/com/yd/csf/service/service/IReferrerTodoService.java
+20
-0
yd-csf-service/src/main/java/com/yd/csf/service/service/impl/ReferrerPortalServiceImpl.java
+543
-0
yd-csf-service/src/main/java/com/yd/csf/service/service/impl/ReferrerTodoServiceImpl.java
+35
-0
yd-csf-service/src/main/java/com/yd/csf/service/vo/ReferrerDashboardVO.java
+41
-0
yd-csf-service/src/main/java/com/yd/csf/service/vo/ReferrerPipelineVO.java
+59
-0
yd-csf-service/src/main/java/com/yd/csf/service/vo/ReferrerPolicyCommissionItemVO.java
+62
-0
yd-csf-service/src/main/java/com/yd/csf/service/vo/ReferrerPolicyCommissionVO.java
+36
-0
yd-csf-service/src/main/java/com/yd/csf/service/vo/ReferrerPolicyListVO.java
+26
-0
yd-csf-service/src/main/java/com/yd/csf/service/vo/ReferrerPolicyVO.java
+66
-0
yd-csf-service/src/main/java/com/yd/csf/service/vo/ReferrerStatementDetailVO.java
+75
-0
yd-csf-service/src/main/java/com/yd/csf/service/vo/ReferrerStatementItemVO.java
+71
-0
yd-csf-service/src/main/java/com/yd/csf/service/vo/ReferrerStatementVO.java
+59
-0
yd-csf-service/src/main/resources/sql/referrer_todo.sql
+18
-0
No files found.
yd-csf-api/src/main/java/com/yd/csf/api/controller/ReferrerDashboardController.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
api
.
controller
;
import
com.yd.common.result.Result
;
import
com.yd.csf.api.security.ReferrerIdentityContext
;
import
com.yd.csf.service.service.IReferrerPortalService
;
import
com.yd.csf.service.vo.ReferrerDashboardVO
;
import
org.springframework.web.bind.annotation.GetMapping
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RestController
;
import
javax.annotation.Resource
;
/**
* 转介人门户 - 首页指标。
*
* <p>容器内路径 {@code /c/dashboard/**},对外经 context-path 暴露为
* {@code /csf/api/c/dashboard/**},走转介人门户独立安全链。</p>
*/
@RestController
@RequestMapping
(
"/c/dashboard"
)
public
class
ReferrerDashboardController
{
@Resource
private
IReferrerPortalService
referrerPortalService
;
/**
* 首页指标:本月已发 / 本课税年度累计已发 / 在途佣金 / 生效件数 / 首年 FYP。
*/
@GetMapping
(
"/summary"
)
public
Result
<
ReferrerDashboardVO
>
summary
()
{
String
brokerBizId
=
ReferrerIdentityContext
.
requireBrokerBizId
();
return
Result
.
success
(
referrerPortalService
.
getDashboard
(
brokerBizId
));
}
}
yd-csf-api/src/main/java/com/yd/csf/api/controller/ReferrerPipelineController.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
api
.
controller
;
import
com.yd.common.result.Result
;
import
com.yd.csf.api.security.ReferrerIdentityContext
;
import
com.yd.csf.service.service.IReferrerPortalService
;
import
com.yd.csf.service.vo.ReferrerPipelineVO
;
import
org.springframework.web.bind.annotation.GetMapping
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RestController
;
import
javax.annotation.Resource
;
import
java.util.List
;
/**
* 转介人门户 - 在途佣金。
*
* <p>容器内路径 {@code /c/pipeline/**},对外经 context-path 暴露为
* {@code /csf/api/c/pipeline/**},走转介人门户独立安全链。
* 展示待出账的预计发佣及预计到账日;保留/失效翻译为可读文案。</p>
*/
@RestController
@RequestMapping
(
"/c/pipeline"
)
public
class
ReferrerPipelineController
{
@Resource
private
IReferrerPortalService
referrerPortalService
;
/**
* 在途佣金列表:含预计到账日,按 payout_date 升序。
*/
@GetMapping
(
"/list"
)
public
Result
<
List
<
ReferrerPipelineVO
>>
list
()
{
String
brokerBizId
=
ReferrerIdentityContext
.
requireBrokerBizId
();
return
Result
.
success
(
referrerPortalService
.
listPipeline
(
brokerBizId
));
}
}
yd-csf-api/src/main/java/com/yd/csf/api/controller/ReferrerPolicyController.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
api
.
controller
;
import
com.yd.common.result.Result
;
import
com.yd.csf.api.security.ReferrerIdentityContext
;
import
com.yd.csf.service.service.IReferrerPortalService
;
import
com.yd.csf.service.vo.ReferrerPolicyCommissionVO
;
import
com.yd.csf.service.vo.ReferrerPolicyListVO
;
import
org.springframework.web.bind.annotation.GetMapping
;
import
org.springframework.web.bind.annotation.PathVariable
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RestController
;
import
javax.annotation.Resource
;
/**
* 转介人门户 - 我的保单。
*
* <p>容器内路径 {@code /c/policy/**},对外经 context-path 暴露为
* {@code /csf/api/c/policy/**},走转介人门户独立安全链。
* 生效保单整单展示;退保/失效/拒保分组可见不计件。</p>
*/
@RestController
@RequestMapping
(
"/c/policy"
)
public
class
ReferrerPolicyController
{
@Resource
private
IReferrerPortalService
referrerPortalService
;
/**
* 我的保单:生效 / 未生效失效两组。
*/
@GetMapping
(
"/list"
)
public
Result
<
ReferrerPolicyListVO
>
list
()
{
String
brokerBizId
=
ReferrerIdentityContext
.
requireBrokerBizId
();
return
Result
.
success
(
referrerPortalService
.
listPolicies
(
brokerBizId
));
}
/**
* 保单各年度佣金钻取:首年(FYP/FYC)与续期(RYP/RYC)分列。
*/
@GetMapping
(
"/{policyNo}/commission"
)
public
Result
<
ReferrerPolicyCommissionVO
>
commission
(
@PathVariable
String
policyNo
)
{
String
brokerBizId
=
ReferrerIdentityContext
.
requireBrokerBizId
();
return
Result
.
success
(
referrerPortalService
.
getPolicyCommission
(
brokerBizId
,
policyNo
));
}
}
yd-csf-api/src/main/java/com/yd/csf/api/controller/ReferrerStatementController.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
api
.
controller
;
import
com.yd.common.result.Result
;
import
com.yd.csf.api.security.ReferrerIdentityContext
;
import
com.yd.csf.service.service.IReferrerPortalService
;
import
com.yd.csf.service.vo.ReferrerStatementDetailVO
;
import
com.yd.csf.service.vo.ReferrerStatementVO
;
import
org.springframework.web.bind.annotation.GetMapping
;
import
org.springframework.web.bind.annotation.PathVariable
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RestController
;
import
javax.annotation.Resource
;
import
java.util.List
;
/**
* 转介人门户 - 月结单。
*
* <p>容器内路径 {@code /c/statement/**},对外经 context-path 暴露为
* {@code /csf/api/c/statement/**},走转介人门户独立安全链。
* 仅展示已推送(salary.status=5)的薪资单;头部与逐笔明细各自忠实呈现,不强制二者相等。</p>
*/
@RestController
@RequestMapping
(
"/c/statement"
)
public
class
ReferrerStatementController
{
@Resource
private
IReferrerPortalService
referrerPortalService
;
/**
* 月结单列表:仅已推送,按月份降序。
*/
@GetMapping
(
"/list"
)
public
Result
<
List
<
ReferrerStatementVO
>>
list
()
{
String
brokerBizId
=
ReferrerIdentityContext
.
requireBrokerBizId
();
return
Result
.
success
(
referrerPortalService
.
listStatements
(
brokerBizId
));
}
/**
* 月结单详情:头部应发/MPF/其他调整/实发/发放日/银行尾号 + 逐笔保单发佣明细。
*/
@GetMapping
(
"/{salaryBizId}"
)
public
Result
<
ReferrerStatementDetailVO
>
detail
(
@PathVariable
String
salaryBizId
)
{
String
brokerBizId
=
ReferrerIdentityContext
.
requireBrokerBizId
();
return
Result
.
success
(
referrerPortalService
.
getStatementDetail
(
brokerBizId
,
salaryBizId
));
}
}
yd-csf-api/src/main/java/com/yd/csf/api/controller/ReferrerTodoController.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
api
.
controller
;
import
com.yd.common.result.Result
;
import
com.yd.csf.api.dto.ReferrerTodoNotifyRequest
;
import
com.yd.csf.api.security.ReferrerIdentityContext
;
import
com.yd.csf.service.service.IReferrerTodoService
;
import
org.springframework.validation.annotation.Validated
;
import
org.springframework.web.bind.annotation.PostMapping
;
import
org.springframework.web.bind.annotation.RequestBody
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RestController
;
import
javax.annotation.Resource
;
/**
* 转介人待办通知留痕接口
*/
@RestController
@RequestMapping
(
"/c/todo"
)
public
class
ReferrerTodoController
{
@Resource
private
IReferrerTodoService
referrerTodoService
;
/**
* 已通知 / 处理留痕
*/
@PostMapping
(
"/notify"
)
public
Result
<
Boolean
>
notify
(
@Validated
@RequestBody
ReferrerTodoNotifyRequest
request
)
{
String
clientUserBizId
=
ReferrerIdentityContext
.
requireClientUserBizId
();
referrerTodoService
.
recordNotify
(
clientUserBizId
,
request
.
getPolicyNo
(),
request
.
getTodoType
());
return
Result
.
success
(
Boolean
.
TRUE
);
}
}
\ No newline at end of file
yd-csf-api/src/main/java/com/yd/csf/api/dto/ReferrerTodoNotifyRequest.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
api
.
dto
;
import
lombok.Data
;
import
javax.validation.constraints.NotBlank
;
/**
* 待办通知留痕请求
*/
@Data
public
class
ReferrerTodoNotifyRequest
{
@NotBlank
(
message
=
"保单号不能为空"
)
private
String
policyNo
;
@NotBlank
(
message
=
"待办类型不能为空"
)
private
String
todoType
;
}
\ No newline at end of file
yd-csf-api/src/main/java/com/yd/csf/api/security/ReferrerIdentityContext.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
api
.
security
;
import
com.yd.common.exception.BusinessException
;
/**
* 当前转介人登录身份上下文。
*
* <p>业务接口只从本类取登录身份,不接收前端身份入参。</p>
*
* <p>身份说明:csf 库的 {@code broker_biz_id}(policy_broker / salary / fortune /
* expected_fortune 等行级归属字段)与 IAM 的 {@code clientUserBizId} 是<b>同一个 ID、
* 同一命名空间</b>,并非两套主键。依据:{@code policy_broker.broker_biz_id} 实际落值为
* {@code client_user_xxx};预计发佣生成逻辑中直接
* {@code resDto.setClientUserBizId(brokerDto.getBrokerBizId())},
* {@code ConditionConfigMapper} 亦以 {@code agent_id AS brokerBizId}。
* 因此行级数据查询直接用登录 {@code clientUserBizId} 作为 {@code brokerBizId},
* 无需任何映射表或手机号反查。</p>
*/
public
final
class
ReferrerIdentityContext
{
private
static
final
ThreadLocal
<
String
>
CLIENT_USER_BIZ_ID
=
new
ThreadLocal
<>();
private
ReferrerIdentityContext
()
{
}
public
static
void
setClientUserBizId
(
String
clientUserBizId
)
{
CLIENT_USER_BIZ_ID
.
set
(
clientUserBizId
);
}
public
static
String
getClientUserBizId
()
{
return
CLIENT_USER_BIZ_ID
.
get
();
}
public
static
String
requireClientUserBizId
()
{
String
clientUserBizId
=
CLIENT_USER_BIZ_ID
.
get
();
if
(
clientUserBizId
==
null
||
clientUserBizId
.
trim
().
isEmpty
())
{
throw
new
BusinessException
(
"缺失转介人登录身份"
);
}
return
clientUserBizId
;
}
/**
* 当前登录转介人在 csf 行级数据上使用的 {@code brokerBizId}。
*
* <p>当前与 {@link #requireClientUserBizId()} 同值(恒等),语义化方法:
* Controller 取行级归属 ID 应调用本方法,而非直接拿 clientUserBizId。
* 若将来出现独立 broker 命名空间,只需在此桥接一处。</p>
*/
public
static
String
requireBrokerBizId
()
{
return
requireClientUserBizId
();
}
public
static
void
clear
()
{
CLIENT_USER_BIZ_ID
.
remove
();
}
}
\ No newline at end of file
yd-csf-api/src/main/java/com/yd/csf/api/security/ReferrerIdentityFilter.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
api
.
security
;
import
com.yd.common.enums.CommonEnum
;
import
com.yd.common.result.Result
;
import
com.yd.user.feign.client.clientuser.ApiClientUserFeignClient
;
import
com.yd.user.feign.response.clientuser.ApiClientUserIdpLoginResponse
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.security.authentication.UsernamePasswordAuthenticationToken
;
import
org.springframework.security.core.context.SecurityContextHolder
;
import
org.springframework.security.oauth2.jwt.Jwt
;
import
org.springframework.security.oauth2.jwt.JwtDecoder
;
import
org.springframework.security.oauth2.jwt.JwtException
;
import
org.springframework.util.StringUtils
;
import
org.springframework.web.filter.OncePerRequestFilter
;
import
javax.servlet.FilterChain
;
import
javax.servlet.ServletException
;
import
javax.servlet.http.HttpServletRequest
;
import
javax.servlet.http.HttpServletResponse
;
import
java.io.IOException
;
import
java.util.Collections
;
import
java.util.Objects
;
/**
* 转介人门户身份过滤器。
*
* <p>两种身份来源,按优先级:</p>
* <ol>
* <li>请求携带 {@code Authorization: Bearer} 且配置了 JWKS:用 {@link JwtDecoder} 校验
* RS256 + iss + aud,取 {@code sub} 调 yd-user 解析客户端用户,
* 并强制 {@code status=1 && approval_status=CG} 门禁;</li>
* <li>未携带 token 但配置了 {@code referrer.oidc.mock-client-user-biz-id}:注入固定身份,
* 仅供开发期联调(生产不得配置)。</li>
* </ol>
*
* <p>无法建立身份时不写入 SecurityContext,由链内授权规则返回 401。</p>
*/
@Slf4j
public
class
ReferrerIdentityFilter
extends
OncePerRequestFilter
{
private
static
final
String
BEARER_PREFIX
=
"Bearer "
;
private
final
ReferrerOidcProperties
properties
;
private
final
ApiClientUserFeignClient
clientUserFeignClient
;
private
final
JwtDecoder
jwtDecoder
;
public
ReferrerIdentityFilter
(
ReferrerOidcProperties
properties
,
ApiClientUserFeignClient
clientUserFeignClient
,
JwtDecoder
jwtDecoder
)
{
this
.
properties
=
properties
;
this
.
clientUserFeignClient
=
clientUserFeignClient
;
this
.
jwtDecoder
=
jwtDecoder
;
}
@Override
protected
void
doFilterInternal
(
HttpServletRequest
request
,
HttpServletResponse
response
,
FilterChain
filterChain
)
throws
ServletException
,
IOException
{
boolean
authenticated
=
false
;
String
token
=
resolveBearerToken
(
request
);
if
(
StringUtils
.
hasText
(
token
)
&&
jwtDecoder
!=
null
)
{
authenticated
=
authenticateWithJwt
(
token
);
}
else
if
(
StringUtils
.
hasText
(
token
))
{
log
.
warn
(
"收到 Bearer token 但 JwtDecoder 未初始化:referrer.oidc.jwk-set-uri 是否配置?"
);
}
else
if
(
StringUtils
.
hasText
(
properties
.
getMockClientUserBizId
()))
{
applyIdentity
(
properties
.
getMockClientUserBizId
());
authenticated
=
true
;
}
else
{
log
.
debug
(
"请求未携带 Bearer token,且未配置 mock 身份"
);
}
if
(!
authenticated
)
{
filterChain
.
doFilter
(
request
,
response
);
return
;
}
try
{
filterChain
.
doFilter
(
request
,
response
);
}
finally
{
ReferrerIdentityContext
.
clear
();
}
}
/**
* 验签并按 sub 解析转介人身份;任何校验失败返回 false(不抛错、不泄露原因)。
*/
private
boolean
authenticateWithJwt
(
String
token
)
{
Jwt
jwt
;
try
{
jwt
=
jwtDecoder
.
decode
(
token
);
}
catch
(
JwtException
|
IllegalArgumentException
e
)
{
log
.
warn
(
"JWT 验签/校验失败:{}"
,
e
.
getMessage
());
return
false
;
}
String
sub
=
jwt
.
getSubject
();
if
(!
StringUtils
.
hasText
(
sub
))
{
log
.
warn
(
"JWT 缺少 sub claim"
);
return
false
;
}
Result
<
ApiClientUserIdpLoginResponse
>
result
=
clientUserFeignClient
.
getByIdpSub
(
properties
.
getTenantBizId
(),
sub
);
if
(
Objects
.
isNull
(
result
))
{
log
.
warn
(
"调用 yd-user getByIdpSub 失败(Feign 降级返回 null),tenantBizId={} sub={}"
,
properties
.
getTenantBizId
(),
sub
);
return
false
;
}
if
(
Objects
.
isNull
(
result
.
getData
()))
{
log
.
warn
(
"IdP 身份未绑定 client_user 或租户不匹配,tenantBizId={} sub={} code={} msg={}"
,
properties
.
getTenantBizId
(),
sub
,
result
.
getCode
(),
result
.
getMsg
());
return
false
;
}
ApiClientUserIdpLoginResponse
login
=
result
.
getData
();
if
(!
Integer
.
valueOf
(
1
).
equals
(
login
.
getStatus
()))
{
log
.
warn
(
"转介人账号已停用 status={} clientUserBizId={}"
,
login
.
getStatus
(),
login
.
getClientUserBizId
());
return
false
;
}
if
(!
CommonEnum
.
APPROVAL_STATUS_CG
.
getCode
().
equals
(
login
.
getApprovalStatus
()))
{
log
.
warn
(
"转介人审批状态不可登录 approvalStatus={} clientUserBizId={}"
,
login
.
getApprovalStatus
(),
login
.
getClientUserBizId
());
return
false
;
}
applyIdentity
(
login
.
getClientUserBizId
());
return
true
;
}
private
void
applyIdentity
(
String
clientUserBizId
)
{
ReferrerIdentityContext
.
setClientUserBizId
(
clientUserBizId
);
UsernamePasswordAuthenticationToken
authentication
=
new
UsernamePasswordAuthenticationToken
(
clientUserBizId
,
null
,
Collections
.
emptyList
());
SecurityContextHolder
.
getContext
().
setAuthentication
(
authentication
);
}
private
String
resolveBearerToken
(
HttpServletRequest
request
)
{
String
header
=
request
.
getHeader
(
"Authorization"
);
if
(
StringUtils
.
hasText
(
header
)
&&
header
.
startsWith
(
BEARER_PREFIX
))
{
return
header
.
substring
(
BEARER_PREFIX
.
length
()).
trim
();
}
return
null
;
}
}
yd-csf-api/src/main/java/com/yd/csf/api/security/ReferrerOidcProperties.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
api
.
security
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.stereotype.Component
;
/**
* 转介人门户 OIDC 配置占位。
*
* <p>真实值由 Nacos 提供,开发期可只配置 {@code mockClientUserBizId} 跑通数据接口。</p>
*/
@Configuration
public
class
ReferrerOidcProperties
{
@Value
(
"${referrer.oidc.jwk-set-uri:}"
)
private
String
jwkSetUri
;
@Value
(
"${referrer.oidc.issuer:}"
)
private
String
issuer
;
@Value
(
"${referrer.oidc.audience:}"
)
private
String
audience
;
/**
* 转介人所属租户业务ID(csf 当前为单租户部署,认证时身份解析限定该租户)
*/
@Value
(
"${referrer.oidc.tenant-biz-id:}"
)
private
String
tenantBizId
;
@Value
(
"${referrer.oidc.mock-client-user-biz-id:}"
)
private
String
mockClientUserBizId
;
public
String
getJwkSetUri
()
{
return
jwkSetUri
;
}
public
String
getIssuer
()
{
return
issuer
;
}
public
String
getAudience
()
{
return
audience
;
}
public
String
getTenantBizId
()
{
return
tenantBizId
;
}
public
String
getMockClientUserBizId
()
{
return
mockClientUserBizId
;
}
}
\ No newline at end of file
yd-csf-api/src/main/java/com/yd/csf/api/security/ReferrerPortalSecurityConfig.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
api
.
security
;
import
com.yd.user.feign.client.clientuser.ApiClientUserFeignClient
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.core.annotation.Order
;
import
org.springframework.http.HttpStatus
;
import
org.springframework.security.config.annotation.web.builders.HttpSecurity
;
import
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
;
import
org.springframework.security.config.http.SessionCreationPolicy
;
import
org.springframework.security.oauth2.core.OAuth2TokenValidator
;
import
org.springframework.security.oauth2.jwt.Jwt
;
import
org.springframework.security.oauth2.jwt.JwtClaimValidator
;
import
org.springframework.security.oauth2.jwt.JwtDecoder
;
import
org.springframework.security.oauth2.jwt.JwtIssuerValidator
;
import
org.springframework.security.oauth2.jwt.JwtTimestampValidator
;
import
org.springframework.security.oauth2.jwt.NimbusJwtDecoder
;
import
org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator
;
import
org.springframework.security.oauth2.jose.jws.SignatureAlgorithm
;
import
org.springframework.security.web.authentication.HttpStatusEntryPoint
;
import
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
;
import
org.springframework.util.StringUtils
;
import
java.util.ArrayList
;
import
java.util.Collection
;
import
java.util.List
;
/**
* 转介人门户独立安全链。
*
* <p>必须沿用 {@link WebSecurityConfigurerAdapter} 老式风格:yd-auth-core 的后台链也是
* Adapter 风格,Spring Security 5.6 不允许 Adapter 与 {@code SecurityFilterChain} Bean 混用,
* 否则启动报 “Found WebSecurityConfigurerAdapter as well as SecurityFilterChain”。</p>
*
* <p>多个 Adapter 凭 {@link Order} 共存:本链 {@code @Order(1)} 优先匹配容器内
* {@code /c/**}(对外 {@code /csf/api/c/**}),其余请求落老链(兜底 {@code /**})。</p>
*/
@Configuration
@Order
(
1
)
public
class
ReferrerPortalSecurityConfig
extends
WebSecurityConfigurerAdapter
{
private
final
ReferrerOidcProperties
properties
;
private
final
ApiClientUserFeignClient
clientUserFeignClient
;
public
ReferrerPortalSecurityConfig
(
ReferrerOidcProperties
properties
,
ApiClientUserFeignClient
clientUserFeignClient
)
{
this
.
properties
=
properties
;
this
.
clientUserFeignClient
=
clientUserFeignClient
;
}
@Override
protected
void
configure
(
HttpSecurity
http
)
throws
Exception
{
http
.
antMatcher
(
"/c/**"
)
.
csrf
().
disable
()
.
sessionManagement
().
sessionCreationPolicy
(
SessionCreationPolicy
.
STATELESS
)
.
and
()
.
exceptionHandling
()
// 未携带有效身份时直接 401,避免落到默认 403/登录页
.
authenticationEntryPoint
(
new
HttpStatusEntryPoint
(
HttpStatus
.
UNAUTHORIZED
))
.
and
()
.
authorizeRequests
()
.
anyRequest
().
authenticated
()
.
and
()
// 链内手动 new:不加 @Component,避免被 Boot 注册成容器级 Filter 污染老后台 /** 链
.
addFilterBefore
(
new
ReferrerIdentityFilter
(
properties
,
clientUserFeignClient
,
buildJwtDecoder
()),
UsernamePasswordAuthenticationFilter
.
class
);
}
/**
* 构建 RS256 JWKS 解码器,校验时间戳 + iss + aud(aud 为数组时要求包含配置 audience)。
* 未配置 jwk-set-uri 时返回 null,过滤器退化为开发期 mock 身份。
*/
private
JwtDecoder
buildJwtDecoder
()
{
if
(!
StringUtils
.
hasText
(
properties
.
getJwkSetUri
()))
{
return
null
;
}
NimbusJwtDecoder
decoder
=
NimbusJwtDecoder
.
withJwkSetUri
(
properties
.
getJwkSetUri
())
.
jwsAlgorithm
(
SignatureAlgorithm
.
RS256
)
.
build
();
List
<
OAuth2TokenValidator
<
Jwt
>>
validators
=
new
ArrayList
<>();
validators
.
add
(
new
JwtTimestampValidator
());
if
(
StringUtils
.
hasText
(
properties
.
getIssuer
()))
{
validators
.
add
(
new
JwtIssuerValidator
(
properties
.
getIssuer
()));
}
if
(
StringUtils
.
hasText
(
properties
.
getAudience
()))
{
String
audience
=
properties
.
getAudience
();
validators
.
add
(
new
JwtClaimValidator
<
Collection
<?>>(
"aud"
,
aud
->
aud
!=
null
&&
aud
.
contains
(
audience
)));
}
decoder
.
setJwtValidator
(
new
DelegatingOAuth2TokenValidator
<>(
validators
));
return
decoder
;
}
}
yd-csf-service/src/main/java/com/yd/csf/service/dao/ReferrerTodoMapper.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
service
.
dao
;
import
com.baomidou.mybatisplus.core.mapper.BaseMapper
;
import
com.yd.csf.service.model.ReferrerTodo
;
/**
* 转介人待办通知留痕表 Mapper
*/
public
interface
ReferrerTodoMapper
extends
BaseMapper
<
ReferrerTodo
>
{
}
\ No newline at end of file
yd-csf-service/src/main/java/com/yd/csf/service/enums/ReferrerCommissionStage.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
service
.
enums
;
/**
* 转介人门户佣金四态(design D4.1)。
*
* <p>系统内部出账状态(expected_fortune/fortune 0–7、salary 1–6)不直接对外,
* 统一收敛为转介人语言的四个「钱的位置」。守恒式(互斥且闭合):
* {@code 应得 = 已发 + 在途 + 保留 + 失效}。</p>
*/
public
enum
ReferrerCommissionStage
{
/** 已发:已完成出账、已到手。 */
PAID
,
/** 在途:钱会来、尚未到(待出账、部分出账的未付段、分期未到期、未找到来佣 7)。 */
PIPELINE
,
/** 保留:暂扣、未说不给。 */
RETAINED
,
/** 失效:这笔没了。 */
LAPSED
;
/**
* expected_fortune 整行状态 -> 四态(用于只展示剩余待发的在途列表)。
* 不产生 {@link #PAID}:全额出账无独立状态码,已发须按 paid/unpaid 金额判定。
*/
public
static
ReferrerCommissionStage
fromExpectedStatus
(
String
status
)
{
if
(
"4"
.
equals
(
status
))
{
return
RETAINED
;
}
if
(
"5"
.
equals
(
status
))
{
return
LAPSED
;
}
// 0/1/2/3/6/7 均按在途;7(未找到来佣)遵 D4.1 归在途,不得判失效。
return
PIPELINE
;
}
}
yd-csf-service/src/main/java/com/yd/csf/service/model/ReferrerTodo.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
service
.
model
;
import
com.baomidou.mybatisplus.annotation.IdType
;
import
com.baomidou.mybatisplus.annotation.TableField
;
import
com.baomidou.mybatisplus.annotation.TableId
;
import
com.baomidou.mybatisplus.annotation.TableName
;
import
lombok.Getter
;
import
lombok.Setter
;
import
java.io.Serializable
;
import
java.time.LocalDateTime
;
/**
* 转介人待办通知留痕表
*/
@Getter
@Setter
@TableName
(
"referrer_todo"
)
public
class
ReferrerTodo
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
@TableId
(
value
=
"id"
,
type
=
IdType
.
AUTO
)
private
Long
id
;
@TableField
(
"todo_biz_id"
)
private
String
todoBizId
;
@TableField
(
"broker_biz_id"
)
private
String
brokerBizId
;
@TableField
(
"policy_no"
)
private
String
policyNo
;
@TableField
(
"todo_type"
)
private
String
todoType
;
@TableField
(
"notify_time"
)
private
LocalDateTime
notifyTime
;
@TableField
(
"create_time"
)
private
LocalDateTime
createTime
;
@TableField
(
"update_time"
)
private
LocalDateTime
updateTime
;
}
\ No newline at end of file
yd-csf-service/src/main/java/com/yd/csf/service/service/IReferrerPortalService.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
service
.
service
;
import
com.yd.csf.service.vo.ReferrerDashboardVO
;
import
com.yd.csf.service.vo.ReferrerPipelineVO
;
import
com.yd.csf.service.vo.ReferrerPolicyCommissionVO
;
import
com.yd.csf.service.vo.ReferrerPolicyListVO
;
import
com.yd.csf.service.vo.ReferrerStatementDetailVO
;
import
com.yd.csf.service.vo.ReferrerStatementVO
;
import
java.util.List
;
/**
* 转介人门户只读数据服务。
*
* <p>所有方法均要求调用方传入已解析的转介人 {@code brokerBizId},
* 服务层不接收任何前端身份入参。</p>
*/
public
interface
IReferrerPortalService
{
/**
* 首页指标。
*/
ReferrerDashboardVO
getDashboard
(
String
brokerBizId
);
/**
* 月结单列表(仅已推送,按月份降序)。
*/
List
<
ReferrerStatementVO
>
listStatements
(
String
brokerBizId
);
/**
* 月结单详情。
*/
ReferrerStatementDetailVO
getStatementDetail
(
String
brokerBizId
,
String
salaryBizId
);
/**
* 我的保单(生效 / 未生效失效分组)。
*/
ReferrerPolicyListVO
listPolicies
(
String
brokerBizId
);
/**
* 保单各年度佣金钻取。
*/
ReferrerPolicyCommissionVO
getPolicyCommission
(
String
brokerBizId
,
String
policyNo
);
/**
* 在途佣金列表。
*/
List
<
ReferrerPipelineVO
>
listPipeline
(
String
brokerBizId
);
}
\ No newline at end of file
yd-csf-service/src/main/java/com/yd/csf/service/service/IReferrerTodoService.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
service
.
service
;
import
com.baomidou.mybatisplus.extension.service.IService
;
import
com.yd.csf.service.model.ReferrerTodo
;
/**
* 转介人待办通知留痕服务
*/
public
interface
IReferrerTodoService
extends
IService
<
ReferrerTodo
>
{
/**
* 记录一次「已通知」留痕。
*
* @param brokerBizId 登录态转介人业务 ID
* @param policyNo 保单号
* @param todoType 待办类型
*/
void
recordNotify
(
String
brokerBizId
,
String
policyNo
,
String
todoType
);
}
\ No newline at end of file
yd-csf-service/src/main/java/com/yd/csf/service/service/impl/ReferrerPortalServiceImpl.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
service
.
service
.
impl
;
import
com.yd.common.exception.BusinessException
;
import
com.yd.csf.feign.enums.PaymentFrequencyEnum
;
import
com.yd.csf.feign.enums.SalaryStatusEnum
;
import
com.yd.csf.service.enums.FortuneAccountStatusEnum
;
import
com.yd.csf.service.enums.PolicyStatusEnum
;
import
com.yd.csf.service.enums.ReferrerCommissionStage
;
import
com.yd.csf.service.model.ExpectedFortune
;
import
com.yd.csf.service.model.Fortune
;
import
com.yd.csf.service.model.FortuneAccount
;
import
com.yd.csf.service.model.Policy
;
import
com.yd.csf.service.model.PolicyBroker
;
import
com.yd.csf.service.model.Salary
;
import
com.yd.csf.service.model.SalaryRemittance
;
import
com.yd.csf.service.service.FortuneAccountService
;
import
com.yd.csf.service.service.FortuneService
;
import
com.yd.csf.service.service.IExpectedFortuneService
;
import
com.yd.csf.service.service.IReferrerPortalService
;
import
com.yd.csf.service.service.ISalaryRemittanceService
;
import
com.yd.csf.service.service.ISalaryService
;
import
com.yd.csf.service.service.PolicyBrokerService
;
import
com.yd.csf.service.service.PolicyService
;
import
com.yd.csf.service.vo.ReferrerDashboardVO
;
import
com.yd.csf.service.vo.ReferrerPipelineVO
;
import
com.yd.csf.service.vo.ReferrerPolicyCommissionItemVO
;
import
com.yd.csf.service.vo.ReferrerPolicyCommissionVO
;
import
com.yd.csf.service.vo.ReferrerPolicyListVO
;
import
com.yd.csf.service.vo.ReferrerPolicyVO
;
import
com.yd.csf.service.vo.ReferrerStatementDetailVO
;
import
com.yd.csf.service.vo.ReferrerStatementItemVO
;
import
com.yd.csf.service.vo.ReferrerStatementVO
;
import
org.apache.commons.lang3.StringUtils
;
import
org.springframework.stereotype.Service
;
import
javax.annotation.Resource
;
import
java.math.BigDecimal
;
import
java.math.RoundingMode
;
import
java.time.LocalDate
;
import
java.time.YearMonth
;
import
java.time.ZoneId
;
import
java.time.format.DateTimeFormatter
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Collections
;
import
java.util.Comparator
;
import
java.util.Date
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Set
;
import
java.util.stream.Collectors
;
/**
* 转介人门户只读数据服务实现。
*
* <p>行级归属:所有查询均强制 {@code broker_biz_id = 当前登录转介人},
* 不接收任何前端身份入参。</p>
*/
@Service
public
class
ReferrerPortalServiceImpl
implements
IReferrerPortalService
{
private
static
final
DateTimeFormatter
MONTH_FORMAT
=
DateTimeFormatter
.
ofPattern
(
"yyyyMM"
);
/**
* 在途视图可见 / 在途金额计入状态:除失效(5)外全部,
* 含 4=保留、7=未找到来佣(遵 D4.1 归在途)、3=部分出账。
*/
private
static
final
List
<
String
>
ACTIVE_STATUSES
=
Arrays
.
asList
(
"0"
,
"1"
,
"2"
,
"3"
,
"4"
,
"6"
,
"7"
);
@Resource
private
ISalaryService
salaryService
;
@Resource
private
IExpectedFortuneService
expectedFortuneService
;
@Resource
private
FortuneService
fortuneService
;
@Resource
private
FortuneAccountService
fortuneAccountService
;
@Resource
private
ISalaryRemittanceService
salaryRemittanceService
;
@Resource
private
PolicyService
policyService
;
@Resource
private
PolicyBrokerService
policyBrokerService
;
@Override
public
ReferrerDashboardVO
getDashboard
(
String
brokerBizId
)
{
ReferrerDashboardVO
vo
=
new
ReferrerDashboardVO
();
vo
.
setMonthPaidCommission
(
BigDecimal
.
ZERO
);
vo
.
setTaxYearPaidCommission
(
BigDecimal
.
ZERO
);
vo
.
setPipelineCommission
(
BigDecimal
.
ZERO
);
vo
.
setEffectivePolicyCount
(
0L
);
vo
.
setFirstYearFyp
(
BigDecimal
.
ZERO
);
YearMonth
now
=
YearMonth
.
now
();
YearMonth
taxStart
=
now
.
getMonthValue
()
>=
4
?
YearMonth
.
of
(
now
.
getYear
(),
4
)
:
YearMonth
.
of
(
now
.
getYear
()
-
1
,
4
);
YearMonth
taxEnd
=
taxStart
.
plusYears
(
1
).
withMonth
(
3
);
List
<
Salary
>
taxYearSalaries
=
listPushedSalaries
(
brokerBizId
,
taxStart
,
taxEnd
);
String
currentMonth
=
now
.
format
(
MONTH_FORMAT
);
BigDecimal
monthPaid
=
BigDecimal
.
ZERO
;
BigDecimal
taxPaid
=
BigDecimal
.
ZERO
;
for
(
Salary
salary
:
taxYearSalaries
)
{
BigDecimal
paidAmount
=
defaultZero
(
salary
.
getPaidAmount
());
taxPaid
=
taxPaid
.
add
(
paidAmount
);
if
(
StringUtils
.
equals
(
currentMonth
,
salary
.
getMonth
()))
{
monthPaid
=
monthPaid
.
add
(
paidAmount
);
}
}
vo
.
setMonthPaidCommission
(
n2
(
monthPaid
));
vo
.
setTaxYearPaidCommission
(
n2
(
taxPaid
));
// listPipelineRows 已限定 ACTIVE_STATUSES(除失效外,含保留/未找到来佣)
List
<
ExpectedFortune
>
pipelineRows
=
listPipelineRows
(
brokerBizId
);
BigDecimal
pipelineAmount
=
pipelineRows
.
stream
()
.
map
(
r
->
defaultZero
(
r
.
getUnpaidRuleAmountHkd
()))
.
reduce
(
BigDecimal
.
ZERO
,
BigDecimal:
:
add
);
vo
.
setPipelineCommission
(
n2
(
pipelineAmount
));
Map
<
String
,
PolicyBroker
>
brokerMap
=
loadMyBrokerMap
(
brokerBizId
);
List
<
Policy
>
myPolicies
=
loadMyPolicies
(
brokerMap
.
keySet
());
List
<
Policy
>
inforcePolicies
=
myPolicies
.
stream
()
.
filter
(
p
->
PolicyStatusEnum
.
INFORCE
.
getItemValue
().
equals
(
p
.
getStatus
()))
.
collect
(
Collectors
.
toList
());
vo
.
setEffectivePolicyCount
((
long
)
inforcePolicies
.
size
());
BigDecimal
firstYearFyp
=
BigDecimal
.
ZERO
;
for
(
Policy
policy
:
inforcePolicies
)
{
PolicyBroker
policyBroker
=
brokerMap
.
get
(
policy
.
getPolicyNo
());
if
(
policyBroker
==
null
)
{
continue
;
}
firstYearFyp
=
firstYearFyp
.
add
(
personalFirstYearPremiumHkd
(
policy
,
policyBroker
));
}
vo
.
setFirstYearFyp
(
n2
(
firstYearFyp
));
return
vo
;
}
@Override
public
List
<
ReferrerStatementVO
>
listStatements
(
String
brokerBizId
)
{
YearMonth
start
=
YearMonth
.
of
(
2026
,
4
);
List
<
Salary
>
salaries
=
listPushedSalaries
(
brokerBizId
,
start
,
YearMonth
.
now
());
salaries
.
sort
(
Comparator
.
comparing
(
Salary:
:
getMonth
,
Comparator
.
nullsLast
(
Comparator
.
reverseOrder
())));
return
salaries
.
stream
().
map
(
salary
->
{
ReferrerStatementVO
vo
=
new
ReferrerStatementVO
();
vo
.
setSalaryBizId
(
salary
.
getSalaryBizId
());
vo
.
setMonth
(
salary
.
getMonth
());
vo
.
setSalaryNo
(
salary
.
getSalaryNo
());
vo
.
setGrossAmount
(
n2
(
salary
.
getGrossAmount
()));
vo
.
setMpfAmount
(
n2
(
salary
.
getMpfAmount
()));
vo
.
setOtherAmount
(
n2
(
salary
.
getOtherAmount
()));
vo
.
setPaidAmount
(
n2
(
salary
.
getPaidAmount
()));
List
<
FortuneAccount
>
accounts
=
fortuneAccountService
.
queryList
(
brokerBizId
,
salary
.
getMonth
(),
FortuneAccountStatusEnum
.
SENT
.
getItemValue
());
if
(
accounts
!=
null
&&
!
accounts
.
isEmpty
())
{
vo
.
setPayoutDate
(
toLocalDate
(
accounts
.
get
(
0
).
getFortuneAccountDate
()));
}
return
vo
;
}).
collect
(
Collectors
.
toList
());
}
@Override
public
ReferrerStatementDetailVO
getStatementDetail
(
String
brokerBizId
,
String
salaryBizId
)
{
Salary
salary
=
salaryService
.
lambdaQuery
()
.
eq
(
Salary:
:
getSalaryBizId
,
salaryBizId
)
.
eq
(
Salary:
:
getIsDeleted
,
0
)
.
one
();
if
(
salary
==
null
)
{
throw
new
BusinessException
(
"月结单不存在"
);
}
if
(!
StringUtils
.
equals
(
brokerBizId
,
salary
.
getBrokerBizId
()))
{
throw
new
BusinessException
(
"无权查看该月结单"
);
}
if
(!
SalaryStatusEnum
.
YTS
.
getItemValue
().
equals
(
salary
.
getStatus
()))
{
throw
new
BusinessException
(
"月结单尚未推送"
);
}
ReferrerStatementDetailVO
vo
=
new
ReferrerStatementDetailVO
();
vo
.
setSalaryBizId
(
salary
.
getSalaryBizId
());
vo
.
setMonth
(
salary
.
getMonth
());
vo
.
setSalaryNo
(
salary
.
getSalaryNo
());
vo
.
setGrossAmount
(
n2
(
salary
.
getGrossAmount
()));
vo
.
setMpfAmount
(
n2
(
salary
.
getMpfAmount
()));
vo
.
setOtherAmount
(
n2
(
salary
.
getOtherAmount
()));
vo
.
setPaidAmount
(
n2
(
salary
.
getPaidAmount
()));
// 发放日 / 银行尾号
List
<
FortuneAccount
>
accounts
=
fortuneAccountService
.
queryList
(
brokerBizId
,
salary
.
getMonth
(),
FortuneAccountStatusEnum
.
SENT
.
getItemValue
());
if
(
accounts
!=
null
&&
!
accounts
.
isEmpty
())
{
vo
.
setPayoutDate
(
toLocalDate
(
accounts
.
get
(
0
).
getFortuneAccountDate
()));
}
List
<
SalaryRemittance
>
remittances
=
salaryRemittanceService
.
queryList
(
salaryBizId
);
if
(
remittances
!=
null
&&
!
remittances
.
isEmpty
())
{
SalaryRemittance
remittance
=
remittances
.
get
(
0
);
vo
.
setBankName
(
remittance
.
getBankName
());
vo
.
setAccountEndNo
(
remittance
.
getAccountEndNo
());
}
vo
.
setItems
(
buildStatementItems
(
brokerBizId
,
salary
.
getMonth
()));
return
vo
;
}
@Override
public
ReferrerPolicyListVO
listPolicies
(
String
brokerBizId
)
{
Map
<
String
,
PolicyBroker
>
brokerMap
=
loadMyBrokerMap
(
brokerBizId
);
List
<
Policy
>
myPolicies
=
loadMyPolicies
(
brokerMap
.
keySet
());
List
<
ReferrerPolicyVO
>
effective
=
new
ArrayList
<>();
List
<
ReferrerPolicyVO
>
inactive
=
new
ArrayList
<>();
for
(
Policy
policy
:
myPolicies
)
{
PolicyBroker
policyBroker
=
brokerMap
.
get
(
policy
.
getPolicyNo
());
ReferrerPolicyVO
item
=
toPolicyVO
(
policy
,
policyBroker
);
if
(
PolicyStatusEnum
.
INFORCE
.
getItemValue
().
equals
(
policy
.
getStatus
()))
{
effective
.
add
(
item
);
}
else
{
inactive
.
add
(
item
);
}
}
ReferrerPolicyListVO
vo
=
new
ReferrerPolicyListVO
();
vo
.
setEffectivePolicies
(
effective
);
vo
.
setInactivePolicies
(
inactive
);
return
vo
;
}
@Override
public
ReferrerPolicyCommissionVO
getPolicyCommission
(
String
brokerBizId
,
String
policyNo
)
{
Long
ownCount
=
policyBrokerService
.
lambdaQuery
()
.
eq
(
PolicyBroker:
:
getBrokerBizId
,
brokerBizId
)
.
eq
(
PolicyBroker:
:
getPolicyNo
,
policyNo
)
.
eq
(
PolicyBroker:
:
getIsDeleted
,
0
)
.
count
();
if
(
ownCount
==
null
||
ownCount
==
0L
)
{
throw
new
BusinessException
(
"无权查看该保单"
);
}
List
<
ExpectedFortune
>
rows
=
expectedFortuneService
.
lambdaQuery
()
.
eq
(
ExpectedFortune:
:
getBrokerBizId
,
brokerBizId
)
.
eq
(
ExpectedFortune:
:
getPolicyNo
,
policyNo
)
.
in
(
ExpectedFortune:
:
getIsPart
,
0
,
1
)
.
eq
(
ExpectedFortune:
:
getIsDeleted
,
0
)
.
orderByAsc
(
ExpectedFortune:
:
getFortunePeriod
)
.
list
();
List
<
ReferrerPolicyCommissionItemVO
>
items
=
new
ArrayList
<>();
for
(
ExpectedFortune
row
:
rows
)
{
appendCommissionSegments
(
items
,
row
);
}
ReferrerPolicyCommissionVO
vo
=
new
ReferrerPolicyCommissionVO
();
vo
.
setPolicyNo
(
policyNo
);
vo
.
setItems
(
items
);
vo
.
setFirstYearCommission
(
n2
(
items
.
stream
()
.
filter
(
it
->
Integer
.
valueOf
(
1
).
equals
(
it
.
getFortunePeriod
()))
.
filter
(
it
->
!
ReferrerCommissionStage
.
LAPSED
.
name
().
equals
(
it
.
getStage
()))
.
map
(
ReferrerPolicyCommissionItemVO:
:
getAmount
)
.
reduce
(
BigDecimal
.
ZERO
,
BigDecimal:
:
add
)));
vo
.
setRenewalCommission
(
n2
(
items
.
stream
()
.
filter
(
it
->
it
.
getFortunePeriod
()
!=
null
&&
it
.
getFortunePeriod
()
>
1
)
.
filter
(
it
->
!
ReferrerCommissionStage
.
LAPSED
.
name
().
equals
(
it
.
getStage
()))
.
map
(
ReferrerPolicyCommissionItemVO:
:
getAmount
)
.
reduce
(
BigDecimal
.
ZERO
,
BigDecimal:
:
add
)));
return
vo
;
}
@Override
public
List
<
ReferrerPipelineVO
>
listPipeline
(
String
brokerBizId
)
{
List
<
ExpectedFortune
>
rows
=
listPipelineRows
(
brokerBizId
);
Set
<
String
>
policyNos
=
rows
.
stream
()
.
map
(
ExpectedFortune:
:
getPolicyNo
)
.
filter
(
StringUtils:
:
isNotBlank
)
.
collect
(
Collectors
.
toSet
());
Map
<
String
,
Policy
>
policyMap
=
policyNos
.
isEmpty
()
?
Collections
.
emptyMap
()
:
policyService
.
lambdaQuery
()
.
in
(
Policy:
:
getPolicyNo
,
policyNos
)
.
eq
(
Policy:
:
getIsDeleted
,
0
)
.
list
()
.
stream
()
.
collect
(
Collectors
.
toMap
(
Policy:
:
getPolicyNo
,
p
->
p
,
(
a
,
b
)
->
a
));
return
rows
.
stream
()
.
sorted
(
Comparator
.
comparing
(
ExpectedFortune:
:
getPayoutDate
,
Comparator
.
nullsLast
(
Comparator
.
naturalOrder
())))
.
map
(
row
->
{
ReferrerPipelineVO
item
=
new
ReferrerPipelineVO
();
item
.
setExpectedFortuneBizId
(
row
.
getExpectedFortuneBizId
());
item
.
setPolicyNo
(
row
.
getPolicyNo
());
Policy
policy
=
policyMap
.
get
(
row
.
getPolicyNo
());
item
.
setProductName
(
policy
==
null
?
null
:
policy
.
getProductName
());
item
.
setFortunePeriod
(
row
.
getFortunePeriod
());
item
.
setFortuneName
(
row
.
getFortuneName
());
item
.
setPayoutDate
(
row
.
getPayoutDate
());
item
.
setStage
(
ReferrerCommissionStage
.
fromExpectedStatus
(
row
.
getStatus
()).
name
());
item
.
setHkdAmount
(
n4
(
row
.
getUnpaidRuleAmountHkd
()));
return
item
;
}).
collect
(
Collectors
.
toList
());
}
// ==================== 私有组装方法 ====================
private
List
<
Salary
>
listPushedSalaries
(
String
brokerBizId
,
YearMonth
start
,
YearMonth
end
)
{
return
salaryService
.
lambdaQuery
()
.
eq
(
Salary:
:
getBrokerBizId
,
brokerBizId
)
.
eq
(
Salary:
:
getStatus
,
SalaryStatusEnum
.
YTS
.
getItemValue
())
.
eq
(
Salary:
:
getIsDeleted
,
0
)
.
ge
(
Salary:
:
getMonth
,
start
.
format
(
MONTH_FORMAT
))
.
le
(
Salary:
:
getMonth
,
end
.
format
(
MONTH_FORMAT
))
.
list
();
}
private
List
<
ExpectedFortune
>
listPipelineRows
(
String
brokerBizId
)
{
return
expectedFortuneService
.
lambdaQuery
()
.
eq
(
ExpectedFortune:
:
getBrokerBizId
,
brokerBizId
)
.
in
(
ExpectedFortune:
:
getStatus
,
ACTIVE_STATUSES
)
.
in
(
ExpectedFortune:
:
getIsPart
,
0
,
1
)
.
eq
(
ExpectedFortune:
:
getIsDeleted
,
0
)
.
list
();
}
private
Map
<
String
,
PolicyBroker
>
loadMyBrokerMap
(
String
brokerBizId
)
{
List
<
PolicyBroker
>
brokers
=
policyBrokerService
.
lambdaQuery
()
.
eq
(
PolicyBroker:
:
getBrokerBizId
,
brokerBizId
)
.
eq
(
PolicyBroker:
:
getIsDeleted
,
0
)
.
list
();
Map
<
String
,
PolicyBroker
>
map
=
new
HashMap
<>();
for
(
PolicyBroker
broker
:
brokers
)
{
if
(
StringUtils
.
isBlank
(
broker
.
getPolicyNo
()))
{
continue
;
}
map
.
putIfAbsent
(
broker
.
getPolicyNo
(),
broker
);
}
return
map
;
}
private
List
<
Policy
>
loadMyPolicies
(
Set
<
String
>
policyNos
)
{
if
(
policyNos
==
null
||
policyNos
.
isEmpty
())
{
return
new
ArrayList
<>();
}
return
policyService
.
lambdaQuery
()
.
in
(
Policy:
:
getPolicyNo
,
policyNos
)
.
eq
(
Policy:
:
getIsDeleted
,
0
)
.
list
();
}
private
ReferrerPolicyVO
toPolicyVO
(
Policy
policy
,
PolicyBroker
policyBroker
)
{
ReferrerPolicyVO
vo
=
new
ReferrerPolicyVO
();
vo
.
setPolicyNo
(
policy
.
getPolicyNo
());
vo
.
setPolicyHolder
(
policy
.
getPolicyHolder
());
vo
.
setInsured
(
policy
.
getInsured
());
vo
.
setProductName
(
policy
.
getProductName
());
vo
.
setInsuranceCompany
(
policy
.
getInsuranceCompany
());
vo
.
setCurrency
(
policy
.
getCurrency
());
vo
.
setFullPremium
(
n4
(
policy
.
getTotalPaymentPremium
()));
vo
.
setFullPremiumHkd
(
n4
(
policy
.
getTotalPaymentPremiumHkd
()));
vo
.
setMyRatio
(
policyBroker
==
null
?
null
:
policyBroker
.
getBrokerRatio
());
vo
.
setStatus
(
policy
.
getStatus
());
return
vo
;
}
private
List
<
ReferrerStatementItemVO
>
buildStatementItems
(
String
brokerBizId
,
String
salaryMonth
)
{
if
(
StringUtils
.
length
(
salaryMonth
)
<
6
)
{
return
new
ArrayList
<>();
}
String
reconciliationMonth
=
salaryMonth
.
substring
(
0
,
4
)
+
"-"
+
salaryMonth
.
substring
(
4
,
6
);
List
<
Fortune
>
fortunes
=
fortuneService
.
lambdaQuery
()
.
eq
(
Fortune:
:
getBrokerBizId
,
brokerBizId
)
.
eq
(
Fortune:
:
getReconciliationYearMonth
,
reconciliationMonth
)
.
eq
(
Fortune:
:
getStatus
,
"2"
)
.
in
(
Fortune:
:
getIsPart
,
0
,
1
)
.
orderByAsc
(
Fortune:
:
getId
)
.
list
();
if
(
fortunes
.
isEmpty
())
{
return
new
ArrayList
<>();
}
Set
<
String
>
policyNos
=
fortunes
.
stream
().
map
(
Fortune:
:
getPolicyNo
).
collect
(
Collectors
.
toSet
());
Map
<
String
,
Policy
>
policyMap
=
policyService
.
lambdaQuery
()
.
in
(
Policy:
:
getPolicyNo
,
policyNos
)
.
eq
(
Policy:
:
getIsDeleted
,
0
)
.
list
()
.
stream
()
.
collect
(
Collectors
.
toMap
(
Policy:
:
getPolicyNo
,
p
->
p
,
(
a
,
b
)
->
a
));
Set
<
String
>
expectedBizIds
=
fortunes
.
stream
()
.
map
(
Fortune:
:
getExpectedFortuneBizId
)
.
filter
(
StringUtils:
:
isNotBlank
)
.
collect
(
Collectors
.
toSet
());
// 不能用 Collectors.toMap:broker_ratio 允许为 null,toMap 会抛 NPE
Map
<
String
,
String
>
ratioMap
=
new
HashMap
<>();
if
(!
expectedBizIds
.
isEmpty
())
{
List
<
ExpectedFortune
>
expectedList
=
expectedFortuneService
.
lambdaQuery
()
.
in
(
ExpectedFortune:
:
getExpectedFortuneBizId
,
expectedBizIds
)
.
list
();
for
(
ExpectedFortune
expected
:
expectedList
)
{
if
(
expected
.
getBrokerRatio
()
!=
null
)
{
ratioMap
.
putIfAbsent
(
expected
.
getExpectedFortuneBizId
(),
expected
.
getBrokerRatio
());
}
}
}
return
fortunes
.
stream
().
map
(
fortune
->
{
ReferrerStatementItemVO
item
=
new
ReferrerStatementItemVO
();
item
.
setPolicyNo
(
fortune
.
getPolicyNo
());
Policy
policy
=
policyMap
.
get
(
fortune
.
getPolicyNo
());
if
(
policy
!=
null
)
{
item
.
setPolicyHolder
(
policy
.
getPolicyHolder
());
item
.
setInsuranceCompany
(
policy
.
getInsuranceCompany
());
item
.
setProductName
(
policy
.
getProductName
());
}
item
.
setFortunePeriod
(
fortune
.
getFortunePeriod
());
item
.
setFortuneName
(
fortune
.
getFortuneName
());
item
.
setBrokerRatio
(
ratioMap
.
get
(
fortune
.
getExpectedFortuneBizId
()));
item
.
setOriginalCurrency
(
fortune
.
getOriginalCurrency
());
item
.
setOriginalAmount
(
n4
(
fortune
.
getOriginalAmount
()));
item
.
setOriginalToHkdRate
(
n4
(
fortune
.
getOriginalToHkdRate
()));
item
.
setHkdAmount
(
n4
(
fortune
.
getHkdAmount
()));
return
item
;
}).
collect
(
Collectors
.
toList
());
}
/**
* 个人首年保费(HKD)= 期交保费按付款频率年化 × 保单快照港币汇率 × 介绍费占比。
*/
private
BigDecimal
personalFirstYearPremiumHkd
(
Policy
policy
,
PolicyBroker
policyBroker
)
{
BigDecimal
periodPremium
=
defaultZero
(
policy
.
getPaymentPremium
());
BigDecimal
annualPremium
=
periodPremium
.
multiply
(
annualFactor
(
policy
.
getPaymentFrequency
()));
BigDecimal
hkdRate
=
BigDecimal
.
ONE
;
BigDecimal
totalOriginal
=
defaultZero
(
policy
.
getTotalPaymentPremium
());
BigDecimal
totalHkd
=
defaultZero
(
policy
.
getTotalPaymentPremiumHkd
());
if
(
totalOriginal
.
compareTo
(
BigDecimal
.
ZERO
)
>
0
)
{
hkdRate
=
totalHkd
.
divide
(
totalOriginal
,
8
,
RoundingMode
.
HALF_UP
);
}
BigDecimal
annualHkd
=
annualPremium
.
multiply
(
hkdRate
);
return
annualHkd
.
multiply
(
ratioMultiplier
(
policyBroker
.
getBrokerRatio
()));
}
private
BigDecimal
annualFactor
(
String
paymentFrequency
)
{
if
(
StringUtils
.
isBlank
(
paymentFrequency
))
{
return
BigDecimal
.
ONE
;
}
if
(
PaymentFrequencyEnum
.
MONTH
.
getItemValue
().
equals
(
paymentFrequency
))
{
return
BigDecimal
.
valueOf
(
12
);
}
if
(
PaymentFrequencyEnum
.
SEASON
.
getItemValue
().
equals
(
paymentFrequency
))
{
return
BigDecimal
.
valueOf
(
4
);
}
// YEAR / FULL_PAYMENT / 未知频率均按 1 处理
return
BigDecimal
.
ONE
;
}
/**
* 介绍费占比:兼容 "60" / "60%" / "0.6" 三种存储形式。
*/
private
BigDecimal
ratioMultiplier
(
String
ratio
)
{
if
(
StringUtils
.
isBlank
(
ratio
))
{
return
BigDecimal
.
ZERO
;
}
try
{
BigDecimal
value
=
new
BigDecimal
(
ratio
.
trim
().
replace
(
"%"
,
""
).
trim
());
if
(
value
.
compareTo
(
BigDecimal
.
ONE
)
>
0
)
{
value
=
value
.
divide
(
BigDecimal
.
valueOf
(
100
),
6
,
RoundingMode
.
HALF_UP
);
}
return
value
;
}
catch
(
NumberFormatException
e
)
{
return
BigDecimal
.
ZERO
;
}
}
private
String
periodLabel
(
Integer
period
)
{
if
(
period
==
null
)
{
return
""
;
}
if
(
period
==
1
)
{
return
"首年"
;
}
return
"第"
+
period
+
"年"
;
}
/**
* 把一个保单年度(expected_fortune 行)按 D4.1 拆成 1~2 个互斥金额段。
*
* <p>保留(4)/失效(5)各一段、金额取全额应得;其余(0/1/2/3/6/7)按
* {@code paid_amount / unpaid_amount} 拆 {@code PAID + PIPELINE},
* 7(未找到来佣)随 PIPELINE;金额未回填时以应得兜底,不丢钱。</p>
*/
private
void
appendCommissionSegments
(
List
<
ReferrerPolicyCommissionItemVO
>
items
,
ExpectedFortune
row
)
{
ReferrerCommissionStage
whole
=
ReferrerCommissionStage
.
fromExpectedStatus
(
row
.
getStatus
());
if
(
whole
==
ReferrerCommissionStage
.
RETAINED
||
whole
==
ReferrerCommissionStage
.
LAPSED
)
{
items
.
add
(
buildCommissionSegment
(
whole
,
row
,
n4
(
row
.
getHkdAmount
())));
return
;
}
BigDecimal
paid
=
defaultZero
(
row
.
getPaidAmount
());
BigDecimal
unpaid
=
defaultZero
(
row
.
getUnpaidAmount
());
if
(
paid
.
signum
()
==
0
&&
unpaid
.
signum
()
==
0
)
{
unpaid
=
defaultZero
(
row
.
getHkdAmount
());
}
if
(
paid
.
signum
()
>
0
)
{
items
.
add
(
buildCommissionSegment
(
ReferrerCommissionStage
.
PAID
,
row
,
n4
(
paid
)));
}
if
(
unpaid
.
signum
()
>
0
)
{
items
.
add
(
buildCommissionSegment
(
ReferrerCommissionStage
.
PIPELINE
,
row
,
n4
(
unpaid
)));
}
}
private
ReferrerPolicyCommissionItemVO
buildCommissionSegment
(
ReferrerCommissionStage
stage
,
ExpectedFortune
row
,
BigDecimal
amount
)
{
ReferrerPolicyCommissionItemVO
it
=
new
ReferrerPolicyCommissionItemVO
();
it
.
setStage
(
stage
.
name
());
it
.
setFortunePeriod
(
row
.
getFortunePeriod
());
it
.
setPeriodLabel
(
periodLabel
(
row
.
getFortunePeriod
()));
it
.
setFortuneName
(
row
.
getFortuneName
());
it
.
setStatusDesc
(
row
.
getStatusDesc
());
it
.
setAmount
(
amount
);
it
.
setPayoutDate
(
row
.
getPayoutDate
());
it
.
setActualPayoutDate
(
row
.
getActualPayoutDate
());
return
it
;
}
private
LocalDate
toLocalDate
(
Date
date
)
{
if
(
date
==
null
)
{
return
null
;
}
return
date
.
toInstant
().
atZone
(
ZoneId
.
systemDefault
()).
toLocalDate
();
}
private
BigDecimal
defaultZero
(
BigDecimal
value
)
{
return
value
==
null
?
BigDecimal
.
ZERO
:
value
;
}
private
BigDecimal
n2
(
BigDecimal
value
)
{
return
defaultZero
(
value
).
setScale
(
2
,
RoundingMode
.
HALF_UP
);
}
private
BigDecimal
n4
(
BigDecimal
value
)
{
return
defaultZero
(
value
).
setScale
(
4
,
RoundingMode
.
HALF_UP
);
}
}
\ No newline at end of file
yd-csf-service/src/main/java/com/yd/csf/service/service/impl/ReferrerTodoServiceImpl.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
service
.
service
.
impl
;
import
com.baomidou.mybatisplus.extension.service.impl.ServiceImpl
;
import
com.yd.csf.service.dao.ReferrerTodoMapper
;
import
com.yd.csf.service.model.ReferrerTodo
;
import
com.yd.csf.service.service.IReferrerTodoService
;
import
org.springframework.stereotype.Service
;
import
org.springframework.transaction.annotation.Transactional
;
import
java.time.LocalDateTime
;
import
java.util.UUID
;
/**
* 转介人待办通知留痕服务实现
*/
@Service
public
class
ReferrerTodoServiceImpl
extends
ServiceImpl
<
ReferrerTodoMapper
,
ReferrerTodo
>
implements
IReferrerTodoService
{
@Override
@Transactional
(
rollbackFor
=
Exception
.
class
)
public
void
recordNotify
(
String
brokerBizId
,
String
policyNo
,
String
todoType
)
{
LocalDateTime
now
=
LocalDateTime
.
now
();
ReferrerTodo
todo
=
new
ReferrerTodo
();
todo
.
setTodoBizId
(
UUID
.
randomUUID
().
toString
().
replace
(
"-"
,
""
));
todo
.
setBrokerBizId
(
brokerBizId
);
todo
.
setPolicyNo
(
policyNo
);
todo
.
setTodoType
(
todoType
);
todo
.
setNotifyTime
(
now
);
todo
.
setCreateTime
(
now
);
todo
.
setUpdateTime
(
now
);
this
.
save
(
todo
);
}
}
\ No newline at end of file
yd-csf-service/src/main/java/com/yd/csf/service/vo/ReferrerDashboardVO.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
service
.
vo
;
import
lombok.Data
;
import
java.io.Serializable
;
import
java.math.BigDecimal
;
/**
* 转介人首页指标。
*/
@Data
public
class
ReferrerDashboardVO
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
/**
* 本月已发佣金(HKD)
*/
private
BigDecimal
monthPaidCommission
;
/**
* 本课税年度累计已发佣金(HKD)
*/
private
BigDecimal
taxYearPaidCommission
;
/**
* 在途佣金(HKD,不含失效/未找到来佣)
*/
private
BigDecimal
pipelineCommission
;
/**
* 生效件数
*/
private
Long
effectivePolicyCount
;
/**
* 首年业绩 FYP(HKD,按本人占比折算)
*/
private
BigDecimal
firstYearFyp
;
}
\ No newline at end of file
yd-csf-service/src/main/java/com/yd/csf/service/vo/ReferrerPipelineVO.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
service
.
vo
;
import
com.fasterxml.jackson.annotation.JsonFormat
;
import
lombok.Data
;
import
java.io.Serializable
;
import
java.math.BigDecimal
;
import
java.time.LocalDate
;
/**
* 转介人在途佣金项。
*/
@Data
public
class
ReferrerPipelineVO
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
/**
* 预计发佣业务ID
*/
private
String
expectedFortuneBizId
;
/**
* 保单号
*/
private
String
policyNo
;
/**
* 产品名称
*/
private
String
productName
;
/**
* 发佣期数(1=第一年...)
*/
private
Integer
fortunePeriod
;
/**
* 发佣名称
*/
private
String
fortuneName
;
/**
* 预计出账日期
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd"
,
timezone
=
"GMT+8"
)
private
LocalDate
payoutDate
;
/**
* 语义状态码:PAID / PIPELINE / RETAINED / LAPSED(D4.1)
*/
private
String
stage
;
/**
* 在途金额(剩余发放折合港币)
*/
private
BigDecimal
hkdAmount
;
}
\ No newline at end of file
yd-csf-service/src/main/java/com/yd/csf/service/vo/ReferrerPolicyCommissionItemVO.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
service
.
vo
;
import
com.fasterxml.jackson.annotation.JsonFormat
;
import
lombok.Data
;
import
java.io.Serializable
;
import
java.math.BigDecimal
;
import
java.time.LocalDate
;
/**
* 转介人保单佣金段:一个保单年度的钱按「钱的位置」(D4.1) 拆分后的一段。
*
* <p>部分出账的年度会产出两段:{@code PAID(已出账金额)} + {@code PIPELINE(待出账金额)},
* 保证各段金额相加恒等于该年度应得。</p>
*/
@Data
public
class
ReferrerPolicyCommissionItemVO
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
/**
* 语义状态码:PAID / PIPELINE / RETAINED / LAPSED
*/
private
String
stage
;
/**
* 发佣期数(1=第一年...)
*/
private
Integer
fortunePeriod
;
/**
* 年度文案:首年 / 第2年 / 第3年...
*/
private
String
periodLabel
;
/**
* 发佣名称
*/
private
String
fortuneName
;
/**
* 保留/失效原因(财务登记,未登记时为空,前端不臆造)
*/
private
String
statusDesc
;
/**
* 该段金额(HKD)
*/
private
BigDecimal
amount
;
/**
* 预计出账日
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd"
,
timezone
=
"GMT+8"
)
private
LocalDate
payoutDate
;
/**
* 实际出账日
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd"
,
timezone
=
"GMT+8"
)
private
LocalDate
actualPayoutDate
;
}
yd-csf-service/src/main/java/com/yd/csf/service/vo/ReferrerPolicyCommissionVO.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
service
.
vo
;
import
lombok.Data
;
import
java.io.Serializable
;
import
java.util.List
;
/**
* 转介人保单佣金钻取结果。
*/
@Data
public
class
ReferrerPolicyCommissionVO
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
/**
* 保单号
*/
private
String
policyNo
;
/**
* 首年佣金(FYC,HKD)
*/
private
java
.
math
.
BigDecimal
firstYearCommission
;
/**
* 续期佣金(RYC,HKD)
*/
private
java
.
math
.
BigDecimal
renewalCommission
;
/**
* 各年度佣金明细
*/
private
List
<
ReferrerPolicyCommissionItemVO
>
items
;
}
\ No newline at end of file
yd-csf-service/src/main/java/com/yd/csf/service/vo/ReferrerPolicyListVO.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
service
.
vo
;
import
lombok.Data
;
import
java.io.Serializable
;
import
java.util.List
;
/**
* 转介人名下保单分组。
*/
@Data
public
class
ReferrerPolicyListVO
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
/**
* 生效保单(计件计业绩)
*/
private
List
<
ReferrerPolicyVO
>
effectivePolicies
;
/**
* 未生效/已失效保单(可见不计件计业绩)
*/
private
List
<
ReferrerPolicyVO
>
inactivePolicies
;
}
\ No newline at end of file
yd-csf-service/src/main/java/com/yd/csf/service/vo/ReferrerPolicyVO.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
service
.
vo
;
import
lombok.Data
;
import
java.io.Serializable
;
import
java.math.BigDecimal
;
/**
* 转介人名下保单。
*/
@Data
public
class
ReferrerPolicyVO
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
/**
* 保单号
*/
private
String
policyNo
;
/**
* 保單持有人
*/
private
String
policyHolder
;
/**
* 受保人
*/
private
String
insured
;
/**
* 产品名称
*/
private
String
productName
;
/**
* 保险公司
*/
private
String
insuranceCompany
;
/**
* 保单币种
*/
private
String
currency
;
/**
* 全额保费(承保规模,原币)
*/
private
BigDecimal
fullPremium
;
/**
* 全额保费(HKD)
*/
private
BigDecimal
fullPremiumHkd
;
/**
* 我的介绍费占比
*/
private
String
myRatio
;
/**
* 保单状态
*/
private
String
status
;
}
\ No newline at end of file
yd-csf-service/src/main/java/com/yd/csf/service/vo/ReferrerStatementDetailVO.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
service
.
vo
;
import
com.fasterxml.jackson.annotation.JsonFormat
;
import
lombok.Data
;
import
java.io.Serializable
;
import
java.math.BigDecimal
;
import
java.time.LocalDate
;
import
java.util.List
;
/**
* 转介人月结单详情。
*/
@Data
public
class
ReferrerStatementDetailVO
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
/**
* 薪资单业务ID
*/
private
String
salaryBizId
;
/**
* 薪资月份 yyyyMM
*/
private
String
month
;
/**
* 发放编号
*/
private
String
salaryNo
;
/**
* 应发总金额(HKD)
*/
private
BigDecimal
grossAmount
;
/**
* MPF事项总金额(HKD)
*/
private
BigDecimal
mpfAmount
;
/**
* 其他调整总金额(HKD)
*/
private
BigDecimal
otherAmount
;
/**
* 实发总金额(HKD)
*/
private
BigDecimal
paidAmount
;
/**
* 发放日
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd"
,
timezone
=
"GMT+8"
)
private
LocalDate
payoutDate
;
/**
* 收款银行名称
*/
private
String
bankName
;
/**
* 账户尾号
*/
private
String
accountEndNo
;
/**
* 逐笔保单明细
*/
private
List
<
ReferrerStatementItemVO
>
items
;
}
\ No newline at end of file
yd-csf-service/src/main/java/com/yd/csf/service/vo/ReferrerStatementItemVO.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
service
.
vo
;
import
lombok.Data
;
import
java.io.Serializable
;
import
java.math.BigDecimal
;
/**
* 转介人月结单逐笔明细项。
*/
@Data
public
class
ReferrerStatementItemVO
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
/**
* 保单号
*/
private
String
policyNo
;
/**
* 保單持有人
*/
private
String
policyHolder
;
/**
* 保险公司
*/
private
String
insuranceCompany
;
/**
* 产品名称
*/
private
String
productName
;
/**
* 发佣期数(1=第一年...)
*/
private
Integer
fortunePeriod
;
/**
* 发佣名称
*/
private
String
fortuneName
;
/**
* 转介人介绍费占比
*/
private
String
brokerRatio
;
/**
* 原币种
*/
private
String
originalCurrency
;
/**
* 原币种金额
*/
private
BigDecimal
originalAmount
;
/**
* 原币种→港币汇率
*/
private
BigDecimal
originalToHkdRate
;
/**
* 折算港币金额
*/
private
BigDecimal
hkdAmount
;
}
\ No newline at end of file
yd-csf-service/src/main/java/com/yd/csf/service/vo/ReferrerStatementVO.java
0 → 100644
View file @
0ac1d533
package
com
.
yd
.
csf
.
service
.
vo
;
import
com.fasterxml.jackson.annotation.JsonFormat
;
import
lombok.Data
;
import
java.io.Serializable
;
import
java.math.BigDecimal
;
import
java.time.LocalDate
;
/**
* 转介人月结单列表项。
*/
@Data
public
class
ReferrerStatementVO
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
/**
* 薪资单业务ID
*/
private
String
salaryBizId
;
/**
* 薪资月份 yyyyMM
*/
private
String
month
;
/**
* 发放编号
*/
private
String
salaryNo
;
/**
* 应发总金额(HKD)
*/
private
BigDecimal
grossAmount
;
/**
* MPF事项总金额(HKD)
*/
private
BigDecimal
mpfAmount
;
/**
* 其他调整总金额(HKD)
*/
private
BigDecimal
otherAmount
;
/**
* 实发总金额(HKD)
*/
private
BigDecimal
paidAmount
;
/**
* 发放日(取当月已出账头日期)
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd"
,
timezone
=
"GMT+8"
)
private
LocalDate
payoutDate
;
}
\ No newline at end of file
yd-csf-service/src/main/resources/sql/referrer_todo.sql
0 → 100644
View file @
0ac1d533
-- 转介人待办通知留痕表 DDL
-- 执行前确认数据库名称
CREATE
TABLE
IF
NOT
EXISTS
`referrer_todo`
(
`id`
BIGINT
NOT
NULL
AUTO_INCREMENT
COMMENT
'主键'
,
`todo_biz_id`
VARCHAR
(
64
)
NOT
NULL
COMMENT
'待办留痕业务ID'
,
`broker_biz_id`
VARCHAR
(
64
)
NOT
NULL
COMMENT
'转介人业务ID(登录态注入)'
,
`policy_no`
VARCHAR
(
64
)
NOT
NULL
COMMENT
'保单号快照'
,
`todo_type`
VARCHAR
(
32
)
NOT
NULL
COMMENT
'待办类型:续期/失效/生日'
,
`notify_time`
DATETIME
COMMENT
'通知时间'
,
`create_time`
DATETIME
DEFAULT
CURRENT_TIMESTAMP
COMMENT
'创建时间'
,
`update_time`
DATETIME
DEFAULT
CURRENT_TIMESTAMP
ON
UPDATE
CURRENT_TIMESTAMP
COMMENT
'更新时间'
,
PRIMARY
KEY
(
`id`
),
INDEX
`idx_todo_biz_id`
(
`todo_biz_id`
),
INDEX
`idx_broker_biz_id`
(
`broker_biz_id`
),
INDEX
`idx_policy_no`
(
`policy_no`
)
)
ENGINE
=
InnoDB
DEFAULT
CHARSET
=
utf8mb4
COMMENT
=
'转介人待办通知留痕表'
;
\ No newline at end of file
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