近期负责的项目中有关于充值会员的功能,特做一个会员充值流程小结,这中间或许也存在着不足。希望可以得到大家的理解和建议。在调用第三方接口支付时修改表的状态(此处不做详细阐述)。

业务分析:

如下图,用户可以选择想要开通的会员等级(初级、中级、高级、超级),不同等级有不同的开通时长选择(一个月、一个季度、半年、一年),每个等级的会员对应的权限不一致,用户可以随意选择。

Java 开发对接充值花费接口 java实现账户余额充值_当前用户

思路介绍:
在购买VIP成功后,要判断该用户当前是否为会员:

场景一:用户之前没有充值过会员,或者已经过期,就根据当前系统时间,增加对应充值时长。
场景二:用户目前是会员状态; 会员状态下还有三种场景:
    1.充值比当前会员等级低的会员; 例如:当前用户已经是超级会员,但是又充值了初级会员
    2.充值比当前会员等级高的会员;例如:当前用户已经是初级会员,但是又充值了超级会员
    3.充值与当前会员等级一样的会员;例如:当前用户已经是中级会员,又充值了中级会员

算法思路:

模拟会员价格表: 单位为分比较好计算

等级



半年

全年

价格单位

初级会员

600

1500

2500

15500


中级会员

700

1600

2600

15600


高级会员

800

1700

2700

15700


超级会员

900

1800

2800

15800


月标准都按31天计算

场景

初级 升 超级:

1.先算出初级还剩多少天 :公式:到期时间-当前时间=相差时间 ,如果低于一天直接按一天算
2.算出初级每天多少钱: 公式:原来开通方式的价格/ 套餐天数=每天价格  
3.初级剩余天数换算成钱还剩多少钱:公式:剩余天数*每天价格 =剩余金额
4.计算超级会员每天价格:  公式:开通方式价格/套餐时长=每天价格
5.初级换算的钱可以买几天超级:剩余金额/超级会员每天价格 =换n天 
最终:超级会员套餐时长+换n天=超级会员到期时间

超级 升初级:

1.算出超级每天价格:公式: 开通方式价格/套餐时长=超级会员每天价格
2.算出初级会员的钱可以买几天超级会员:公式:初级会员开通方式价格/初级会员每天价格=换n天
最终:到期时间+换n天=超级会员到期时间

中间 升 中级:

到期时间+中间套餐时间=中级会员到期时间


代码实现

表结构:

会员规则表:

Java 开发对接充值花费接口 java实现账户余额充值_Java 开发对接充值花费接口_02

用户表:

Java 开发对接充值花费接口 java实现账户余额充值_上传_03

请求实体:

@Data
public class PayMember {
    // 充值对象
    private Long userId;
    // 商户ID
    private Long dealerId;
    // 会员[1:初级;2:中级;3:高级;4:超级]
    private Byte memberType;
    // 购买的套餐[0:月;1:季度;2:半年;3:全年;]
    private Byte comboId;
}

业务代码:

