123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- import datetime
- import logging
- import time
- import typing
- from mongoengine import DoesNotExist, ValidationError
- from apilib.monetary import Ratio, VirtualCoin, RMB
- from apps.web.constant import DeviceCmdCode, Const, DEALER_CONSUMPTION_AGG_KIND, USER_RECHARGE_TYPE
- from apps.web.core.device_define.changyuan import CYCardMixin
- from apps.web.core.networking import MessageSender
- from apps.web.core.payment import WithdrawGateway
- from apps.web.device.models import Device, Group
- from apps.web.eventer.base import WorkEvent
- from apps.web.eventer import EventBuilder
- from apps.web.report.ledger import Ledger
- from apps.web.user.models import ConsumeRecord
- from apps.web.user.transaction_deprecated import refund_money, refund_cash
- from apps.web.user.models import ServiceProgress, RechargeRecord, MyUser
- logger = logging.getLogger(__name__)
- class builder(EventBuilder):
- def __getEvent__(self, device_event):
- if device_event['cmd'] == 100:
- event_data = self.deviceAdapter.analyze_event_data(device_event['data'])
- if event_data is None or 'cmdCode' not in event_data:
- return None
- if 'a8id' in device_event:
- event_data.update({
- 'a8id': device_event.get('a8id')
- })
- return ChangYuanCaiWorkEventer(self.deviceAdapter, event_data)
- class ChangYuanCaiWorkEventer(CYCardMixin, WorkEvent):
- def _answer_a8(self, a8id):
- MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, {
- "IMEI": self.device["devNo"],
- "funCode": "FA",
- "data": "",
- "a8id": a8id
- })
-
- def do(self, **args):
- devNo = self.device["devNo"]
- logger.info("chang yuan car event detected, devNo=%s, info=%s" % (devNo, self.event_data))
- # 充电停止状态
- if self.event_data["cmdCode"] == "A8":
- # 收到A8 指令后需要回复模块,将其从队列中清除
- self._answer_a8(self.event_data.get("a8id", ""))
- cardNo = self.event_data["cardNo"]
- self.do_finish(cardNo)
- # 待机情况下枪把连接状态 事件
- elif self.event_data["cmdCode"] == "AE":
- logger.info("Charging pile connected event")
- status = self.event_data["status"]
- if status == "B1":
- Device.update_dev_control_cache(self.device["devNo"], {"status": Const.DEV_WORK_STATUS_CONNECTED})
- elif status == "B5":
- Device.update_dev_control_cache(self.device["devNo"], {"status": Const.DEV_WORK_STATUS_IDLE})
- else:
- logger.error("undefined status, dev is %s, status is %s" % (devNo, status))
-
- elif self.event_data["cmdCode"] == "B1":
- cardNo = self.event_data["cardNo"]
- if cardNo != "00000000":
- # 判断是否是失窃卡 不存在此卡说明卡未被录入且第一次使用,需要创建
- card = self.update_card_dealer_and_type(self.event_data["cardNo"])
- if not self.check_card_can_use(card):
- logger.info("[ChangYuanCaiWorkEventer do_event_start] check card not can use devNo = {}".format(self.device.devNo))
- self.notify_invalid_card_to_dealer(self.event_data["cardNo"], card)
- return self.deviceAdapter.stop()
- devCache = {
- "isStart": True,
- "startTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
- 'status': Const.DEV_WORK_STATUS_WORKING,
- 'finishedTime': int(time.time()) + 12 * 60 * 60
- }
- Device.update_dev_control_cache(self.device["devNo"], devCache)
- if not card:
- return
- ServiceProgress.register_card_service(
- self.device,
- 0,
- card,
- )
- # 心跳连接,每3分钟一次,及时更新订单的当前使用消费情况
- elif self.event_data["cmdCode"] == "B2":
- devInfo = Device.get_dev_control_cache(self.device["devNo"]) or dict()
- openId = devInfo.get("openId")
- orderNo = devInfo.get("orderNo")
- if not openId or not orderNo:
- logger.info("dev <{}> heart beat but not openId, {}".format(self.device.devNo, self.event_data))
- return
- leftBalance = RMB(self.event_data["leftBalance"])
- # A6指令会查询 剩余金额 在经销商查看端口的时候 这个地方也顺便更新一下订单的
- try:
- record = ConsumeRecord.objects.get(orderNo=orderNo, openId=openId) # type:ConsumeRecord
- record.servicedInfo["leftMoney"] = str(leftBalance)
- record.save()
- devInfo.update({"leftMoney": str(leftBalance)})
- Device.update_dev_control_cache(self.device.devNo, devInfo)
- except Exception as e:
- logger.exception(e)
- return
- elif self.event_data["cmdCode"] == "B3":
- beforeRefund = self.event_data.get("beforeRefund")
- refund = self.event_data.get("refund")
- afterRefund = self.event_data.get("afterRefund")
- cardNo = self.event_data.get("cardNo")
- if VirtualCoin(beforeRefund) + VirtualCoin(refund) != VirtualCoin(afterRefund):
- logger.info("bad refund event, beforeRefund is {}, refund is {} afterRefund is {}".format(
- beforeRefund, refund, afterRefund
- ))
- return
- card = self.update_card_dealer_and_type(cardNo, cardType="IC")
- if not card:
- logger.info("bad cardNo cardNo is {}".format(cardNo))
- return
- if VirtualCoin(card.balance) != VirtualCoin(beforeRefund):
- logger.info(
- "beforeRefund isn't equal card balance, cardNo is {}, beforeRefund is {}, card balance is {}".format(
- cardNo, beforeRefund, card.balance
- ))
- return
- self.refund_money_for_card(RMB(refund), str(card.id))
- self.notify_user(
- card.managerialOpenId,
- 'refund_coins',
- **{
- 'title': u"充电桩返费",
- 'backCount': u'%s' % refund,
- 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
- }
- )
- else:
- logger.error("unknown command , so event doesn't work!")
- def do_net_pay_finish(self):
- """
- 网络支付 结束事件处理, 现金返还原路 金币退还金币 现金退还现金
- 2021-05-27修改流程 将退款和分账分开 进来之后 如果该笔订单 有充值金额 并且没有分账 直接先分账 后面退费流程和主退款流程统一
- :return:
- """
- devCache = Device.get_dev_control_cache(self.device["devNo"])
- openId = devCache.pop("openId", None)
- coins = devCache.get("coins")
- price = devCache.get("price")
- rechargeRcdId = devCache.get("rechargeRcdId")
- vCardId = devCache.get("vCardId")
- # 进入之后优先进行分账
- rechargeRecord = self.do_ledger(rechargeRcdId)
- 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["electricNum"],
- "duration": self.event_data["chargeTime"],
- "coin": str(VirtualCoin(coins)),
- "spendMoney": str(RMB(price - self.event_data["balance"])),
- "reason": self.event_data["desc"],
- "finishedTime": datetime.datetime.strftime(nowTime, "%Y-%m-%d %H:%M:%S")
- }
- user = MyUser.objects.filter(openId=openId, groupId=self.device.groupId).first()
- group = self.device.group
- logger.info("ChangYuan net pay finish and start to notify user! {}".format(self.device["devNo"]))
- extra = [
- {u"使用时长": u"{} 分钟".format(self.event_data['chargeTime'])},
- {u"付款金额": u"{} {}".format(coins, u"金币" if not rechargeRcdId else u"元")}
- ]
- self.notify_user_service_complete(
- service_name=u"充电",
- openid=user.managerialOpenId if user else "",
- port=self.event_data['port'],
- address=group.address,
- reason=self.event_data["desc"],
- finished_time=nowTime.strftime("%Y-%m-%d %H:%M:%S"),
- extra=extra
- )
- # 走到这个地方的时候 只要是支付现金的订单 都已经分账了 所以可以不论条件 只要存在rechargeRecord 直接执行退款 但是如果是金币的 则需要判断金币是否退钱
- refundMoney = RMB(self.event_data["balance"])
- if refundMoney > RMB(0):
- # 现金支付的
- if rechargeRecord and rechargeRecord.isQuickPay:
- # 由于前边已经分账了 这个地方直接还是需要从经销商统计里面扣除的
- refund_order = refund_cash(
- rechargeRecord, refundMoney, VirtualCoin(0),
- user = user, minus_total_consume = VirtualCoin(rechargeRecord.coins))
- if refund_order:
- logger.info("refund cash apply success!")
- consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.REFUNDED_CASH: refundMoney.mongo_amount})
- self.notify_user(
- user.managerialOpenId if user else '',
- 'refund_coins',
- **{
- 'title': u"退款(退款金额将于1-5个工作日内返还)",
- 'backCount': u'%s(元)' % refundMoney,
- 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
- }
- )
- else:
- logger.info("refund cash fail")
- # 金币支付的并且打开了金币退款的开关
- elif not vCardId and self.device.is_auto_refund:
- # 重新计算退币数量 防止coins不等于price
- refundMoney = VirtualCoin(coins) * Ratio(refundMoney.amount / RMB(price).amount)
- refund_money(self.device, refundMoney, openId)
- consumeDict.update({
- DEALER_CONSUMPTION_AGG_KIND.COIN: (VirtualCoin(coins) - 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": 0,
- "isFinished": False
- },
- consumeDict
- )
- # 上报结束的消息到山东省平台
- startTime = Device.get_dev_control_cache(self.device.devNo).get("startTime")
- if startTime:
- consumeDict.update({"startTime": startTime})
- self.notify_to_sd_norther(portStr="1", consumeDict=consumeDict)
- Device.invalid_device_control_cache(self.device.devNo)
- def do_ic_card_pay_finish(self):
- """
- 处理刷卡结束事件
- :return:
- """
- # 离线卡支付,此处只对离线卡的 金额发生变化 其余均只做日志类处理,不污染用户消费记录数据
- servicedInfo = {
- "cardNo": self.event_data["cardNo"],
- "elec": self.event_data["electricNum"], # 充电电量
- "duration": self.event_data["chargeTime"], # 充电时间
- "coin": str(self.event_data["payMoney"]), # 付款金额
- "cardBalance": str(self.event_data["cardBalance"]), # 卡内余额
- "spendMoney": str(self.event_data["payMoney"] - self.event_data["balance"]), # 实际消费
- "reason": self.event_data["desc"],
- "finishedTime": datetime.datetime.strftime(datetime.datetime.now(), "%Y-%m-%d %H:%M:%S")
- }
- cardNo = self.event_data.get("cardNo")
- payMoney = self.event_data.get("payMoney")
- cardBalance = self.event_data.get("cardBalance")
- card = self.update_card_dealer_and_type(cardNo, cardType="IC")
- if not card: # zjl
- Device.invalid_device_control_cache(self.device.devNo)
- return # zjl
- card.balance = RMB(cardBalance)
- try:
- self.record_consume_for_card(card, RMB(payMoney), servicedInfo=servicedInfo)
- card.save()
- except Exception as e:
- logger.error(e)
- else:
- extra = [
- {u"使用卡号": u"{}".format(cardNo)},
- {u"使用时长": u"{} 分钟".format(self.event_data['chargeTime'])},
- {u"付款金额": u"{} 元".format(self.event_data["payMoney"])},
- {u"待返费": u"{} 元 请将卡片贴近充电桩返费".format(self.event_data['balance'])}
- ]
- self.notify_user_service_complete(
- service_name=u"充电",
- openid=card.managerialOpenId,
- port=self.event_data['port'],
- address=self.device.group.address,
- reason=self.event_data["reason"],
- finished_time=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
- extra=extra
- )
- finally:
- ServiceProgress.update_progress_and_consume_rcd(
- self.device["ownerId"],
- {
- "device_imei": self.device["devNo"],
- "cardId": str(card.id),
- "port": 0,
- "isFinished": False
- },
- {}
- )
- # 推送订单结束信息
- startTime = Device.get_dev_control_cache(self.device.devNo).get("startTime")
- if startTime:
- servicedInfo.update({"startTime": startTime})
- self.notify_to_sd_norther(portStr="1", consumeDict=servicedInfo)
- Device.invalid_device_control_cache(self.device.devNo)
- def do_finish(self, cardNo):
- if cardNo == "00000000":
- self.do_net_pay_finish()
- else:
- self.do_ic_card_pay_finish()
|