核心业务1:账户绑定业务

核心业务1:账户绑定业务

1.业务流程图

2.账户绑定数据库设计

3.账户绑定业务流程

4.代码逻辑

5.代码逻辑细节

核心业务1:账户绑定业务

1.业务流程图

核心业务1:账户绑定业务_第1张图片

①用户绑定数据到商户平台(已登录用户)

  • A 用户在绑定页输入信息
    核心业务1:账户绑定业务_第2张图片

  • B单击开户绑定数据到尚融宝

  • C并且尚融宝提交表单数据给汇付宝

②用户绑定数据到汇付宝

  • D用户再跳转汇付宝绑定页(根据表单数据展示绑定页)
    核心业务1:账户绑定业务_第3张图片
  • E单击确认绑定成功汇付宝
  • F汇付宝异步调用尚融宝同步数据
  • G汇付宝同步返回结果页面
    核心业务1:账户绑定业务_第4张图片

2.账户绑定数据库设计

①尚融宝数据库表

  • 包含全局信息的user_info
    在F操作中更新表

核心业务1:账户绑定业务_第5张图片

  • 包含绑定信息的user_bind
    在B操作中更新表
    在F操作中更新表
    核心业务1:账户绑定业务_第6张图片

②汇付宝数据库表

  • user_bind表
    在E操作中更新表
    核心业务1:账户绑定业务_第7张图片
  • user_account表
    在E操作中更新表
    核心业务1:账户绑定业务_第8张图片

③尚融宝汇付宝数据库表

  • user_id
    所有的数据表都有唯一user_id
  • bind_code和status
    在汇付宝中生成后异步更新尚融宝的user_bind和user_info

3.账户绑定业务流程

①前端

  • 必须登录才可以进入bind页面
    发送到后端时携带token(登录后会保存在浏览器中,根据请求拦截器自动携带token),token中携带数据全局user_id
  • 进入bind绑定页面输入信息后点击确认
    信息更新到尚融宝user_bind中,同时尚融宝返回自动提交表单数据直接提交到汇付宝
  • 跳转到汇付宝提交页面
    汇付宝根据提交表单信息回显表单页,点击提交后进入汇付宝返回的结果页

②尚融宝

  • 接受bind绑定页传入的表单
    对token进行校验,生成汇付宝需要的表单信息返回前端并且设置前端自动提交
  • 接受尚融宝的异步调用
    验证签名并且将尚融宝的user_info表和user_bind表与汇付宝中的表绑定(通过bind_code属性)

③汇付宝

  • 接受尚融宝提交的页面回显给用户提交页
  • 接受用户提交的数据更新汇付宝user_account和user_bind表
  • 异步返回给尚融宝绑定数据
  • 同步返回给用户结果页

4.代码逻辑

①前端

  • 绑定页
    完成调用后端/api/core/userBind/auth/bind接口的调用,自动提交接口返回的结果表单
