huopo.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import logging
  5. import time
  6. from mongoengine import DoesNotExist
  7. from typing import Optional
  8. from apilib.monetary import RMB, VirtualCoin
  9. from apps.web.constant import Const
  10. from apps.web.core.device_define.huopo import HuopoCacheMgr, DefaultParams
  11. from apps.web.core.exceptions import ServiceException
  12. from apps.web.device.models import DeviceDict, Group, Device
  13. from apps.web.eventer.base import WorkEvent
  14. from apps.web.eventer import EventBuilder
  15. from apps.web.user.models import ServiceProgress, MyUser, ConsumeRecord, CardConsumeRecord, MonthlyPackage, Card
  16. logger = logging.getLogger(__name__)
  17. class builder(EventBuilder):
  18. def __getEvent__(self, device_event):
  19. event_data = self.deviceAdapter.analyze_event_data(device_event['data'])
  20. return HuoPoEvent(self.deviceAdapter, event_data)
  21. """
  22. 经销商设置
  23. 功能 方向
  24. 进入 左(1)
  25. 出门 右(2)
  26. """
  27. class HuoPoEvent(WorkEvent):
  28. def __init__(self, smartBox, event_data):
  29. super(HuoPoEvent, self).__init__(smartBox, event_data)
  30. @staticmethod
  31. def _get_service_progress(openId):
  32. """检查是否是出门"""
  33. devTypeCode = Const.DEVICE_TYPE_CODE_HP_GATE
  34. sp = ServiceProgress.objects.filter(open_id = openId, devTypeCode = devTypeCode, isFinished = False)
  35. if not sp:
  36. return
  37. return sp
  38. @staticmethod
  39. def _check_parking(dev):
  40. # type:(DeviceDict)->bool
  41. _key = "maxParking"
  42. maxParking = dev["otherConf"].get(_key, 250)
  43. serviceInfo = HuopoCacheMgr.get_parking_cache(dev.groupId)
  44. if serviceInfo is not None:
  45. parkingInfo = serviceInfo.get("parkingInfo", {})
  46. parkingNum = parkingInfo.get(_key, 0)
  47. else:
  48. parkingNum = 0
  49. return int(parkingNum) < int(maxParking)
  50. @staticmethod
  51. def _update_parking(dev, enter = True):
  52. # type:(DeviceDict, Optional[bool])->None
  53. _key = "maxParking"
  54. serviceInfo = HuopoCacheMgr.get_parking_cache(dev.groupId)
  55. if serviceInfo is not None:
  56. parkingInfo = serviceInfo.get("parkingInfo", {})
  57. parkingNum = parkingInfo.get(_key, 0)
  58. else:
  59. parkingInfo = {}
  60. parkingNum = 0
  61. if enter:
  62. parkingNum += 1
  63. else:
  64. if parkingNum > 0:
  65. parkingNum -= 1
  66. parkingInfo.update({_key: parkingNum})
  67. HuopoCacheMgr.set_parking_cache(dev.groupId, {"parkingInfo": parkingInfo})
  68. def do_virtual_card_user(self, card, vCard, dutou):
  69. """
  70. 虚拟卡用户直接启动,做单次记录
  71. :param dutou:
  72. :param card:
  73. :param vCard:
  74. :return:
  75. """
  76. # 开启道闸
  77. self.deviceAdapter._open()
  78. oper = 'enter' if dutou == '1' else 'out'
  79. # 记录相应的消费情况
  80. group = Group.get_group(self.device["groupId"])
  81. if oper == 'enter':
  82. attachParas = {
  83. "cardId": str(card.id),
  84. "vCardId": str(vCard.id),
  85. "startOper": oper,
  86. "startDutou": dutou
  87. }
  88. orderNo, cardOrderNo = self.record_consume_for_card(card, money=RMB(0.0), desc=u'使用绑定的虚拟卡')
  89. vCard.consume_hp_gate(openId = card.openId, group = group, dev = self.device, attachParas=attachParas)
  90. consumeRecord = ConsumeRecord.objects.filter(orderNo=orderNo).first()
  91. consumeRecord.attachParas = attachParas
  92. consumeRecord.attachParas.update({
  93. "orderNo": orderNo,
  94. "cardOrderNo": cardOrderNo
  95. })
  96. consumeRecord.servicedInfo.update({
  97. "oper": u'进门 卡号为%s' % card.cardNo if oper == 'enter' else u'读头数据异常'
  98. })
  99. try:
  100. consumeRecord.save()
  101. except Exception as e:
  102. logger.exception(e)
  103. else:
  104. # TODO: 只查3个月内的
  105. consumeRecord = ConsumeRecord.objects(openId = card.openId,
  106. dateTimeAdded__gte = (
  107. datetime.datetime.now() - datetime.timedelta(days = 90)),
  108. devNo = self.device['devNo']).order_by('-dateTimeAdded').hint(
  109. [('openId', 1), ('dateTimeAdded', -1)]).first()
  110. if not consumeRecord:
  111. logger.warning("[do_virtual_card_user] not find consumeRecord, openId={}, devNo={}".format(card.openId, self.device.devNo) )
  112. return
  113. duration = int((datetime.datetime.now() - consumeRecord.dateTimeAdded).total_seconds() / 60)
  114. if consumeRecord.servicedInfo != {"oper": u'进门 卡号为%s' % card.cardNo}:
  115. return
  116. else:
  117. consumeRecord.finishedTime = datetime.datetime.now()
  118. consumeRecord.attachParas.update({
  119. "endOper": oper,
  120. "endDutou": dutou
  121. })
  122. consumeRecord.servicedInfo.update({
  123. "oper": u'出门 卡号为%s' % card.cardNo if oper == 'out' else u'读头数据异常',
  124. "duration": duration,
  125. "finishedTime": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  126. })
  127. try:
  128. consumeRecord.save()
  129. except Exception as e:
  130. logger.exception(e)
  131. # 将之前临时启动的服务清空
  132. try:
  133. for sp in self._get_service_progress(card.openId):
  134. sp.update(isFinished = True)
  135. except Exception as e:
  136. logger.error(e)
  137. def do_monthly_package_user(self, card, monthlyPackage, dutou): # type:(Card, MonthlyPackage, str) -> None
  138. """
  139. 包月用户使用包月卡
  140. :param card:
  141. :param monthlyPackage:
  142. :param dutou:
  143. :return:
  144. """
  145. # 尝试启动
  146. try:
  147. self.deviceAdapter._open()
  148. except ServiceException:
  149. return
  150. except Exception as e:
  151. logger.exception(e)
  152. return
  153. # 启动成功的情况下记录消费
  154. attachParas = {
  155. "cardId": str(card.id),
  156. "monthlyPackageId": str(monthlyPackage.id),
  157. }
  158. servicedInfo = {
  159. "oper": u'{} 卡号为 {}'.format(u"进门" if dutou == "1" else u"出门", card.cardNo)
  160. }
  161. orderNo, cardOrderNo = self.record_consume_for_card(
  162. card,
  163. money=RMB(0),
  164. desc=u"使用包月套餐",
  165. attachParas=attachParas,
  166. servicedInfo=servicedInfo
  167. )
  168. order = ConsumeRecord.objects.get(orderNo=orderNo)
  169. # 包月套餐抵扣
  170. monthlyPackage.deduct(order)
  171. def do_normal_user(self, card, dutou):
  172. sp = self._get_service_progress(card.openId)
  173. dev = Device.get_dev(self.device["devNo"])
  174. user = MyUser.objects.filter(openId = card.openId, group = dev.get("groupId")).first()
  175. oper = 'enter' if dutou == '1' else 'out'
  176. if not sp:
  177. # 校验停车人数
  178. if not self._check_parking(dev):
  179. self.notify_service_fault(card, u"停车人数已经达到上限!")
  180. return
  181. # 校验卡上是否还有余额,没有余额的卡不允许启动
  182. if RMB(card.balance) <= RMB(10):
  183. self.notify_service_fault(card, u"月票已过期或者尚未购买月票!当前实体卡内余额{},小于最低启动金额{}!".format(card.balance, 10))
  184. return
  185. washConfig = self.device["washConfig"]
  186. attachParas = {
  187. "washConfig": washConfig,
  188. "cardId": str(card.id),
  189. "startOper": oper,
  190. "startDutou": dutou
  191. }
  192. orderNo, cardOrderNo = self.record_consume_for_card(card, money = RMB(0.0))
  193. consumeRecord = ConsumeRecord.objects.filter(orderNo = orderNo).first()
  194. consumeRecord.attachParas = attachParas
  195. consumeRecord.attachParas.update({
  196. "orderNo": orderNo,
  197. "cardOrderNo": cardOrderNo
  198. })
  199. consumeRecord.servicedInfo.update({
  200. "oper": u'进门 卡号为%s' % card.cardNo if oper == 'enter' else u'读头数据异常'
  201. })
  202. # 开门
  203. self.deviceAdapter._open()
  204. # 建立当前服务信息
  205. new_service_progress = ServiceProgress(
  206. open_id = card.openId,
  207. device_imei = self.device["devNo"],
  208. devTypeCode = Const.DEVICE_TYPE_CODE_HP_GATE,
  209. attachParas = attachParas,
  210. start_time = int(time.time()),
  211. finished_time = int(time.time()) + 3600 * 24 * 365,
  212. consumes = [str(consumeRecord.id)],
  213. cardId = str(card.id),
  214. )
  215. try:
  216. new_service_progress.save()
  217. consumeRecord.save()
  218. except Exception as e:
  219. logger.exception(e)
  220. # 更新最大停车数量
  221. self._update_parking(self.device)
  222. else:
  223. sp = sp.first()
  224. if sp.cardId != str(card.id):
  225. return
  226. now_time = time.time()
  227. start_time = int(sp.start_time)
  228. duration = int(now_time - start_time) # 时间求整
  229. # 校验是否足够支付
  230. consumeRecord = ConsumeRecord.objects.get(id=sp.consumes[0])
  231. cardOrderNo = consumeRecord.attachParas.get("cardOrderNo")
  232. needBalance = self.deviceAdapter.calculate_consume(duration=duration, package=self.device.package("1", False).get("1", dict()))
  233. if card.balance < RMB(needBalance):
  234. self.notify_service_fault(card, u"卡内余额不足以支付此次消费,请先充值")
  235. return
  236. # 开启闸门
  237. self.deviceAdapter._open()
  238. # 扣钱
  239. self.update_card_balance(card, RMB(card.balance - RMB(needBalance)))
  240. # 完善消费订单
  241. consumeRecord.money = RMB(needBalance)
  242. consumeRecord.coin = VirtualCoin(needBalance)
  243. consumeRecord.finishedTime = datetime.datetime.now()
  244. consumeRecord.attachParas.update({
  245. "endOper": oper,
  246. "endDutou": dutou
  247. })
  248. consumeRecord.servicedInfo.update({
  249. "oper": u'出门 卡号为%s' % card.cardNo if oper == 'out' else u'读头数据异常',
  250. })
  251. if cardOrderNo:
  252. try:
  253. cardConsumeRecord = CardConsumeRecord.objects.get(orderNo = cardOrderNo)
  254. cardConsumeRecord.balance = card.balance
  255. cardConsumeRecord.money = RMB(needBalance)
  256. cardConsumeRecord.finishedTime = datetime.datetime.now()
  257. except DoesNotExist:
  258. pass
  259. # 结束服务进程
  260. sp.isFinished = True
  261. sp.finishedTime = int(time.time())
  262. try:
  263. consumeRecord.save()
  264. sp.save()
  265. except Exception as e:
  266. logger.exception(e)
  267. self._update_parking(self.device, enter = False)
  268. self.deviceAdapter.notify_user(consumeRecord, True)
  269. def notify_service_fault(self, card, service):
  270. self.notify_user(
  271. card.managerialOpenId if card else "",
  272. "service_complete",
  273. **{
  274. "title": u"服务失败",
  275. "service": service,
  276. "finishTime": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  277. 'remark': u'谢谢您的支持'
  278. }
  279. )
  280. def _registe_sn(self, snNum):
  281. conf = self.device.get("otherConf", dict())
  282. defaultSnNum = conf.get("snNum", DefaultParams.DEFAULT_SN_NUM)
  283. if defaultSnNum != defaultSnNum:
  284. return
  285. conf.update({"snNum": snNum})
  286. Device.objects.get(devNo=self.device.devNo).update(otherConf=conf)
  287. Device.invalid_device_cache(self.device.devNo)
  288. def do(self, **args):
  289. snNum = self.event_data.get("snNum")
  290. self._registe_sn(snNum)
  291. cardNo = self.event_data.get("cardNo")
  292. dutou = self.event_data.get("dutou")
  293. # 查询虚拟卡
  294. card = self.update_card_dealer_and_type(cardNo)
  295. if not card or not card.openId or card.frozen:
  296. logger.info("card can not be used, cardNo is %s , devNo is %s" % (cardNo, self.device.devNo))
  297. return
  298. # 查询该地址下是否有可用虚拟卡
  299. vCard = card.related_virtual_card
  300. if not vCard or not vCard.can_use_hp_gate():
  301. self.do_normal_user(card, dutou)
  302. # 虚拟卡用户处理
  303. else:
  304. self.do_virtual_card_user(card, vCard, dutou)