踩坑笔记 ---- 使用LocalDateTime开通会员到期时间与自动续费业务某天用户突然为0

news2024/11/16 1:29:38

写在前面

使用LocalDateTime的同学需要注意下,这东西的plusMonth可能会有点点超出你的认知,如果不慎掉坑里,希望这篇笔记可以给你提供思路

业务背景

此业务场景非常简单,自动续费业务,需要在用户会员到期前24小时执行扣款服务,为用户开通续费会员

踩坑描述

这是一个组合坑,续费job无任何问题,会员管理端也没有任何问题,但是他们组合在一起,就是问题。
某日自动续费用户突然为0,相关负责同事让我排查问题,经排查发现,续费日后一天权益到期的用户数为0,因此导致不能扣费,具体排查过程简介如下

业务实现逻辑

续费端

定时任务

使用 elastic-job 配置定时器,在每日固定时间7点执行扣费任务

业务步骤

  • 扫描全部具有签约协议的用户
  • 查询每个用户的会员到期时间,与当前时间做对比,判断是否在24小时内,如果在24小时内,则进行扣费
  • 扣费记录入库,预订单入库,预开通权限入库,通知扣款服务商进行扣费,等待异步扣费结果通知

续费端业务逻辑并不复杂,按照每天扫描一次的方式执行,有到期的用户就进行续费,

会员管理端(踩坑点)

开通权限流程

  • 接受异步扣款通知
  • 验证通知中的签名等基本安全认证信息
  • 根据通知中的订单记录id,查找订单和预开通权限
  • 根据预开通权限的内容,为当前订单用户开通相关权益
  • 更新记录,订单完毕,续费表记录完毕

踩坑点细节

主要问题在开通权益这里,权益表存储数据包括了续费周期(1),和续费单位(MONTH),因此需要为用户开通一个月权益,考虑到用户权益可能未到期,因此需要把用户的最大到期时间,和当前时间做对比,取最大值,然后根据最大值开通一个月会员即可,伪代码如下

// 查询用户到期时间
LocalDateTime dbEndTime = fromDbEndTime(userId);
// 当前时间
LocalDateTime now = LocalDateTime.now();
// 二者取最大值作为权限的开始时间
LocalDateTime startTime = maxTime(now,dbEndTime);
// 计算用户应该开通的最大到期时间(异常点在这里)
LocalDateTime endTime = startTime.plusMonths(duration).minusDays(1).toLocalDate().atTime(LocalTime.MAX)
// 数据入库
save(userId,startTime,endTime);

异常说明

LocalDateTime的plusMonths这个api,他会计算本月延后一个月的天数,是否在下个月存在,如果不存在,他会保留下个月的最大值,比如
你是在1.31日任意时间点开通权限,向后延长一个月,保留时间为2.27 23:59:59
在这里插入图片描述
如果你在1.30 开通,也是这个时间
在这里插入图片描述
也就是说:大月份(31天的月份)衔接小月份(不足31天的月份),对于自动续费业务来说,会在某一天的用户总量会翻倍

你以为这就完了?没,小月份衔接大月份,某一天会没用户
比如:
4.30日开通权益的用户,到期时间是 5.29. 23:59:59
5.1日开通权益的用户,到期时间是 5.31 23:59:59
在这里插入图片描述
在这里插入图片描述
看出问题没?5.30那天,没人能到期,因此5.29日的扣费程序就不会有任何人被扣款

查看LocalDateTime的plusMonths 说明,并没有说明小月份衔接大月份的问题
在这里插入图片描述

总结

这个不能算JDK的bug,没有踩过这个坑的人,可能也想不到这个问题,如果使用这个api,你的程序大概也会这样,希望这个笔记能对你有帮助,在有人找你排查问题的时候,能快速定位并解决问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/594090.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux 防火墙开启端口

文章目录 查看防火墙状态开启指定端口重启防火墙重新加载防火墙查看已开启的端口关闭指定端口临时关闭防火墙永久关闭防火墙(必须先临时关闭防火墙,再执行该命令,进行永久关闭)结语 以下是一些常用的 firewalld 命令: …

C++11中条件标量和互斥锁应用出现死锁思考

条件变量和互斥锁在多线程同步过程中经常被使用&#xff0c;以下测试程序测试其使用。 目录 1.测试程序1 2.测试程序2 3.运行结果思考 1.测试程序1 #include <mutex> #include <deque> #include <iostream> #include <thread> #include <condi…

5-网络初识——封装和分用

目录 1.数据封装的过程 2.数据分用的过程 PS&#xff1a;网络数据传输的基本流程&#xff08;以QQ为例&#xff0c;A给B发送一个hello&#xff09;&#xff1a; 一、发送方&#xff1a; 二、接收方&#xff1a; 不同的协议层对数据包有不同的称谓&#xff0c;在传输层叫做…

Linux:忘记root密码解决办法

如果你是虚拟机只要将光盘镜像连接到虚拟机上&#xff0c;以光盘iso镜像启动 如果你是真机或服务器那将实体u盘或实体光盘连接至设备并且以连接的设备启动 开机时候打断开机 使用 &#xff08;u盘|光盘&#xff09;引导启动 troubleshooting rescue a centos system 输入 1…

前后端分离式项目架构流程(爆肝两万字)

文章目录 &#x1f412;个人主页&#x1f3c5;JavaEE系列专栏&#x1f4d6;前言&#xff1a; 【&#x1f387;前端】先创建Vue-cli项目&#xff0c;请选择此项目【&#x1f380;创建路由】打开命令行工具&#xff0c;进入你的项目目录&#xff0c;输入下面命令。1.创建 router …

SimpleCG绘图函数(4)--绘制圆

