# coding=utf-8 import datetime import logging import time from apilib.monetary import VirtualCoin, RMB from apilib.utils_string import make_title_from_dict from apps.web.constant import DEALER_CONSUMPTION_AGG_KIND, DeviceCmdCode from apps.web.core.accounting import Accounting from apps.web.device.models import Device, Group from apps.web.eventer import EventBuilder from apps.web.eventer.base import WorkEvent, FaultEvent from apps.web.user.transaction_deprecated import refund_money from apps.web.user.models import ServiceProgress, Card, MyUser, CardRechargeOrder, ConsumeRecord, MonthlyPackage logger = logging.getLogger(__name__) class builder(EventBuilder): def __getEvent__(self, device_event): event_data = self.deviceAdapter.analyze_event_data(device_event['data']) if not event_data: return event_data['raw_msg'] = device_event cmdCode = event_data.get("cmdCode") if cmdCode in []: return ChaoChenFaultEventer(self.deviceAdapter, event_data) return ChaoChenWorkEventer(self.deviceAdapter, event_data) class ChaoChenFaultEventer(FaultEvent): pass class ChaoChenWorkEventer(WorkEvent): FINISH_REASON = { 0x00: u"充电服务已经结束", 0x01: u"用户手动停止", 0x02: u"电量充满,充点服务已经结束", 0x03: u"当前充电端口出现故障,充电服务结束", 0x04: u"功率超出最大限制", 0x05: u"当前刷卡结束充电", 0x06: u"未连接充电器,充电服务已经结束", 0x07: u"远程停止端口,充电服务已经结束", 0x08: u"烟雾报警,充电服务已经结束", } def do(self): cmdCode = self.event_data.get("cmdCode") logger.info("receive message for dev <{}>, message is <{}>".format(self.device.devNo, self.event_data)) if cmdCode == "B6": self._do_offline_card_finish() elif cmdCode == "B5": self._do_offline_coin_finish() elif cmdCode == "B4": pass elif cmdCode == "C9": self._do_netpay_finish() elif cmdCode == "C8": self._do_online_card_finish() elif cmdCode == "C4": self._do_status_report() elif cmdCode == "EA": self._do_elec_finish() elif cmdCode == "F0": self._do_elec_report() elif cmdCode == "F9": self._do_consume_id_card() elif cmdCode == "FA": self._do_refund_id_card() def _do_offline_card_finish(self): portStr = self.event_data['portStr'] cardNo = self.event_data['cardNo'] usedCoins = self.event_data['usedCoins'] usedTime = self.event_data['usedTime'] balance = self.event_data['balance'] card = self.update_card_dealer_and_type(cardNo=cardNo, cardType="IC") if not card: return attachParas = { "chargeIndex": portStr } servicedInfo = { DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: float(str(usedCoins)), DEALER_CONSUMPTION_AGG_KIND.DURATION: float(str(usedTime)) } self.record_consume_for_card(card, VirtualCoin(usedCoins), servicedInfo=servicedInfo, attachParas=attachParas) Card.update_balance(cardId=card.id, balance=RMB(balance)) def _do_offline_coin_finish(self): coins = self.event_data['usedCoins'] self.record_consume_for_coin(VirtualCoin(coins)) Accounting.recordOfflineCoin( self.device, int(time.time()), int(float(coins)) ) def _do_netpay_finish(self): portStr = self.event_data['portStr'] usedTime = self.event_data['usedTime'] leftTime = self.event_data['leftTime'] devCache = Device.get_dev_control_cache(self.device.devNo) or dict() portCache = devCache.get(portStr) or dict() if not portCache: return openId = portCache.get("openId") consumeDict = { "duration": usedTime, "finishedTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") } if self.device.is_auto_refund: coins = VirtualCoin(portCache.get("coins", 0)) usedCoins = min(coins * (float(usedTime) / (float(usedTime) + float(leftTime))), coins) refundCoins = coins - usedCoins refund_money(self.device, refundCoins, openId) consumeDict.update( { DEALER_CONSUMPTION_AGG_KIND.COIN: float(str(coins)), DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: float(str(usedCoins)), DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS: float(str(refundCoins)) } ) ServiceProgress.update_progress_and_consume_rcd( self.device["ownerId"], { "open_id": openId, "device_imei": self.device.devNo, "port": int(portStr), "isFinished": False }, consumeDict ) Device.clear_port_control_cache(self.device.devNo, portStr) def _do_online_card_finish(self): pass def _do_status_report(self): pass def _do_elec_finish(self): """ AA0100EA 03 02 0060 20 暂时理解为 只有电量会上报 :return: """ chargeMode = self.device.get("otherConf", dict()).get("chargeType", self.deviceAdapter.DEFAULT_CHARGE_TYPE) # 时间模式直接忽略 if chargeMode == self.deviceAdapter.DEFAULT_CHARGE_TYPE: return reasonCode = self.event_data.get("reasonCode", "") leftElec = self.event_data.get("leftElec", 0) portStr = self.event_data.get("portStr") reason = self.FINISH_REASON.get(reasonCode) devCache = Device.get_dev_control_cache(self.device.devNo) portCache = devCache.get(portStr, dict()) if not portCache: return openId = portCache.get("openId") needElec = portCache.get("needElec") if not openId or not needElec: Device.clear_port_control_cache(self.device.devNo, portStr) return consumeDict = { "needElec": needElec, "usedElec": needElec - leftElec, "leftElec": leftElec, "finishData": self.event_data["raw_msg"]["data"] } if self.device.is_auto_refund: coins = VirtualCoin(portCache.get("coins", 0)) refundCoins = VirtualCoin(coins) * (float(leftElec)/float(needElec)) refundCoins = min(refundCoins, coins) usedCoins = coins - refundCoins refund_money(self.device, refundCoins, openId) consumeDict.update( { DEALER_CONSUMPTION_AGG_KIND.COIN: float(str(coins)), DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: float(str(usedCoins)), DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS: float(str(refundCoins)) } ) ServiceProgress.update_progress_and_consume_rcd( self.device["ownerId"], { "open_id": openId, "device_imei": self.device.devNo, "port": int(portStr), "isFinished": False }, consumeDict ) Device.clear_port_control_cache(self.device.devNo, portStr) group = Group.get_group(self.device.get("groupId")) user = MyUser.objects.filter(openId=openId, groupId=self.device.get("groupId")).first() self.notify_user( managerialOpenId=user.managerialOpenId if user else "", templateName="service_complete", title=u"\\n\\n结束原因:\\t\\t{reason}\\n\\n设备编号:\\t\\t{logicalCode}-{port}\\n\\n服务地址:\\t\\t{group}".format( reason=reason, logicalCode=self.device["logicalCode"], port=portStr, group=group["address"], ), service=u"充电桩充电服务", finishTime=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), remark=u'谢谢您的支持' ) def _do_elec_report(self): totalElec = self.event_data['totalElec'] otherConf = self.device.get("otherConf", dict()) otherConf.update({"totalElec": totalElec}) Device.objects.filter(devNo=self.device.devNo).update(otherConf=otherConf) Device.invalid_device_cache(self.device.devNo) def _do_consume_id_card(self): cardNo = self.event_data.get('cardNo', None) port = self.event_data.get('port', None) card = self.update_card_dealer_and_type(str(cardNo)) consumeMoney = RMB(self.event_data.get('consumeMoney')) if not card or not card.openId or card.frozen: logger.info('no find card devNo=<{}> cardNo=<{}>'.format(self.device.devNo, cardNo)) resp = { 'funCode': '{:02X}F9'.format(int(port)), 'content': '00', } else: # 获取有余额的月票 monthly_ticket = MonthlyPackage.get_user_ticket(openId=card.openId, groupId=self.device.groupId, cardNo=card.cardNo) if len(monthly_ticket): package = {'coins': 1, 'unit': u'次', 'time': 1} ticket = MonthlyPackage.get_can_use_one(monthly_ticket, package) if ticket: resp = { 'funCode': '{:02X}F9'.format(int(port)), 'content': '01{:0>8X}{:0>6X}'.format(cardNo, 0), } attachParas = { 'chargeIndex': port } servicedInfo = { 'cardNo': cardNo, 'chargeIndex': port, 'monthpackage': str(ticket.id), } orderNo, cardOrderNo = self.record_consume_for_card(card, servicedInfo=servicedInfo, attachParas=attachParas, money=RMB(0.0), desc=u'包月套餐') order = ConsumeRecord.objects.get(orderNo=orderNo) ticket.deduct(order) else: resp = { 'funCode': '{:02X}F9'.format(int(port)), 'content': '00', } else: # 检查刷卡的是否有充值订单详情 card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id)) if card_recharge_order: result = self.recharge_id_card(card=card, rechargeType='append', order=card_recharge_order) card.reload() if card.balance >= consumeMoney: resp = { 'funCode': '{:02X}F9'.format(int(port)), 'content': '01{:0>8X}{:0>6X}'.format(cardNo, RMB.yuan_to_fen(card.balance - consumeMoney)), } attachParas = { 'chargeIndex': port } servicedInfo = { 'cardNo': cardNo, 'chargeIndex': port } self.consume_money_for_card(card, consumeMoney) orderNo, cardOrderNo = self.record_consume_for_card(card, consumeMoney, servicedInfo=servicedInfo, attachParas=attachParas) else: resp = { 'funCode': '{:02X}F9'.format(int(port)), 'content': '00', } self.deviceAdapter._send_data(cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, **resp) def _do_refund_id_card(self): cardNo = self.event_data.get('cardNo', None) port = self.event_data.get('port', None) card = self.update_card_dealer_and_type(str(cardNo)) refundMoney = RMB(self.event_data.get('refundMoney')) if not card or not card.openId or card.frozen: logger.info('no find card devNo=<{}> cardNo=<{}>'.format(self.device.devNo, cardNo)) resp = { 'funCode': '{:02X}FA'.format(int(port)), 'content': '00', } else: resp = { 'funCode': '{:02X}FA'.format(int(port)), 'content': '01{:0>8X}'.format(cardNo), } if refundMoney >= RMB(0): monthly_ticket = MonthlyPackage.get_user_ticket(openId=card.openId, groupId=self.device.groupId, cardNo=card.cardNo) if len(monthly_ticket): pass else: self.refund_money_for_card(refundMoney, str(card.id)) refundDict = { 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') } titleDictList = [ {u'设备编号': self.device['logicalCode']}, ] refundDict.update( { 'title': make_title_from_dict(titleDictList), 'backCount': u'金币:%s' % refundMoney } ) self.notify_user(card.managerialOpenId, 'refund_coins', **refundDict) self.deviceAdapter._send_data(cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, **resp)