> 文章列表 > 核心业务1:账户绑定业务

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

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

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

1.业务流程图

2.账户绑定数据库设计

3.账户绑定业务流程

4.代码逻辑

5.代码逻辑细节

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

1.业务流程图

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

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

  • A 用户在绑定页输入信息
    核心业务1:账户绑定业务

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

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

②用户绑定数据到汇付宝

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

2.账户绑定数据库设计

①尚融宝数据库表

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

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

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

②汇付宝数据库表

  • user_bind表
    在E操作中更新表
    核心业务1:账户绑定业务
  • user_account表
    在E操作中更新表
    核心业务1:账户绑定业务

③尚融宝汇付宝数据库表

  • 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><inputv-model="userBind.name"type="text"class="pay-txt"maxlength="16"placeholder="您的真实姓名"/></li><li><label>身份证号</label><inputv-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><inputv-model="userBind.bankType"type="text"class="pay-txt"placeholder="银行名称"/></li><li><label>银行卡号</label><inputv-model="userBind.bankNo"type="text"class="pay-txt"placeholder="本人持有的银行卡"/></li><li><label>预留手机</label><inputv-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('<div style="size: 18px;color: red;">您即将前往汇付宝绑定账号</div>','前往汇付宝资金托管平台',{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>

②尚融宝

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

  • 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;/* <p>* 用户绑定表 前端控制器* </p> @author Likejin* @since 2023-04-09*/
@RestController
@Api(tags = "会员账号绑定")
@RequestMapping("/api/core/userBind")
@Slf4j
public class UserBindController {@Resourceprivate 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为mapMap<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;/* <p>* 用户绑定表 服务类* </p> @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;/* <p>* 用户绑定表 服务实现类* </p> @author Likejin* @since 2023-04-09*/
@Service
public class UserBindServiceImpl extends ServiceImpl<UserBindMapper, UserBind> implements UserBindService {@Resourceprivate UserInfoMapper userInfoMapper;@Overridepublic 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)@Overridepublic 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_binduserBind.setBindCode(bindCode);userBind.setStatus(UserBindEnum.BIND_OK.getStatus());baseMapper.updateById(userBind);//更新尚融宝数据用户信息表 user_info//原来这些值都没传递,都传到了汇付宝,需要用这些值更新用户信息表 user_infoUserInfo 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:账户绑定业务
  • 异步请求数据
    核心业务1:账户绑定业务
  • 异步接受返回结果类型
    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字符串

未更新

未更新

未更新

未更新

未更新

未更新

未更新

未更新

未更新