kunyuanCar.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. # coding=utf-8
  2. import datetime
  3. import logging
  4. import typing
  5. from apilib.monetary import VirtualCoin, RMB, Ratio
  6. from apps.web.constant import Const, DEALER_CONSUMPTION_AGG_KIND
  7. from apps.web.core.device_define.kunyuanCar import DEFAULT_VERSION
  8. from apps.web.device.models import Group, Device
  9. from apps.web.eventer import EventBuilder
  10. from apps.web.eventer.base import WorkEvent
  11. from apps.web.user.models import ConsumeRecord, CardRechargeOrder
  12. from apps.web.user.models import ServiceProgress, MyUser
  13. from apps.web.user.transaction_deprecated import refund_money
  14. if typing.TYPE_CHECKING:
  15. from apps.web.user.models import 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. if event_data is None or 'cmdCode' not in event_data:
  21. return None
  22. if 'ack' in device_event:
  23. self.deviceAdapter._response_ack(device_event["ack"])
  24. return ChangYuanCarWorkEventer(self.deviceAdapter, event_data)
  25. class ChangYuanCarWorkEventer(WorkEvent):
  26. def do(self, **args):
  27. cmdCode = self.event_data.get("cmdCode")
  28. if cmdCode == "A8":
  29. self.do_finished()
  30. elif cmdCode == "AE":
  31. self.do_status()
  32. elif cmdCode == "B1":
  33. self.do_start()
  34. elif cmdCode == "B2":
  35. self.do_report()
  36. elif cmdCode == "C1":
  37. self.do_card()
  38. else:
  39. logger.error("error cmdCode".format(cmdCode))
  40. def do_finished(self):
  41. """ 结束事件 """
  42. cardNo = self.event_data["cardNo"]
  43. port = self.event_data["port"]
  44. portCache = Device.get_port_control_cache(self.device.devNo, port)
  45. # 订单版本的
  46. # if "sequanceNo" in self.event_data and portCache.get("version") > str(DEFAULT_VERSION):
  47. # return self._do_new_finished()
  48. if not cardNo:
  49. self._do_net_pay_finish()
  50. else:
  51. self._do_card_pay_finish()
  52. def do_status(self):
  53. """ 枪把状态变更 """
  54. status = self.event_data.get("status")
  55. port = self.event_data.get("port")
  56. Device.update_dev_control_cache(self.device.devNo, {port: {"status": status}})
  57. def do_start(self):
  58. """ 整体启动后 会上报一次启动信息 """
  59. logger.info("[KYCAR_do_start] start, dev = {} , event_info = {}".format(self.device.devNo, self.event_data))
  60. cardNo = self.event_data["cardNo"]
  61. money = RMB(self.event_data["money"])
  62. port = self.event_data["port"]
  63. # 没有卡号的情况 暂时不需要处理
  64. if not cardNo:
  65. return
  66. card = self.update_card_dealer_and_type(cardNo) # type: Card
  67. if not card:
  68. return logger.info("[KYCAR_do_start] not find card = {}".format(cardNo))
  69. # 然后对卡进行扣费使用 创建消费记录
  70. self.consume_money_for_card(card, money)
  71. orderNo, cardOrderNo = self.record_consume_for_card(card, money)
  72. # 创建消费记录 通知用户 扣费
  73. self.notify_balance_has_consume_for_card(card, money)
  74. logger.info("[KYCAR_do_start] finished deduct money card = {}, orderNo = {}, cardConsumeOrder = {}".format(cardNo, orderNo, cardOrderNo))
  75. # 注册服务 个人中心辨别
  76. ServiceProgress.register_card_service(
  77. dev=self.device,
  78. port=int(port),
  79. card=card
  80. )
  81. portCache = {
  82. "isStart": True,
  83. "openId": card.openId,
  84. "startTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  85. "status": Const.DEV_WORK_STATUS_WORKING,
  86. "price": int(money),
  87. "orderNo": orderNo,
  88. "cardOrderNo": cardOrderNo
  89. }
  90. Device.update_dev_control_cache(self.device.devNo, {port: portCache})
  91. def do_report(self):
  92. """ 状态上报 """
  93. port = self.event_data["port"]
  94. portCache = Device.get_port_control_cache(self.device.devNo, port)
  95. # 订单版本的
  96. if "sequanceNo" in self.event_data and portCache.get("version") > str(DEFAULT_VERSION):
  97. order = ConsumeRecord.objects.filter(sequanceNo=self.event_data["sequanceNo"]).first()
  98. if not order:
  99. orderNo, openId = portCache.get("orderNo"), portCache.get("openId")
  100. order = ConsumeRecord.objects.filter(orderNo=orderNo).first()
  101. else:
  102. orderNo, openId = portCache.get("orderNo"), portCache.get("openId")
  103. order = ConsumeRecord.objects.filter(orderNo=orderNo).first()
  104. if not order:
  105. logger.info("[{} do_report not find order, portCache = {}]".format(self.__class__.__name__, portCache))
  106. return
  107. leftMoney = RMB(self.event_data.get("leftBalance"))
  108. usedElec = self.event_data.get("usedElec")
  109. temperature = self.event_data.get("temperature")
  110. power = self.event_data.get("power")
  111. voltage = self.event_data.get("voltage")
  112. sid = self.event_data.get("sid")
  113. data = {
  114. "leftMoney": str(leftMoney),
  115. "usedElec": usedElec,
  116. "temperature": temperature,
  117. "power": power,
  118. "voltage": voltage,
  119. "sid": sid
  120. }
  121. if "sequanceNo" in self.event_data:
  122. data["sequanceNo"] = self.event_data["sequanceNo"]
  123. order.servicedInfo = data
  124. order.save()
  125. portCache.update(data)
  126. Device.update_dev_control_cache(self.device.devNo, {str(order.used_port): portCache})
  127. def do_card(self):
  128. """ 查询卡的余额 """
  129. cardNo = self.event_data["cardNo"]
  130. card = self.update_card_dealer_and_type(cardNo) # type: Card
  131. if not card:
  132. logger.info("card <{}> not refund".format(cardNo))
  133. return
  134. # 进行卡的充值
  135. card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id))
  136. self.recharge_id_card(card=card, rechargeType='append', order=card_recharge_order)
  137. card.reload()
  138. # 进行余额查询
  139. self.deviceAdapter._response_card(card)
  140. def _do_net_pay_finish(self):
  141. port = self.event_data["port"]
  142. devCache = Device.get_dev_control_cache(self.device.devNo) or dict()
  143. portCache = devCache.get(str(port), dict())
  144. openId = portCache.pop("openId", None)
  145. vCardId = portCache.get("vCardId")
  146. leftMoney = self.event_data["leftMoney"]
  147. payCoins = portCache["coins"]
  148. payPrice = portCache["price"] # 下发的是price 则退款的时候需要使用price进行一次计算
  149. # 对于leftMoney 做一次校验保护 主板返回的剩余金额可能是错误的
  150. leftMoney = min(RMB(leftMoney), RMB(payCoins))
  151. if not openId:
  152. logger.info("ChangYuan net pay finish with no openId! {}".format(self.device["devNo"]))
  153. return
  154. nowTime = datetime.datetime.now()
  155. consumeDict = {
  156. "elec": self.event_data["usedElec"],
  157. "duration": self.event_data["usedTime"],
  158. "coin": str(VirtualCoin(payCoins)),
  159. "spendMoney": str(RMB(RMB(payCoins) - RMB(leftMoney))),
  160. "reason": self.event_data["reason"],
  161. "finishedTime": datetime.datetime.strftime(nowTime, "%Y-%m-%d %H:%M:%S")
  162. }
  163. user = MyUser.objects.filter(openId=openId, groupId=self.device.groupId).first()
  164. group = Group.get_group(self.device.groupId)
  165. logger.info("ChangYuan net pay finish and start to notify user! {}".format(self.device["devNo"]))
  166. self.notify_user(
  167. managerialOpenId = user.managerialOpenId if user else "",
  168. templateName = "service_complete",
  169. 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(
  170. reason = self.event_data["reason"],
  171. logicalCode = self.device.logicalCode,
  172. group = group.address,
  173. duration = self.event_data["usedTime"],
  174. coin = u"{}金币".format(payCoins)
  175. ),
  176. service = u"汽车桩充电服务",
  177. finishTime = datetime.datetime.strftime(nowTime, "%Y-%m-%d %H:%M:%S"),
  178. remark = u'谢谢您的支持'
  179. )
  180. refundMoney = leftMoney
  181. if refundMoney > RMB(0):
  182. # 金币支付的并且打开了金币退款的开关
  183. if not vCardId and self.device.is_auto_refund:
  184. # 重新计算退币数量 防止coins不等于price
  185. refundMoney = VirtualCoin(payCoins) * Ratio(refundMoney.amount / RMB(payPrice).amount)
  186. refund_money(self.device, refundMoney, openId)
  187. consumeDict.update({
  188. DEALER_CONSUMPTION_AGG_KIND.COIN: (VirtualCoin(payCoins) - refundMoney).mongo_amount,
  189. DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS: refundMoney.mongo_amount
  190. })
  191. self.notify_user(
  192. user.managerialOpenId if user else '',
  193. 'refund_coins',
  194. **{
  195. 'title': u"退款",
  196. 'backCount': u'%s(金币)' % refundMoney,
  197. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  198. }
  199. )
  200. # 剩下的 支付方式有 1.虚拟卡支付 2.金币支付但是没有打开金币支付的开关
  201. else:
  202. logger.info("not need to refund pay type! {}".format(self.event_data))
  203. ServiceProgress.update_progress_and_consume_rcd(
  204. self.device["ownerId"],
  205. {
  206. "open_id": openId,
  207. "device_imei": self.device["devNo"],
  208. "port": int(port),
  209. "isFinished": False
  210. },
  211. consumeDict)
  212. Device.clear_port_control_cache(self.device.devNo, port)
  213. def _do_card_pay_finish(self):
  214. logger.info("[KYCar_do_card_pay_finish] dev <{}> card finished, event_info = {}".format(self.device.devNo, self.event_data))
  215. port = self.event_data["port"]
  216. cardNo = self.event_data["cardNo"]
  217. reason = self.event_data["reason"]
  218. usedElec = self.event_data["usedElec"]
  219. usedTime = self.event_data["usedTime"]
  220. leftMoney = self.event_data["leftMoney"]
  221. card = self.update_card_dealer_and_type(cardNo)
  222. if not card:
  223. return logger.info("[KYCar_do_card_pay_finish] dev <{}> card finished, not find card = {}".format(self.device.devNo, cardNo))
  224. # 获取缓存里面的一些信息
  225. devCache = Device.get_dev_control_cache(self.device.devNo) or dict()
  226. portCache = devCache.get(port) or dict()
  227. price = portCache.get("price", 0)
  228. if not price:
  229. return logger.info("[KYCar_do_card_pay_finish] dev <{}> card finished, not find cache = {}".format(self.device.devNo, cardNo))
  230. cacheLeftMoney = portCache.get("leftMoney", price)
  231. # 防止主板误报 取最小的一个 然后取大于0的
  232. leftMoney = max(min(RMB(price), RMB(leftMoney), RMB(cacheLeftMoney)), RMB(0))
  233. # 对卡进行退费
  234. self.refund_money_for_card(money=leftMoney, cardId=str(card.id))
  235. logger.info("[KYCar_do_card_pay_finish] dev <{}> card finished, card = {}, refund = {}".format(self.device.devNo, cardNo, leftMoney))
  236. # 通知用户
  237. group = Group.get_group(self.device["groupId"])
  238. self.notify_user(
  239. managerialOpenId=card.managerialOpenId,
  240. templateName="service_complete",
  241. title=u"\\n\\n结束原因:\\t\\t{reason}\\n\\n设备编号:\\t\\t{logicalCode}\\n\\n服务地址:\\t\\t{group}\\n\\n使用时长:\\t\\t{duration}分钟".format(
  242. reason=u"充电结束",
  243. logicalCode=self.device["logicalCode"],
  244. group=group.get("address", ""),
  245. duration=self.event_data["usedTime"],
  246. ),
  247. service=u"本次充电结束",
  248. finishTime=datetime.datetime.strftime(datetime.datetime.now(), "%Y-%m-%d %H:%M:%S"),
  249. remark=u'谢谢您的支持'
  250. )
  251. servicedInfo = {
  252. "usedElec": usedElec,
  253. "duration": usedTime,
  254. "cardNo": cardNo,
  255. "refundedMoney": leftMoney,
  256. "reason": reason,
  257. "finishedTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  258. }
  259. ServiceProgress.update_progress_and_consume_rcd(
  260. self.device["ownerId"],
  261. {
  262. "device_imei": self.device["devNo"],
  263. "cardId": str(card.id),
  264. "port": int(port),
  265. "isFinished": False
  266. },
  267. servicedInfo
  268. )
  269. Device.clear_port_control_cache(self.device.devNo, port)
  270. def _do_new_finished(self):
  271. """
  272. 新版本的结束 通过sequanceNo直接找到订单进行处理
  273. """
  274. order = ConsumeRecord.objects.filter(sequanceNo=self.event_data["sequanceNo"]).first() # type: ConsumeRecord
  275. if not order:
  276. logger.info("[ChangYuanCarWorkEventer _do_new_finished] not find consume order, sequanceNo = {}".format(self.event_data["sequanceNo"]))
  277. return
  278. if order.status == "finished":
  279. logger.info("[ChangYuanCarWorkEventer _do_new_finished] order status finished, sequanceNo = {}".format(self.event_data["sequanceNo"]))
  280. return
  281. leftMoney = self.event_data["leftMoney"]
  282. refundMoney = min(RMB(leftMoney), RMB(order.money))
  283. nowTime = datetime.datetime.now()
  284. user = order.user
  285. self.notify_user(
  286. managerialOpenId=user.managerialOpenId if user else "",
  287. templateName="service_complete",
  288. title=u"\\n\\n结束原因:\\t\\t{reason}\\n\\n设备编号:\\t\\t{logicalCode}\\n\\n服务地址:\\t\\t{group}\\n\\n使用时长:\\t\\t{duration}分钟".format(
  289. reason=self.event_data["reason"],
  290. logicalCode=self.device.logicalCode,
  291. group=self.device.group.get("address", ""),
  292. duration=self.event_data["usedTime"],
  293. ),
  294. service=u"汽车桩充电服务",
  295. finishTime=datetime.datetime.strftime(nowTime, "%Y-%m-%d %H:%M:%S"),
  296. remark=u'谢谢您的支持'
  297. )
  298. consumeDict = {"elec": self.event_data["usedElec"], "duration": self.event_data["usedTime"], "coin": str(VirtualCoin(order.coin)),
  299. "spendMoney": str(RMB(RMB(order.money) - RMB(leftMoney))), "reason": self.event_data["reason"],
  300. "finishedTime": datetime.datetime.strftime(nowTime, "%Y-%m-%d %H:%M:%S"), "sequanceNo": self.event_data["sequanceNo"]}
  301. if self.device.is_auto_refund and RMB(leftMoney) > RMB(0):
  302. refundMoney = VirtualCoin(order.coin) * Ratio(refundMoney.amount / RMB(order.money).amount)
  303. cardNo = self.event_data["cardNo"]
  304. if not cardNo:
  305. refund_money(self.device, refundMoney, user.openId)
  306. else:
  307. card = self.update_card_dealer_and_type(cardNo)
  308. self.refund_money_for_card(money=leftMoney, cardId=str(card.id))
  309. consumeDict.update({
  310. DEALER_CONSUMPTION_AGG_KIND.COIN: (VirtualCoin(order.money) - refundMoney).mongo_amount,
  311. DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS: refundMoney.mongo_amount
  312. })
  313. self.notify_user(
  314. user.managerialOpenId if user else '',
  315. 'refund_coins',
  316. **{
  317. 'title': u"退款",
  318. 'backCount': u'%s(金币)' % refundMoney,
  319. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  320. }
  321. )
  322. ServiceProgress.update_progress_and_consume_rcd(
  323. self.device.ownerId,
  324. {
  325. "open_id": user.openId,
  326. "device_imei": self.device.devNo,
  327. "port": order.used_port,
  328. "isFinished": False
  329. },
  330. consumeDict
  331. )
  332. Device.clear_port_control_cache(self.device.devNo, order.used_port)
  333. order.status = "finished"
  334. order.save()