SpringBoot,SpringCloudAlibaba,Vue3大型电商项目实战分布式高并发高可用微服务


SpringBoot,SpringCloudAlibaba,Vue3大型电商项目实战分布式高并发高可用微服务

1 基于策略+模板方法实现异步回调重构
  • 2 异步回调通知实现的原理
  • 3 支付宝官方demo异步回调代码的实现
  • 4 聚合支付项目如何采用模板+策略重构
  • 5 基于模板模式重构聚合支付平台
  • 6 异步回调重构验证签名代码
  • 7 基于策略模式id找到模板类
  • 8 异步回调更改订单状态的信息
  • 9 支付异步回调代码继续重构
  • 1 基于策略+模板方法实现异步回调重构

    今日课程任务

    1. 第三方支付异步回调实现的原理
    2. 基于模板+策略模式实现异步回调
    3. 基于ThreadLocal传递参数信息内容
    4. 采用多线程/MQ异步写入支付的日志信息参数
    5. 如何防止支付金额不一致的问题
    6. 如何防止支付回调幂等性问题
    7. 支付服务存在那些分布式事务难题

    2 异步回调通知实现的原理

    同步回调:以浏览器重定向方式跳转
    异步回调:第三方支付以post请求格式通知给商户端 修改订单状态

    支付宝官网文档:https://opendocs.alipay.com/open/270/105899
    在这里插入图片描述
    调用流程如下:

    1. 商户系统调用 alipay.trade.page.pay(统一收单下单并支付页面接口)向支付宝发起支付请求,支付宝对商户请求参数进行校验,而后重新定向至用户登录页面。
    2. 用户确认支付后,支付宝通过 get 请求 returnUrl(商户入参传入),返回同步返回参数。
    3. 交易成功后,支付宝通过 post 请求 notifyUrl(商户入参传入),返回异步通知参数。
    4. 若由于网络等原因,导致商户系统没有收到异步通知,商户可自行调用alipay.trade.query(统一收单线下交易查询)接口查询交易以及支付信息(商户也可以直接调用该查询接口,不需要依赖异步通知)。

    先触发异步回调,再触发同步回调。

    注意:

    1. 由于同步返回的不可靠性,支付结果必须以异步通知或查询接口返回为准,不能依赖同步跳转。
    2. 商户系统接收到异步通知以后,必须通过验签(验证通知中的sign参数)来确保支付通知是由支付宝发送的。详细验签规则请参见 异步通知验签。
    3. 接收到异步通知并验签通过后,请务必核对通知中的app_id、out_trade_no、total_amount等参数值是否与请求中的一致,并根据trade_status进行后续业务处理。
    4. 在支付宝端,partnerId与out_trade_no唯一对应一笔单据,商户端保证不同次支付out_trade_no不可重复;若重复,支付宝会关联到原单据,基本信息一致的情况下会以原单据为准进行支付。

    3 支付宝官方demo异步回调代码的实现

    异步回调中的商户订单号码与支付宝交易号码有哪些区别?
    商户订单号码—支付的id
    支付宝交易号码—支付宝流水号码 补偿

    如果商户端没有及时返回success给支付宝的情况下,支付宝可能会触发重试策略,重试过程中可能会发生幂等性问题。
    注意:同步回调以浏览器重定向形式返回,商户端只是解析报文验证签名成功,将支付结果告诉给用户,但是不能修改订单状态。

    4 聚合支付项目如何采用模板+策略重构

    如何对聚合支付回调进行设计重构?
    策略+模板方法组合
    策略模式:解决多重if判断的问题
    模板方法模式:定义共同骨架(代码)统一都放入在父类,不同的代码让子类实现。

    异步回调代码
    1 记录日志
    2 验证签名(不同)
    3 修改订单状态为已支付
    4 调用积分接口增加积分

    5 基于模板模式重构聚合支付平台

    模板方法骨架类

    @Slf4j
    public abstract class AbstractPayCallbackTemplate {
    
        @Autowired
        private PayThreadInfoHolder payThreadInfoHolder;
    
        public String asyncCallBack() {
            // 1. 验证签名
            boolean isSign = verifySignature();
            // 2. 记录日志 注意问题:一定要异步处理(为了快速响应给支付宝避免重试造成的幂等性问题)
            addPayLog();
            if (!isSign) {
                // 银联支付的时候 必须返回ok;支付宝必须返回success
                return resultFail();
            }
            // 更改订单状态
            return asyncService();
        }
    
        /**
         * 3.更改订单状态已经支付
         * 4.调用积分服务接口增加积分
         * @return
         */
        protected abstract String asyncService();
    
        /**
         * 交给子类实现
         *
         * @return
         */
        protected abstract String resultFail();
    
    
        /**
         * 返回成功
         *
         * @return
         */
        protected abstract String resultSuccess();
    
        /**
         * 定义成抽象方法让子类实现
         * 不管验证签名成功还是失败都会将参数放入到ThreadLocal中
         * @return
         */
        protected abstract boolean verifySignature();
    
        /**
         * 将该方法单独放入在一个类中调用,@Async不经过代理类不会生效
         */
        public void addPayLog() {
            // 从ThreadLocal中获取
            Map<String, String> requestParams = payThreadInfoHolder.getRequestParams();
            log.info(">>>记录支付的日志requestParams:<<<" + requestParams);
        }
    
    }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    使用ThreadLocal存放参数

    @Component
    public class PayThreadInfoHolder {
        private ThreadLocal<Map<String, String>> payThreadLocal = new ThreadLocal();
    
        public void setRequestParams(Map<String, String> requestParams) {
            payThreadLocal.set(requestParams);
        }
    
        public Map<String, String> getRequestParams() {
            return payThreadLocal.get();
        }
    }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    6 异步回调重构验证签名代码

    验证签名

    @Component
    @Slf4j
    public class AliPayCallbackTemplate extends AbstractPayCallbackTemplate {
        @Autowired
        private PayThreadInfoHolder payThreadInfoHolder;
    
        @Override
        protected String resultFail() {
            return "fail";
        }
    
        @Override
        protected String resultSuccess() {
            return "success";
        }
    
        @Override
        protected boolean verifySignature(PaymentChannelEntity pce) {
            // 根据当前线程获取Request
            HttpServletRequest request = ServletUtils.getCurrentRequest();
            HttpServletResponse response = ServletUtils.getCurrentResponse();
            //获取支付宝POST过来反馈信息
            Map<String, String> params = new HashMap<String, String>();
            Map<String, String[]> requestParams = request.getParameterMap();
            ServletOutputStream out = null;
            try {
                out = response.getOutputStream();
                for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
                    String name = (String) iter.next();
                    String[] values = (String[]) requestParams.get(name);
                    String valueStr = "";
                    for (int i = 0; i < values.length; i++) {
                        valueStr = (i == values.length - 1) ? valueStr + values[i]
                                : valueStr + values[i] + ",";
                    }
                    //乱码解决,这段代码在出现乱码时使用
                    valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
                    params.put(name, valueStr);
                    // 防止验证签名失败
                    params.remove("channelId");
                }
                boolean signVerified = AlipaySignature.rsaCheckV1(
                        params, pce.getPublicKey(), AlipayConfig.CHARSET, AlipayConfig.SIGN_TYPE); //调用SDK验证签名
                if (signVerified) {
                    //验证成功
                    //商户订单号
                    String outTradeNo = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
                    //支付宝交易号
                    String tradeNo = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");
                    //交易状态
                    String tradeStatus = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8");
                    if (tradeStatus.equals("TRADE_FINISHED")) {
    
                    } else if (tradeStatus.equals("TRADE_SUCCESS")) {
    
                    }
                    out.println("success");
                    return true;
                } else {
                    //验证失败
                    return false;
                }
            } catch (Exception e) {
                log.error("verifySignature()>> errorMsg:" + e.getMessage());
                return false;
            } finally {
                try {
                    if (out != null) {
                        out.close();
                    }
                } catch (Exception e) {
    
                }
                payThreadInfoHolder.setRequestParams(params);
            }
        }
    
        @Override
        protected String asyncService() {
            return null;
        }
    }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82

    7 基于策略模式id找到模板类

    数据库payment_channel表新增字段callback_bean_id,aliPayCallbackTemplate对应支付回调模板子类AliPayCallbackTemplate。

    异步回调方法

    public interface PayCallbackService {
    
        /**
         * 异步回调
         *
         * @return
         */
        @RequestMapping("/asynCallback")
        Object asynCallback(String channelId);
    }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    @RestController
    public class PayCallbackServiceImpl extends BaseApiService implements PayCallbackService {
        @Autowired
        private PaymentChannelMapper paymentChannelMapper;
    
        @Override
        public Object asynCallback(String channelId) {
            if (StringUtils.isEmpty(channelId)) {
                return setResultError("channelId不能为空");
            }
            // 1.根据渠道id查询渠道信息
            PaymentChannelEntity pce = paymentChannelMapper.selectBychannelId(channelId);
            if (pce == null) {
                return setResultError("该渠道已关闭或不存在,请联系管理员");
            }
            //2.获取模板的baenid
            String callbackBeanId = pce.getCallbackBeanId();
            if (StringUtils.isEmpty(callbackBeanId)) {
                return setResultError("没有配置callbackBeanId");
            }
            //3.直接从Spring容器上下文获取
            AbstractPayCallbackTemplate payCallbackTemplate =
                    SpringContextUtils.getBean(callbackBeanId, AbstractPayCallbackTemplate.class);
            String result = payCallbackTemplate.asyncCallBack(pce);
            return result;
        }
    }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    8 异步回调更改订单状态的信息

    阿里支付回调模板实现类

    @Component
    @Slf4j
    public class AliPayCallbackTemplate extends AbstractPayCallbackTemplate {
        @Autowired
        private PayThreadInfoHolder payThreadInfoHolder;
        @Autowired
        private PaymentTransactionMapper paymentTransactionMapper;
    
        @Override
        protected String resultFail() {
            return "fail";
        }
    
        @Override
        protected String resultSuccess() {
            return "success";
        }
    
        @Override
        protected boolean verifySignature(PaymentChannelEntity pce) {
            // 根据当前线程获取Request
            HttpServletRequest request = ServletUtils.getCurrentRequest();
            HttpServletResponse response = ServletUtils.getCurrentResponse();
            //获取支付宝POST过来反馈信息
            Map<String, String> params = new HashMap<String, String>();
            Map<String, String[]> requestParams = request.getParameterMap();
            ServletOutputStream out = null;
            try {
                out = response.getOutputStream();
                for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
                    String name = (String) iter.next();
                    String[] values = (String[]) requestParams.get(name);
                    String valueStr = "";
                    for (int i = 0; i < values.length; i++) {
                        valueStr = (i == values.length - 1) ? valueStr + values[i]
                                : valueStr + values[i] + ",";
                    }
                    //乱码解决,这段代码在出现乱码时使用
                    valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
                    params.put(name, valueStr);
                    // 防止验证签名失败
                    params.remove("channelId");
                }
                boolean signVerified = AlipaySignature.rsaCheckV1(
                        params, pce.getPublicKey(), AlipayConfig.CHARSET, AlipayConfig.SIGN_TYPE); //调用SDK验证签名
                if (signVerified) {
                    out.println("success");
                    return true;
                } else {
                    //验证失败
                    return false;
                }
            } catch (Exception e) {
                log.error("verifySignature()>> errorMsg:" + e.getMessage());
                return false;
            } finally {
                try {
                    if (out != null) {
                        out.close();
                    }
                } catch (Exception e) {
    
                }
                payThreadInfoHolder.setRequestParams(params);
            }
        }
    
        @Override
        protected String asyncService() {
            // 1. 根据支付订单号码查询数据库 ,是否已经支付过
            Map<String, String> requestParams = currentRequestParams();
            // 支付订单号码
            String outTradeNo = requestParams.get("out_trade_no");
            PaymentTransactionEntity pte = paymentTransactionMapper.selectByPaymentId(outTradeNo);
            // 以下代码可以重构进入父类里面
            if ("1".equals(pte.getPaymentStatus())) {
                // 返回成功告诉给支付不要再继续重试
                return resultSuccess();
            }
            // 2.判断支付的金额 是否一致
            Long payAmount = pte.getPayAmount();
            String totalAmount = requestParams.get("total_amount");
            Long aliPayAmount = yuanToFen(totalAmount);
            if (!payAmount.equals(aliPayAmount)) {
                // 返回success,后台将该订单状态修改为异常订单状态,然后分析异常原因用定时任务处理
                return resultSuccess();
            }
            // 3. 在修改为已经支付
            int result = paymentTransactionMapper.updatePaymentStatus("1", outTradeNo, "ali_pay");
            if (result < 0) {
                // 返回失败告诉支付宝重试
                return resultFail();
            }
            // 调用积分服务接口增加积分 存在分布式事务的问题
            return resultSuccess();
        }
    
        private static Long yuanToFen(String amount) {
            NumberFormat format = NumberFormat.getInstance();
            try {
                Number number = format.parse(amount);
                double temp = number.doubleValue() * 100.0;
                format.setGroupingUsed(false);
                // 设置返回数的小数部分所允许的最大位数
                format.setMaximumFractionDigits(0);
                amount = format.format(temp);
                return Long.parseLong(amount);
            } catch (ParseException e) {
                return null;
            }
    
        }
    }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113

    测试效果:
    在这里插入图片描述

    9 支付异步回调代码继续重构

    重构asyncService()方法 -修改订单状态是通用的逻辑,可以放在父类中实现

    @Slf4j
    public abstract class AbstractPayCallbackTemplate {
    
        @Autowired
        private PayThreadInfoHolder payThreadInfoHolder;
        @Autowired
        private PaymentTransactionMapper paymentTransactionMapper;
    
        public String asyncCallBack(PaymentChannelEntity pce) {
            // 1. 验证签名
            boolean isSign = verifySignature(pce);
            // 2. 记录日志 注意问题:一定要异步处理(为了快速响应给支付宝避免重试造成的幂等性问题)
            addPayLog();
            if (!isSign) {
                // 银联支付的时候 必须返回ok;支付宝必须返回success
                return resultFail();
            }
            // 更改订单状态 获取订单号码,支付金额
            String orderNo = getPayOrderNo();
            Long totalAmount = getTotalAmount();
            return asyncService(orderNo, totalAmount);
        }
    
        /**
         * 3.更改订单状态已经支付
         * 4.调用积分服务接口增加积分
         *
         * @return
         */
        private String asyncService(String outTradeNo, Long totalAmount) {
            // 支付订单号码
            PaymentTransactionEntity pte = paymentTransactionMapper.selectByPaymentId(outTradeNo);
            // 以下代码可以重构进入父类里面
            if (PayConstant.PAY_PAID_STATUS.equals(pte.getPaymentStatus())) {
                // 返回成功告诉给支付不要再继续重试
                return resultSuccess();
            }
            // 2.判断支付的金额 是否一致
            Long payAmount = pte.getPayAmount();
            if (!payAmount.equals(totalAmount)) {
                // 返回success,后台将该订单状态修改为异常订单状态,然后分析异常原因用定时任务处理
                return resultSuccess();
            }
            // 3. 在修改为已经支付
            int result = paymentTransactionMapper.updatePaymentStatus(PayConstant.PAY_PAID_STATUS + "", outTradeNo, "ali_pay");
            if (result < PayConstant.DB_FAIL) {
                // 返回失败告诉支付宝重试
                return resultFail();
            }
            // 调用积分服务接口增加积分 存在分布式事务的问题
            return resultSuccess();
        }
    
        /**
         * 获取真实的支付订单号码
         *
         * @return
         */
        protected abstract String getPayOrderNo();
    
        /**
         * 获取真实的支付金额
         *
         * @return
         */
        protected abstract Long getTotalAmount();
    
        /**
         * 交给子类实现
         *
         * @return
         */
        protected abstract String resultFail();
    
    
        /**
         * 返回成功
         *
         * @return
         */
        protected abstract String resultSuccess();
    
        /**
         * 定义成抽象方法让子类实现
         * 不管验证签名成功还是失败都会将参数放入到ThreadLocal中
         *
         * @return
         */
        protected abstract boolean verifySignature(PaymentChannelEntity pce);
    
        /**
         * 将该方法单独放入在一个类中调用,@Async不经过代理类不会生效
         */
        public void addPayLog() {
            // 从ThreadLocal中获取
            Map<String, String> requestParams = currentRequestParams();
            log.info(">>>记录支付的日志requestParams:<<<" + requestParams);
        }
    
        protected Map<String, String> currentRequestParams() {
            return payThreadInfoHolder.getRequestParams();
        }
    
    }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    @Component
    @Slf4j
    public class AliPayCallbackTemplate extends AbstractPayCallbackTemplate {
        @Autowired
        private PayThreadInfoHolder payThreadInfoHolder;
    
    
        @Override
        protected String getPayOrderNo() {
            // 1. 根据支付订单号码查询数据库 ,是否已经支付过
            Map<String, String> requestParams = currentRequestParams();
            // 支付订单号码
            String outTradeNo = requestParams.get("out_trade_no");
            return outTradeNo;
        }
    
        @Override
        protected Long getTotalAmount() {
            // 1. 根据支付订单号码查询数据库 ,是否已经支付过
            Map<String, String> requestParams = currentRequestParams();
            String totalAmount = requestParams.get("total_amount");
            Long aliPayAmount = yuanToFen(totalAmount);
            return aliPayAmount;
        }
    
        @Override
        protected String resultFail() {
            return "fail";
        }
    
        @Override
        protected String resultSuccess() {
            return "success";
        }
    
        @Override
        protected boolean verifySignature(PaymentChannelEntity pce) {
            // 根据当前线程获取Request
            HttpServletRequest request = ServletUtils.getCurrentRequest();
            HttpServletResponse response = ServletUtils.getCurrentResponse();
            //获取支付宝POST过来反馈信息
            Map<String, String> params = new HashMap<String, String>();
            Map<String, String[]> requestParams = request.getParameterMap();
            ServletOutputStream out = null;
            try {
                out = response.getOutputStream();
                for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
                    String name = (String) iter.next();
                    String[] values = (String[]) requestParams.get(name);
                    String valueStr = "";
                    for (int i = 0; i < values.length; i++) {
                        valueStr = (i == values.length - 1) ? valueStr + values[i]
                                : valueStr + values[i] + ",";
                    }
                    //乱码解决,这段代码在出现乱码时使用
                    valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
                    params.put(name, valueStr);
                    // 防止验证签名失败
                    params.remove("channelId");
                }
                boolean signVerified = AlipaySignature.rsaCheckV1(
                        params, pce.getPublicKey(), AlipayConfig.CHARSET, AlipayConfig.SIGN_TYPE); //调用SDK验证签名
                if (signVerified) {
                    out.println("success");
                    return true;
                } else {
                    //验证失败
                    return false;
                }
            } catch (Exception e) {
                log.error("verifySignature()>> errorMsg:" + e.getMessage());
                return false;
            } finally {
                try {
                    if (out != null) {
                        out.close();
                    }
                } catch (Exception e) {
    
                }
                payThreadInfoHolder.setRequestParams(params);
            }
        }
    
        private static Long yuanToFen(String amount) {
            NumberFormat format = NumberFormat.getInstance();
            try {
                Number number = format.parse(amount);
                double temp = number.doubleValue() * 100.0;
                format.setGroupingUsed(false);
                // 设置返回数的小数部分所允许的最大位数
                format.setMaximumFractionDigits(0);
                amount = format.format(temp);
                return Long.parseLong(amount);
            } catch (ParseException e) {
                return null;
            }
    
        }
    }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100