123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- import datetime
- import logging
- import time
- from mongoengine import DoesNotExist
- from typing import Optional
- from apilib.monetary import RMB, VirtualCoin
- from apps.web.constant import Const
- from apps.web.core.device_define.huopo import HuopoCacheMgr, DefaultParams
- from apps.web.core.exceptions import ServiceException
- from apps.web.device.models import DeviceDict, Group, Device
- from apps.web.eventer.base import WorkEvent
- from apps.web.eventer import EventBuilder
- from apps.web.user.models import ServiceProgress, MyUser, ConsumeRecord, CardConsumeRecord, MonthlyPackage, Card
- logger = logging.getLogger(__name__)
- class builder(EventBuilder):
- def __getEvent__(self, device_event):
- event_data = self.deviceAdapter.analyze_event_data(device_event['data'])
- return HuoPoEvent(self.deviceAdapter, event_data)
- """
- 经销商设置
- 功能 方向
- 进入 左(1)
- 出门 右(2)
- """
- class HuoPoEvent(WorkEvent):
- def __init__(self, smartBox, event_data):
- super(HuoPoEvent, self).__init__(smartBox, event_data)
- @staticmethod
- def _get_service_progress(openId):
- """检查是否是出门"""
- devTypeCode = Const.DEVICE_TYPE_CODE_HP_GATE
- sp = ServiceProgress.objects.filter(open_id = openId, devTypeCode = devTypeCode, isFinished = False)
- if not sp:
- return
- return sp
- @staticmethod
- def _check_parking(dev):
- # type:(DeviceDict)->bool
- _key = "maxParking"
- maxParking = dev["otherConf"].get(_key, 250)
- serviceInfo = HuopoCacheMgr.get_parking_cache(dev.groupId)
- if serviceInfo is not None:
- parkingInfo = serviceInfo.get("parkingInfo", {})
- parkingNum = parkingInfo.get(_key, 0)
- else:
- parkingNum = 0
- return int(parkingNum) < int(maxParking)
- @staticmethod
- def _update_parking(dev, enter = True):
- # type:(DeviceDict, Optional[bool])->None
- _key = "maxParking"
- serviceInfo = HuopoCacheMgr.get_parking_cache(dev.groupId)
- if serviceInfo is not None:
- parkingInfo = serviceInfo.get("parkingInfo", {})
- parkingNum = parkingInfo.get(_key, 0)
- else:
- parkingInfo = {}
- parkingNum = 0
- if enter:
- parkingNum += 1
- else:
- if parkingNum > 0:
- parkingNum -= 1
- parkingInfo.update({_key: parkingNum})
- HuopoCacheMgr.set_parking_cache(dev.groupId, {"parkingInfo": parkingInfo})
- def do_virtual_card_user(self, card, vCard, dutou):
- """
- 虚拟卡用户直接启动,做单次记录
- :param dutou:
- :param card:
- :param vCard:
- :return:
- """
- # 开启道闸
- self.deviceAdapter._open()
- oper = 'enter' if dutou == '1' else 'out'
- # 记录相应的消费情况
- group = Group.get_group(self.device["groupId"])
- if oper == 'enter':
- attachParas = {
- "cardId": str(card.id),
- "vCardId": str(vCard.id),
- "startOper": oper,
- "startDutou": dutou
- }
- orderNo, cardOrderNo = self.record_consume_for_card(card, money=RMB(0.0), desc=u'使用绑定的虚拟卡')
- vCard.consume_hp_gate(openId = card.openId, group = group, dev = self.device, attachParas=attachParas)
- consumeRecord = ConsumeRecord.objects.filter(orderNo=orderNo).first()
- consumeRecord.attachParas = attachParas
- consumeRecord.attachParas.update({
- "orderNo": orderNo,
- "cardOrderNo": cardOrderNo
- })
- consumeRecord.servicedInfo.update({
- "oper": u'进门 卡号为%s' % card.cardNo if oper == 'enter' else u'读头数据异常'
- })
- try:
- consumeRecord.save()
- except Exception as e:
- logger.exception(e)
- else:
- # TODO: 只查3个月内的
- consumeRecord = ConsumeRecord.objects(openId = card.openId,
- dateTimeAdded__gte = (
- datetime.datetime.now() - datetime.timedelta(days = 90)),
- devNo = self.device['devNo']).order_by('-dateTimeAdded').hint(
- [('openId', 1), ('dateTimeAdded', -1)]).first()
- if not consumeRecord:
- logger.warning("[do_virtual_card_user] not find consumeRecord, openId={}, devNo={}".format(card.openId, self.device.devNo) )
- return
- duration = int((datetime.datetime.now() - consumeRecord.dateTimeAdded).total_seconds() / 60)
- if consumeRecord.servicedInfo != {"oper": u'进门 卡号为%s' % card.cardNo}:
- return
- else:
- consumeRecord.finishedTime = datetime.datetime.now()
- consumeRecord.attachParas.update({
- "endOper": oper,
- "endDutou": dutou
- })
- consumeRecord.servicedInfo.update({
- "oper": u'出门 卡号为%s' % card.cardNo if oper == 'out' else u'读头数据异常',
- "duration": duration,
- "finishedTime": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
- })
- try:
- consumeRecord.save()
- except Exception as e:
- logger.exception(e)
- # 将之前临时启动的服务清空
- try:
- for sp in self._get_service_progress(card.openId):
- sp.update(isFinished = True)
- except Exception as e:
- logger.error(e)
- def do_monthly_package_user(self, card, monthlyPackage, dutou): # type:(Card, MonthlyPackage, str) -> None
- """
- 包月用户使用包月卡
- :param card:
- :param monthlyPackage:
- :param dutou:
- :return:
- """
- # 尝试启动
- try:
- self.deviceAdapter._open()
- except ServiceException:
- return
- except Exception as e:
- logger.exception(e)
- return
- # 启动成功的情况下记录消费
- attachParas = {
- "cardId": str(card.id),
- "monthlyPackageId": str(monthlyPackage.id),
- }
- servicedInfo = {
- "oper": u'{} 卡号为 {}'.format(u"进门" if dutou == "1" else u"出门", card.cardNo)
- }
- orderNo, cardOrderNo = self.record_consume_for_card(
- card,
- money=RMB(0),
- desc=u"使用包月套餐",
- attachParas=attachParas,
- servicedInfo=servicedInfo
- )
- order = ConsumeRecord.objects.get(orderNo=orderNo)
- # 包月套餐抵扣
- monthlyPackage.deduct(order)
- def do_normal_user(self, card, dutou):
- sp = self._get_service_progress(card.openId)
- dev = Device.get_dev(self.device["devNo"])
- user = MyUser.objects.filter(openId = card.openId, group = dev.get("groupId")).first()
- oper = 'enter' if dutou == '1' else 'out'
- if not sp:
- # 校验停车人数
- if not self._check_parking(dev):
- self.notify_service_fault(card, u"停车人数已经达到上限!")
- return
- # 校验卡上是否还有余额,没有余额的卡不允许启动
- if RMB(card.balance) <= RMB(10):
- self.notify_service_fault(card, u"月票已过期或者尚未购买月票!当前实体卡内余额{},小于最低启动金额{}!".format(card.balance, 10))
- return
- washConfig = self.device["washConfig"]
- attachParas = {
- "washConfig": washConfig,
- "cardId": str(card.id),
- "startOper": oper,
- "startDutou": dutou
- }
- orderNo, cardOrderNo = self.record_consume_for_card(card, money = RMB(0.0))
- consumeRecord = ConsumeRecord.objects.filter(orderNo = orderNo).first()
- consumeRecord.attachParas = attachParas
- consumeRecord.attachParas.update({
- "orderNo": orderNo,
- "cardOrderNo": cardOrderNo
- })
- consumeRecord.servicedInfo.update({
- "oper": u'进门 卡号为%s' % card.cardNo if oper == 'enter' else u'读头数据异常'
- })
- # 开门
- self.deviceAdapter._open()
- # 建立当前服务信息
- new_service_progress = ServiceProgress(
- open_id = card.openId,
- device_imei = self.device["devNo"],
- devTypeCode = Const.DEVICE_TYPE_CODE_HP_GATE,
- attachParas = attachParas,
- start_time = int(time.time()),
- finished_time = int(time.time()) + 3600 * 24 * 365,
- consumes = [str(consumeRecord.id)],
- cardId = str(card.id),
- )
- try:
- new_service_progress.save()
- consumeRecord.save()
- except Exception as e:
- logger.exception(e)
- # 更新最大停车数量
- self._update_parking(self.device)
- else:
- sp = sp.first()
- if sp.cardId != str(card.id):
- return
- now_time = time.time()
- start_time = int(sp.start_time)
- duration = int(now_time - start_time) # 时间求整
- # 校验是否足够支付
- consumeRecord = ConsumeRecord.objects.get(id=sp.consumes[0])
- cardOrderNo = consumeRecord.attachParas.get("cardOrderNo")
- needBalance = self.deviceAdapter.calculate_consume(duration=duration, package=self.device.package("1", False).get("1", dict()))
- if card.balance < RMB(needBalance):
- self.notify_service_fault(card, u"卡内余额不足以支付此次消费,请先充值")
- return
- # 开启闸门
- self.deviceAdapter._open()
- # 扣钱
- self.update_card_balance(card, RMB(card.balance - RMB(needBalance)))
- # 完善消费订单
- consumeRecord.money = RMB(needBalance)
- consumeRecord.coin = VirtualCoin(needBalance)
- consumeRecord.finishedTime = datetime.datetime.now()
- consumeRecord.attachParas.update({
- "endOper": oper,
- "endDutou": dutou
- })
- consumeRecord.servicedInfo.update({
- "oper": u'出门 卡号为%s' % card.cardNo if oper == 'out' else u'读头数据异常',
- })
- if cardOrderNo:
- try:
- cardConsumeRecord = CardConsumeRecord.objects.get(orderNo = cardOrderNo)
- cardConsumeRecord.balance = card.balance
- cardConsumeRecord.money = RMB(needBalance)
- cardConsumeRecord.finishedTime = datetime.datetime.now()
- except DoesNotExist:
- pass
- # 结束服务进程
- sp.isFinished = True
- sp.finishedTime = int(time.time())
- try:
- consumeRecord.save()
- sp.save()
- except Exception as e:
- logger.exception(e)
- self._update_parking(self.device, enter = False)
- self.deviceAdapter.notify_user(consumeRecord, True)
- def notify_service_fault(self, card, service):
- self.notify_user(
- card.managerialOpenId if card else "",
- "service_complete",
- **{
- "title": u"服务失败",
- "service": service,
- "finishTime": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
- 'remark': u'谢谢您的支持'
- }
- )
- def _registe_sn(self, snNum):
- conf = self.device.get("otherConf", dict())
- defaultSnNum = conf.get("snNum", DefaultParams.DEFAULT_SN_NUM)
- if defaultSnNum != defaultSnNum:
- return
- conf.update({"snNum": snNum})
- Device.objects.get(devNo=self.device.devNo).update(otherConf=conf)
- Device.invalid_device_cache(self.device.devNo)
- def do(self, **args):
- snNum = self.event_data.get("snNum")
- self._registe_sn(snNum)
- cardNo = self.event_data.get("cardNo")
- dutou = self.event_data.get("dutou")
- # 查询虚拟卡
- card = self.update_card_dealer_and_type(cardNo)
- if not card or not card.openId or card.frozen:
- logger.info("card can not be used, cardNo is %s , devNo is %s" % (cardNo, self.device.devNo))
- return
- # 查询该地址下是否有可用虚拟卡
- vCard = card.related_virtual_card
- if not vCard or not vCard.can_use_hp_gate():
- self.do_normal_user(card, dutou)
- # 虚拟卡用户处理
- else:
- self.do_virtual_card_user(card, vCard, dutou)
|