chaochen.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. # coding=utf-8
  2. import datetime
  3. import logging
  4. import time
  5. from apilib.monetary import VirtualCoin, RMB
  6. from apilib.utils_string import make_title_from_dict
  7. from apps.web.constant import DEALER_CONSUMPTION_AGG_KIND, DeviceCmdCode
  8. from apps.web.core.accounting import Accounting
  9. from apps.web.device.models import Device, Group
  10. from apps.web.eventer import EventBuilder
  11. from apps.web.eventer.base import WorkEvent, FaultEvent
  12. from apps.web.user.transaction_deprecated import refund_money
  13. from apps.web.user.models import ServiceProgress, Card, MyUser, CardRechargeOrder, ConsumeRecord, MonthlyPackage
  14. logger = logging.getLogger(__name__)
  15. class builder(EventBuilder):
  16. def __getEvent__(self, device_event):
  17. event_data = self.deviceAdapter.analyze_event_data(device_event['data'])
  18. if not event_data:
  19. return
  20. event_data['raw_msg'] = device_event
  21. cmdCode = event_data.get("cmdCode")
  22. if cmdCode in []:
  23. return ChaoChenFaultEventer(self.deviceAdapter, event_data)
  24. return ChaoChenWorkEventer(self.deviceAdapter, event_data)
  25. class ChaoChenFaultEventer(FaultEvent):
  26. pass
  27. class ChaoChenWorkEventer(WorkEvent):
  28. FINISH_REASON = {
  29. 0x00: u"充电服务已经结束",
  30. 0x01: u"用户手动停止",
  31. 0x02: u"电量充满,充点服务已经结束",
  32. 0x03: u"当前充电端口出现故障,充电服务结束",
  33. 0x04: u"功率超出最大限制",
  34. 0x05: u"当前刷卡结束充电",
  35. 0x06: u"未连接充电器,充电服务已经结束",
  36. 0x07: u"远程停止端口,充电服务已经结束",
  37. 0x08: u"烟雾报警,充电服务已经结束",
  38. }
  39. def do(self):
  40. cmdCode = self.event_data.get("cmdCode")
  41. logger.info("receive message for dev <{}>, message is <{}>".format(self.device.devNo, self.event_data))
  42. if cmdCode == "B6":
  43. self._do_offline_card_finish()
  44. elif cmdCode == "B5":
  45. self._do_offline_coin_finish()
  46. elif cmdCode == "B4":
  47. pass
  48. elif cmdCode == "C9":
  49. self._do_netpay_finish()
  50. elif cmdCode == "C8":
  51. self._do_online_card_finish()
  52. elif cmdCode == "C4":
  53. self._do_status_report()
  54. elif cmdCode == "EA":
  55. self._do_elec_finish()
  56. elif cmdCode == "F0":
  57. self._do_elec_report()
  58. elif cmdCode == "F9":
  59. self._do_consume_id_card()
  60. elif cmdCode == "FA":
  61. self._do_refund_id_card()
  62. def _do_offline_card_finish(self):
  63. portStr = self.event_data['portStr']
  64. cardNo = self.event_data['cardNo']
  65. usedCoins = self.event_data['usedCoins']
  66. usedTime = self.event_data['usedTime']
  67. balance = self.event_data['balance']
  68. card = self.update_card_dealer_and_type(cardNo=cardNo, cardType="IC")
  69. if not card:
  70. return
  71. attachParas = {
  72. "chargeIndex": portStr
  73. }
  74. servicedInfo = {
  75. DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: float(str(usedCoins)),
  76. DEALER_CONSUMPTION_AGG_KIND.DURATION: float(str(usedTime))
  77. }
  78. self.record_consume_for_card(card, VirtualCoin(usedCoins), servicedInfo=servicedInfo, attachParas=attachParas)
  79. Card.update_balance(cardId=card.id, balance=RMB(balance))
  80. def _do_offline_coin_finish(self):
  81. coins = self.event_data['usedCoins']
  82. self.record_consume_for_coin(VirtualCoin(coins))
  83. Accounting.recordOfflineCoin(
  84. self.device,
  85. int(time.time()),
  86. int(float(coins))
  87. )
  88. def _do_netpay_finish(self):
  89. portStr = self.event_data['portStr']
  90. usedTime = self.event_data['usedTime']
  91. leftTime = self.event_data['leftTime']
  92. devCache = Device.get_dev_control_cache(self.device.devNo) or dict()
  93. portCache = devCache.get(portStr) or dict()
  94. if not portCache:
  95. return
  96. openId = portCache.get("openId")
  97. consumeDict = {
  98. "duration": usedTime,
  99. "finishedTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  100. }
  101. if self.device.is_auto_refund:
  102. coins = VirtualCoin(portCache.get("coins", 0))
  103. usedCoins = min(coins * (float(usedTime) / (float(usedTime) + float(leftTime))), coins)
  104. refundCoins = coins - usedCoins
  105. refund_money(self.device, refundCoins, openId)
  106. consumeDict.update(
  107. {
  108. DEALER_CONSUMPTION_AGG_KIND.COIN: float(str(coins)),
  109. DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: float(str(usedCoins)),
  110. DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS: float(str(refundCoins))
  111. }
  112. )
  113. ServiceProgress.update_progress_and_consume_rcd(
  114. self.device["ownerId"],
  115. {
  116. "open_id": openId,
  117. "device_imei": self.device.devNo,
  118. "port": int(portStr),
  119. "isFinished": False
  120. },
  121. consumeDict
  122. )
  123. Device.clear_port_control_cache(self.device.devNo, portStr)
  124. def _do_online_card_finish(self):
  125. pass
  126. def _do_status_report(self):
  127. pass
  128. def _do_elec_finish(self):
  129. """
  130. AA0100EA 03 02 0060 20
  131. 暂时理解为 只有电量会上报
  132. :return:
  133. """
  134. chargeMode = self.device.get("otherConf", dict()).get("chargeType", self.deviceAdapter.DEFAULT_CHARGE_TYPE)
  135. # 时间模式直接忽略
  136. if chargeMode == self.deviceAdapter.DEFAULT_CHARGE_TYPE:
  137. return
  138. reasonCode = self.event_data.get("reasonCode", "")
  139. leftElec = self.event_data.get("leftElec", 0)
  140. portStr = self.event_data.get("portStr")
  141. reason = self.FINISH_REASON.get(reasonCode)
  142. devCache = Device.get_dev_control_cache(self.device.devNo)
  143. portCache = devCache.get(portStr, dict())
  144. if not portCache:
  145. return
  146. openId = portCache.get("openId")
  147. needElec = portCache.get("needElec")
  148. if not openId or not needElec:
  149. Device.clear_port_control_cache(self.device.devNo, portStr)
  150. return
  151. consumeDict = {
  152. "needElec": needElec,
  153. "usedElec": needElec - leftElec,
  154. "leftElec": leftElec,
  155. "finishData": self.event_data["raw_msg"]["data"]
  156. }
  157. if self.device.is_auto_refund:
  158. coins = VirtualCoin(portCache.get("coins", 0))
  159. refundCoins = VirtualCoin(coins) * (float(leftElec)/float(needElec))
  160. refundCoins = min(refundCoins, coins)
  161. usedCoins = coins - refundCoins
  162. refund_money(self.device, refundCoins, openId)
  163. consumeDict.update(
  164. {
  165. DEALER_CONSUMPTION_AGG_KIND.COIN: float(str(coins)),
  166. DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: float(str(usedCoins)),
  167. DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS: float(str(refundCoins))
  168. }
  169. )
  170. ServiceProgress.update_progress_and_consume_rcd(
  171. self.device["ownerId"],
  172. {
  173. "open_id": openId,
  174. "device_imei": self.device.devNo,
  175. "port": int(portStr),
  176. "isFinished": False
  177. },
  178. consumeDict
  179. )
  180. Device.clear_port_control_cache(self.device.devNo, portStr)
  181. group = Group.get_group(self.device.get("groupId"))
  182. user = MyUser.objects.filter(openId=openId, groupId=self.device.get("groupId")).first()
  183. self.notify_user(
  184. managerialOpenId=user.managerialOpenId if user else "",
  185. templateName="service_complete",
  186. title=u"\\n\\n结束原因:\\t\\t{reason}\\n\\n设备编号:\\t\\t{logicalCode}-{port}\\n\\n服务地址:\\t\\t{group}".format(
  187. reason=reason,
  188. logicalCode=self.device["logicalCode"],
  189. port=portStr,
  190. group=group["address"],
  191. ),
  192. service=u"充电桩充电服务",
  193. finishTime=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  194. remark=u'谢谢您的支持'
  195. )
  196. def _do_elec_report(self):
  197. totalElec = self.event_data['totalElec']
  198. otherConf = self.device.get("otherConf", dict())
  199. otherConf.update({"totalElec": totalElec})
  200. Device.objects.filter(devNo=self.device.devNo).update(otherConf=otherConf)
  201. Device.invalid_device_cache(self.device.devNo)
  202. def _do_consume_id_card(self):
  203. cardNo = self.event_data.get('cardNo', None)
  204. port = self.event_data.get('port', None)
  205. card = self.update_card_dealer_and_type(str(cardNo))
  206. consumeMoney = RMB(self.event_data.get('consumeMoney'))
  207. if not card or not card.openId or card.frozen:
  208. logger.info('no find card devNo=<{}> cardNo=<{}>'.format(self.device.devNo, cardNo))
  209. resp = {
  210. 'funCode': '{:02X}F9'.format(int(port)),
  211. 'content': '00',
  212. }
  213. else:
  214. # 获取有余额的月票
  215. monthly_ticket = MonthlyPackage.get_user_ticket(openId=card.openId, groupId=self.device.groupId, cardNo=card.cardNo)
  216. if len(monthly_ticket):
  217. package = {'coins': 1, 'unit': u'次', 'time': 1}
  218. ticket = MonthlyPackage.get_can_use_one(monthly_ticket, package)
  219. if ticket:
  220. resp = {
  221. 'funCode': '{:02X}F9'.format(int(port)),
  222. 'content': '01{:0>8X}{:0>6X}'.format(cardNo, 0),
  223. }
  224. attachParas = {
  225. 'chargeIndex': port
  226. }
  227. servicedInfo = {
  228. 'cardNo': cardNo,
  229. 'chargeIndex': port,
  230. 'monthpackage': str(ticket.id),
  231. }
  232. orderNo, cardOrderNo = self.record_consume_for_card(card, servicedInfo=servicedInfo,
  233. attachParas=attachParas, money=RMB(0.0),
  234. desc=u'包月套餐')
  235. order = ConsumeRecord.objects.get(orderNo=orderNo)
  236. ticket.deduct(order)
  237. else:
  238. resp = {
  239. 'funCode': '{:02X}F9'.format(int(port)),
  240. 'content': '00',
  241. }
  242. else:
  243. # 检查刷卡的是否有充值订单详情
  244. card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id))
  245. if card_recharge_order:
  246. result = self.recharge_id_card(card=card,
  247. rechargeType='append',
  248. order=card_recharge_order)
  249. card.reload()
  250. if card.balance >= consumeMoney:
  251. resp = {
  252. 'funCode': '{:02X}F9'.format(int(port)),
  253. 'content': '01{:0>8X}{:0>6X}'.format(cardNo, RMB.yuan_to_fen(card.balance - consumeMoney)),
  254. }
  255. attachParas = {
  256. 'chargeIndex': port
  257. }
  258. servicedInfo = {
  259. 'cardNo': cardNo,
  260. 'chargeIndex': port
  261. }
  262. self.consume_money_for_card(card, consumeMoney)
  263. orderNo, cardOrderNo = self.record_consume_for_card(card, consumeMoney, servicedInfo=servicedInfo, attachParas=attachParas)
  264. else:
  265. resp = {
  266. 'funCode': '{:02X}F9'.format(int(port)),
  267. 'content': '00',
  268. }
  269. self.deviceAdapter._send_data(cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, **resp)
  270. def _do_refund_id_card(self):
  271. cardNo = self.event_data.get('cardNo', None)
  272. port = self.event_data.get('port', None)
  273. card = self.update_card_dealer_and_type(str(cardNo))
  274. refundMoney = RMB(self.event_data.get('refundMoney'))
  275. if not card or not card.openId or card.frozen:
  276. logger.info('no find card devNo=<{}> cardNo=<{}>'.format(self.device.devNo, cardNo))
  277. resp = {
  278. 'funCode': '{:02X}FA'.format(int(port)),
  279. 'content': '00',
  280. }
  281. else:
  282. resp = {
  283. 'funCode': '{:02X}FA'.format(int(port)),
  284. 'content': '01{:0>8X}'.format(cardNo),
  285. }
  286. if refundMoney >= RMB(0):
  287. monthly_ticket = MonthlyPackage.get_user_ticket(openId=card.openId, groupId=self.device.groupId,
  288. cardNo=card.cardNo)
  289. if len(monthly_ticket):
  290. pass
  291. else:
  292. self.refund_money_for_card(refundMoney, str(card.id))
  293. refundDict = {
  294. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  295. }
  296. titleDictList = [
  297. {u'设备编号': self.device['logicalCode']},
  298. ]
  299. refundDict.update(
  300. {
  301. 'title': make_title_from_dict(titleDictList),
  302. 'backCount': u'金币:%s' % refundMoney
  303. }
  304. )
  305. self.notify_user(card.managerialOpenId, 'refund_coins', **refundDict)
  306. self.deviceAdapter._send_data(cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, **resp)