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;
        }
    }

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

  • 相关阅读:
    101. Symmetric Tree(js)
    100. Same Tree(js)
    99. Recover Binary Search Tree(js)
    98. Validate Binary Search Tree(js)
    97. Interleaving String(js)
    96. Unique Binary Search Trees(js)
    95. Unique Binary Search Trees II(js)
    94. Binary Tree Inorder Traversal(js)
    93. Restore IP Addresses(js)
    92. Reverse Linked List II(js)
  • 原文地址:https://www.cnblogs.com/javashop-docs/p/12834415.html
Copyright © 2011-2022 走看看