# coding=utf-8 import datetime import logging import typing from apilib.monetary import VirtualCoin, RMB, Ratio from apps.web.constant import Const, DEALER_CONSUMPTION_AGG_KIND from apps.web.core.device_define.kunyuanCar import DEFAULT_VERSION from apps.web.device.models import Group, Device from apps.web.eventer import EventBuilder from apps.web.eventer.base import WorkEvent from apps.web.user.models import ConsumeRecord, CardRechargeOrder from apps.web.user.models import ServiceProgress, MyUser from apps.web.user.transaction_deprecated import refund_money if typing.TYPE_CHECKING: from apps.web.user.models import Card logger = logging.getLogger(__name__) class builder(EventBuilder): def __getEvent__(self, device_event): event_data = self.deviceAdapter.analyze_event_data(device_event['data']) if event_data is None or 'cmdCode' not in event_data: return None if 'ack' in device_event: self.deviceAdapter._response_ack(device_event["ack"]) return ChangYuanCarWorkEventer(self.deviceAdapter, event_data) class ChangYuanCarWorkEventer(WorkEvent): def do(self, **args): cmdCode = self.event_data.get("cmdCode") if cmdCode == "A8": self.do_finished() elif cmdCode == "AE": self.do_status() elif cmdCode == "B1": self.do_start() elif cmdCode == "B2": self.do_report() elif cmdCode == "C1": self.do_card() else: logger.error("error cmdCode".format(cmdCode)) def do_finished(self): """ 结束事件 """ cardNo = self.event_data["cardNo"] port = self.event_data["port"] portCache = Device.get_port_control_cache(self.device.devNo, port) # 订单版本的 # if "sequanceNo" in self.event_data and portCache.get("version") > str(DEFAULT_VERSION): # return self._do_new_finished() if not cardNo: self._do_net_pay_finish() else: self._do_card_pay_finish() def do_status(self): """ 枪把状态变更 """ status = self.event_data.get("status") port = self.event_data.get("port") Device.update_dev_control_cache(self.device.devNo, {port: {"status": status}}) def do_start(self): """ 整体启动后 会上报一次启动信息 """ logger.info("[KYCAR_do_start] start, dev = {} , event_info = {}".format(self.device.devNo, self.event_data)) cardNo = self.event_data["cardNo"] money = RMB(self.event_data["money"]) port = self.event_data["port"] # 没有卡号的情况 暂时不需要处理 if not cardNo: return card = self.update_card_dealer_and_type(cardNo) # type: Card if not card: return logger.info("[KYCAR_do_start] not find card = {}".format(cardNo)) # 然后对卡进行扣费使用 创建消费记录 self.consume_money_for_card(card, money) orderNo, cardOrderNo = self.record_consume_for_card(card, money) # 创建消费记录 通知用户 扣费 self.notify_balance_has_consume_for_card(card, money) logger.info("[KYCAR_do_start] finished deduct money card = {}, orderNo = {}, cardConsumeOrder = {}".format(cardNo, orderNo, cardOrderNo)) # 注册服务 个人中心辨别 ServiceProgress.register_card_service( dev=self.device, port=int(port), card=card ) portCache = { "isStart": True, "openId": card.openId, "startTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "status": Const.DEV_WORK_STATUS_WORKING, "price": int(money), "orderNo": orderNo, "cardOrderNo": cardOrderNo } Device.update_dev_control_cache(self.device.devNo, {port: portCache}) def do_report(self): """ 状态上报 """ port = self.event_data["port"] portCache = Device.get_port_control_cache(self.device.devNo, port) # 订单版本的 if "sequanceNo" in self.event_data and portCache.get("version") > str(DEFAULT_VERSION): order = ConsumeRecord.objects.filter(sequanceNo=self.event_data["sequanceNo"]).first() if not order: orderNo, openId = portCache.get("orderNo"), portCache.get("openId") order = ConsumeRecord.objects.filter(orderNo=orderNo).first() else: orderNo, openId = portCache.get("orderNo"), portCache.get("openId") order = ConsumeRecord.objects.filter(orderNo=orderNo).first() if not order: logger.info("[{} do_report not find order, portCache = {}]".format(self.__class__.__name__, portCache)) return leftMoney = RMB(self.event_data.get("leftBalance")) usedElec = self.event_data.get("usedElec") temperature = self.event_data.get("temperature") power = self.event_data.get("power") voltage = self.event_data.get("voltage") sid = self.event_data.get("sid") data = { "leftMoney": str(leftMoney), "usedElec": usedElec, "temperature": temperature, "power": power, "voltage": voltage, "sid": sid } if "sequanceNo" in self.event_data: data["sequanceNo"] = self.event_data["sequanceNo"] order.servicedInfo = data order.save() portCache.update(data) Device.update_dev_control_cache(self.device.devNo, {str(order.used_port): portCache}) def do_card(self): """ 查询卡的余额 """ cardNo = self.event_data["cardNo"] card = self.update_card_dealer_and_type(cardNo) # type: Card if not card: logger.info("card <{}> not refund".format(cardNo)) return # 进行卡的充值 card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id)) self.recharge_id_card(card=card, rechargeType='append', order=card_recharge_order) card.reload() # 进行余额查询 self.deviceAdapter._response_card(card) def _do_net_pay_finish(self): port = self.event_data["port"] devCache = Device.get_dev_control_cache(self.device.devNo) or dict() portCache = devCache.get(str(port), dict()) openId = portCache.pop("openId", None) vCardId = portCache.get("vCardId") leftMoney = self.event_data["leftMoney"] payCoins = portCache["coins"] payPrice = portCache["price"] # 下发的是price 则退款的时候需要使用price进行一次计算 # 对于leftMoney 做一次校验保护 主板返回的剩余金额可能是错误的 leftMoney = min(RMB(leftMoney), RMB(payCoins)) if not openId: logger.info("ChangYuan net pay finish with no openId! {}".format(self.device["devNo"])) return nowTime = datetime.datetime.now() consumeDict = { "elec": self.event_data["usedElec"], "duration": self.event_data["usedTime"], "coin": str(VirtualCoin(payCoins)), "spendMoney": str(RMB(RMB(payCoins) - RMB(leftMoney))), "reason": self.event_data["reason"], "finishedTime": datetime.datetime.strftime(nowTime, "%Y-%m-%d %H:%M:%S") } user = MyUser.objects.filter(openId=openId, groupId=self.device.groupId).first() group = Group.get_group(self.device.groupId) logger.info("ChangYuan net pay finish and start to notify user! {}".format(self.device["devNo"])) self.notify_user( managerialOpenId = user.managerialOpenId if user else "", templateName = "service_complete", title = u"\\n\\n结束原因:\\t\\t{reason}\\n\\n设备编号:\\t\\t{logicalCode}\\n\\n服务地址:\\t\\t{group}\\n\\n使用时长:\\t\\t{duration}分钟\\n\\n付款金额:\\t\\t{coin}".format( reason = self.event_data["reason"], logicalCode = self.device.logicalCode, group = group.address, duration = self.event_data["usedTime"], coin = u"{}金币".format(payCoins) ), service = u"汽车桩充电服务", finishTime = datetime.datetime.strftime(nowTime, "%Y-%m-%d %H:%M:%S"), remark = u'谢谢您的支持' ) refundMoney = leftMoney if refundMoney > RMB(0): # 金币支付的并且打开了金币退款的开关 if not vCardId and self.device.is_auto_refund: # 重新计算退币数量 防止coins不等于price refundMoney = VirtualCoin(payCoins) * Ratio(refundMoney.amount / RMB(payPrice).amount) refund_money(self.device, refundMoney, openId) consumeDict.update({ DEALER_CONSUMPTION_AGG_KIND.COIN: (VirtualCoin(payCoins) - refundMoney).mongo_amount, DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS: refundMoney.mongo_amount }) self.notify_user( user.managerialOpenId if user else '', 'refund_coins', **{ 'title': u"退款", 'backCount': u'%s(金币)' % refundMoney, 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') } ) # 剩下的 支付方式有 1.虚拟卡支付 2.金币支付但是没有打开金币支付的开关 else: logger.info("not need to refund pay type! {}".format(self.event_data)) ServiceProgress.update_progress_and_consume_rcd( self.device["ownerId"], { "open_id": openId, "device_imei": self.device["devNo"], "port": int(port), "isFinished": False }, consumeDict) Device.clear_port_control_cache(self.device.devNo, port) def _do_card_pay_finish(self): logger.info("[KYCar_do_card_pay_finish] dev <{}> card finished, event_info = {}".format(self.device.devNo, self.event_data)) port = self.event_data["port"] cardNo = self.event_data["cardNo"] reason = self.event_data["reason"] usedElec = self.event_data["usedElec"] usedTime = self.event_data["usedTime"] leftMoney = self.event_data["leftMoney"] card = self.update_card_dealer_and_type(cardNo) if not card: return logger.info("[KYCar_do_card_pay_finish] dev <{}> card finished, not find card = {}".format(self.device.devNo, cardNo)) # 获取缓存里面的一些信息 devCache = Device.get_dev_control_cache(self.device.devNo) or dict() portCache = devCache.get(port) or dict() price = portCache.get("price", 0) if not price: return logger.info("[KYCar_do_card_pay_finish] dev <{}> card finished, not find cache = {}".format(self.device.devNo, cardNo)) cacheLeftMoney = portCache.get("leftMoney", price) # 防止主板误报 取最小的一个 然后取大于0的 leftMoney = max(min(RMB(price), RMB(leftMoney), RMB(cacheLeftMoney)), RMB(0)) # 对卡进行退费 self.refund_money_for_card(money=leftMoney, cardId=str(card.id)) logger.info("[KYCar_do_card_pay_finish] dev <{}> card finished, card = {}, refund = {}".format(self.device.devNo, cardNo, leftMoney)) # 通知用户 group = Group.get_group(self.device["groupId"]) self.notify_user( managerialOpenId=card.managerialOpenId, templateName="service_complete", title=u"\\n\\n结束原因:\\t\\t{reason}\\n\\n设备编号:\\t\\t{logicalCode}\\n\\n服务地址:\\t\\t{group}\\n\\n使用时长:\\t\\t{duration}分钟".format( reason=u"充电结束", logicalCode=self.device["logicalCode"], group=group.get("address", ""), duration=self.event_data["usedTime"], ), service=u"本次充电结束", finishTime=datetime.datetime.strftime(datetime.datetime.now(), "%Y-%m-%d %H:%M:%S"), remark=u'谢谢您的支持' ) servicedInfo = { "usedElec": usedElec, "duration": usedTime, "cardNo": cardNo, "refundedMoney": leftMoney, "reason": reason, "finishedTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") } ServiceProgress.update_progress_and_consume_rcd( self.device["ownerId"], { "device_imei": self.device["devNo"], "cardId": str(card.id), "port": int(port), "isFinished": False }, servicedInfo ) Device.clear_port_control_cache(self.device.devNo, port) def _do_new_finished(self): """ 新版本的结束 通过sequanceNo直接找到订单进行处理 """ order = ConsumeRecord.objects.filter(sequanceNo=self.event_data["sequanceNo"]).first() # type: ConsumeRecord if not order: logger.info("[ChangYuanCarWorkEventer _do_new_finished] not find consume order, sequanceNo = {}".format(self.event_data["sequanceNo"])) return if order.status == "finished": logger.info("[ChangYuanCarWorkEventer _do_new_finished] order status finished, sequanceNo = {}".format(self.event_data["sequanceNo"])) return leftMoney = self.event_data["leftMoney"] refundMoney = min(RMB(leftMoney), RMB(order.money)) nowTime = datetime.datetime.now() user = order.user self.notify_user( managerialOpenId=user.managerialOpenId if user else "", templateName="service_complete", title=u"\\n\\n结束原因:\\t\\t{reason}\\n\\n设备编号:\\t\\t{logicalCode}\\n\\n服务地址:\\t\\t{group}\\n\\n使用时长:\\t\\t{duration}分钟".format( reason=self.event_data["reason"], logicalCode=self.device.logicalCode, group=self.device.group.get("address", ""), duration=self.event_data["usedTime"], ), service=u"汽车桩充电服务", finishTime=datetime.datetime.strftime(nowTime, "%Y-%m-%d %H:%M:%S"), remark=u'谢谢您的支持' ) consumeDict = {"elec": self.event_data["usedElec"], "duration": self.event_data["usedTime"], "coin": str(VirtualCoin(order.coin)), "spendMoney": str(RMB(RMB(order.money) - RMB(leftMoney))), "reason": self.event_data["reason"], "finishedTime": datetime.datetime.strftime(nowTime, "%Y-%m-%d %H:%M:%S"), "sequanceNo": self.event_data["sequanceNo"]} if self.device.is_auto_refund and RMB(leftMoney) > RMB(0): refundMoney = VirtualCoin(order.coin) * Ratio(refundMoney.amount / RMB(order.money).amount) cardNo = self.event_data["cardNo"] if not cardNo: refund_money(self.device, refundMoney, user.openId) else: card = self.update_card_dealer_and_type(cardNo) self.refund_money_for_card(money=leftMoney, cardId=str(card.id)) consumeDict.update({ DEALER_CONSUMPTION_AGG_KIND.COIN: (VirtualCoin(order.money) - refundMoney).mongo_amount, DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS: refundMoney.mongo_amount }) self.notify_user( user.managerialOpenId if user else '', 'refund_coins', **{ 'title': u"退款", 'backCount': u'%s(金币)' % refundMoney, 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') } ) ServiceProgress.update_progress_and_consume_rcd( self.device.ownerId, { "open_id": user.openId, "device_imei": self.device.devNo, "port": order.used_port, "isFinished": False }, consumeDict ) Device.clear_port_control_cache(self.device.devNo, order.used_port) order.status = "finished" order.save()