停车场收费系统

该系统支持用户通过扫码进行停车和付款,自动管理车位占用。具备多种计费方案,如按次、按时、阶梯和时段收费。同时,为月卡用户提供特殊服务,无需每次创建订单。后台管理涵盖用户、商家、设备和订单等多个方面,还涉及物联网设备的参数调试和数据处理。
摘要由CSDN通过智能技术生成

扫码停车

扫码付款后,自动降锁,车位数据设置占用,用户有操作锁的权限。

小程序功能
用户注册:普通用户的账号由用户自行注册
用户登录:用自己注册的账号登录,也可以使用微信账号授权登录
资讯功能:预约小程序的资讯,用户可以任意浏览资讯列表和详细信息
停车场列表:列出所有的停车场信息
停车场查询:可以输入关键字,查询指定的停车场
停车场详情:显示名称、收费标准、停车场介绍
停车场评论:使用过该停车场人员的评论列表
停车场收藏:在详情页面底部点击收藏,加入收藏列表
预约车位:选择停车场、选车停车位置、立即预约车位
我的车位预约订单:显示我的所有预约信息列表 
取消预约:对于刚提交的车位预约,还没有付款之前,可以取消
去付款:模拟付款
出库和付费:模拟付费后成功出库
去评论:对于已经完结的订单,可以对停车场进行评论
我的收藏:用户收藏的停车场列表
用户信息修改
退出登录:清除登状态


后台管理员功能
1.	后台管理系统前、后端功能开发,功能包括用户管理、商家管理、权限菜单、数据字典、系统日志、城市管理、车场车位管理、设备管理、营收管理、订单管理、分账管理、提现管理、优惠券管理。
2.	巡检人员手动打单、计次收费、小票打印功能开发,设备调拨、月报报表功能开发。
3.	负责占位锁、地磁等Iot设备硬件参数调试,MQTT消息收发,将设备及上报的数据进行解析处理,存储,分析;后台业务处理。
4.	将其他智能锁设备上报的TCP进行业务处理,存储,分析;后台进行远程控制,苹果安卓手机消息推送等。
5.	对接智能网关设备,实现电子标签图片远程刷新;
车位预约 略

用户预约列表,未实现
预约时车位数据不能被设置占用,与扫码占位不同。

操作占位锁

最近一次扫码支付的人,拥有操作锁的权限。

月卡用户

月卡用户扫设备,不创建订单,但是可以分配设备权限。

计费方式

车场计费方案分为四种:一、按次收费;二、按时收费;三、按阶梯收费;四、按时段收费。
这四种为互斥关系,每个计费方案只能选择其中的一种。

  1. 按次收费:可设置每次前多少分钟免费,和每次停车收费多少元;
  2. 按时收费:可设置每多少分钟收多少元,前多少分钟收费多少元(如果选择了超过所设时长是否有效,则每次超过所设时长后,前多少分钟收费多少元都起作用),还可设置前多少分钟最高收多少元,每日单次最高收多少元;
  3. 按阶梯收费,即将一天划分为多个阶梯,通过判断停车时间属于哪个阶梯来收费;
  4. 按时段收费:设置临时车辆的计费标准,可分日间收费和夜间收费。可对车型分开进行计费。
    在临时车收费区间中,可设置前多少分钟收费多少元,也可设置每日单次收费上限钱数。在收费选项中,还可以设置嵌套过场多少分钟内不收费,每日收费上限及前多少分钟是不收费。
多退少补

系统设计30分钟免费时间内,不允许取消订单,只能完成结算,离位升锁。

收费规则

超过24小时重新滚动当前计费规则。