@Override
public ResultUtils payMember(PayMember payMember) {
    // combosMap的SQL:
    //        SELECT `month`,
    //               season,
    //               semester,
    //               yearly,
    //               type
    //        FROM dealer_member_rule
    //        WHERE dealer_id = #{dealerId}
    Map<Integer, Object> combosMap = dealerMemberRuleMapper.selectCombos(payMember.getDealerId());
    // 查询当前用户会员情况
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("user_id", payMember.getUserId())
            .select("member_type", "membership_due","member_pay");
    User user = userMapper.selectOne(queryWrapper);
    UpdateWrapper<User> wrapper = new UpdateWrapper<>();
    // 判断会员情况
    if (ObjectUtil.isEmpty(user.getMembershipDue()) || LocalDateTime.now().isAfter(user.getMembershipDue())) {//第一次开通会员|| 会员已经到期
        // 非升级续费,第一次开通 || 到期后开通
        wrapper.eq("user_id", payMember.getUserId())
                .set("member_pay", payMember.getComboId())
                .set("membership_due", LocalDateTime.now().plusMonths(combo(payMember.getComboId())))
                .set("member_type", payMember.getMemberType());
    } else { // 续费 || 升级
        if (user.getMemberType() > payMember.getMemberType()) {// 购买了比当前低级的套餐 (当前超级升初级)
            // 获取当前使用的套餐  旧套餐
            Map<String, Object> oldCombo = (Map<String, Object>) combosMap.get(user.getMemberType().intValue());
            // 得出上传开通旧套餐的时长名字
            String timeName = comboName(user.getMemberPay());
            Integer oldPrice = (Integer) oldCombo.get(timeName);// 得出旧套餐的价格
            // totalDays=这个套餐有多少天
            int totalDays = combo(user.getMemberPay()) * 31;// 套餐总天数=n月*标准天数(31)
            // 每天价格=套餐价格/总天数
            int oldEveryDayThePrice = oldPrice / totalDays;
            // 算出新套餐可以买几天旧套餐
            // 即将要买的套餐 新套餐
            Map<String, Object> newCombo = (Map<String, Object>) combosMap.get(payMember.getMemberType().intValue());
            Integer newPrice = (Integer) newCombo.get(comboName(payMember.getComboId()));// 新套餐价格
            // 可以买几天旧套餐=新套餐价格/旧套餐每天价格
            int day = newPrice / oldEveryDayThePrice;
            if (day<0) day = 1;// 少用一天送一天
            // 会员到期时间=当前时间+新套餐天数+旧套餐这算天数
            //到期时间加上最终天数
            wrapper.eq("user_id", payMember.getUserId())
                    .set("member_pay", user.getMemberPay())// 还是就套餐的时长
                    .set("membership_due", user.getMembershipDue().plusDays(day))//到期时间加最终天数
                    .set("member_type", user.getMemberType());
        } else if (user.getMemberType() == payMember.getMemberType()) {// 购买了和当前一样的套餐
            //一样的套餐直接加上对应的天数
            wrapper.eq("user_id", payMember.getUserId())
                    .set("member_pay", user.getMemberPay())// 还是就套餐的时长
                    .set("membership_due", user.getMembershipDue().plusMonths(combo(payMember.getComboId())))//到期时间加最终天数
                    .set("member_type", user.getMemberType());
        } else {// 购买的比当前高级的套餐
            // 算法和 高买低相反
            // 初级还剩余几天
            Duration duration = Duration.between(LocalDateTime.now(), user.getMembershipDue());//到期时间和当前时间的差
            long hours = duration.toHours();//相差的小时数
            long residueDay = hours / 24L;//旧套餐剩余天数
            if (residueDay < 1) residueDay = 1; // 小于一天就送一天

            // 旧套餐每天价格=原来开通方式的价格/套餐天数
            // 获取当前使用的套餐  旧套餐
            Map<String, Object> oldCombo = (Map<String, Object>) combosMap.get(user.getMemberType().intValue());
            // 得出上传开通旧套餐的时长名字
            String timeName = comboName(user.getMemberPay());
            Integer oldPrice = (Integer) oldCombo.get(timeName);// 得出旧套餐的价格
            int totalDays = combo(user.getMemberPay()) * 31;// 套餐总天数=n月*标准天数(31)
            // 每天价格=套餐价格/总天数
            int oldEveryDayThePrice = oldPrice / totalDays;
            long residueMoney = residueDay * oldEveryDayThePrice;//剩余的钱=剩余天数*每天价格
            // 获取新套餐
            Map<String, Object> newCombo = (Map<String, Object>) combosMap.get(payMember.getMemberType().intValue());
            // 对应时长的套餐价格
            Integer newPrice = (Integer) newCombo.get(comboName(payMember.getComboId()));
            // 算出新套餐每天价格
            int daysMoney = newPrice / (combo(payMember.getComboId()) * 31);
            // 算出剩余的钱可以买几天新套餐  可购买天数=剩余的钱/新套餐每天价格
            long buyDaysNumber = residueMoney / daysMoney;
            if (buyDaysNumber < 1) {// 如果小于1天就直接送一天
                buyDaysNumber = 1;
            }
            // 最终到期时间=到期时间+((月*31)+旧套餐可以买的天数)
            LocalDateTime membership_due = user.getMembershipDue().plusDays((combo(payMember.getComboId()) * 31) + buyDaysNumber);
            wrapper.eq("user_id", payMember.getUserId())
                    .set("member_pay", payMember.getComboId())// 还是就套餐的时长
                    .set("membership_due", membership_due)//到期时间加最终天数
                    .set("member_type", payMember.getMemberType());
        }
    }
    int update = userMapper.update(new User(), wrapper);
    log.info("充值会员模块-用户ID:{},充值结果:{}", payMember.getUserId(), update > 0 ? "成功" : "失败");
    return ResultUtils.success();
}

/**
 * 套餐名称 方便取套餐值
 *
 * @param type [0:月;1:季度;2:半年;3:全年;]
 * @return
 */
private String comboName(Byte type) {
    switch (type) {
        case 0:
            return "month"; // 取月字段的价格
        case 1:
            return "season";// 取季字段的价格
        case 2:
            return "semester";// 半年
        case 3:
            return "yearly";// 一年
    }
    return null;
}
/**
 * 套餐时间
 *
 * @param type [0:月;1:季度;2:半年;3:全年;]
 * @return
 */
private Integer combo(Byte type) {
    switch (type) {
        case 0:
            return 1;// 一个月 1一个月
        case 1:
            return 3;// 一个季度 三个月
        case 2:
            return 6;// 半年 六个月
        case 3:
            return 12;// 一年 十二个月
    }
    return null;
}