支持拉取行内和金综流水

This commit is contained in:
wjj
2026-07-20 13:07:49 +08:00
parent 1561df924f
commit 75d094dad4
13 changed files with 283 additions and 30 deletions

View File

@@ -17,6 +17,7 @@ public class LsfxConstants {
/** 数据渠道编码 */
public static final String DATA_CHANNEL_ZJRCU = "ZJRCU";
public static final String DATA_CHANNEL_JZL = "JZL";
/** 分析类型 */
public static final String ANALYSIS_TYPE = "-1";

View File

@@ -14,7 +14,7 @@ public class FetchInnerFlowRequest {
/** 客户身份证号 */
private String customerNo;
/** 数据渠道编码(固定值:ZJRCU) */
/** 数据渠道编码(ZJRCU-行内JZL-金综) */
private String dataChannelCode;
/** 发起请求的时间(格式:yyyyMMdd) */

View File

@@ -14,6 +14,7 @@ import com.ruoyi.common.core.page.PageDomain;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.page.TableSupport;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.lsfx.constants.LsfxConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
@@ -103,10 +104,10 @@ public class CcdiFileUploadController extends BaseController {
}
/**
* 提交拉取本行信息任务
* 提交拉取行内/金综流水任务
*/
@PostMapping("/pull-bank-info")
@Operation(summary = "拉取本行信息", description = "身份证号批量提交拉取本行信息任务")
@Operation(summary = "拉取行内/金综流水", description = "证件号码批量提交拉取行内/金综流水任务")
@PreAuthorize("@ss.hasPermi('ccdi:project:edit')")
public AjaxResult pullBankInfo(@RequestBody CcdiPullBankInfoSubmitDTO dto) {
if (dto == null || dto.getProjectId() == null) {
@@ -116,7 +117,16 @@ public class CcdiFileUploadController extends BaseController {
if (CollectionUtils.isEmpty(dto.getIdCards())) {
return AjaxResult.error("身份证号不能为空");
}
if (!StringUtils.hasText(dto.getStartDate()) || !StringUtils.hasText(dto.getEndDate())) {
if (!StringUtils.hasText(dto.getDataChannelCode())) {
return AjaxResult.error("流水来源不能为空");
}
String dataChannelCode = dto.getDataChannelCode().trim().toUpperCase();
if (!LsfxConstants.DATA_CHANNEL_ZJRCU.equals(dataChannelCode)
&& !LsfxConstants.DATA_CHANNEL_JZL.equals(dataChannelCode)) {
return AjaxResult.error("流水来源不支持");
}
if (LsfxConstants.DATA_CHANNEL_ZJRCU.equals(dataChannelCode)
&& (!StringUtils.hasText(dto.getStartDate()) || !StringUtils.hasText(dto.getEndDate()))) {
return AjaxResult.error("开始日期和结束日期不能为空");
}
@@ -125,6 +135,7 @@ public class CcdiFileUploadController extends BaseController {
String batchId = fileUploadService.submitPullBankInfo(
dto.getProjectId(),
dto.getIdCards(),
dataChannelCode,
dto.getStartDate(),
dto.getEndDate(),
userId,

View File

@@ -5,7 +5,7 @@ import lombok.Data;
import java.util.List;
/**
* 拉取本行信息提交参数
* 拉取行内/金综流水提交参数
*/
@Data
public class CcdiPullBankInfoSubmitDTO {
@@ -16,6 +16,9 @@ public class CcdiPullBankInfoSubmitDTO {
/** 身份证号列表 */
private List<String> idCards;
/** 数据渠道编码ZJRCU-行内JZL-金综 */
private String dataChannelCode;
/** 开始日期 */
private String startDate;

View File

@@ -35,10 +35,11 @@ public interface ICcdiFileUploadService {
List<String> parseIdCardFile(MultipartFile file);
/**
* 提交拉取本行信息任务
* 提交拉取行内/金综流水任务
*
* @param projectId 项目ID
* @param idCards 身份证号列表
* @param dataChannelCode 数据渠道编码
* @param startDate 开始日期
* @param endDate 结束日期
* @param userId 当前登录用户ID
@@ -47,6 +48,7 @@ public interface ICcdiFileUploadService {
*/
String submitPullBankInfo(Long projectId,
List<String> idCards,
String dataChannelCode,
String startDate,
String endDate,
Long userId,

View File

@@ -154,6 +154,7 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
@Override
public String submitPullBankInfo(Long projectId,
List<String> idCards,
String dataChannelCode,
String startDate,
String endDate,
Long userId,
@@ -161,17 +162,20 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
if (projectId == null) {
throw new IllegalArgumentException("项目ID不能为空");
}
if (!StringUtils.hasText(startDate) || !StringUtils.hasText(endDate)) {
throw new IllegalArgumentException("开始日期和结束日期不能为空");
}
if (idCards == null || idCards.isEmpty()) {
throw new IllegalArgumentException("身份证号不能为空");
}
String normalizedDataChannelCode = normalizePullBankInfoDataChannelCode(dataChannelCode);
LocalDate start = LocalDate.parse(startDate);
LocalDate end = LocalDate.parse(endDate);
if (start.isAfter(end)) {
throw new IllegalArgumentException("开始日期不能晚于结束日期");
if (LsfxConstants.DATA_CHANNEL_ZJRCU.equals(normalizedDataChannelCode)) {
if (!StringUtils.hasText(startDate) || !StringUtils.hasText(endDate)) {
throw new IllegalArgumentException("开始日期和结束日期不能为空");
}
LocalDate start = LocalDate.parse(startDate);
LocalDate end = LocalDate.parse(endDate);
if (start.isAfter(end)) {
throw new IllegalArgumentException("开始日期不能晚于结束日期");
}
}
projectService.ensureProjectNotArchived(projectId, "已归档项目暂不允许上传或拉取数据");
@@ -218,13 +222,26 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
@Override
public void afterCommit() {
CompletableFuture.runAsync(() -> submitPullBankInfoTasks(
projectId, lsfxProjectId, records, normalizedIdCards, startDate, endDate, batchId
projectId, lsfxProjectId, records, normalizedIdCards,
normalizedDataChannelCode, startDate, endDate, batchId
));
}
});
return batchId;
}
private String normalizePullBankInfoDataChannelCode(String dataChannelCode) {
if (!StringUtils.hasText(dataChannelCode)) {
throw new IllegalArgumentException("流水来源不能为空");
}
String normalized = dataChannelCode.trim().toUpperCase();
if (!LsfxConstants.DATA_CHANNEL_ZJRCU.equals(normalized)
&& !LsfxConstants.DATA_CHANNEL_JZL.equals(normalized)) {
throw new IllegalArgumentException("流水来源不支持");
}
return normalized;
}
@Override
public String deleteFileUploadRecord(Long id, Long operatorUserId) {
CcdiFileUploadRecord record = recordMapper.selectById(id);
@@ -580,6 +597,7 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
Integer lsfxProjectId,
List<CcdiFileUploadRecord> records,
List<String> idCards,
String dataChannelCode,
String startDate,
String endDate,
String batchId) {
@@ -601,7 +619,7 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
while (!submitted && retryCount < 2) {
try {
CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(
() -> processPullBankInfoAsync(projectId, lsfxProjectId, record, idCard, startDate, endDate),
() -> processPullBankInfoAsync(projectId, lsfxProjectId, record, idCard, dataChannelCode, startDate, endDate),
fileUploadExecutor
);
futures.add(future);
@@ -640,16 +658,23 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
Integer lsfxProjectId,
CcdiFileUploadRecord record,
String idCard,
String dataChannelCode,
String startDate,
String endDate ) {
try {
String normalizedDataChannelCode = normalizePullBankInfoDataChannelCode(dataChannelCode);
FetchInnerFlowRequest request = new FetchInnerFlowRequest();
request.setGroupId(lsfxProjectId);
request.setCustomerNo(idCard);
request.setDataChannelCode(LsfxConstants.DEFAULT_DATA_CHANNEL_CODE);
request.setDataChannelCode(normalizedDataChannelCode);
request.setRequestDateId(Integer.parseInt(LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE)));
request.setDataStartDateId(Integer.parseInt(startDate.replace("-", "")));
request.setDataEndDateId(Integer.parseInt(endDate.replace("-", "")));
if (LsfxConstants.DATA_CHANNEL_JZL.equals(normalizedDataChannelCode)) {
request.setDataStartDateId(0);
request.setDataEndDateId(0);
} else {
request.setDataStartDateId(Integer.parseInt(startDate.replace("-", "")));
request.setDataEndDateId(Integer.parseInt(endDate.replace("-", "")));
}
request.setUploadUserId(LsfxConstants.DEFAULT_USER_ID);
FetchInnerFlowResponse response = lsfxClient.fetchInnerFlow(request);

View File

@@ -88,11 +88,12 @@ class CcdiFileUploadControllerTest {
CcdiPullBankInfoSubmitDTO dto = new CcdiPullBankInfoSubmitDTO();
dto.setProjectId(PROJECT_ID);
dto.setIdCards(List.of("110101199001018888"));
dto.setDataChannelCode("ZJRCU");
dto.setStartDate("2026-03-01");
dto.setEndDate("2026-03-10");
setLoginUser(9527L, "admin");
when(fileUploadService.submitPullBankInfo(PROJECT_ID, dto.getIdCards(), "2026-03-01", "2026-03-10", 9527L, "admin"))
when(fileUploadService.submitPullBankInfo(PROJECT_ID, dto.getIdCards(), "ZJRCU", "2026-03-01", "2026-03-10", 9527L, "admin"))
.thenReturn("batch-1");
AjaxResult result = controller.pullBankInfo(dto);
@@ -100,6 +101,35 @@ class CcdiFileUploadControllerTest {
assertEquals(200, result.get("code"));
}
@Test
void pullBankInfo_shouldAllowJzlWithoutDateRange() {
CcdiPullBankInfoSubmitDTO dto = new CcdiPullBankInfoSubmitDTO();
dto.setProjectId(PROJECT_ID);
dto.setIdCards(List.of("110101199001018888"));
dto.setDataChannelCode("JZL");
setLoginUser(9527L, "admin");
when(fileUploadService.submitPullBankInfo(PROJECT_ID, dto.getIdCards(), "JZL", null, null, 9527L, "admin"))
.thenReturn("batch-1");
AjaxResult result = controller.pullBankInfo(dto);
assertEquals(200, result.get("code"));
}
@Test
void pullBankInfo_shouldRejectUnsupportedDataChannelCode() {
CcdiPullBankInfoSubmitDTO dto = new CcdiPullBankInfoSubmitDTO();
dto.setProjectId(PROJECT_ID);
dto.setIdCards(List.of("110101199001018888"));
dto.setDataChannelCode("OTHER");
AjaxResult result = controller.pullBankInfo(dto);
assertEquals(500, result.get("code"));
assertEquals("流水来源不支持", result.get("msg"));
}
@Test
void deleteFile_shouldUseCurrentLoginUserId() {
setLoginUser(9527L, "admin");

View File

@@ -16,6 +16,8 @@ import com.ruoyi.ccdi.project.service.ICcdiBankTagService;
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.lsfx.client.LsfxAnalysisClient;
import com.ruoyi.lsfx.constants.LsfxConstants;
import com.ruoyi.lsfx.domain.request.FetchInnerFlowRequest;
import com.ruoyi.lsfx.domain.request.GetBankStatementRequest;
import com.ruoyi.lsfx.domain.response.CheckParseStatusResponse;
import com.ruoyi.lsfx.domain.response.DeleteFilesResponse;
@@ -144,6 +146,7 @@ class CcdiFileUploadServiceImplTest {
String batchId = service.submitPullBankInfo(
PROJECT_ID,
List.of("110101199001018888", "110101199001019999"),
LsfxConstants.DATA_CHANNEL_ZJRCU,
"2026-03-01",
"2026-03-10",
9527L,
@@ -171,6 +174,7 @@ class CcdiFileUploadServiceImplTest {
() -> service.submitPullBankInfo(
PROJECT_ID,
List.of("3301"),
LsfxConstants.DATA_CHANNEL_ZJRCU,
"2026-01-01",
"2026-01-31",
1L,
@@ -438,6 +442,7 @@ class CcdiFileUploadServiceImplTest {
LSFX_PROJECT_ID,
record,
"110101199001018888",
LsfxConstants.DATA_CHANNEL_ZJRCU,
"2026-03-01",
"2026-03-10"
);
@@ -450,6 +455,34 @@ class CcdiFileUploadServiceImplTest {
);
}
@Test
void processPullBankInfoAsync_shouldFetchJzlWithZeroDateRange() {
when(lsfxClient.fetchInnerFlow(any())).thenReturn(buildFetchInnerFlowResponse(LOG_ID));
when(lsfxClient.checkParseStatus(LSFX_PROJECT_ID, String.valueOf(LOG_ID)))
.thenReturn(buildCheckParseStatusResponse(false));
when(lsfxClient.getFileUploadStatus(any())).thenReturn(buildParsedSuccessStatusResponse());
when(lsfxClient.getBankStatement(any(GetBankStatementRequest.class)))
.thenReturn(buildEmptyBankStatementResponse());
CcdiFileUploadRecord record = buildRecord();
service.processPullBankInfoAsync(
PROJECT_ID,
LSFX_PROJECT_ID,
record,
"110101199001018888",
LsfxConstants.DATA_CHANNEL_JZL,
null,
null
);
verify(lsfxClient).fetchInnerFlow(argThat((FetchInnerFlowRequest request) ->
LsfxConstants.DATA_CHANNEL_JZL.equals(request.getDataChannelCode())
&& Integer.valueOf(0).equals(request.getDataStartDateId())
&& Integer.valueOf(0).equals(request.getDataEndDateId())
));
}
@Test
void processFileAsync_shouldUploadToLsfxWithOriginalRecordFileName() throws IOException {
when(lsfxClient.uploadFile(eq(LSFX_PROJECT_ID), any(), eq("原始流水.xlsx")))

View File

@@ -0,0 +1,20 @@
# 拉取行内/金综流水后端实施计划
## 背景
项目详情页“上传数据”中的拉取流水能力需要支持两种来源:行内流水 `ZJRCU` 与金综流水 `JZL`。流水平台仍使用原 `/getJZFileOrZjrcuFile` 接口,一次请求只传一种 `dataChannelCode`
## 实施范围
- `CcdiPullBankInfoSubmitDTO` 增加 `dataChannelCode`
- `CcdiFileUploadController#pullBankInfo` 校验流水来源,仅允许 `ZJRCU``JZL`
- `ICcdiFileUploadService#submitPullBankInfo` 与实现类透传来源。
- `CcdiFileUploadServiceImpl#processPullBankInfoAsync` 按来源组装流水平台请求:
- `ZJRCU`:使用页面传入的开始、结束日期。
- `JZL``dataStartDateId=0``dataEndDateId=0`
- `LsfxConstants` 增加 `DATA_CHANNEL_JZL` 常量。
## 验证计划
- 运行 `CcdiFileUploadControllerTest`,验证 Controller 透传来源与金综不要求日期。
- 运行 `CcdiFileUploadServiceImplTest`,验证金综请求传 `JZL``0/0` 日期参数。

View File

@@ -0,0 +1,25 @@
# 拉取行内/金综流水前端实施计划
## 背景
原弹窗只体现“拉取本行信息”,用户无法选择流水平台来源。新需求是在同一入口中区分拉取行内流水和拉取金综流水。
## 实施范围
- 将上传数据工具栏入口文案调整为“拉取流水”。
- 将弹窗标题调整为“拉取行内/金综流水”。
- 在弹窗中增加“流水类型”单选:
- 默认选择 `ZJRCU` 行内流水。
- 可切换为 `JZL` 金综流水。
- 两种来源均沿用证件号码批量输入能力,多个证件号按列表提交给后端。
- 行内流水保留时间跨度选择与近一年校验。
- 金综流水隐藏时间跨度,提交时 `startDate/endDate` 传空,由后端固定转换为 `0/0`
- 类型备注按页面截图口径展示:
- 行内流水:`备注:仅支持获取近一年流水`
- 金综流水:`备注:自动获取最近30天内授权的流水文件`
- 提交 payload 增加 `dataChannelCode`
## 验证计划
- 前端构建检查。
- 使用真实项目页面打开弹窗,确认默认行内、切换金综后隐藏时间跨度。

View File

@@ -0,0 +1,44 @@
# 拉取行内/金综流水来源选择实施记录
## 修改内容
- 后端拉取流水提交 DTO 增加 `dataChannelCode`
- 后端 Controller 校验流水来源,仅允许 `ZJRCU``JZL`
- 后端 Service 透传来源到流水平台请求。
- 行内流水继续传页面日期;金综流水固定传 `dataStartDateId=0``dataEndDateId=0`
- 前端上传数据页入口改为“拉取流水”,弹窗增加行内/金综单选。
- 前端选择金综流水时隐藏时间跨度,提交 `dataChannelCode=JZL`
- 行内和金综均支持多个证件号码批量提交,沿用原 `idCards` 列表。
- 类型备注调整为截图口径,金综备注不再出现“时间参数”。
## 影响范围
- 项目详情页上传数据模块。
- `/ccdi/file-upload/pull-bank-info` 接口。
- 流水平台拉取行内/金综流水请求参数组装。
## 验证情况
- 后端单测通过:
- `mvn -pl ccdi-project -am test "-Dtest=CcdiFileUploadControllerTest,CcdiFileUploadServiceImplTest" "-Dsurefire.failIfNoSpecifiedTests=false"`
- 结果40 个测试通过。
- 前端构建通过:
- `.nvmrc` 指向 `14.21.3`,本机 `nvm use` 未读取版本参数;按本机已验证环境使用 Node `v22.22.3`、npm `10.9.8`
- `npm run build:prod`
- 结果:构建成功,仅存在既有资源体积告警。
- 真实页面验证通过:
- 启动真实 `ruoyi-admin.jar` 后端并访问 `http://127.0.0.1:62318/swagger-ui.html`,返回 200。
- 启动前端开发服务,实际访问地址为 `http://127.0.0.1:8089/`
- 在真实项目详情页 `90342` 打开“拉取流水”弹窗,默认选中“拉取行内流水”并显示时间跨度。
- 切换“拉取金综流水”后,时间跨度隐藏。
- 金综备注显示为“备注:自动获取最近30天内授权的流水文件”。
- 复查行内备注显示为“备注:仅支持获取近一年流水”,金综备注未出现“时间参数”。
- 未点击“确认拉取”,未提交真实拉取任务。
- 测试结束后已停止本轮启动的前端和后端进程,端口 `8088``8089``62318` 无本轮服务监听。
## 提交前复验
- 后端单测复验通过:`CcdiFileUploadControllerTest``CcdiFileUploadServiceImplTest` 共 40 个测试通过。
- 前端生产构建复验通过:继续使用 Node `v22.22.3`、npm `10.9.8`,构建成功,仅存在既有资源体积告警。
- 真实页面复验通过:访问 `http://127.0.0.1:8088/ccdiProject/detail/90342`,打开“拉取流水”弹窗后默认选中“拉取行内流水”;切换“拉取金综流水”后时间跨度隐藏;两条备注与截图一致,页面未出现“时间参数”。
- 复验未点击“确定”,未提交真实拉取任务;复验结束后已停止后端 `62318` 与前端 `8088` 监听进程。

View File

@@ -56,7 +56,7 @@ export function executeQualityCheck(projectId) {
})
}
// 拉取本行信息
// 解析证件号码文件
export function parseIdCardFile(file) {
const formData = new FormData()
formData.append('file', file)
@@ -70,7 +70,7 @@ export function parseIdCardFile(file) {
})
}
// 拉取本行信息
// 拉取行内/金综流水
export function pullBankInfo(data) {
return request({
url: '/ccdi/file-upload/pull-bank-info',

View File

@@ -25,7 +25,7 @@
:disabled="isWriteLocked"
@click="handleFetchBankInfo"
>
拉取本行信息
拉取流水
</el-button>
<el-button
size="small"
@@ -110,7 +110,7 @@
</div>
<el-dialog
title="拉取本行信息"
title="拉取行内/金综流水"
:visible.sync="pullBankInfoDialogVisible"
:close-on-click-modal="false"
width="640px"
@@ -120,6 +120,24 @@
:model="pullBankInfoForm"
label-width="100px"
>
<el-form-item label="流水类型">
<el-radio-group
v-model="pullBankInfoForm.dataChannelCode"
:disabled="isWriteLocked"
class="pull-bank-channel-group"
>
<el-radio label="ZJRCU">
<span class="pull-bank-channel-title">拉取行内流水</span>
<span class="pull-bank-channel-tip">备注:仅支持获取近一年流水</span>
</el-radio>
<el-radio label="JZL">
<span class="pull-bank-channel-title">拉取金综流水</span>
<span class="pull-bank-channel-tip">
备注:自动获取最近30天内授权的流水文件
</span>
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="证件号码">
<el-input
v-model="pullBankInfoForm.idCardText"
@@ -171,7 +189,7 @@
</div>
</div>
</el-form-item>
<el-form-item label="时间跨度">
<el-form-item v-if="isZjrcuPullBankInfo" label="时间跨度">
<el-date-picker
class="pull-bank-range-picker"
v-model="pullBankInfoForm.dateRange"
@@ -309,6 +327,7 @@ export default {
parsingIdCardFile: false,
idCardFileList: [],
pullBankInfoForm: {
dataChannelCode: "ZJRCU",
idCardText: "",
dateRange: [],
},
@@ -365,6 +384,9 @@ export default {
isWriteLocked() {
return !this.canOperate || this.isProjectTagging || this.isProjectArchived;
},
isZjrcuPullBankInfo() {
return this.pullBankInfoForm.dataChannelCode === "ZJRCU";
},
writeLockedMessage() {
if (!this.canOperate) {
return "当前项目仅可查看,暂不可上传或拉取数据。";
@@ -440,6 +462,7 @@ export default {
},
resetPullBankInfoForm() {
this.pullBankInfoForm = {
dataChannelCode: "ZJRCU",
idCardText: "",
dateRange: this.buildDefaultPullBankInfoDateRange(),
};
@@ -594,18 +617,22 @@ export default {
}
const idCards = this.buildFinalIdCardList();
const [startDate, endDate] = this.pullBankInfoForm.dateRange || [];
const dataChannelCode = this.pullBankInfoForm.dataChannelCode;
if (idCards.length === 0) {
this.$message.warning("请至少输入一个身份证号");
return;
}
if (!startDate || !endDate) {
if (this.isZjrcuPullBankInfo && (!startDate || !endDate)) {
this.$message.warning("请选择完整的时间跨度");
return;
}
if (this.hasInvalidPullBankInfoDateRange([startDate, endDate])) {
if (
this.isZjrcuPullBankInfo &&
this.hasInvalidPullBankInfoDateRange([startDate, endDate])
) {
this.$message.warning("时间跨度仅支持近一年内日期,且最晚只能选择到昨天");
return;
}
@@ -616,8 +643,9 @@ export default {
const payload = {
projectId: this.projectId,
idCards,
startDate,
endDate,
dataChannelCode,
startDate: this.isZjrcuPullBankInfo ? startDate : null,
endDate: this.isZjrcuPullBankInfo ? endDate : null,
};
const res = await pullBankInfo(payload);
@@ -642,11 +670,11 @@ export default {
} catch (error) {
this.pullBankInfoLoading = false;
this.$message.error(
"拉取本行信息失败:" + ((error && error.message) || "未知错误")
"拉取流水失败:" + ((error && error.message) || "未知错误")
);
}
},
/** 拉取本行信息 */
/** 拉取流水 */
handleFetchBankInfo() {
if (this.isWriteLocked) {
this.showWriteLockedMessage();
@@ -1171,6 +1199,37 @@ export default {
}
.pull-bank-info-form {
.pull-bank-channel-group {
display: flex;
flex-direction: column;
gap: 12px;
::v-deep .el-radio {
display: flex;
align-items: center;
margin-right: 0;
line-height: 24px;
}
::v-deep .el-radio__label {
display: inline-flex;
align-items: baseline;
gap: 16px;
flex-wrap: wrap;
padding-left: 10px;
}
}
.pull-bank-channel-title {
font-size: 15px;
color: #303133;
}
.pull-bank-channel-tip {
font-size: 13px;
color: #909399;
}
.pull-bank-field-tip {
margin-top: 8px;
font-size: 12px;