您现在的位置是:首页 >技术教程 >尚融宝22-提交借款申请网站首页技术教程

尚融宝22-提交借款申请

zoeil 2023-06-02 16:00:02
简介尚融宝22-提交借款申请

目录

一、需求介绍

二、图片上传

(一)前端页面

(二)实现图片上传

三、数据字典展示

(一)后端

(二)前端 

四、表单信息提交

(一)后端

1、VO对象(表单对象)

2、定义借款认证状态枚举

3、controller

4、service

(二)前端

五、获取借款人状态

(一)后端

1、borrowerController

2、service

(二)前端


一、需求介绍

step1:用户在个人中心点击 “立即借款”  (http://localhost:3000/user/borrower)

step2:展示借款人信息认证页面

step3:借款人填写信息并提交

step4:展示等待审核页面

 

step5:平台审核

step6:显示审批结果

 

二、图片上传

(一)前端页面

<template>
  <div class="personal-main">
    <div class="personal-pay">
      <h3><i>借款人信息认证</i></h3>

      <el-steps :active="active" style="margin: 40px">
        <el-step title="填写借款人信息"></el-step>
        <el-step title="提交平台审核"></el-step>
        <el-step title="等待认证结果"></el-step>
      </el-steps>

      <div v-if="active === 0" class="user-borrower">
        <h6>个人基本信息</h6>
        <el-form label-width="120px">
          <el-form-item label="年龄">
            <el-col :span="5">
              <el-input v-model="borrower.age" />
            </el-col>
          </el-form-item>

          <el-form-item label="性别">
            <el-select v-model="borrower.sex">
              <el-option :value="1" :label="'男'" />
              <el-option :value="0" :label="'女'" />
            </el-select>
          </el-form-item>
          <el-form-item label="婚否">
            <el-select v-model="borrower.marry">
              <el-option :value="true" :label="'是'" />
              <el-option :value="false" :label="'否'" />
            </el-select>
          </el-form-item>
          <el-form-item label="学历">
            <el-select v-model="borrower.education">
              <el-option
                v-for="item in educationList"
                :key="item.value"
                :label="item.name"
                :value="item.value"
              />
            </el-select>
          </el-form-item>
          <el-form-item label="行业">
            <el-select v-model="borrower.industry">
              <el-option
                v-for="item in industryList"
                :key="item.value"
                :label="item.name"
                :value="item.value"
              />
            </el-select>
          </el-form-item>
          <el-form-item label="月收入">
            <el-select v-model="borrower.income">
              <el-option
                v-for="item in incomeList"
                :key="item.value"
                :label="item.name"
                :value="item.value"
              />
            </el-select>
          </el-form-item>
          <el-form-item label="还款来源">
            <el-select v-model="borrower.returnSource">
              <el-option
                v-for="item in returnSourceList"
                :key="item.value"
                :label="item.name"
                :value="item.value"
              />
            </el-select>
          </el-form-item>
        </el-form>

        <h6>联系人信息</h6>
        <el-form label-width="120px">
          <el-form-item label="联系人姓名">
            <el-col :span="5">
              <el-input v-model="borrower.contactsName" />
            </el-col>
          </el-form-item>
          <el-form-item label="联系人手机">
            <el-col :span="5">
              <el-input v-model="borrower.contactsMobile" />
            </el-col>
          </el-form-item>
          <el-form-item label="联系人关系">
            <el-select v-model="borrower.contactsRelation">
              <el-option
                v-for="item in contactsRelationList"
                :key="item.value"
                :label="item.name"
                :value="item.value"
              />
            </el-select>
          </el-form-item>
        </el-form>

        <h6>身份认证信息</h6>
        <el-form label-width="120px">
          <el-form-item label="身份证人像面">
            <el-upload
              :on-success="onUploadSuccessIdCard1"
              :on-remove="onUploadRemove"
              :multiple="false"
              :action="uploadUrl"
              :data="{ module: 'idCard1' }"
              :limit="1"
              list-type="picture-card"
            >
              <i class="el-icon-plus"></i>
            </el-upload>
          </el-form-item>
          <el-form-item label="身份证国徽面">
            <el-upload
              :on-success="onUploadSuccessIdCard2"
              :on-remove="onUploadRemove"
              :multiple="false"
              :action="uploadUrl"
              :data="{ module: 'idCard2' }"
              :limit="1"
              list-type="picture-card"
            >
              <i class="el-icon-plus"></i>
            </el-upload>
          </el-form-item>
        </el-form>

        <h6>其他信息</h6>
        <el-form label-width="120px">
          <el-form-item label="房产信息">
            <el-upload
              :on-success="onUploadSuccessHouse"
              :on-remove="onUploadRemove"
              :multiple="false"
              :action="uploadUrl"
              :data="{ module: 'house' }"
              list-type="picture-card"
            >
              <i class="el-icon-plus"></i>
            </el-upload>
          </el-form-item>
          <el-form-item label="车辆信息">
            <el-upload
              :on-success="onUploadSuccessCar"
              :on-remove="onUploadRemove"
              :multiple="false"
              :action="uploadUrl"
              :data="{ module: 'car' }"
              list-type="picture-card"
            >
              <i class="el-icon-plus"></i>
            </el-upload>
          </el-form-item>
        </el-form>

        <el-form label-width="120px">
          <el-form-item>
            <el-button
              type="primary"
              :disabled="submitBtnDisabled"
              @click="save"
            >
              提交
            </el-button>
          </el-form-item>
        </el-form>
      </div>

      <div v-if="active === 1">
        <div style="margin-top:40px;">
          <el-alert
            title="您的认证申请已成功提交,请耐心等待"
            type="warning"
            show-icon
            :closable="false"
          >
            我们将在2小时内完成审核,审核时间为周一至周五8:00至20:00。
          </el-alert>
        </div>
      </div>

      <div v-if="active === 2">
        <div style="margin-top:40px;">
          <el-alert
            v-if="borrowerStatus === 2"
            title="您的认证审批已通过"
            type="success"
            show-icon
            :closable="false"
          >
          </el-alert>

          <el-alert
            v-if="borrowerStatus === -1"
            title="您的认证审批未通过"
            type="error"
            show-icon
            :closable="false"
          >
          </el-alert>
        </div>
      </div>
    </div>
  </div>
</template>

(二)实现图片上传

前面四个success是el文件上传组件成功后的回调函数,response和file是组件为我们自动封装的,传递type的原因是要区别四张图片,上传阿里云时存储在不同文件中

这里前端我们要将整个表单封装到borrow对象中,比如年龄(borrow.age),还有附件(borrower.borrowerAttachList),即四张图片的信息(包括图片名字,图片类型,图片在阿里云上的地址)

其次当用户上传图片又删除后,我们需要删除已经上传阿里云的图片并删除borrower.borrowerAttachList里对应的信息,这里使用filter函数实现

onUploadSuccessIdCard1(response, file) {
  this.onUploadSuccess(response, file, 'idCard1')
},

onUploadSuccessIdCard2(response, file) {
  this.onUploadSuccess(response, file, 'idCard2')
},

onUploadSuccessHouse(response, file) {
  this.onUploadSuccess(response, file, 'house')
},

onUploadSuccessCar(response, file) {
  this.onUploadSuccess(response, file, 'car')
},

onUploadSuccess(response, file, type) {
  // debugger
  if (response.code !== 0) {
    this.$message.error(response.message)
    return
  }
  // 填充上传文件列表
  this.borrower.borrowerAttachList.push({
    imageName: file.name,
    imageUrl: response.data.url,
    imageType: type,
  })
},

onUploadRemove(file, fileList) {
  console.log('fileList', fileList)
  //删除oss服务器上的内容
  this.$axios
    .$delete('/api/oss/file/remove?url=' + file.response.data.url)
    .then((response) => {
      // debugger
      console.log('远程删除')
      this.borrower.borrowerAttachList = this.borrower.borrowerAttachList.filter(
        function(item) {
          console.log('item', item)
          return item.imageUrl != file.response.data.url
        }
      )
    })
},

 在浏览器通过vue插件查看对象封装情况

三、数据字典展示

对于以下下拉表单选项的内容储存在数据字典dict表中,因此我们去查询数据库将数据字典对应的内容查询出来进行展示

 

思路:首先根据dict_code查询出该对象的id,然后通过这个id查询下面的子节点(通过子节点parent_id==父节点id)

(一)后端

DictController

    @ApiOperation("根据dictCode查询下级节点")
    @GetMapping("/findByDictCode/{dictCode}")
    public R findByDictCode(
            @ApiParam(value = "节点编码", required = true)
            @PathVariable String dictCode) {

        List<Dict> dictList = dictService.findByDictCode(dictCode);

        return R.ok().data("dictList", dictList);
    }

DictService

    List<Dict> findByDictCode(String dictCode);

DictServiceImpl 

    @Override
    public List<Dict> findByDictCode(String dictCode) {

        QueryWrapper<Dict> wrapper = new QueryWrapper<>();
        wrapper.eq("dict_code", dictCode);
        Dict dict = baseMapper.selectOne(wrapper); // 父节点

        return this.listByParentId(dict.getId());
    }


    @Override
    public List<Dict> listByParentId(Long parent_id) {
        try {
            // 首先查询redis有无数据
            List<Dict> dictList = (List<Dict>)redisTemplate.opsForValue().get("src:core:dictList" + parent_id);
            // 如果查询到数据直接返回
            if(dictList != null) {
                log.info("redis查到数据,准备返回");
                return dictList;
            }
        } catch (Exception e) {
            log.error("redis服务器异常:" + ExceptionUtils.getStackTrace(e));
        }
        // redis没有数据就查询数据库
        log.info("查询数据库");
        QueryWrapper<Dict> wrapper = new QueryWrapper<>();
        wrapper.eq("parent_id", parent_id);
        List<Dict> dicts = baseMapper.selectList(wrapper);
        dicts.forEach(dict -> {
            dict.setHasChildren(this.hasChildren(dict.getId()));
        });

        try {
            // 将查询到的数据放入redis
            log.info("将数据库查到的数据放入redis");
            redisTemplate.opsForValue().set("src:core:dictList" + parent_id, dicts, 5, TimeUnit.MINUTES);
        } catch (Exception e) {
            log.error("redis服务器异常:" + ExceptionUtils.getStackTrace(e));
        }
        // 返回查询到的数据
        return dicts;
    }

(二)前端 

pages/user/borrower.vue中调用接口

  created() {
    this.initSelected()
  },
  initSelected() {
      //学历列表
      this.$axios
        .$get('/api/core/dict/findByDictCode/education')
        .then((response) => {
          this.educationList = response.data.dictList
        })

      //行业列表
      this.$axios
        .$get('/api/core/dict/findByDictCode/industry')
        .then((response) => {
          this.industryList = response.data.dictList
        })

      //收入列表
      this.$axios
        .$get('/api/core/dict/findByDictCode/income')
        .then((response) => {
          this.incomeList = response.data.dictList
        })

      //还款来源列表
      this.$axios
        .$get('/api/core/dict/findByDictCode/returnSource')
        .then((response) => {
          this.returnSourceList = response.data.dictList
        })

      //联系人关系列表
      this.$axios
        .$get('/api/core/dict/findByDictCode/relation')
        .then((response) => {
          this.contactsRelationList = response.data.dictList
        })
    },

四、表单信息提交

(一)后端

1、VO对象(表单对象)

service-core微服务,创建BorrowerVO,对应的是填写借款申请时的表单对象VO(value object)

@Data
@ApiModel(description="借款人认证信息")
public class BorrowerVO {

    @ApiModelProperty(value = "性别(1:男 0:女)")
    private Integer sex;

    @ApiModelProperty(value = "年龄")
    private Integer age;

    @ApiModelProperty(value = "学历")
    private Integer education;

    @ApiModelProperty(value = "是否结婚(1:是 0:否)")
    private Boolean marry;

    @ApiModelProperty(value = "行业")
    private Integer industry;

    @ApiModelProperty(value = "月收入")
    private Integer income;

    @ApiModelProperty(value = "还款来源")
    private Integer returnSource;

    @ApiModelProperty(value = "联系人名称")
    private String contactsName;

    @ApiModelProperty(value = "联系人手机")
    private String contactsMobile;

    @ApiModelProperty(value = "联系人关系")
    private Integer contactsRelation;

    @ApiModelProperty(value = "借款人附件资料")
    private List<BorrowerAttach> borrowerAttachList;
}

2、定义借款认证状态枚举

BorrowerStatusEnum

数据库设计中对应认证状态status (0:未认证,1:认证中, 2:认证通过, -1:认证失败)

@AllArgsConstructor
@Getter
//@ToString
public enum BorrowerStatusEnum {

    NO_AUTH(0, "未认证"),
    AUTH_RUN(1, "认证中"),
    AUTH_OK(2, "认证成功"),
    AUTH_FAIL(-1, "认证失败"),
    ;

    private Integer status;
    private String msg;

    public static String getMsgByStatus(int status) {
        BorrowerStatusEnum arrObj[] = BorrowerStatusEnum.values();
        for (BorrowerStatusEnum obj : arrObj) {
            if (status == obj.getStatus().intValue()) {
                return obj.getMsg();
            }
        }
        return "";
    }
}

3、controller

borrowerController

    @ApiOperation("保存借款人信息")
    @PostMapping("/auth/save")
    public R save(@RequestBody BorrowerVO borrowerVO, HttpServletRequest request) {
        String token = request.getHeader("token");
        Long userId = JwtUtils.getUserId(token);
        borrowerService.saveBorrowerVOByUserId(borrowerVO, userId);

        return R.ok().message("信息保存成功");
    }

4、service

BorrowerService

    void saveBorrowerVOByUserId(BorrowerVO borrowerVO, Long userId);

 BorrowerServiceImpl

@Resource
private BorrowerAttachMapper borrowerAttachMapper;

@Resource
private UserInfoMapper userInfoMapper;

@Transactional(rollbackFor = Exception.class)
@Override
public void saveBorrowerVOByUserId(BorrowerVO borrowerVO, Long userId) {

    UserInfo userInfo = userInfoMapper.selectById(userId);

    //保存借款人信息
    Borrower borrower = new Borrower();
    BeanUtils.copyProperties(borrowerVO, borrower);
    borrower.setUserId(userId);
    borrower.setName(userInfo.getName());
    borrower.setIdCard(userInfo.getIdCard());
    borrower.setMobile(userInfo.getMobile());
    borrower.setStatus(BorrowerStatusEnum.AUTH_RUN.getStatus());//认证中
    baseMapper.insert(borrower);

    //保存附件
    List<BorrowerAttach> borrowerAttachList = borrowerVO.getBorrowerAttachList();
    borrowerAttachList.forEach(borrowerAttach -> {
        borrowerAttach.setBorrowerId(borrower.getId());
        borrowerAttachMapper.insert(borrowerAttach);
    });
    
    //更新会员状态,更新为认证中
    userInfo.setBorrowAuthStatus(BorrowerStatusEnum.AUTH_RUN.getStatus());
    userInfoMapper.updateById(userInfo);
}

(二)前端

pages/user/borrower.vue 脚本

save() {
  // debugger
  this.submitBtnDisabled = true
  this.$axios
    .$post('/api/core/borrower/save', this.borrower)
    .then((response) => {
      this.active = 1
    })
},

五、获取借款人状态

当借款人申请后,再一次刷新页面会发现仍然是表单,这里正确的是应该显示认证中,认证成功或者认证失败,所以在加载页面之前我们应该请求后端获取borrowerStatus

这里的认证状态是由active和borrowerstatus共同决定的

(一)后端

1、borrowerController

@ApiOperation("获取借款人认证状态")
@GetMapping("/auth/getBorrowerStatus")
public R getBorrowerStatus(HttpServletRequest request){
    String token = request.getHeader("token");
    Long userId = JwtUtils.getUserId(token);
    Integer status = borrowerService.getStatusByUserId(userId);
    return R.ok().data("borrowerStatus", status);
}

2、service

 BorrowerService

Integer getStatusByUserId(Long userId);

BorrowerServiceImpl

@Override
public Integer getStatusByUserId(Long userId) {

    QueryWrapper<Borrower> borrowerQueryWrapper = new QueryWrapper<>();
    borrowerQueryWrapper.select("status").eq("user_id", userId);
    List<Object> objects = baseMapper.selectObjs(borrowerQueryWrapper);

    if(objects.size() == 0){
        //借款人尚未提交信息
        return BorrowerStatusEnum.NO_AUTH.getStatus();
    }
    Integer status = (Integer)objects.get(0);
    return status;
}

(二)前端

 pages/user/borrower.vue 脚本

created() {
  // 这里由initSelected换成getUserInfo,获取borrowerStatus 状态后再决定是否调用initSelected
    this.getUserInfo()  
},
//获取借款人信息
getUserInfo() {
    this.$axios
        .$get('/api/core/borrower/auth/getBorrowerStatus')
        .then((response) => {
        this.borrowerStatus = response.data.borrowerStatus
        if (this.borrowerStatus === 0) {
            //未认证
            this.active = 0
            //获取下拉列表
            this.initSelected()
        } else if (this.borrowerStatus === 1) {
            //认证中
            this.active = 1
        } else if (this.borrowerStatus === 2) {
            //认证成功
            this.active = 2
        } else if (this.borrowerStatus === -1) {
            //认证失败
            this.active = 2
        }
    })
}

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。