在前一篇教程我们利用绘制矩形功能绘制了一个城市,接下来我们讲解另外一个同样重要且基础的图形----圆形。并一起看看该图形能绘制哪些应用呢。 绘制圆形相关函数如下&#xff1a; //圆心坐标(nXCenter,nYCenter),半径为nRatio//绘无填充制圆 void circle( int nXCenter, int …

KG-BERT: BERT for Knowledge Graph Completion 2019ACL

把BERT用在知识图谱补全上 提出KG-BERT模型&#xff0c;在预训练好的BERT基础上继续fine-tuning。 1.传统KGC方法 传统的KGC方法一般依赖于KGE&#xff0c;而KGE往往通过将KG中的三元组关系投影到某个表示空间中&#xff0c;然后使用打分函数对三元组的合理性进行评估&#x…

OA系统流程传出文档

泛微OA如何与第三方接口交互 注意: 1.对于泛微OA中不能作为节点后的自定义编码代码,可能有以下几种原因: 代码存在语法错误:节点后的自定义编码代码应该是正确的Java代码,如果代码中存在语法错误,如缺少分号或者括号不匹配等,将不能正常编译执行。 缺少必要的依赖:节点…

C++ 内存分区模型

C程序在执行时&#xff0c;将内存大方向划分为4个区域 代码区&#xff1a;存放函数体的二进制代码&#xff0c;由操作系统进行管理的 全局区&#xff1a;存放全局变量和静态变量以及常量 栈区&#xff1a;由编译器自动分配释放 , 存放函数的参数值 , 局部变量等 堆区&…

CS5366芯片方案|单芯片type-C转HMID+PD+U3拓展坞方案|CS5366设计电路原理图

CS5366是一款高性能USB Type-c/DisplayPort TM&#xff08;DP&#xff09;到HDMI2.0转换器&#xff0c;设计用于USB Type-c源到HDMI2.0sink。CS5366集成了符合DP1.4标准的接收器和符合hdmi2.0标准的发射器。还包括两个CC控制器用于CC通信&#xff0c;以实现DP Alt Mode和功率传…

目标检测中,DETR方法为何class设置为91+1,DINO中为91

基于DEtection TRansformer的DETR框架https://github.com/facebookresearch/detr因为end-to-end&#xff0c;无需后处理等优点&#xff0c;逐渐得到青睐。DINO方法https://github.com/IDEA-Research/DINO更是取得了在COCO2017的SOTA结果。 其中&#xff0c;在DETR方法中&#…

Revit简单的门族创建及CAD图纸翻模门窗

一、Revit简单的门族创建步骤 门是我们建筑模型中不可缺少的一个构件&#xff0c;如何在族中绘制一个自己的门族呢?下面教大家绘制一个简单的门族&#xff0c;让你了解门的构件绘制。 打开公制门族进行创建 首先我们要进行门框的创建&#xff0c;很多人会以为系统自带的这个是…

【数据结构】一篇文章带你彻底学会《后缀表达式》

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c语言系列专栏&#xff1a;c语言之路重点知识整合 &#x…

什么是项目可交付成果?定义、示例及管理工具

项目产生可交付成果&#xff0c;这只是项目活动的结果。项目可交付成果可大可小&#xff0c;其数量也因项目而异。它们是由项目管理团队和利益相关者在项目规划阶段商定的。 换句话说&#xff0c;任何类型的项目都有投入和产出。投入是你投入到项目中的东西&#xff0c;如数据…

Pixhawk无人机-ArduPilot 软件SITL仿真模拟飞行(SITL+MAVProxy)

1 引言 本人是先看了多个博客实现了&#xff1a;在ubuntu下建立完整的ardupilot开发环境。 该文是基于搭建完编译环境后&#xff0c;也就是搭建好ardupilot的仿真环境实现的。 在文章: 《Pixhawk无人机扩展教程(5)—SITL仿真模拟飞行&#xff1a;开发环境搭建》.中指出&#…

究竟什么是CRM?一文带您了解

目录 一、什么是CRM&#xff1f;它是用来做什么的&#xff1f; 1、获取商机的源头&#xff1a;全渠道营销管理 2、业绩提升的核心&#xff1a;销售管理 3、有温度的触达&#xff1a;客户服务 二、企业为什么需要布局CRM 1、销售是一场没有硝烟的战争 2、信息孤岛存在&am…

调用华为API实现中文语音识别

目录 1. 作者介绍2. 华为云中文语音识别2.1 录音文件识别与获取2.2 限制要求2.3 对象存储服务&#xff08;OBS&#xff09;简介 3. 实验过程以及结果3.1获得API3.2 代码实现3.3 运行结果 参考&#xff08;可供参考的链接和引用文献&#xff09; 1. 作者介绍 张楠&#xff0c;女…

【unity之c#专题篇】——进阶知识实践练习

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…

如果你很焦虑、浮躁,不如去读一读林曦这本《无用之美》

生活中不可或缺的是柴米油盐&#xff0c;而濡养人心的往往是无用之美。——林曦    很难用语言写出这本书的美。有些书会给你带来内心的激荡&#xff0c;有些会让你充满力量&#xff0c;而林曦老师的书便自带一种轻盈的美&#xff0c;越往下读越能让人凝神静气。      …

浅谈一下@Async和SpringSecurityContext可能会遇到的问题和解决方案

Async和SpringSecurityContext 场景回溯 在执行一个用时较长的批量插入业务的时候,我尝试使用Async异步对业务进行优化,但是却给我报了空指针的错误,定位之后发现 此处我是基于SpringSecurity来获取用户的 是currentUserService获取到的当前登陆用户为空导致的,但是当前确实是…