<template>
  <div class="personal-main">
    <div class="personal-pay">
      <h3><i>开通第三方账户</i></h3>
      <div class="pay-notice">
        <p>
          请开通汇付宝存管账户以便于您正常理财
        </p>
      </div>
      <div class="pay-form">
        <ul>
          <li>
            <label>真实姓名</label>
            <input
              v-model="userBind.name"
              type="text"
              class="pay-txt"
              maxlength="16"
              placeholder="您的真实姓名"
            />
          </li>
          <li>
            <label>身份证号</label>
            <input
              v-model="userBind.idCard"
              type="text"
              class="pay-txt"
              maxlength="18"
              placeholder="您的身份证号"
            />
            <div id="idCardErrorDiv">
              <p style="margin-top:10px;">
                身份证信息认证后将不可修改,请您仔细填写
              </p>
            </div>
          </li>
          <li>
            <label>绑定银行</label>
            <input
              v-model="userBind.bankType"
              type="text"
              class="pay-txt"
              placeholder="银行名称"
            />
          </li>
          <li>
            <label>银行卡号</label>
            <input
              v-model="userBind.bankNo"
              type="text"
              class="pay-txt"
              placeholder="本人持有的银行卡"
            />
          </li>
          <li>
            <label>预留手机</label>
            <input
              v-model="userBind.mobile"
              type="text"
              class="pay-txt"
              placeholder="银行卡预留手机号"
            />
          </li>
          <li>
            <label>&nbsp;</label>
            <input v-model="agree" type="checkbox" />
            我已阅读并同意
            <a href="#" class="c-orange" target="_blank">
              《汇付宝托管账户协议》
            </a>
          </li>
          <li>
            <label>&nbsp;</label>
            <el-button :disabled="!agree" @click="commitBind()" type="primary">
              开户
            </el-button>
          </li>
        </ul>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      agree: false,
      userBind: {},
    }
  },

  methods: {
    commitBind() {
      this.$alert(
        '
您即将前往汇付宝绑定账号
'
, '前往汇付宝资金托管平台', { dangerouslyUseHTMLString: true, confirmButtonText: '立即前往', callback: (action) => { //TODO 提交用户信息 console.log(action) if (action == 'confirm') { this.$axios .$post('/api/core/userBind/auth/bind', this.userBind) .then((response) => { document.write(response.data.formStr) }) } }, } ) }, }, } </script>

②尚融宝

在这里插入图片描述

  • controller
    完成前端接口/api/core/userBind/auth/bind的调用和汇付宝异步调用/api/core/userBind/auth/bind/notify
package com.atguigu.srb.core.controller.api;


import com.alibaba.fastjson.JSON;
import com.atguigu.common.result.R;
import com.atguigu.srb.base.util.JwtUtils;
import com.atguigu.srb.core.hfb.RequestHelper;
import com.atguigu.srb.core.pojo.vo.UserBindVO;
import com.atguigu.srb.core.service.UserBindService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
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;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
 * 

* 用户绑定表 前端控制器 *

* * @author Likejin * @since 2023-04-09 */
@RestController @Api(tags = "会员账号绑定") @RequestMapping("/api/core/userBind") @Slf4j public class UserBindController { @Resource private UserBindService userBindService; //需要登录的才能访问的接口用/auth来定义 @ApiOperation("账户绑定提交数据") @PostMapping("/auth/bind") public R bind( @RequestBody UserBindVO userBindVO, HttpServletRequest request){ //从header中拿到token,确保用户已经登录,从token中获取到userId(token中有id和username) String token = request.getHeader("token"); //已经对token进行校验了 Long userId = JwtUtils.getUserId(token); //根据userId做账户绑定,生成一个动态表单的字符串 String formStr = userBindService.commitBindUser(userBindVO,userId); return R.ok().data("formStr",formStr); } @ApiOperation("账户绑定异步回调") //汇付宝以post发起请求 @PostMapping("/notify") //返回值时String,返回为sucess则成功回调,汇付宝不会重试 public String notify(HttpServletRequest request) { //解析汇付宝发过来的request为map Map<String, Object> paramMap = RequestHelper.switchMap(request.getParameterMap()); log.info("账户绑定异步回调接受参数{}", JSON.toJSONString(paramMap)); //校验签名(约定相同的算法,计算用其他参数得到的签名和实际签名是否相同) if(!RequestHelper.isSignEquals(paramMap)){ log.error("用户账号绑定异步回调签名验证错误:"+ JSON.toJSONString(paramMap)); return "fail"; } log.info("验签成功!开始账户绑定"); userBindService.notify(paramMap); return "success"; } }
  • service
package com.atguigu.srb.core.service;

import com.atguigu.srb.core.pojo.entity.UserBind;
import com.atguigu.srb.core.pojo.vo.UserBindVO;
import com.baomidou.mybatisplus.extension.service.IService;

import java.util.Map;

/**
 * 

* 用户绑定表 服务类 *

* * @author Likejin * @since 2023-04-09 */
public interface UserBindService extends IService<UserBind> { String commitBindUser(UserBindVO userBindVO, Long userId); void notify(Map<String, Object> paramMap); }
package com.atguigu.srb.core.service.impl;

import com.atguigu.common.exception.Assert;
import com.atguigu.common.result.ResponseEnum;
import com.atguigu.srb.core.enums.UserBindEnum;
import com.atguigu.srb.core.hfb.FormHelper;
import com.atguigu.srb.core.hfb.HfbConst;
import com.atguigu.srb.core.hfb.RequestHelper;
import com.atguigu.srb.core.mapper.UserBindMapper;
import com.atguigu.srb.core.mapper.UserInfoMapper;
import com.atguigu.srb.core.pojo.entity.UserBind;
import com.atguigu.srb.core.pojo.entity.UserInfo;
import com.atguigu.srb.core.pojo.vo.UserBindVO;
import com.atguigu.srb.core.service.UserBindService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

/**
 * 

* 用户绑定表 服务实现类 *

* * @author Likejin * @since 2023-04-09 */
@Service public class UserBindServiceImpl extends ServiceImpl<UserBindMapper, UserBind> implements UserBindService { @Resource private UserInfoMapper userInfoMapper; @Override public String commitBindUser(UserBindVO userBindVO, Long userId) { //不同的userid,相同的身份证存在则不允许 QueryWrapper<UserBind> userBindQueryWrapper = new QueryWrapper<>(); userBindQueryWrapper.eq("id_card",userBindVO.getIdCard()) .ne("user_id",userId); UserBind userBind = baseMapper.selectOne(userBindQueryWrapper); Assert.isNull(userBind, ResponseEnum.USER_BIND_IDCARD_EXIST_ERROR); //检查用户是否曾经填写过绑定表单 userBindQueryWrapper = new QueryWrapper<>(); userBindQueryWrapper.eq("user_id",userId); userBind = baseMapper.selectOne(userBindQueryWrapper); //如果userbind不存在则创建记录 if(userBind ==null){ //创建用户绑定记录 userBind = new UserBind(); //把前端传入的数据与数据库user_bind绑定 BeanUtils.copyProperties(userBindVO,userBind); userBind.setUserId(userId); userBind.setStatus(UserBindEnum.NO_BIND.getStatus()); baseMapper.insert(userBind); }else{ //相同的user_id,存在,则更新 BeanUtils.copyProperties(userBindVO,userBind); baseMapper.updateById(userBind); } //动态生成表单 //组装表单的参数 HashMap<String, Object> paramMap = new HashMap<>(); //商户唯一标识 paramMap.put("agentId", HfbConst.AGENT_ID); //前端提交的表单对象 paramMap.put("agentUserId", userId); paramMap.put("idCard",userBindVO.getIdCard()); paramMap.put("personalName", userBindVO.getName()); paramMap.put("bankType", userBindVO.getBankType()); paramMap.put("bankNo", userBindVO.getBankNo()); paramMap.put("mobile", userBindVO.getMobile()); //返回结果给平台前端的结果页 paramMap.put("returnUrl", HfbConst.USERBIND_RETURN_URL); //返回商户平台的后端的接口 paramMap.put("notifyUrl", HfbConst.USERBIND_NOTIFY_URL); //提交时间戳(远程调用) paramMap.put("timestamp", RequestHelper.getTimestamp()); //生成签名,有已知的Sign_key(两者商量好的) paramMap.put("sign", RequestHelper.getSign(paramMap)); //生成动态表单字符串(辅助生成字符串) String formStr = FormHelper.buildForm(HfbConst.USERBIND_URL,paramMap); //前端得到一个字符串,直接提交给汇付宝绑定用户 return formStr; } /** * @param paramMap: * @return void * @author Likejin * @description 处理汇付宝回调参数 * @date 2023/4/14 21:28 */ @Transactional(rollbackFor = Exception.class) @Override public void notify(Map<String, Object> paramMap) { String bindCode = (String)paramMap.get("bindCode"); String agentUserId = (String)paramMap.get("agentUserId"); //根据userid查询userBind对象 QueryWrapper<UserBind> userBindQueryWrapper = new QueryWrapper<>(); userBindQueryWrapper.eq("user_id",agentUserId); UserBind userBind = baseMapper.selectOne(userBindQueryWrapper); //更新尚融宝数据用户绑定表 user_bind userBind.setBindCode(bindCode); userBind.setStatus(UserBindEnum.BIND_OK.getStatus()); baseMapper.updateById(userBind); //更新尚融宝数据用户信息表 user_info //原来这些值都没传递,都传到了汇付宝,需要用这些值更新用户信息表 user_info UserInfo userInfo = userInfoMapper.selectById(agentUserId); userInfo.setBindCode(bindCode); //名字原来和手机号绑定 userInfo.setName(userBind.getName()); userInfo.setIdCard(userBind.getIdCard()); userInfo.setBindStatus(UserBindEnum.BIND_OK.getStatus()); userInfoMapper.updateById(userInfo); } }

③汇付宝

  • 表单提交数据
    核心业务1:账户绑定业务_第9张图片
  • 异步请求数据
    核心业务1:账户绑定业务_第10张图片
  • 异步接受返回结果类型
    String (success,其他)

5.代码逻辑细节

①引入工具类的作用

  • JwtUtils
    前端发送的请求用于校验token
  • BeanUtils
    复制对象相同的属性给另一个对象
  • 枚举类
    字符串类型的签名
    汇付宝异步回调和同步回调地址,提交给汇付宝表单的地址
  • RequestHelper
    解析汇付宝发来的request为map
    校验汇付宝带的签名是否正确

②业务逻辑细节

  • 前端发送数据给尚融宝时
    尚融宝需要先检验token是否正确
    尚融宝需要确认身份证数据唯一:如果数据库中已有身份证,则判断id是否相同,不相同则返回身份证只能注册一个账号。
    尚融宝需要确认如果id存在则进行更新操作,不存在则进行插入操作
    尚融宝组成动态表单时,需要携带returnUrl,notifyUrl,表单提交的地址,sign令牌
  • 汇付宝异步调用尚融宝时
    尚融宝验证签名是否正确
    尚融宝更新自己的user_info和user_bind
    其中user_bind只需要更新(bind_code和status)
    其中user_info由于之前未更新,此处更新多数据包括bind_code和status
    返回success字符串

未更新

未更新

未更新

未更新

未更新

未更新

未更新

未更新

未更新

你可能感兴趣的:(项目1:金融借钱还钱,数据库,java,前端)