合规改造 · 免增加出参名单
为金融机构调用的单一数据源产品增加朴道侧评分/画像出参场景下,提供一个免增加出参名单配置能力:命中名单的账户,其出参不增加合规用固定出参。
1需求概述
合规改造增加特殊名单。命中名单的账户,出参不增加合规用固定出参。名单以账户为粒度,支持启用/停用状态切换,停用不参与命中。
分支:
iteration/v2.57_产品合规改造_20260810172346缓存 Key:
AGG_SPECIAL_LIST(命中则该账户出参不增加合规用固定出参)
2数据库设计
2.1 建表 DDL
| 字段 | 类型 | 键/约束 | 默认值 | 含义 |
|---|---|---|---|---|
| id | bigint | 🔑 PK AUTO_INCREMENT | - | 自增主键(即规则编号) |
| account_id | bigint | NOT NULL UNIQUE | - | 客户账户标识 |
| status | varchar(10) | NOT NULL | '1' | 状态:1=启用,0=停用(字典 JG0000019) |
| creator | varchar(128) | NULL | 创建人(username) | |
| created_time | datetime | NULL | 创建时间 | |
| modifier | varchar(128) | NULL | 修改人(username) | |
| modified_time | datetime | NOT NULL | CURRENT_TIMESTAMP | 更新时间,ON UPDATE CURRENT_TIMESTAMP |
CREATE TABLE `agg_special_list` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增主键(即规则编号)', `account_id` bigint NOT NULL COMMENT '客户账户标识', `status` varchar(10) NOT NULL DEFAULT '1' COMMENT '状态:1=启用,0=停用(字典JG0000019)', `creator` varchar(128) DEFAULT NULL COMMENT '创建人(username)', `created_time` datetime DEFAULT NULL COMMENT '创建时间', `modifier` varchar(128) DEFAULT NULL COMMENT '修改人(username)', `modified_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_account_id` (`account_id`) ) ENGINE=InnoDB COMMENT='免增加出参名单(聚合特殊名单)';
2.2 字段说明
| 字段 | 类型 | 默认值 | 含义 |
|---|---|---|---|
id | bigint AUTO_INCREMENT | - | 自增主键,即规则编号。启用/停用不改编号 |
account_id | bigint | - | 客户账户标识,关联 account_info.id |
status | varchar(10) | '1' | 状态(字典 JG0000019):'1'=启用,'0'=停用。停用不参与命中(不入缓存) |
creator | varchar(128) | NULL | 创建人,MetaObjectHandler 插入时填充 |
created_time | datetime | NULL | 创建时间,MetaObjectHandler 插入时填充 |
modifier | varchar(128) | NULL | 修改人,MetaObjectHandler 插入/更新时填充 |
modified_time | datetime NOT NULL | CURRENT_TIMESTAMP | 更新时间,MetaObjectHandler 填充;ON UPDATE CURRENT_TIMESTAMP 兜底非 MP 的直接 SQL |
2.3 设计说明
account_id 加唯一索引 uk_account_id,数据库层保障一账户一记录。新增时若 account_id 已存在(任意状态),代码预校验 count(account_id=?) > 0 提示「该账户已在免增加出参名单中」并阻止;并发插入由唯一索引兜底。id 为数据库自增,直接作为列表"规则编号"展示。启用/停用为同记录状态切换,不新增/删除行,编号天然不变。3缓存设计
3.1 缓存结构
Field: accountId
Value: "1"
- 类型:
R2MHashCache(Redis Hash,主 R2M 集群) - Redis Key:
AGG_SPECIAL_LIST(常量R2MConstant.AGG_SPECIAL_LIST) - Hash Field:
accountId.toString(),Value:"1"(仅作命中标志,无业务含义,命中 Field 即表示该账户在名单中) - 业务语义:按
accountId查缓存,命中则该账户出参不增加合规用固定出参
3.2 缓存加载
- 加载器:
AggSpecialListCacheLoader extends AbstractCacheVersionLoader<String, String>,构造器传业务类型VersionBusinessTypeEnum.AGG_SPECIAL_LIST.getType() - 实现
loadAll():全量加载status='1'(启用)记录,按accountId建映射;停用记录不入缓存 - 加载时机:启动加载 + 定时任务检测版本号变化刷新
3.3 版本号
- 版本号由
BusinessVersionAspect切面维护:对AggSpecialList实体的 save/update/remove/delete 操作,切面自动递增OperationBusinessVersion表中业务类型aggSpecialList的 version(version = 当前时间戳) getLatestGlobalVersion()由AbstractCacheVersionLoader父类实现,查OperationBusinessVersion表取 version,子类无需覆盖- 新增/启用/停用操作均会推高 version,定时任务检测到变化即触发全量重载
3.4 刷新机制
只更新数据库
version = 时间戳
检测版本号变化
下一周期感知
- 缓存刷新统一由定时任务
AggSpecialListCacheTask(EasyJob)负责:检测OperationBusinessVersion版本号变化,变化时触发全量重载 - 写操作(add / changeStatus)只更新数据库,不主动调缓存刷新方法;版本号递增由
BusinessVersionAspect切面在写操作后自动完成,定时任务下一周期感知并刷新 - 刷新延迟:取决于定时任务执行周期(配置表低频写入,延迟可接受)
3.5 注册
CacheConfig新增@Bean:返回AbstractCache<String, String>,实例化new R2MHashCache(R2MConstant.AGG_SPECIAL_LIST, aggSpecialListCacheLoader, null, r2mClusterClient, null, null, false, false)(参照现有 R2MHashCache 注册)R2MConstant新增常量AGG_SPECIAL_LIST
4分层实现
4.1 包结构与文件清单
mvc/aggSpecialList/ controller/AggSpecialListController.java dto/AggSpecialListPageDto.java # 继承项目分页基类 dto/AggSpecialListAddDto.java dto/AggSpecialListChangeStatusDto.java vo/AggSpecialListPageVo.java vo/AggSpecialListAccountInfoVo.java common/AggSpecialListConvertBasic.java # MapStruct, DTO->BO
cache/loader/AggSpecialListCacheLoader.java # extends AbstractCacheVersionLoader task/AggSpecialListCacheTask.java config/CacheConfig.java # 追加 @Bean(非新建文件) common/constants/R2MConstant.java # 追加常量(非新建文件) common/constants/Enum/VersionBusinessTypeEnum.java # 追加枚举值 AGG_SPECIAL_LIST(非新建文件) resources/mapper/aggSpecialList/AggSpecialListMapper.xml
实体统一放
service/aggSpecialList/dao/model/下,不沿用历史分包。
4.2 实体 / BO / DTO / VO / 转换
4.2 实体类 AggSpecialList
extends BaseEntity,字段:id(@TableId(type=IdType.AUTO))、accountId、status;审计字段(creator/createdTime/modifier/modifiedTime)继承自 BaseEntity,由 MetaObjectHandler 自动填充。
4.3 BO
| BO | 字段 | 用途 |
|---|---|---|
AggSpecialListBo | accountId | 新增用 |
AggSpecialListPageBo | orgNo、merchantName、accountId、status | 分页查询用 |
4.4 DTO
| DTO | 字段 | 校验 |
|---|---|---|
AggSpecialListPageDto | pageNum/pageSize(继承)+ orgNo/merchantName/accountId/status | - |
AggSpecialListAddDto | accountId | @NotNull |
AggSpecialListChangeStatusDto | id、status | @NotNull |
4.5 VO
AggSpecialListPageVo:见 5.1 返回字段表。creator/modifier加@GenerateStrField(type=TranslateType.USER, suffix="Name"),时间字段加@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone="GMT+8")AggSpecialListAccountInfoVo:accountId/orgNo/fullName/shortName/merchantName/accountName/status
4.6 对象转换
AggSpecialListConvertBasic(@Mapper(componentModel="spring")):PageDto→PageBo、AddDto→Bo。Entity→VO 在 Service 手动组装(需关联查询客户信息)。
4.7 Service 方法
| 方法 | 说明 |
|---|---|
page(pageNum, pageSize, PageBo) | 分页查询,返回 IPage<PageVo> |
add(Bo) | 账户有效性 + 唯一性校验后 insert,不刷缓存 |
changeStatus(id, status) | 校验 status 合法后 update,不刷缓存 |
checkAccount(accountId) | count 查询返回 Boolean |
accountInfo(accountId) | 查 account_info + t_crm_org_base_info 组装 VO |
缓存刷新由定时任务
AggSpecialListCacheTask统一负责;Service 写操作不主动刷缓存,版本号递增由BusinessVersionAspect切面自动完成。
5接口说明(前端)
5.0 接口总览
权限说明:本功能接口均为 NORMAL(走菜单按钮鉴权)。
统一返回包装
Result<T>,分页返回Result<PageResult<T>>。
5.1 规则列表分页
入参
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
pageNum | int | 是 | 页码 |
pageSize | int | 是 | 每页条数(10/20/50/100) |
orgNo | string | 否 | 客户编号,模糊匹配 |
merchantName | string | 否 | 客户全称/简称,模糊匹配(任一命中) |
accountId | long | 否 | 客户账户标识,精确匹配 |
status | string | 否 | 状态筛选:'1'=启用,'0'=停用 |
返回 Result<PageResult<AggSpecialListPageVo>>
| 字段 | 类型 | 说明 |
|---|---|---|
id | long | 规则编号(自增,启用/停用不改编号) |
accountId | long | 客户账户标识 |
orgNo | string | 客户编号 |
merchantName | string | 客户名称,格式:客户全称(客户简称) |
accountName | string | 客户账户名称 |
status | string | 状态:'1'/'0' |
statusStr | string | 状态文案:启用 / 停用 |
creator | string | 创建人(username,鼠标悬浮展示) |
creatorName | string | 创建人中文名(直接展示) |
createdTime | string | 创建时间(yyyy-MM-dd HH:mm:ss) |
modifier | string | 修改人(username) |
modifierName | string | 修改人中文名 |
modifiedTime | string | 修改时间(yyyy-MM-dd HH:mm:ss) |
modified_time DESC, id DESC(编辑时间倒序,相同时规则编号倒序)|规则总数:PageResult.total,随查询/状态切换自动更新|停用行:前端文字弱化展示5.2 新增配置
入参
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
accountId | long | 是 | 客户账户标识(输入+下拉精确选择生效中账户) |
校验(阻止保存并提示)
accountId不能为空,且须为系统中有效启用账户,否则提示并阻止- 若该账户已存在记录(任意状态),提示「该账户已在免增加出参名单中」并阻止(唯一索引兜底)
返回 Result<?>
MetaObjectHandler 自动填充(缓存由定时任务刷新);前端刷新清单页。5.3 启用/停用
入参(@RequestBody JSON)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | long | 是 | 规则编号 |
status | string | 是 | 目标状态:'1'=启用,'0'=停用 |
⚙️ 逻辑:校验记录存在且 status 值合法后,
update set status=?(modifier/modified_time 由 MetaObjectHandler 自动填充)。停用后该 accountId 不再入缓存、不参与命中;启用后重新入缓存(缓存由定时任务刷新)。
返回 Result<?>
5.4 校验账户是否已有生效规则
入参
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
accountId | long | 是 | 客户账户标识 |
返回 Result<Boolean>
| 返回值 | 含义 |
|---|---|
| true | 该账户已有记录(前端提示「该账户已在免增加出参名单中」,阻止继续) |
| false | 可新增 |
5.5 按账户标识查客户信息
入参
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
accountId | long | 是 | 客户账户标识 |
返回 Result<AggSpecialListAccountInfoVo>
| 字段 | 类型 | 说明 |
|---|---|---|
accountId | long | 客户账户标识 |
orgNo | string | 客户编号 |
fullName | string | 客户全称 |
shortName | string | 客户简称 |
merchantName | string | 客户全称(客户简称) 展示格式 |
accountName | string | 客户账户名称 |
status | string | 账户状态:'1'=已启用 |
5.6 字段翻译约定
creator/modifier:后端用@GenerateStrField(type=TranslateType.USER)注解,序列化时自动追加creatorName/modifierName(中文名)。前端直接展示xxxName,鼠标悬浮展示xxx(username)。- 时间字段:
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone="GMT+8")。
6关键业务逻辑
6.1 分页查询
单表查询 + 关联账户/客户信息,因多表 JOIN 采用自定义 XML 分页(selectPage)。
SELECT t.id, t.account_id, t.status, t.creator, t.created_time, t.modifier, t.modified_time, obi.org_no, ai.account_name, CASE WHEN obi.short_name IS NOT NULL AND obi.short_name != '' THEN CONCAT(obi.full_name, '(', obi.short_name, ')') ELSE obi.full_name END AS merchantName FROM agg_special_list t LEFT JOIN account_info ai ON ai.id = t.account_id LEFT JOIN t_crm_org_base_info obi ON obi.org_no = ai.org_no <where> <if test="orgNo != null and orgNo != ''"> AND obi.org_no LIKE CONCAT('%', #{orgNo}, '%') </if> <if test="merchantName != null and merchantName != ''"> AND (instr(fullwidth_to_halfwidth(obi.full_name), fullwidth_to_halfwidth(#{merchantName})) != 0 OR instr(fullwidth_to_halfwidth(obi.short_name), fullwidth_to_halfwidth(#{merchantName})) != 0) </if> <if test="accountId != null"> AND t.account_id = #{accountId} </if> <if test="status != null and status != ''"> AND t.status = #{status} </if> </where> ORDER BY t.modified_time DESC, t.id DESC
LIKE)|客户全称/简称:fullwidth_to_halfwidth(项目已有函数)全半角统一后 instr 匹配,full_name / short_name 任一命中|账户标识、状态:精确匹配。statusStr 翻译:'1'→生效中,'0'→已删除。6.2 新增配置校验
且 status='1'(已启用)
否则提示并阻止
(不限状态)
提示「该账户已在名单中」
唯一索引兜底并发
审计字段 MetaObjectHandler 填充
缓存由定时任务刷新
6.3 启用/停用(changeStatus)
modifier/modified_time
MetaObjectHandler 自动填充
启用→重新入缓存
6.4 校验账户是否已有生效规则(checkAccount)
count(account_id=?) > 0(不限状态)返回true(该账户已有记录)- 用途:新增弹窗选中账户后实时校验,提示「该账户已在免增加出参名单中」
6.5 按账户标识查客户信息(accountInfo)
full_name / short_name
short_name 空时取 full_name
status = 账户启用状态
💡 用途:新增弹窗选中账户后带出客户编号、客户名称、账户名称(只读)。
7后续事项
-
🗄️
建表执行按 2.1 DDL 在对应环境建表。
-
🏷️
版本类型注册
VersionBusinessTypeEnum新增AGG_SPECIAL_LIST枚举值(type=aggSpecialList,关联AggSpecialList实体),供BusinessVersionAspect切面识别。 -
🌱
业务版本初始化
OperationBusinessVersion表插入一行buss_type='aggSpecialList'初始记录(version 初始值),否则getLatestGlobalVersion()查无数据会报错。 -
⏰
定时任务配置
AggSpecialListCacheTask需在 EasyJob 调度平台注册任务并配置执行周期(配置表低频写入,周期可适当放宽)。 -
🤝
缓存契约对齐与出参组装方约定
AGG_SPECIAL_LIST的 Key(accountId)与命中语义,确认消费方按约定读取。 -
🔑
resourceId 配置暂不考虑,后续前端联调时补充菜单与权限码映射。