spring boot + mybatis 模拟银行系统余额查询、转账、存取钱功能实现
实现银行系统的余额查询在我们之前写的token认证登陆功能完成的基础上实现,token认证登陆功能代码地址:https://github.com/yeyuting-1314/selectFuPan_tokenAndRedisfengzhuagn.git
银行系统余额查询、转账、入账、出账功能项目代码地址:https://github.com/yeyuting-1314/tokenLogin1.0.git
一、准备工作
新建一个数据库,数据库名字是tokenLogin,数据库内新建两张表,第一张是用户账户信息表,第二张是转账、入账、出账记录表,分别如下所示:
1. sys_user用户账户信息表:
CREATE TABLE tokenLogin.sys_user( `id` bigint NOT NULL comment 'ID ', `username` varchar(50) comment '用户名', `password` varchar(50) comment '密码', PRIMARY KEY (`id`) ); insert into tokenLogin.sys_user values('1' , 'admmin' , '1') insert into tokenLogin.sys_user values('2' , 'superadmmin' , '11') insert into tokenLogin.sys_user values('3' , 'user' , '111') alter table tokenLogin.sys_user add column account double default null;
接着对account进行赋值,赋值完成后表内容如下:
2. 转账记录表:
CREATE TABLE tokenLogin.trasf_record( `id` int NOT NULL AUTO_INCREMENT, `username` varchar(50) default null, `account_time` TIMESTAMP DEFAULT now() , /*交易时间,默认是几率生成的时间,也就是交易操作完成的时间*/ `old_account` double default null, /*原账户的余额*/ `new_account` double default null, /*交易后账户最新余额*/ `target_account` varchar(50) default null , /*交易对方账户*/ `account_type` varchar(50) default null , /*交易类型*/ PRIMARY KEY (`id`) ); alter table tokenLogin.trasf_record add column transaction_amount double default null after old_account ; /*在原账户余额和交易后账户余额中间插入交易余额*/
创建一张空表即可,后面产生交易的时候将数据插入,实现资金追溯。
3. 常量设置:在转账记录表中,有一个字段account_type , 这个字段无非有两个结果,要么是入账类型,要么是出账类型,所以这里我们进行常量设置。
在constants包下面新建TransactionType类,类下内容如下:
/** * @author yeyuting * @create 2021/2/18 */ public class TransactionType { public static final String SAVEMONEY = "入账" ; public static final String WITHDRAWMONEY = "出账" ; }
这样一来,准备工作就做好了,现在我们依次来实现账户余额查询、转账、入账和出账记录。
二、账户余额查询
1. 首先控制层创建余额查询接口:
@GetMapping("/selectByUserName") public Result selectByUserName(@RequestParam("userName") String username){ return Results.successWithData(userService.selectByUserName(username) , BaseEnums.SUCCESS.code()) ; }
2. service层进行数据传递:
//UserService类: public User selectByUserName(String username) ; //UserServiceImpl类: //查询金额 public User selectByUserName(String username) { return userMapper.selectByUserName(username) ; }
3. mapper层,将参数进行传递并和数据库打交道:
UserMapper.java:
public User selectByUserName(String username) ;
userMapper.xml:
<select id="selectByUserName" resultType="User"> select * from sys_user where username = #{userName} select>
4. 进行登陆操作,访问LoginCheck接口,生成对应的token,将token塞到余额查询接口的头信息中,过拦截器,进而顺利执行接口代码逻辑。
4. postman前端模拟:
这样一来,最简单的账户余额查询就实现了。
三、转账功能实现:
1. controller层创建转账接口:
@PostMapping("/transferAccount") public Result transferAccount (@RequestParam("accountMoney") Double accountMoney , @RequestParam("targetAccount") String targetAccount , HttpServletRequest request){ return Results.successWithData(userService.transferAccount(accountMoney , targetAccount , request) , BaseEnums.SUCCESS.code()) ; }
2. service层实现:
service接口:
//转账 public String transferAccount(Double accountMoney , String targetAccount , HttpServletRequest request) ;
serviceImpl实现:
//转账 public String transferAccount(Double accountMoney , String targetAccount , HttpServletRequest request){ Jedis jedis = jedisUtil.getSource() ; String token = request.getHeader("token") ; String userName = jedis.get(token) ; User user = userMapper.selectByName(userName) ; double nowAccountMoney = user.getAccount() ; if(accountMoney > nowAccountMoney){ return "余额不足" ; } User user1 = userMapper.selectByName(targetAccount) ; if (user1.equals(null)){ return "对方账户不存在" ; } //转出账户余额更新 boolean result = userMapper.updateAccountOut(accountMoney , userName) ; //转入账户余额更新 boolean result1 = userMapper.updateAccountIn(accountMoney , user1.getUserName()) ; if ((result == false)||(result1 == false) ){ return "转账操作失败" ; } //转账记录生成------------ //String accountType = TransactionType.WITHDRAWMONEY ; //出账记录生成 boolean insertReult = userMapper.accountOutInsert(userName ,user.getAccount() , accountMoney , targetAccount , TransactionType.WITHDRAWMONEY ) ; //入账记录生成 //String accountType1 = TransactionType.SAVEMONEY ; boolean insertReult1 = userMapper.accountInInsert(user1.getUserName() , user1.getAccount() , accountMoney , user.getUserName() , TransactionType.SAVEMONEY ) ; if((insertReult == false) || (insertReult1 == false)){ return "转账记录生成失败" ; } return "转账成功!" ; }
service实现中每一行代码都有详细的讲解,总的来说,就是要实现出账方账户余额相应减少,入账方账户余额相应增加,同时生成对应的转账记录。
3. mapper层实现:
//转出更新 public boolean updateAccountOut (Double accountMoney , String userName) ; //转入更新 public boolean updateAccountIn (Double accountMoney , String userName) ; //转出记录插入 public boolean accountOutInsert(String userName ,Double account , Double accountMoney , String targetAccount , String accountType) ; //转入记录插入 public boolean accountInInsert(String userName ,Double account , Double accountMoney , String targetAccount , String accountType) ;
<update id="updateAccountOut"> update sys_user set account = (account - #{accountMoney}) where username = #{userName} update> <update id="updateAccountIn"> update sys_user set account = (account + #{accountMoney}) where username = #{userName} update> <insert id="accountOutInsert"> insert into trasf_record ( username , old_account ,transaction_amount , new_account , target_account , account_type ) values ( #{userName} ,#{account} , #{accountMoney} , (#{account} - #{accountMoney}) , #{targetAccount} , #{accountType}) insert> <insert id="accountInInsert"> insert into trasf_record (username , old_account ,transaction_amount , new_account , target_account , account_type ) values (#{userName} ,#{account} , #{accountMoney} , (#{account} + #{accountMoney}) , #{targetAccount} , #{accountType}) insert>
4. postman前端模拟:
将上面生成的token塞到转账接口头信息中,访问数据库:
数据库信息刷新:
这样一来,转账功能也就实现了,在充分理解了转账功能后,入账出账功能就迎刃而解了。接下来继续实现账户入账和出账功能。
四、入账功能
1. controller层:
//存钱 @PostMapping("/saveMoney") public Result saveMoney (@RequestParam("accountMoney") Double accountMoney , HttpServletRequest request){ return Results.successWithData(userService.saveMoney(accountMoney , request) , BaseEnums.SUCCESS.code()) ; }
2. service层:
service接口:
//存钱 public String saveMoney(Double accountMoney , HttpServletRequest request) ;
serviceImpl实现:
//存钱 public String saveMoney(Double accountMoney , HttpServletRequest request){ Jedis jedis = jedisUtil.getSource() ; String token = request.getHeader("token") ; String userName = jedis.get(token) ; User user = userMapper.selectByName(userName) ; //存入余额更新 boolean result = userMapper.updateAccountIn(accountMoney , userName) ; if(result = false){ return "存入失败" ; } //存入记录生成 boolean insertResult = userMapper.accountInInsert(userName ,user.getAccount() , accountMoney , userName , TransactionType.SAVEMONEY) ; if((insertResult == false)){ return "入账记录生成失败" ; } return "成功存入" + accountMoney + "元!" ; }
3. mapper层和转账公用一套代码。
4. postman前端实现:
和转账一样,要记得将token塞进头信息中。
执行后结果如下:
数据库刷新结果如下:
五、出账功能
1. controller层:
//取钱 @PostMapping("withdrawMoney") public Result withdrawMoney (@RequestParam("accountMoney") Double accountMoney , HttpServletRequest request){ return Results.successWithData(userService.withdrawMoney(accountMoney , request) , BaseEnums.SUCCESS.code()) ; }
2. service层:
service接口:
//取钱 public String withdrawMoney(Double accountMoney , HttpServletRequest request) ;
serviceImpl具体实现:
//取钱 public String withdrawMoney(Double accountMoney , HttpServletRequest request){ Jedis jedis = jedisUtil.getSource() ; String token = request.getHeader("token") ; String userName = jedis.get(token) ; User user = userMapper.selectByName(userName) ; double nowAccountMoney = user.getAccount() ; if(accountMoney > nowAccountMoney){ return "余额不足" ; } boolean result = userMapper.updateAccountOut(accountMoney , userName) ; if(result = false){ return "取钱失败" ; } //出账记录生成 boolean insertResult = userMapper.accountOutInsert(userName ,user.getAccount() , accountMoney , userName , TransactionType.WITHDRAWMONEY) ; if((insertResult == false)){ return "出账记录生成失败" ; } return "成功取出" + accountMoney + "元!" ; }
3. mapper层和转账公用一套代码。
4. postman前端实现:
和转账一样,要记得将token塞进头信息中。
这样一来,出账功能也就实现了。
待解决问题:
1. redis暂未实现redis缓存对象,因此每次访问redis只是为了拿到token和对应的value,并未真正的利用到redis,因此,后期要将对象完整的存入到redis中,便于后期数据的获取。
2. 这里的转账记录表还可以做成索引表,每个用户单列一张表出来,更加的浅显易懂,但是任务量过大,需商榷后再决定是否将整表分开。
至此,结束。