分时收费规则
/**
     * <p>根据时长,匹配并计算出一个车场/车位的费用金额 </p>
     * 计费方式: 10计次收费; 20计时收费; 30分段收费
     */
    public BigDecimal findParkingFeeTotalMoney(long feeId, final double totalDuration) {
        ParkingFee fee = findOne(feeId);
        if(fee == null || fee.getEnabled() == 2){
            return 0d;
        }
        if(fee.getFeeWayId() == 20){
            //剩余 匹配多个费用规则计算的总费用
            BigDecimal totalFee = getTotalFeeByFeePeriodDuration(feeId, totalDuration);
            log.info("[20 计时收费] 总时长:{}, 总费用:{}, feeId:{}", totalDuration, totalFee, feeId);
            return totalFee;
        }
        else if(fee.getFeeWayId() == 30){
            ParkingFeeRule rule = findFeeRuleByPeriodTime(feeId);//分段收费
            BigDecimal amount = rule == null ? 0d: rule.getAmount();
            log.info("[30 分段收费] 总时长:{}, 总费用:{}, feeId:{}", totalDuration, amount, feeId);
            return amount;
        }
        return 0d;
    }
    /**
     * <p>按 [计时收费]计算总费用 重要</p>
     *
     * 1.根据时长,循环匹配费用规则明细,按 [计时收费]合计一个总费用;
     * 2.先过滤免费时长、封顶时长;
     * 3.首1小时代码固定写死进行判断。
     */
    public BigDecimal getTotalFeeByFeePeriodDuration(long feeId, final double totalDuration) {
        BigDecimal totalFee = new BigDecimal(0);
        BigDecimal remainDur = new BigDecimal(totalDuration); //剩余时长
        if(totalDuration < 0.1){
            return totalFee;
        }
        //查询一个费用标价下的所有时段规则
        List<ParkingFeeRule> ruleList = parkingFeeRuleService.findRulesByFeeId(feeId);
        if (ruleList.isEmpty()) {
            return totalFee;
        }

        //大于封顶金额 超过24小时
        for (ParkingFeeRule ru : ruleList) {

                if(ru.getFeeTypeId() == 4 && remainDur.doubleValue() > ru.getFeePeriodDuration()){

                    // 大于24h, 费用35元
                    if(remainDur.doubleValue() >= 24){
                        int day = remainDur.intValue() / 24;
                        totalFee = Misc.sum(totalFee, Misc.cheng(new BigDecimal(day), ru.getAmount()));
                        remainDur = Misc.subtract(remainDur, day*24);
                        log.info("rule-4 封顶, day:{}, 总时长:{}, remainDur:{}, totalFee:{}", day, totalDuration, remainDur, totalFee);
                    }
                    // 大于封顶时长, 封顶金额35元
                    if(remainDur.doubleValue() > ru.getFeePeriodDuration()){
                        remainDur = Misc.subtract(remainDur, 24);
                        totalFee = Misc.sum(totalFee, ru.getAmount());//35元
                        log.info("rule-4 封顶, 计费时长:{}, 总:{}, 剩:{}, totalFee:{}", ru.getFeePeriodDuration(), totalDuration, remainDur, totalFee);
                    }
                    if(totalDuration < 0.5){
                        return totalFee;
                    }
                }
            }

        // mf=0.5, first=1; total=13 sheng=0
        //  if(mf == 0.5 && total > 1){} //sheng=13-0.5=12.5
        //  if(first == 1 && total > 1){} //sheng=12.5-1=11.5
        //  if(total <= 13 && sheng > 0 && sheng <= 13){} //sheng=0h

        //开始遍历时段规则信息
        for (ParkingFeeRule ru : ruleList) {
            if(ru.getFeePeriodDuration() == null){
                continue;
            }
            //剩余时长计算完了
            if(remainDur.doubleValue() < 0.1){
                return totalFee;
            }
            //小于免费时长
            if(ru.getFeeTypeId() == 1 && totalDuration <= ru.getFeePeriodDuration()){
                log.info("rule 免费, 免费时长:{}, 总时长:{}", ru.getFeePeriodDuration(), totalDuration);
                return totalFee;
            }
            //大于免费时长
            if(ru.getFeeTypeId() == 1 && totalDuration > ru.getFeePeriodDuration()){
                log.info("rule-1 免费, 免费时长:{}, 总:{}, 剩余:{}", ru.getFeePeriodDuration(), totalDuration, remainDur);
                continue;
            }
            //首1小时
            if(ru.getFeeTypeId() == 2){
                //只有1条记录, 费用=费用单价×费用时长
                if(ruleList.size() == 1){
                    return totalFee.add(Misc.cheng(ru.getAmount(), totalDuration));
                }
                //
                // 硬编码匹配首0.25小时/首半小时/首1小时
                // 首1小时
                if(ru.getFeePeriodDuration() == 1 && totalDuration >= 1) {
                    //费用=费用单价*费用时长
                    totalFee = totalFee.add(Misc.cheng(ru.getAmount(), 1));
                    //剩余时长 = 总时长 - 费用时长
                    remainDur = Misc.subtract(remainDur, 1);
                    log.info("rule-2 首1h, 单价:{}, 计费时长:{}, 总:{}, 剩余:{}", ru.getAmount(), 1, totalDuration, remainDur);
                    continue;
                }
                else if(ru.getFeePeriodDuration() == 0.5 && totalDuration >= 0.5) {
                    totalFee = totalFee.add(Misc.cheng(ru.getAmount(), ru.getFeePeriodDuration()));
                    remainDur = Misc.subtract(remainDur, 0.5);
                    log.info("rule-2 首0.5h, 单价:{}, 计费时长:{}, 总:{}, 剩余:{}", ru.getAmount(), 0.5, totalDuration, remainDur);
                    continue;
                }
                else if(ru.getFeePeriodDuration() == 0.25 && totalDuration >= 0.25){
                    totalFee = totalFee.add(Misc.cheng(ru.getAmount(), 0.25));
                    remainDur = Misc.subtract(remainDur, 0.25);
                    log.info("rule-2 首0.25h, 单价:{}, 计费时长:{}, 总:{}, 剩余:{}", ru.getAmount(), 0.25, totalDuration, remainDur);
                    continue;
                }
            }

            //按小时计费匹配, 遍历时段规则信息, 一般会匹配到两个费用规则
            if(ru.getFeeTypeId() == 3){
                // 时长大于当前计费时长 (14 > 13 && 13==13) 这里要优先排除封顶金额,否则计算错误
                if(totalDuration > ru.getFeePeriodDuration() && remainDur.doubleValue() <= ru.getFeePeriodDuration()){
                    //费用=费用单价*费用时长
                    BigDecimal fee = Misc.cheng(ru.getAmount(), remainDur);
                    totalFee = totalFee.add(fee);
                    //剩余时长 = 总时长 - 费用时长
                    remainDur = Misc.subtract(remainDur, ru.getFeePeriodDuration());
                    log.info("rule-2 按小时, 费用:{}, 总费:{}, 单价:{}, 计费时长:{}, 总:{}, 剩余:{}", fee, totalFee, ru.getAmount(), ru.getFeePeriodDuration(), totalDuration, remainDur);
                    continue;
                }

                // 最后 小于计费时长 (2~13h <= 13 and 剩余时长=11.5); 排除负数
                if(totalDuration <= ru.getFeePeriodDuration() && remainDur.doubleValue() > 0 && remainDur.doubleValue() <= ru.getFeePeriodDuration()){
                    BigDecimal fee = Misc.cheng(ru.getAmount(), remainDur);
                    totalFee = totalFee.add(fee);
                    //剩余时长 <= 0
                    remainDur = Misc.subtract(remainDur, ru.getFeePeriodDuration());
                    log.info("rule-3 按小时 单价:{}, 计费时长:{}, 费用:{}, 总费用:{}, 总:{}, 剩余:{}", ru.getAmount(), ru.getFeePeriodDuration(), fee, totalFee, totalDuration, remainDur);
                    return totalFee;
                }
            }
            
            if(ru.getFeeTypeId() == 5 && totalDuration > ru.getFeePeriodDuration()){
                log.info("rule, 超时:{}", Misc.toJson(ru));
                totalFee = totalFee.add(ru.getAmount());
                remainDur = Misc.subtract(remainDur, ru.getFeePeriodDuration());
                log.info("rule, 超时, 费用:{}, 时长:{}", totalFee, remainDur);
                return totalFee;
            }
        }
        return totalFee;
    }
