zoukankan      html  css  js  c++  java
  • b2b2c商城系统-会员预存款架构及源码分享

    业务需求

    可以为预存款充值,在支付订单时使用预存款支付功能

    当预存款余额>商品订单总金额时,完全抵扣商品订单金额;

    当预存款余额<商品订单总金额时,抵扣金额为预存款余额,剩余待支付金额,可选择其他支付方式支付(如支付宝,微信)。

    架构

    充值

    一、充值时序图

    二、数据结构

    1、会员钱包表(es_member_wallet)

    2、后期可能会将会员积分等关于消费抵扣相关信息放入此表中

    字段名

    字段类型

    备注

    是否索引

    id

    int(8)

    主键

    member_id

    int(8)

    会员id

    memner_name

    varchar(200)

    会员名称

    pre_deposite

    decimal(20,2)

    会员预存款,默认为0

    deposite_password

    varchar(50)

    预存款密码,默认为-1

    3、充值记录表(es_deposite_recharge)

    字段名

    字段类型

    备注

    是否索引

    id

    int(10)

    主键

    recharge_sn

    varchar(50)

    充值订单编号

    member_id

    int(10)

    会员id

    member_name

    varchar(100)

    会员名称

    recharge_money

    decimal(20,2)

    充值金额

    recharge_time

    bigint(20)

    充值时间戳

    pay_time

    bigint(20)

    支付时间

    recharge_way

    varchar(20)

    充值方式,如:支付宝,微信

    payment_plugin_id

    varchar(20)

    支付插件ID

    pay_status

    varchar(20)

    支付状态

    4、预存款日志表(es_deposite_log)

    字段名

    字段类型

    备注

    是否索引

    id

    int(10)

    主键

    member_id

    int(10)

    会员id

    member_name

    varchar(100)

    会员名称

    money

    decimal(20,2)

    消费金额

    time

    bigint(20)

    消费时间

    detail

    varchar(200)

    消费明细

    三、充值模型图


    四、源码

    说明:此处仅展示预存款充值相关代码,其他关联业务不做具体展示

    预存款充值相关API

    package com.enation.app.javashop.buyer.api.payment;
    
    import com.enation.app.javashop.core.payment.model.dto.PayParam;
    import com.enation.app.javashop.core.payment.service.OrderPayManager;
    import com.enation.app.javashop.core.trade.deposite.model.dos.RechargeDO;
    import com.enation.app.javashop.core.trade.deposite.service.RechargeManager;
    import com.enation.app.javashop.core.trade.order.model.enums.TradeTypeEnum;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiImplicitParam;
    import io.swagger.annotations.ApiImplicitParams;
    import io.swagger.annotations.ApiOperation;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.validation.annotation.Validated;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.validation.constraints.Max;
    import javax.validation.constraints.Min;
    import java.util.Map;
    
    /**
     * @description: 预存款充值
     * @author: liuyulei
     * @create: 2019-12-30 19:53
     * @version:1.0
     * @since:7.1.4
     **/
    @Api(description = "预存款充值相关API")
    @RestController
    @RequestMapping("/recharge")
    @Validated
    public class RechargePayBuyerController {
    
        @Autowired
        private RechargeManager rechargeManager;
    
        @Autowired
        private OrderPayManager orderPayManager;
    
    
    
        @PostMapping
        @ApiOperation(value    = "创建充值订单")
        @ApiImplicitParams({
                @ApiImplicitParam(name = "price", value = "充值金额", required = true, dataType = "double", paramType = "query")
        })
        public RechargeDO create(@Max(value = 10000,message = "充值金额输入有误,单次最多允许充值10000元") @Min(value = 1, message = "充值金额有误,单次最少充值金额为1元") Double price) {
            return this.rechargeManager.recharge(price);
        }
    
    
        @PostMapping(value = "/{sn}")
        @ApiOperation(value    = "支付充值订单")
        @ApiImplicitParams({
                @ApiImplicitParam(name = "sn", value = "充值订单编号", required = true, dataType = "String", paramType = "path")
        })
        public Map pay(@PathVariable(name = "sn") String sn,  @Validated PayParam payParam)    {
            payParam.setSn(sn);
            payParam.setTradeType(TradeTypeEnum.RECHARGE.name());
            return orderPayManager.pay(payParam);
        }
    
    
    }

    充值相关业务

    package com.enation.app.javashop.core.trade.deposite.service.impl;
    
    import com.enation.app.javashop.core.client.member.DepositeClient;
    import com.enation.app.javashop.core.member.model.dto.DepositeParamDTO;
    import com.enation.app.javashop.core.payment.model.dos.PaymentBillDO;
    import com.enation.app.javashop.core.payment.service.PaymentBillManager;
    import com.enation.app.javashop.core.statistics.util.DateUtil;
    import com.enation.app.javashop.core.trade.TradeErrorCode;
    import com.enation.app.javashop.core.trade.deposite.model.dos.RechargeDO;
    import com.enation.app.javashop.core.trade.deposite.service.RechargeManager;
    import com.enation.app.javashop.core.trade.order.model.enums.PayStatusEnum;
    import com.enation.app.javashop.core.trade.order.model.enums.TradeTypeEnum;
    import com.enation.app.javashop.framework.context.UserContext;
    import com.enation.app.javashop.framework.exception.ServiceException;
    import com.enation.app.javashop.framework.security.model.Buyer;
    import com.enation.app.javashop.framework.util.CurrencyUtil;
    import com.enation.app.javashop.framework.util.SqlSplicingUtil;
    import com.enation.app.javashop.framework.util.StringUtil;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.enation.app.javashop.framework.database.DaoSupport;
    import com.enation.app.javashop.framework.database.Page;
    
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    
    
    /**
     * 充值记录业务类
     * @author liuyulei
     * @version v1.0
     * @since v7.1.5
     * 2019-12-30 16:38:45
     */
    @Service
    public class RechargeManagerImpl implements RechargeManager {
    
       @Autowired
       @Qualifier("tradeDaoSupport")
       private    DaoSupport daoSupport;
    
       @Autowired
       private PaymentBillManager paymentBillManager;
    
       @Autowired
       private DepositeClient depositeClient;
    
    
    
    
       @Override
       @Transactional(value = "tradeTransactionManager",propagation = Propagation.REQUIRED,rollbackFor=Exception.class)
       public RechargeDO recharge(Double money) {
          Buyer buyer =  UserContext.getBuyer();
    
          //本金额支付次数
          int times = 0;
          //账单编号格式: 年月日+ memberID掩码 + 价格掩码 +支付次数
          //掩码均为5位数,不足前面补零
          String snPrefix = createSn(buyer.getUid(), "D", money);
          String sn = snPrefix + times;
    
          RechargeDO rechargeDO = this.getModel(sn);
    
          if(rechargeDO == null ){
             //整合充值订单数据
             rechargeDO = new RechargeDO(sn,buyer.getUid(),buyer.getUsername(),money);
             //添加充值订单
             createBill(rechargeDO);
          }else{
             //如果是已付款
             if(!PayStatusEnum.PAY_NO.name().equals(rechargeDO.getPayStatus())){
                // 获取到已支付次数
                times = Integer.parseInt(sn.substring(19, sn.length()));
                //循环生成sn
                while (true) {
                   times++;
                   sn = snPrefix + times;
                   RechargeDO rechargeTemp = this.getModel(sn);
    
                   // 找到一个没有使用过的 就可以break了
                   if (rechargeTemp == null) {
                      break;
                   }
                }
                //充值订单已经被支付,则表示当天再次支付
                rechargeDO.setRechargeSn(sn);
                rechargeDO.setPayStatus(PayStatusEnum.PAY_NO.name());
                createBill(rechargeDO);
             }
             //如果没有被支付则不用创建充值订单,再次为此订单支付即可
          }
          return rechargeDO;
    
       }
    
    
    
       @Override
       @Transactional(value = "tradeTransactionManager",propagation = Propagation.REQUIRED,rollbackFor=Exception.class)
       public void paySuccess(String sn, Double price) {
          RechargeDO rechargeDO = this.getModel(sn);
    
          if(!rechargeDO.getRechargeMoney().equals(price)){
             throw new ServiceException(TradeErrorCode.E454.code(), "付款金额和应付金额不一致");
          }
    
          this.daoSupport.execute("update es_deposite_recharge set pay_status = ? where recharge_sn = ? ",PayStatusEnum.PAY_YES.name(),sn);
          //增加会员预存款 余额
          depositeClient.increase(price,rechargeDO.getMemberId(),"会员充值,充值单号:" + rechargeDO.getRechargeSn());
       }
    
       @Override
       @Transactional(value = "tradeTransactionManager",propagation = Propagation.REQUIRED,rollbackFor=Exception.class)
       public void updatePaymentMethod(String subSn, String pluginId, String methodName) {
          String sql = "update es_deposite_recharge set payment_plugin_id = ?,recharge_way = ?, pay_time = ?  where recharge_sn = ? ";
          this.daoSupport.execute(sql,pluginId,methodName, DateUtil.getDateline(),subSn);
       }
    
    
       @Override
       public Page list(DepositeParamDTO paramDTO){
    
          StringBuffer sql = new StringBuffer("select * from es_deposite_recharge ");
          List<String> whereSql = new ArrayList<>();
          List<Object> term = new ArrayList<>();
    
          //根据会员名称查询
          if(!StringUtil.isEmpty(paramDTO.getMemberName())){
             whereSql.add(" member_name = ? ");
             term.add(paramDTO.getMemberName());
          }
    
          //根据会员id查询
          if(paramDTO.getMemberId() != null ){
             whereSql.add(" member_id = ? ");
             term.add(paramDTO.getMemberId());
          }
    
          //根据充值编号查询
          if(!StringUtil.isEmpty(paramDTO.getSn()) ){
             whereSql.add(" recharge_sn = ? ");
             term.add(paramDTO.getSn());
          }
    
          //根据充值编号查询
          if(!StringUtil.isEmpty(paramDTO.getSn()) ){
             whereSql.add(" recharge_sn = ? ");
             term.add(paramDTO.getSn());
          }
    
          //根据充值时间查询
          if(paramDTO.getStartTime() != null){
             whereSql.add(" recharge_time >= ? ");
             term.add(paramDTO.getStartTime());
          }
    
          //根据充值时间查询
          if(paramDTO.getStartTime() != null){
             whereSql.add(" recharge_time <= ? ");
             term.add(paramDTO.getEndTime());
          }
          whereSql.add(" pay_status = ? ");
          term.add(PayStatusEnum.PAY_YES.name());
    
          //拼接sql
          sql.append(SqlSplicingUtil.sqlSplicing(whereSql));
    
    
          sql.append("order by recharge_time desc");
          Page  webPage = this.daoSupport.queryForPage(sql.toString(),paramDTO.getPageNo(), paramDTO.getPageSize() , RechargeDO.class,term.toArray() );
          
          return webPage;
       }
    
       @Override
       public Double getPrice(String sn) {
          return this.daoSupport.queryForDouble("select recharge_money from es_deposite_recharge where recharge_sn = ? ",sn);
       }
       
    
    
       @Override
       public RechargeDO getModel(String sn)  {
          return this.daoSupport.queryForObject("select * from es_deposite_recharge where recharge_sn = ?  ",RechargeDO.class,sn);
       }
    
       private String mask(String str) {
          String mask = "000000";
          mask = mask + str;
          mask = mask.substring(mask.length() - 5);
          return mask;
       }
    
       private String createSn(Integer memberId,String prefix,Double price) {
          String memberMask = mask("" + memberId);
          String priceMask = mask("" + CurrencyUtil.mul(price,100).intValue());
          String snPrefix = prefix + DateUtil.toString(new Date(), "yyyyMMdd") + memberMask + priceMask;
          return snPrefix;
       }
    
       private void createBill(RechargeDO rechargeDO) {
          daoSupport.insert(rechargeDO);
          //创建充值 支付 账单数据
          PaymentBillDO paymentBillDO = new PaymentBillDO();
          paymentBillDO.setSubSn(rechargeDO.getRechargeSn());
          paymentBillDO.setTradePrice(rechargeDO.getRechargeMoney());
          paymentBillDO.setServiceType(TradeTypeEnum.RECHARGE.name());
          paymentBillManager.add(paymentBillDO);
       }
    }

    消费

    一、消费时序图

    二、消费数据结构

    1、交易表(es_trade)

    新增预存款抵扣金额字段,记录订单使用的预存款金额

    字段名

    字段类型

    备注

    是否索引

    deposite_money

    decimal(20,2)

    会员预存款

    2、订单表(es_order)

    新增预存款抵扣金额字段,记录订单使用的预存款金额

    字段名

    字段类型

    备注

    是否索引

    deposite_money

    decimal(20,2)

    会员预存款

    3、源码

    说明:此处仅展示预存款消费相关代码,其他关联业务不做具体展示

    预存款支付相关API

    @ApiOperation(value = "使用预存款支付")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "sn", value = "要支付的交易sn", required = true, dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "trade_type", value = "交易类型", required = true, dataType = "String", paramType = "query", allowableValues = "TRADE,ORDER"),
            @ApiImplicitParam(name = "password", value = "支付密码", required = true, dataType = "String", paramType = "query")
    })
    @GetMapping(value = "/{trade_type}/{sn}")
    public BalancePayVO payTrade(@PathVariable(name = "sn") String sn, @PathVariable(name = "trade_type") String tradeType,
                                 @NotEmpty(message = "密码不能为空") String password) {
        Buyer buyer = UserContext.getBuyer();
        return balanceManager.balancePay(sn,buyer.getUid(),tradeType.toUpperCase(),password);
    }
    
    @GetMapping(value = "/cashier")
    @ApiOperation(value    = "获取预存款相关,收银台使用")
    public MemberDepositeVO getDepositeVO()    {
       Buyer buyer = UserContext.getBuyer();
       return depositeManager.getDepositeVO(buyer.getUid());
    }

    预存款支付相关业务

    package com.enation.app.javashop.core.trade.order.service.impl;
    
    import com.enation.app.javashop.core.client.member.DepositeClient;
    import com.enation.app.javashop.core.member.model.dos.MemberWalletDO;
    import com.enation.app.javashop.core.payment.service.PaymentServicePlugin;
    import com.enation.app.javashop.core.trade.order.model.vo.BalancePayVO;
    import com.enation.app.javashop.core.trade.order.service.BalanceManager;
    import com.enation.app.javashop.core.trade.order.service.TradeQueryManager;
    import com.enation.app.javashop.framework.util.CurrencyUtil;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    
    /**
     * @description: 预存款抵扣业务类
     * @author: liuyulei
     * @create: 2020-01-01 11:55
     * @version:1.0
     * @since:7.1.4
     **/
    @Service
    public class BalanceManagerImpl implements BalanceManager {
    
    
        @Autowired
        private TradeQueryManager tradeQueryManager;
    
        @Autowired
        private DepositeClient depositeClient;
    
        @Autowired
        private List<PaymentServicePlugin> paymentServicePlugin;
    
    
        @Override
        public BalancePayVO balancePay(String sn,Integer memberId, String tradeType, String password) {
    
            //检测订单、交易是否属于当前登录会员
            this.tradeQueryManager.checkIsOwner(sn,memberId);
    
            //检测支付密码是否正确
            this.depositeClient.checkPwd(memberId,password);
    
            PaymentServicePlugin plugin = this.findServicePlugin(tradeType);
    
            //获取订单待支付金额
            Double needPay = plugin.getPrice(sn);
    
            //获取会员预存款信息
            MemberWalletDO walletDO = this.depositeClient.getModel(memberId);
    
            //判断预存款余额与订单待支付金额
            Double diffPrice = CurrencyUtil.sub(needPay,walletDO.getPreDeposite());
            Double balance = 0D;
            if(diffPrice >= 0 ){
                //此时预存款不足,无法完全抵扣所有订单支付基恩
                balance = walletDO.getPreDeposite();
                needPay = diffPrice;
    
            }else{
                //此时订单支付金额为0
                balance = needPay;
                needPay = 0D;
    
            }
    
            BalancePayVO payVO = new BalancePayVO();
            payVO.setSn(sn);
            payVO.setBalance(balance);
            payVO.setNeedPay(needPay);
    
            //预存款支付,修改订单待支付金额
            plugin.balancePay(payVO,memberId);
    
            return payVO;
    
        }
    
    
    
        /**
         * 在支付子业务插件中  找到对应业务插件
         * @param tradeType
         * @return
         */
        private PaymentServicePlugin findServicePlugin(String tradeType) {
            for (PaymentServicePlugin plugin:paymentServicePlugin){
                if (tradeType.equals(plugin.getServiceType())) {
                    return plugin;
                }
            }
            return null;
        }
    }

    更多源码分享,请关注“易族智汇”公众号查看更多文章!!

  • 相关阅读:
    vue : 无法加载文件 C:UsersXXXAppDataRoaming pmvue.ps1,因为在此系统上禁止运行脚本
    js全屏和退出全屏浏览器
    js 如何保存代码段并执行以及动态加载script
    计算年龄,精确到年月日
    js闭包问题
    构造函数和继承方法
    js 箭头函数不适用的场景
    获取一组数据的最大值和最小值
    地图
    json传输
  • 原文地址:https://www.cnblogs.com/javashop-docs/p/12834415.html
Copyright © 2011-2022 走看看