总览:
在api模块service包,Invest类下添加(投资理财产品, int 是投资的结果 , 1 投资成功):
package com.bjpowernode.api.service;
import com.bjpowernode.api.pojo.BidInfoProduct;
import java.math.BigDecimal;
import java.util.List;
public interface InvestService {
/** 查询某个产品的投资记录 */
List<BidInfoProduct> queryBidListByProductId(Integer productId,
Integer pageNo,
Integer pageSize);
/*投资理财产品, int 是投资的结果 , 1 投资成功*/
int investProduct(Integer uid, Integer productId, BigDecimal money);
}
实现这个接口方法,在dataservice模块service包下,补充InvestServiceImpl,(投资理财产品, int 是投资的结果 , 1 投资成功):
1、参数检查(用户ID是否存在,产品ID是否存在,金钱是否存在且购买金额大于100和100整数倍)
2、查询账号金额(accountMapper.selectByUidForUpdate(uid)),比较资金(ge),是否满足购买要求
3、检查产品是否可以购买(产品不能为空且还有卖;比较ge:产品剩余可投资总金额大于要投的钱,要投的钱在最小和最大一个人可以投资金额之间)
4、可以购买了,扣除账号资金(更新资金accountMapper.updateAvailableMoneyByInvest(uid, money))
5、扣除产品剩余可投资金额(productInfoMapper.updateLeftProductMoney(productId, money))
6、创建投资记录(bidInfoMapper.insertSelective(bidInfo))
7、判断产品是否卖完(判断产品剩余可卖金额是否为0),更新产品是满标状态(productInfoMapper.updateSelled(productId))
package com.bjpowernode.dataservice.service;
import com.bjpowernode.api.model.BidInfo;
import com.bjpowernode.api.model.FinanceAccount;
import com.bjpowernode.api.model.ProductInfo;
import com.bjpowernode.api.pojo.BidInfoProduct;
import com.bjpowernode.api.service.InvestService;
import com.bjpowernode.common.constants.YLBConstant;
import com.bjpowernode.common.util.CommonUtil;
import com.bjpowernode.dataservice.mapper.BidInfoMapper;
import com.bjpowernode.dataservice.mapper.FinanceAccountMapper;
import com.bjpowernode.dataservice.mapper.ProductInfoMapper;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@DubboService(interfaceClass = InvestService.class, version = "1.0")
public class InvestServiceImpl implements InvestService {
@Resource
private BidInfoMapper bidInfoMapper;
@Resource
private FinanceAccountMapper accountMapper;
@Resource
private ProductInfoMapper productInfoMapper;
/**
* 查询某个产品的投资记录
*/
@Override
public List<BidInfoProduct> queryBidListByProductId(Integer productId,
Integer pageNo,
Integer pageSize) {
List<BidInfoProduct> bidList = new ArrayList<>();
if (productId != null && productId > 0) {
pageNo = CommonUtil.defaultPageNo(pageNo);
pageSize = CommonUtil.defaultPageSize(pageSize);
int offset = (pageNo - 1) * pageSize;
bidList = bidInfoMapper.selectByProductId(productId, offset, pageSize);
}
return bidList;
}
/*投资理财产品, int 是投资的结果 , 1 投资成功*/
@Transactional(rollbackFor = Exception.class)
@Override
public int investProduct(Integer uid, Integer productId, BigDecimal money) {
int result = 0;//默认,参数不正确
int rows = 0;
//1参数检查
if ((uid != null && uid > 0) && (productId != null && productId > 0)
&& (money != null && money.intValue() % 100 == 0 && money.intValue() >= 100)) {
//2.查询账号金额
FinanceAccount account = accountMapper.selectByUidForUpdate(uid);
if (account != null) {
if (CommonUtil.ge(account.getAvailableMoney(), money)) {
//资金满足购买要求
//3.检查产品是否可以购买
ProductInfo productInfo = productInfoMapper.selectByPrimaryKey(productId);
if (productInfo != null
&& productInfo.getProductStatus() == YLBConstant.PRODUCT_STATUS_SELLING) {
if (CommonUtil.ge(productInfo.getLeftProductMoney(), money) &&
CommonUtil.ge(money, productInfo.getBidMinLimit()) &&
CommonUtil.ge(productInfo.getBidMaxLimit(), money)) {
//可以购买了 4. 扣除账号资金
rows = accountMapper.updateAvailableMoneyByInvest(uid, money);
if (rows < 1) {
throw new RuntimeException("投资更新账号资金失败");
}
//5.扣除产品剩余可投资金额
rows = productInfoMapper.updateLeftProductMoney(productId, money);
if (rows < 1) {
throw new RuntimeException("投资更新产品剩余金额失败");
}
//6.创建投资记录
BidInfo bidInfo = new BidInfo();
bidInfo.setBidMoney(money);
bidInfo.setBidStatus(YLBConstant.INVEST_STATUS_SUCC);
bidInfo.setBidTime(new Date());
bidInfo.setProdId(productId);
bidInfo.setUid(uid);
bidInfoMapper.insertSelective(bidInfo);
//7.判断产品是否卖完,更新产品是满标状态
ProductInfo dbProductInfo = productInfoMapper.selectByPrimaryKey(productId);
if( dbProductInfo.getLeftProductMoney().compareTo(new BigDecimal("0")) == 0 ){
rows = productInfoMapper.updateSelled(productId);
if( rows < 1 ){
throw new RuntimeException("投资更新产品满标失败");
}
}
//8.最后这是投资成功
result = 1;
}
} else {
result = 4;//理财产品不存在
}
} else {
result = 3;//资金不足
}
} else {
result = 2;//资金账号不存在
}
}
return result;
}
}
其中:
1、给uid的记录上锁(需要在dataservice模块mapper包下的FinanceAccountMapper接口添加方法,并在resources/mappers/FinanceAccountMapper.xml编写SQL语句):
/*给uid的记录上锁*/
FinanceAccount selectByUidForUpdate(@Param("uid") Integer uid);
<!--/*给uid的记录上锁*/-->
<select id="selectByUidForUpdate" resultMap="BaseResultMap">
select <include refid="Base_Column_List" />
from u_finance_account
where uid = #{uid}
for update
</select>
2、在common模块util包下,CommonUtil类添加一个方法比较金额(比较BigDecimal:ge),是否可以购买:
package com.bjpowernode.common.util;
import java.math.BigDecimal;
import java.util.regex.Pattern;
public class CommonUtil {
/*处理pageNo*/
public static int defaultPageNo(Integer pageNo) {
int pNo = pageNo;
if (pageNo == null || pageNo < 1) {
pNo = 1;
}
return pNo;
}
/*处理pageSize*/
public static int defaultPageSize(Integer pageSize) {
int pSize = pageSize;
if (pageSize == null || pageSize < 1) {
pSize = 1;
}
return pSize;
}
/*手机号脱敏*/
public static String tuoMinPhone(String phone) {
String result = "***********";
if (phone != null && phone.trim().length() == 11) {
result = phone.substring(0,3) + "******" + phone.substring(9,11);
}
return result;
}
/*手机号格式 true:格式正确;false不正确*/
public static boolean checkPhone(String phone){
boolean flag = false;
if( phone != null && phone.length() == 11 ){
//^1[1-9]\\d{9}$
flag = Pattern.matches("^1[1-9]\\d{9}$",phone);
}
return flag;
}
/*比较BigDecimal n1 >=n2 :true ,false*/
public static boolean ge(BigDecimal n1, BigDecimal n2){
if( n1 == null || n2 == null){
throw new RuntimeException("参数BigDecimal是null");
}
return n1.compareTo(n2) >= 0;
}
}
3、更新资金(需要在dataservice模块mapper包下的FinanceAccountMapper接口添加方法,并在resources/mappers/FinanceAccountMapper.xml编写SQL语句):
/*更新资金*/
int updateAvailableMoneyByInvest(@Param("uid") Integer uid, @Param("money") BigDecimal money);
<!--更新资金-->
<update id="updateAvailableMoneyByInvest">
update u_finance_account set available_money = available_money - #{money}
where uid = #{uid} and ( available_money - #{money} >=0 )
</update>
4、扣除产品剩余可投资金额(需要在dataservice模块mapper包下的ProductInfoMapper接口添加方法,并在resources/mappers/ProductInfoMapper.xml编写SQL语句):
/*扣除产品剩余可投资金额*/
int updateLeftProductMoney(@Param("id") Integer productId, @Param("money") BigDecimal money);
<!--扣除产品剩余可投资金额-->
<update id="updateLeftProductMoney">
update b_product_info set left_product_money = left_product_money - #{money}
where id = #{id} and ( left_product_money - #{money} >=0 )
</update>
5、更新产品为满标(需要在dataservice模块mapper包下的ProductInfoMapper接口添加方法,并在resources/mappers/ProductInfoMapper.xml编写SQL语句):
/*更新产品为满标*/
int updateSelled(@Param("id") Integer productId);
<!--*更新产品为满标-->
<update id="updateSelled">
update b_product_info set product_status = 1 , product_full_time = now()
where id = #{id}
</update>
在web模块controller包,InvestController类下添加(购买理财产品, 更新投资排行榜):
1、检查基本参数(用户ID是否存在,产品ID是否存在,金钱是否存在且购买金额大于100和100整数倍)
2、根据investResult结果判断
3、更新投资排行榜(modifyInvestRank(uid,money))
package com.bjpowernode.front.controller;
import com.bjpowernode.api.model.User;
import com.bjpowernode.common.constants.RedisKey;
import com.bjpowernode.common.util.CommonUtil;
import com.bjpowernode.front.view.RespResult;
import com.bjpowernode.front.view.invest.RankView;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* 有关投资功能
*/
@Api(tags = "投资理财产品")
@RestController
public class InvestController extends BaseController {
/*投资排行榜*/
@ApiOperation(value = "投资排行榜", notes = "显式投资金额最高的3位用户信息")
@GetMapping("/v1/invest/rank")
public RespResult showInvestRank() {
//从redis查询数据
Set<ZSetOperations.TypedTuple<String>> sets = stringRedisTemplate
.boundZSetOps(RedisKey.KEY_INVEST_RANK).reverseRangeWithScores(0, 2);
List<RankView> rankList = new ArrayList<>();
//遍历set集合
sets.forEach(tuple -> {
//tuple.getValue();//手机号
//tuple.getScore();//投资金额
rankList.add(new RankView(CommonUtil.tuoMinPhone(tuple.getValue()), tuple.getScore()));
});
RespResult result = RespResult.ok();
result.setList(rankList);
return result;
}
/*购买理财产品, 更新投资排行榜*/
@ApiOperation(value = "投资理财产品")
@PostMapping("/v1/invest/product")
public RespResult investProduct(
@RequestHeader("uid") Integer uid,
@RequestParam("productId") Integer productId,
@RequestParam("money") BigDecimal money){
RespResult result = RespResult.fail();
//1.检查基本参数
if( (uid != null && uid > 0) && (productId != null && productId > 0)
&&( money != null && money.intValue() % 100 == 0 && money.intValue() >= 100)){
int investResult = investService.investProduct(uid,productId,money);
switch (investResult){
case 0:
result.setMsg("投资数据不正确");
break;
case 1:
result = RespResult.ok();
modifyInvestRank(uid,money);
break;
case 2:
result.setMsg("资金账号不存在");
break;
case 3:
result.setMsg("资金不足");
break;
case 4:
result.setMsg("理财产品不存在");
break;
}
}
return result;
}
/*更新投资排行榜*/
private void modifyInvestRank(Integer uid,BigDecimal money){
User user = userService.queryById(uid);
if(user != null){
//更新redis中的投资排行榜
String key = RedisKey.KEY_INVEST_RANK;
stringRedisTemplate.boundZSetOps(key).incrementScore(
user.getPhone(),money.doubleValue());
}
}
}
其中:
1、在web模块controller包,InvestController内添加一个方法,更新redis中的投资排行榜(更新投资排行榜modifyInvestRank):
2、在api模块service包,UserService添加查询用户方法(userService.queryById(uid))
package com.bjpowernode.api.service;
import com.bjpowernode.api.model.User;
import com.bjpowernode.api.pojo.UserAccountInfo;
public interface UserService {
/**
* 根据手机号查询数据
*/
User queryByPhone(String phone);
/*用户注册*/
int userRegister(String phone, String password);
/*登录*/
User userLogin(String phone, String pword);
/*更新实名认证信息*/
boolean modifyRealname(String phone, String name, String idCard);
/*获取用户和资金信息*/
UserAccountInfo queryUserAllInfo(Integer uid);
/*查询用户*/
User queryById(Integer uid);
}
3、在dataservice模块service包,UserService实现这个接口方法
package com.bjpowernode.dataservice.service;
import com.bjpowernode.api.model.FinanceAccount;
import com.bjpowernode.api.model.User;
import com.bjpowernode.api.pojo.UserAccountInfo;
import com.bjpowernode.api.service.UserService;
import com.bjpowernode.common.util.CommonUtil;
import com.bjpowernode.dataservice.mapper.FinanceAccountMapper;
import com.bjpowernode.dataservice.mapper.UserMapper;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
@DubboService(interfaceClass = UserService.class,version = "1.0")
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
@Resource
private FinanceAccountMapper financeAccountMapper;
@Value("${ylb.config.password-salt}")
private String passwordSalt;
@Override
public User queryByPhone(String phone) {
User user = null;
if(CommonUtil.checkPhone(phone)){
user = userMapper.selectByPhone(phone);
}
return user;
}
/*用户注册*/
@Transactional(rollbackFor = Exception.class)
@Override
public synchronized int userRegister(String phone, String password) {
int result = 0;//默认参数不正确
if( CommonUtil.checkPhone(phone)
&& (password != null && password.length()==32)){
//判断手机号在库中是否存在
User queryUser = userMapper.selectByPhone(phone);
if(queryUser == null){
//注册密码的md5二次加密。 给原始的密码加盐(salt)
String newPassword = DigestUtils.md5Hex( password + passwordSalt);
//注册u_user
User user = new User();
user.setPhone(phone);
user.setLoginPassword(newPassword);
user.setAddTime(new Date());
userMapper.insertReturnPrimaryKey(user);
//获取主键user.getId()
FinanceAccount account = new FinanceAccount();
account.setUid(user.getId());
account.setAvailableMoney(new BigDecimal("0"));
financeAccountMapper.insertSelective(account);
//成功result = 1
result = 1;
} else {
//手机号存在
result = 2;
}
}
return result;
}
/*登录*/
@Transactional(rollbackFor = Exception.class)
@Override
public User userLogin(String phone, String password) {
User user = null;
if( CommonUtil.checkPhone(phone) && (password != null && password.length() == 32)) {
String newPassword = DigestUtils.md5Hex( password + passwordSalt);
user = userMapper.selectLogin(phone,newPassword);
//更新最后登录时间
if( user != null){
user.setLastLoginTime(new Date());
userMapper.updateByPrimaryKeySelective(user);
}
}
return user;
}
/*更新实名认证信息*/
@Override
public boolean modifyRealname(String phone, String name, String idCard) {
int rows = 0;
if(!StringUtils.isAnyBlank(phone,name,idCard)){
rows = userMapper.updateRealname(phone,name,idCard);
}
return rows > 0 ;
}
/*获取用户和资金信息*/
@Override
public UserAccountInfo queryUserAllInfo(Integer uid) {
UserAccountInfo info = null;
if( uid != null && uid > 0 ) {
info = userMapper.selectUserAccountById(uid);
}
return info ;
}
/*查询用户*/
@Override
public User queryById(Integer uid) {
User user = null;
if( uid != null && uid > 0 ){
user = userMapper.selectByPrimaryKey(uid);
}
return user;
}
}