30 分段收费

按时收费规则:白天、晚上、周内/周末

车场分两个时间段收费,主要区别是:
8:00-21:00按两个分段(30分钟-1小时内5元/辆,超过1小时,按2元/辆.小时加收),该时段最高不超过20元每辆。
21:00-次日 8:00按两个分段(30分钟-3小时5元/辆.次,超过3小时按10元/ 辆.次),该时段最高不超过10元每辆。

    /**
     * 暂时先不用
     * 根据 [分段收费方式] 按时段时间, 匹配一个车场费用的时段规则
     */
    public ParkingFeeRule findFeeRuleByPeriodTime(long feeId) {
        //查询一个费用标价下的所有时段规则
        List<ParkingFeeRule> ruleList = parkingFeeRuleService.findRulesByFeeId(feeId);
        if (ruleList.isEmpty()) {
            return null;
        }
        //遍历费用规则的时段信息
        for (ParkingFeeRule rule : ruleList) {
            if (Misc.isNull(rule.getPeriodStartTime()) || Misc.isNull(rule.getPeriodEndTime())) {
                continue;
            }
            //判断工作日
            if(!DateUtil.checkWeekType(rule.getDateType())){
                continue;
            }
            //在这个时段范围内
            if (rule.getIsAllDay() == 1 && rule.getAmount().doubleValue() > 0) {
                log.info("时段费用规则 全天:{}", Misc.toJson(rule));
                rule.setPeriodStartTime("00:00");
                rule.setPeriodEndTime("23:59");
                return rule;
            }
            //在这个时段范围内
            log.info("时段费用规则:{}", Misc.toJson(rule));
            if (DateUtil.isBetweenTime(rule.getPeriodStartTime(), rule.getPeriodEndTime())) {
                return rule;
            }
        }
        return null;
    }

在这里插入图片描述

CREATE TABLE `d_parking_fee` (
  `fee_id` bigint(20) NOT NULL COMMENT 'ID',
  `fee_name` varchar(50) DEFAULT NULL COMMENT '收费标准名称',
  `fee_way_id` int(11) DEFAULT '0' COMMENT '计费方式: 10计次收费; 20计时收费; 30分段收费',
  `enabled` int(1) DEFAULT '1' COMMENT '是否启用: 0未使用; 1使用中; 2停止使用',
  `fee_remark` varchar(90) DEFAULT '' COMMENT '费用备注',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `create_user` bigint(20) DEFAULT NULL COMMENT '创建人',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `update_user` bigint(20) DEFAULT NULL COMMENT '更新人',
  PRIMARY KEY (`fee_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='车场收费参考';

参考资料

停车场收费管理系统/停车场管理系统的设计与实现
https://blog.csdn.net/weixin_47958760/article/details/126860836

在这里插入图片描述
在这里插入图片描述

yue ka

在这里插入图片描述
月卡规则

在这里插入图片描述
用户月卡表
在这里插入图片描述

购买记录
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值