# -*- coding: utf-8 -*- # !/usr/bin/env python import sys # noinspection PyUnresolvedReferences from pandas import Interval from apps.web.constant import Const from apps.web.device.models import DevicePortReport class DefaultParams(object): STATUS_MAP = { "00": Const.DEV_WORK_STATUS_IDLE, "01": Const.DEV_WORK_STATUS_WORKING, "02": Const.DEV_WORK_STATUS_OCCUPY, "03": Const.DEV_WORK_STATUS_FAULT, # 故障 "04": Const.DEV_WORK_STATUS_FAULT, # 烟感 "05": Const.DEV_WORK_STATUS_FAULT, # 雨感 "06": Const.DEV_WORK_STATUS_FAULT # 设备锁定 } FAULT_MAP = { "1001": u"烟雾报警", "1002": u"雨感报警", "1003": u"温度报警" } STOP_REASON_MAP = { "00": "充满自停", "01": "未检测到充电设备", "02": "过载结束", "03": "电压输出故障", "04": "刷卡结束", "05": "充满自停", "06": "密码开锁结束订单" } DEFAULT_MIN_START_COINS = 2 DEFAULT_MIN_POWER = 20 DEFAULT_MAX_POWER = 500 DEFAULT_FLOAT_TIME = 2 DEFAULT_IC_CODE = "0001" DEFAULT_PORT_NUM = 10 # 计费相关 DEFAULT_CHARGE_TYPE = "time" # DEFAULT_MIN_CARD_MONEY = DEFAULT_MIN_START_COINS DEFAULT_MIN_CONSUME = 1.0 DEFAULT_MAX_CONSUME = 20.0 DEFAULT_STAY_TIME_UNIT_PRICE = 0.5 DEFAULT_FREE_STAY_TIME = 5 DEFAULT_TIME_UNIT_PRICE = 1 DEFAULT_POWER_PACKAGE = [ { "lowLimit": 20, "upLimit": 250, "price": 1, "time": 180, }, { "lowLimit": 250, "upLimit": 360, "price": 1, "time": 120 }, { "lowLimit": 360, "upLimit": 500, "price": 1, "time": 60, } ] class Calculater(object): """ 计费 策略类 拆分出来实现的比较简单 有时间再重新搞一下 计费 目前来看 只和 账单 以及 设备有关系 账单 影响 账面上的钱 设备影响 账单的其他因素 例如退费保护时间 最低消费 最高消费 后续配置可能还会涉及到人 比如优惠卷、折扣卷等等 """ def __init__(self, device, order): self.device = device self.order = order chargeType = order.servicedInfo.get("chargeType", "") strategyClass = getattr(sys.modules[__name__], "{}Strategy".format(chargeType.capitalize())) self._strategy = strategyClass(self) @property def strategy(self): return self._strategy @property def result(self): # 先计算账单 应该付款的钱 if not self.strategy: consume = 0 else: consume = self.strategy.calculate() # 计算占位费 对于占位费的计算方式都是一样的 时间 * 单价 otherConf = self.device.get("otherConf") or dict() stayPrice = otherConf.get("stayTimeUnitPrice", DefaultParams.DEFAULT_STAY_TIME_UNIT_PRICE) consume += (self.stayTime * stayPrice / 60) # 在看下是否满足最低消费金额 以及最大消费金额 maxConsume = otherConf.get("maxConsume", DefaultParams.DEFAULT_MAX_CONSUME) minConsume = otherConf.get("minConsume", DefaultParams.DEFAULT_MIN_CONSUME) return min(max(minConsume, consume), maxConsume) @property def stayTime(self): """ 需要考虑免费占位时间 :return: """ try: orderNo = self.order.orderNo _time = DevicePortReport.last_one(orderNo).stayTime if DevicePortReport.last_one(orderNo) else 0 # 减去免费占位时间 otherConf = self.device.get("otherConf") or dict() freeStayTime = int(otherConf.get("freeStayTime", DefaultParams.DEFAULT_FREE_STAY_TIME)) # 单位是小时, 这个地方 _time -= (freeStayTime * 60) except Exception as e: _time = 0 return max(_time, 0) class BaseStrategy(object): def __init__(self, context): self.context = context def calculate(self): raise NotImplemented(u"尚未实现") class TimeStrategy(BaseStrategy): def calculate(self): # 先找到单号 以及 端口号 orderNo = self.context.order.orderNo reportRecord = DevicePortReport.last_billing_one(orderNo) chargeTime = reportRecord.chargeTime if reportRecord else 0 # 获取时间计费的单价 otherConf = self.context.device.get("otherConf") or dict() chargePrice = otherConf.get("timeUnitPrice", DefaultParams.DEFAULT_TIME_UNIT_PRICE) return chargeTime * chargePrice / 60 class PowerStrategy(BaseStrategy): def calculate(self): # 同样的先获取单号 # 获取功率的套餐 orderNo = self.context.order.orderNo otherConf = self.context.device.get("otherConf") or dict() powerPackage = otherConf.get("powerPackage", DefaultParams.DEFAULT_POWER_PACKAGE) # 区间结构 mapDict = dict() for package in powerPackage: lowLimit = package.get("lowLimit") upLimit = package.get("upLimit") price = package.get("price") _time = package.get("time") mapDict.update({ Interval(int(lowLimit), int(upLimit), closed="left"): { "unitPrice": float(price) / float(_time), "time": 0 } }) powerRecords = DevicePortReport.billing_records(orderNo) return DevicePortReport.calculate(powerRecords, mapDict)