changyuanPower.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744
  1. # coding=utf-8
  2. import datetime
  3. import logging
  4. import random
  5. import time
  6. import simplejson as json
  7. import typing
  8. from bson import ObjectId
  9. from mongoengine import DoesNotExist, ValidationError
  10. from apilib.monetary import RMB, VirtualCoin, Ratio
  11. from apilib.utils_sys import memcache_lock
  12. from apps.web.agent.models import Agent
  13. from apps.web.common.models import TempValues
  14. from apps.web.constant import FAULT_CODE, FAULT_LEVEL, Const, DEALER_CONSUMPTION_AGG_KIND, USER_RECHARGE_TYPE
  15. from apps.web.core.device_define.changyuan import CYCardMixin
  16. from apps.web.core.payment import WithdrawGateway
  17. from apps.web.dealer.models import Dealer
  18. from apps.web.device.models import Group, Device
  19. from apps.web.eventer import EventBuilder
  20. from apps.web.eventer.base import WorkEvent, FaultEvent
  21. from apps.web.report.ledger import Ledger
  22. from apps.web.user.models import CardRechargeOrder, Card, RechargeRecord, CardRechargeRecord, ServiceProgress, \
  23. ConsumeRecord, CardConsumeRecord, MyUser
  24. from apps.web.user.models import RefundMoneyRecord
  25. from apps.web.user.transaction_deprecated import refund_money, refund_cash
  26. if typing.TYPE_CHECKING:
  27. from apps.web.device.models import GroupDict
  28. logger = logging.getLogger(__name__)
  29. class builder(EventBuilder):
  30. def __getEvent__(self, device_event):
  31. eventData = self.deviceAdapter.analyze_event_data(device_event.get("data"))
  32. if device_event.get('daid'):
  33. eventData['daid'] = device_event.get('daid')
  34. if eventData is None or "cmdCode" not in eventData:
  35. return None
  36. if eventData["cmdCode"] == "D7":
  37. return ChangYuanFaultEventer(self.deviceAdapter, eventData)
  38. else:
  39. return ChangYuanWorkEventer(self.deviceAdapter, eventData)
  40. class ChangYuanFaultEventer(FaultEvent):
  41. def do(self, **args):
  42. self.event_data.update({
  43. "faultName": u"设备火灾预警",
  44. "faultCode": FAULT_CODE.FIRE_ALARM,
  45. "level": FAULT_LEVEL.CRITICAL,
  46. "desc": u"主板上报设备火警"
  47. })
  48. warningData = {
  49. "warningStatus": 2,
  50. "warningDesc": "设备温度超限警告",
  51. "warningTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  52. }
  53. # 整机告警
  54. Device.update_dev_warning_cache(self.device.devNo, {"0": warningData})
  55. super(ChangYuanFaultEventer, self).do(**args)
  56. class ChangYuanWorkEventer(WorkEvent, CYCardMixin):
  57. def do(self, **args):
  58. cmdCode = self.event_data.get("cmdCode")
  59. cacheKey = "{}-{}-event".format(self.device.devNo, cmdCode)
  60. with memcache_lock(key = cacheKey, value = '1', expire = 300) as acquired:
  61. if acquired:
  62. if cmdCode == "F3":
  63. self._do_recharge_card_result()
  64. elif cmdCode == "F4":
  65. self._do_recharge_card()
  66. elif cmdCode == "D8":
  67. self._do_report_device_status()
  68. elif cmdCode == "D9":
  69. self._do_start_charge()
  70. elif cmdCode == "DA":
  71. self._do_finish()
  72. elif cmdCode == "DF":
  73. self._do_card_refund()
  74. else:
  75. logger.warning(
  76. "cyp_event is doing!, dev is <{}>, cmd is <{}>, data is <{}>".format(self.device.devNo, cmdCode,
  77. self.event_data))
  78. def _do_card_refund(self):
  79. beforeRefund = self.event_data.get("beforeRefund")
  80. refund = self.event_data.get("refund")
  81. afterRefund = self.event_data.get("afterRefund")
  82. cardNo = str(self.event_data.get("cardNo"))
  83. if VirtualCoin(beforeRefund) + VirtualCoin(refund) != VirtualCoin(afterRefund):
  84. logger.info("bad refund event, beforeRefund is {}, refund is {} afterRefund is {}".format(
  85. beforeRefund, refund, afterRefund
  86. ))
  87. return
  88. card = self.update_card_dealer_and_type(cardNo, cardType = "IC")
  89. if not card:
  90. logger.info("cyp_ No cardNo cardNo is {}".format(cardNo))
  91. return
  92. if VirtualCoin(card.balance) != VirtualCoin(beforeRefund):
  93. logger.info(
  94. "beforeRefund isn't equal card balance, cardNo is {}, beforeRefund is {}, card balance is {}".format(
  95. cardNo, beforeRefund, card.balance
  96. ))
  97. return
  98. self.refund_money_for_card(RMB(refund), str(card.id))
  99. self.notify_user(
  100. card.managerialOpenId,
  101. 'refund_coins',
  102. **{
  103. 'title': u"充电桩返费",
  104. 'backCount': u'%s' % refund,
  105. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  106. }
  107. )
  108. def _do_over_ack_clear_port(self, daid, port, overServiceProgress = False):
  109. res = self.deviceAdapter._ack_finished_massage(daid)
  110. if res['rst'] == 0:
  111. if overServiceProgress:
  112. sp = ServiceProgress.objects.filter(device_imei = self.device.devNo, port = port,
  113. isFinished = False).first()
  114. if sp:
  115. sp.update(isFinished = True, finished_time = time.time(),
  116. expireAt = datetime.datetime.now())
  117. Device.clear_port_control_cache(self.device["devNo"], port)
  118. logger.info('cyp_do_over_ack_clear_port() is over !!')
  119. def _do_update_ConsumeRecord_CardConsumeRecord_service_progress(self, portCache, updateMoney):
  120. portCache["payCount"] = portCache.get('payCount') + 1
  121. portCache["allPayMoney"] = updateMoney
  122. portCache["coins"] = updateMoney
  123. orderNo = portCache.get('orderNo')
  124. cardOrderNo = portCache.get("cardOrderNo")
  125. try:
  126. order = ConsumeRecord.objects.get(orderNo = orderNo) # type: ConsumeRecord
  127. card_order = CardConsumeRecord.objects.get(orderNo = cardOrderNo)
  128. service_pogress = ServiceProgress.objects(
  129. __raw__ = {'device_imei': order.devNo, 'consumeOrder.orderNo': orderNo}).first()
  130. order.coin = updateMoney
  131. order.money = updateMoney
  132. card_order.money = updateMoney
  133. service_pogress.consumeOrder['money'] = updateMoney
  134. service_pogress.consumeOrder['coin'] = updateMoney
  135. order.save()
  136. card_order.save()
  137. service_pogress.save()
  138. except Exception as e:
  139. logger.debug('cyp_%s' % e)
  140. finally:
  141. pass
  142. def _do_recharge_card(self):
  143. """
  144. 同步卡的请求
  145. :return:
  146. """
  147. cardNo = self.event_data.get("cardNo")
  148. cardBalance = self.event_data.get("cardBalance")
  149. cardType = self.event_data.get("cardType")
  150. card = self.update_card_dealer_and_type(cardNo)
  151. if not card:
  152. logger.error("not card exist, can not async card balance <{}>".format(cardNo))
  153. return
  154. # 更新卡的余额 以设备侧的为准
  155. if RMB(cardBalance) != card.balance:
  156. logger.info('Card<{}> balance<{}> needs to be sync !!!'.format(cardNo, card.balance))
  157. card.balance = RMB(cardBalance)
  158. card = card.save()
  159. # orderNos 表示充值的订单 cardOrderNos 表示卡充值的订单
  160. money, coins, orderNos, cardOrderNos = CardRechargeOrder.get_to_do_list(str(card.id))
  161. # 一旦卡被冻结 立即下发 创建一张为 负数的金额订单 将卡的余额清空
  162. if card.frozen:
  163. group = Group.get_group(card.groupId)
  164. CardRechargeOrder.new_one(
  165. openId = card.openId,
  166. cardId = str(card.id),
  167. cardNo = card.cardNo,
  168. money = RMB(0),
  169. coins = VirtualCoin(0) - VirtualCoin(coins) - VirtualCoin(card.balance),
  170. group = group,
  171. rechargeId = ObjectId(),
  172. rechargeType = u"sendCoin"
  173. )
  174. money, coins, orderNos, cardOrderNos = CardRechargeOrder.get_to_do_list(str(card.id))
  175. # 没有需要同步的订单的情况下 直接退出
  176. if not orderNos:
  177. logger.info('Not card recharge record!, card is <{}>'.format(cardNo))
  178. # self._do_offline_recharge_by_dealer(cardNo, card, cardType)
  179. return
  180. orderNos = [str(item) for item in orderNos]
  181. sid = str(random.randint(0, 0xFFFF))
  182. TempValues.set('%s-%s' % (self.device.devNo, sid), value = json.dumps(orderNos))
  183. TempValues.set('%s-%s-%s' % (self.device.devNo, sid, cardNo), value = json.dumps(cardOrderNos))
  184. TempValues.set('%s-%s' % (self.device.devNo, cardNo), value = str(sid))
  185. # 计算总的需要同步的金额
  186. asyncMoney = RMB(cardBalance) + RMB(coins)
  187. logger.info('ready to recharge card, card is <{}>, money is <{}>'.format(cardNo, coins))
  188. # 已经下发金额后,先将订单的状态改为结束。如果主板上报失败,再将订单状态迁移回来否则就不再变化订单状态了
  189. try:
  190. CardRechargeOrder.update_card_order_has_finished(str(card.id))
  191. except Exception as e:
  192. logger.debug('%s' % e)
  193. else:
  194. self.deviceAdapter._async_card_balance(cardType, cardNo, asyncMoney)
  195. def _do_recharge_card_result(self):
  196. """
  197. 确认卡的同步结果
  198. :return:
  199. """
  200. asyncStatus = self.event_data.get("asyncStatus")
  201. answerSid = self.event_data.get("sid")
  202. cardBalance = self.event_data.get("cardBalance")
  203. cardNo = self.event_data.get("cardNo")
  204. card = self.update_card_dealer_and_type(cardNo)
  205. if not card:
  206. logger.error("not found card, event is <{}>".format(self.event_data))
  207. return
  208. sid = TempValues.get('{}-{}'.format(self.device.devNo, cardNo))
  209. if sid != answerSid:
  210. logger.error('answer sid is not equal sid <{}>-<{}>'.format(sid, answerSid))
  211. return
  212. # 根据sid 以及卡号 查询出此次充值的所有的订单
  213. orderNos = TempValues.get('{}-{}'.format(self.device.devNo, sid))
  214. cardOrderNos = TempValues.get('{}-{}-{}'.format(self.device.devNo, sid, cardNo))
  215. # 明确接收到了主板同步失败的情况下 直接退出 不在转换充值订单的问题 防止出错
  216. if not asyncStatus:
  217. logger.error("card async not success! event data is <{}>".format(self.event_data))
  218. return
  219. # 下面的都不会更改订单的状态 最多走售后
  220. money, coins = RMB(0), VirtualCoin(0)
  221. # 校验金额是否是相等的
  222. orderNos = [ObjectId(item) for item in orderNos]
  223. rds = RechargeRecord.objects.filter(ownerId = card.dealerId, id__in = orderNos)
  224. for item in rds:
  225. money += item.money
  226. coins += item.coins
  227. # 这个地方就是异常值处理了
  228. if VirtualCoin(coins + card.balance) != VirtualCoin(cardBalance):
  229. logger.error('card pre balance not equal now balance! event is <{}>'.format(self.event_data))
  230. return
  231. # 依次更改卡充值的订单 并创建充值订单
  232. cardOrders = CardRechargeOrder.objects.filter(orderNo__in = cardOrderNos)
  233. for _order in cardOrders: # type: CardRechargeOrder
  234. _order.update_after_recharge_ic_card(
  235. device = self.device,
  236. sendMoney = RMB(_order.coins),
  237. preBalance = card.balance
  238. )
  239. preBalance = card.balance
  240. card.balance = card.balance + _order.coins
  241. # 创建充值记录
  242. CardRechargeRecord.add_record(
  243. card = card,
  244. group = Group.get_group(_order.groupId),
  245. order = _order,
  246. device = self.device
  247. )
  248. # 保存
  249. card.save()
  250. # 完成之后将TempValue 的key value 清空
  251. TempValues.remove('%s-%s' % (self.device['devNo'], sid))
  252. TempValues.remove('%s-%s' % (self.device['devNo'], cardNo))
  253. TempValues.remove("%s-%s-%s" % (self.device["devNo"], sid, cardNo))
  254. def _do_report_device_status(self):
  255. """
  256. 同步设备状态
  257. :return:
  258. """
  259. portInfo = self.event_data.get("portInfo")
  260. voltage = self.event_data.get("voltage")
  261. temperature = self.event_data.get("temperature")
  262. # 首先依据上报的状态获取已使用未使用端口数量
  263. allPorts, usedPorts, usePorts = self.deviceAdapter.get_port_static_info(portInfo)
  264. # 端口信息更新
  265. devCache = Device.get_dev_control_cache(self.device.devNo) or dict()
  266. for portStr, value in devCache.items():
  267. if not portStr.isdigit() or not isinstance(value, dict):
  268. continue
  269. # 更新每个端口的信息
  270. tempPortInfo = portInfo.get(portStr, dict())
  271. value.update(tempPortInfo)
  272. devCache[portStr] = value
  273. devCache["voltage"] = voltage
  274. devCache["temperature"] = temperature
  275. devCache["allPorts"] = allPorts
  276. devCache["usedPorts"] = usedPorts
  277. devCache["usePorts"] = usePorts
  278. Device.update_dev_control_cache(self.device.devNo, devCache)
  279. def _do_start_charge(self):
  280. """
  281. 充电开始指令, 主要是更新各种缓存等等,
  282. 对于扫码的启动基本在adapter缓存已经处理完成,主要需要处理的是刷卡的启动
  283. 根据 需求方要求 刷卡的可以做合单处理
  284. :return:
  285. """
  286. logger.info(
  287. 'cyp_do_start_charge() is run!! event_data:{}'.format(json.dumps(self.event_data, ensure_ascii = False)))
  288. portStr = self.event_data.get("portStr")
  289. cardBalance = self.event_data.get("cardBalance")
  290. allPayMoney = self.event_data.get("allPayMoney")
  291. payMoney = self.event_data.get("payMoney")
  292. cardNo = str(self.event_data.get("cardNo"))
  293. if cardNo == '00000000':
  294. return
  295. card = self.update_card_dealer_and_type(cardNo)
  296. if not self.check_card_can_use(card):
  297. logger.info(
  298. "[ChangYuanWorkEventer _do_start_charge] check card not can use devNo = {}".format(self.device.devNo))
  299. self.notify_invalid_card_to_dealer(cardNo, card)
  300. return self.deviceAdapter.stop_charging_port(portStr)
  301. # 判断卡是否存在 如果不存在 直接更新缓存信息即可
  302. if not card:
  303. Device.update_dev_control_cache(self.device.devNo, {portStr: self.event_data})
  304. return
  305. devCache = Device.get_dev_control_cache(self.device.devNo)
  306. portCache = devCache.get(portStr) or dict()
  307. # 离线卡 以设备上报过来的数据为准
  308. self.update_card_balance(card, RMB(cardBalance))
  309. if portCache.get("status", Const.DEV_WORK_STATUS_IDLE) != Const.DEV_WORK_STATUS_IDLE:
  310. # TODO 续充时候的处理
  311. logger.info('cyp_do_card_start_charge again!!')
  312. self._do_update_ConsumeRecord_CardConsumeRecord_service_progress(portCache, allPayMoney)
  313. # 不是续充,第一次刷卡
  314. else:
  315. logger.info('cyp_do_card_start_charge the once!!')
  316. # 记录ID卡的消费
  317. attachParas = {
  318. "chargeIndex": portStr
  319. }
  320. servicedInfo = {
  321. 'cardNo': cardNo,
  322. 'chargeIndex': portStr
  323. }
  324. orderNo, cardOrderNo = self.record_consume_for_card(card, RMB(payMoney), servicedInfo = servicedInfo,
  325. attachParas = attachParas)
  326. # 记录缓存信息 可以在个人中心以及经销商的端口管理显示
  327. portCache = {
  328. "cardNo": cardNo,
  329. "openId": card.openId,
  330. "allPayMoney": allPayMoney,
  331. "lastPayMoney": payMoney,
  332. "payCount": 1,
  333. "coins": allPayMoney,
  334. "status": Const.DEV_WORK_STATUS_WORKING,
  335. "startTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  336. "orderNo": orderNo,
  337. "cardOrderNo": cardOrderNo,
  338. "consumeType": "card",
  339. "cardBalance": cardBalance
  340. }
  341. ServiceProgress.register_card_service(
  342. self.device,
  343. int(portStr),
  344. card,
  345. {
  346. "orderNo": orderNo,
  347. "money": RMB(allPayMoney).mongo_amount,
  348. "coin": RMB(allPayMoney).mongo_amount,
  349. "cardOrderNo": cardOrderNo
  350. }
  351. )
  352. # 更新端口缓存
  353. Device.update_dev_control_cache(self.device.devNo, {portStr: portCache})
  354. # 通知消费
  355. self.notify_balance_has_consume_for_card(card, payMoney)
  356. def _do_finish(self):
  357. """
  358. 充电结束的指令
  359. :return:
  360. """
  361. # if self.event_data.get('reasonCode') == u'E8':
  362. # #远程手工停止
  363. # logger.info('cyp_Remote shut-off !! event_data:{}'.format(json.dumps(self.event_data,ensure_ascii=False)))
  364. cardNo = str(self.event_data.get("cardNo"))
  365. if cardNo == "00000000":
  366. self._do_netpay_finish()
  367. else:
  368. self._do_card_finish()
  369. def _do_card_finish(self):
  370. logger.info(
  371. 'cyp_do_card_finish() is run !! event_data:{}'.format(json.dumps(self.event_data, ensure_ascii = False)))
  372. cardNo = str(self.event_data.get("cardNo"))
  373. leftBalance = self.event_data.get('leftBalance')
  374. usedtime = self.event_data.get('usedTime')
  375. usedElec = self.event_data.get("usedElec")
  376. desc = self.event_data.get('desc')
  377. port = self.event_data['portStr']
  378. daid = self.event_data['daid']
  379. card = self.update_card_dealer_and_type(cardNo, cardType = "IC")
  380. if not card:
  381. logger.debug("cyp_server no find card!!!")
  382. self._do_over_ack_clear_port(daid, port, overServiceProgress = True)
  383. return
  384. dev_control_cache = Device.get_dev_control_cache(self.device.devNo)
  385. try:
  386. port_cache = dev_control_cache[port]
  387. if not port_cache or port_cache == {'status': 0}:
  388. raise
  389. except Exception:
  390. logger.debug('cyp_the device no cache!!! dev_control_cache:{}'.format(
  391. json.dumps(dev_control_cache, ensure_ascii = False)))
  392. self._do_over_ack_clear_port(daid, port, overServiceProgress = True)
  393. return
  394. # port_cache ={'status': 1, 'openId': u'o-VzzwAfpdglJY38Kj7yMvVWlIgw', 'cardNo': u'828CA649', 'price': 5.0, 'coins': 5.0, 'cardOrderNo': '1600314329724498', 'startTime': '2020-09-17 11:45:29', 'orderNo': '1600314329469873'}
  395. if cardNo != port_cache.get('cardNo'):
  396. # 该端口开始时候记录的卡号与结束上报的时候的卡号不相同,可能缓存纪录有问题,基本不可能!!
  397. logger.info('cyp_the device no cardNo cache,do ack,clear port cache!!! port_cache:{}'.format(
  398. json.dumps(port_cache, ensure_ascii = False)))
  399. self._do_over_ack_clear_port(daid, port, overServiceProgress = True)
  400. return
  401. servicedInfo = {
  402. "port": port,
  403. "elec": usedElec,
  404. "usedTime": usedtime,
  405. "reason": desc,
  406. "finishedTime": datetime.datetime.strftime(datetime.datetime.now(), "%Y-%m-%d %H:%M:%S")
  407. }
  408. servicedInfo.update(port_cache)
  409. servicedInfo["leftBalance"] = leftBalance
  410. servicedInfo['spendMoney'] = str(RMB(servicedInfo["allPayMoney"]) - RMB(leftBalance))
  411. logger.info('cyp_servicedInfo:{}'.format(json.dumps(servicedInfo, ensure_ascii = False)))
  412. # card.balance = card.balance + RMB(leftBalance)
  413. #
  414. # try:
  415. # # card.save()
  416. # except Exception as e:
  417. # logger.debug('cyp_update_card_balance ie error %s' % e)
  418. try:
  419. group = Group.get_group(self.device["groupId"])
  420. self.notify_user(
  421. managerialOpenId = card.managerialOpenId,
  422. templateName = "service_complete",
  423. 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}\\n\\n待返费:\\t\\t{refund}".format(
  424. reason = u"充电结束",
  425. logicalCode = self.device["logicalCode"],
  426. group = group.get("address", ""),
  427. duration = usedtime,
  428. coin = u"%s" % servicedInfo['allPayMoney'],
  429. refund = u"%s 请将卡片贴近充电桩进行返费" % leftBalance
  430. ),
  431. service = u"本次充电结束,请将充电卡放在桩的刷卡区域返费,保持静止5秒以上,直到听到 返费成功 语音后,再把卡移开!",
  432. finishTime = datetime.datetime.strftime(datetime.datetime.now(), "%Y-%m-%d %H:%M:%S"),
  433. remark = u'谢谢您的支持'
  434. )
  435. finally:
  436. newServicedInfo = {
  437. "cardNo": servicedInfo["cardNo"],
  438. "reason": servicedInfo["reason"],
  439. "port": servicedInfo["port"],
  440. "elec": servicedInfo["elec"],
  441. "finishedTime": servicedInfo["finishedTime"],
  442. "coin": servicedInfo["coins"]
  443. }
  444. ServiceProgress.update_progress_and_consume_rcd(
  445. self.device["ownerId"],
  446. {
  447. "device_imei": self.device["devNo"],
  448. "cardId": str(card.id),
  449. "port": int(port),
  450. "isFinished": False,
  451. "consumeOrder.orderNo": servicedInfo.get('orderNo'),
  452. },
  453. newServicedInfo
  454. )
  455. self._do_over_ack_clear_port(daid, port)
  456. logger.info('cyp_do_card_finish() is over!!')
  457. def _do_netpay_finish(self):
  458. logger.info('cyp_do_netpay_finish() is run !! event_data:{}'.format(
  459. json.dumps(self.event_data, ensure_ascii = False)))
  460. cardNo = str(self.event_data.get("cardNo"))
  461. leftBalance = self.event_data.get('leftBalance')
  462. usedtime = self.event_data.get('usedTime')
  463. usedElec = self.event_data.get("usedElec")
  464. desc = self.event_data.get('desc')
  465. port = self.event_data['portStr']
  466. daid = self.event_data['daid']
  467. dev_control_cache = Device.get_dev_control_cache(self.device.devNo)
  468. try:
  469. port_cache = dev_control_cache[port]
  470. if not port_cache or port_cache == {'status': 0}:
  471. raise
  472. except Exception:
  473. logger.info('cyp_the device no port_cache,do ack,clear port cache!!! dev_control_cache:{}'.format(
  474. dev_control_cache))
  475. self._do_over_ack_clear_port(daid, port, overServiceProgress = True)
  476. return
  477. try:
  478. servicedInfo = {
  479. "port": port,
  480. "elec": usedElec,
  481. "usedTime": usedtime,
  482. "reason": desc,
  483. "finishedTime": datetime.datetime.strftime(datetime.datetime.now(), "%Y-%m-%d %H:%M:%S")
  484. }
  485. servicedInfo.update(port_cache)
  486. servicedInfo["leftBalance"] = leftBalance
  487. payType = servicedInfo['payType']
  488. openId = servicedInfo['openId']
  489. coins = servicedInfo['coins']
  490. price = servicedInfo['allPayMoney']
  491. vCardId = servicedInfo.get('vCardId')
  492. rechargeRcdId = servicedInfo.get('rechargeRcdId')
  493. # 尝试进行分账
  494. rechargeRecord = self.do_ledger(rechargeRcdId)
  495. # 如果没有openId则为经销商远程上分
  496. if not openId and cardNo == '00000000':
  497. logger.info('cyp_Remote activation by dealer')
  498. return
  499. if payType == 'coin':
  500. servicedInfo['spendCoin'] = str(VirtualCoin(servicedInfo['coins']) - VirtualCoin(leftBalance))
  501. elif payType == 'cash':
  502. servicedInfo['spendMoney'] = str(RMB(servicedInfo['coins']) - RMB(leftBalance))
  503. logger.info('cyp_servicedInfo:{}'.format(json.dumps(servicedInfo, ensure_ascii = False)))
  504. group = Group.get_group(self.device["groupId"])
  505. user = MyUser.objects.filter(openId = openId, groupId = self.device["groupId"]).first()
  506. logger.info("cyp_ChangYuanPower net pay finish and start to notify user! {}".format(self.device["devNo"]))
  507. self.notify_user(
  508. managerialOpenId = user.managerialOpenId if user else "",
  509. templateName = "service_complete",
  510. 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(
  511. reason = u"充电结束",
  512. logicalCode = self.device["logicalCode"],
  513. group = group.get("address", ""),
  514. duration = usedtime,
  515. coin = u"%s金币" % coins if payType == 'coin' else u"%s元" % price
  516. ),
  517. service = u"自助充电服务",
  518. finishTime = datetime.datetime.strftime(datetime.datetime.now(), "%Y-%m-%d %H:%M:%S"),
  519. remark = u'谢谢您的支持')
  520. if RMB(leftBalance) > RMB(0):
  521. if payType == "cash":
  522. if not self.device.is_auto_refund:
  523. return
  524. refundMoney = RMB(rechargeRecord.money) * Ratio(float(leftBalance) / float(coins))
  525. refund_order = refund_cash(
  526. rechargeRecord, refundMoney, VirtualCoin(0),
  527. user = user, minus_total_consume = VirtualCoin(rechargeRecord.coins)) # type: RefundMoneyRecord
  528. if refund_order:
  529. logger.info("refund cash apply success!")
  530. servicedInfo.update({DEALER_CONSUMPTION_AGG_KIND.REFUNDED_CASH: refundMoney.mongo_amount})
  531. self.notify_user(user.managerialOpenId if user else '', 'refund_coins', **{
  532. 'title': u"退款(退款金额将于1-5个工作日内返还)",
  533. 'backCount': u'%s 元' % refundMoney,
  534. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  535. })
  536. # 金币支付并且 设备金币退款的开关打开
  537. elif payType == "coin":
  538. if not self.device.is_auto_refund:
  539. return
  540. refundMoney = VirtualCoin(leftBalance)
  541. servicedInfo.update({DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS: refundMoney.mongo_amount})
  542. refund_money(self.device, refundMoney, openId)
  543. # 虚拟卡支付的情况
  544. else:
  545. logger.info("pay type need not to refund!")
  546. newServicedInfo = {
  547. "reason": servicedInfo["reason"],
  548. "port": servicedInfo["port"],
  549. "coin": servicedInfo["coins"],
  550. "elec": servicedInfo["elec"],
  551. }
  552. if DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS in servicedInfo:
  553. refunded_coins = servicedInfo[DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS]
  554. newServicedInfo.update(
  555. {
  556. DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: (VirtualCoin(coins) - refunded_coins).mongo_amount,
  557. DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS: refunded_coins
  558. })
  559. elif DEALER_CONSUMPTION_AGG_KIND.REFUNDED_CASH in servicedInfo:
  560. refunded_cash = servicedInfo[DEALER_CONSUMPTION_AGG_KIND.REFUNDED_CASH]
  561. newServicedInfo.update(
  562. {
  563. DEALER_CONSUMPTION_AGG_KIND.REFUNDED_CASH: refunded_cash
  564. })
  565. ServiceProgress.update_progress_and_consume_rcd(
  566. self.device["ownerId"],
  567. {
  568. "open_id": openId,
  569. "device_imei": self.device["devNo"],
  570. "port": int(port),
  571. "isFinished": False,
  572. "consumeOrder.orderNo": servicedInfo.get('orderNo'),
  573. },
  574. newServicedInfo
  575. )
  576. logger.info('cyp_do_netpay_finish() is over !!')
  577. finally:
  578. self._do_over_ack_clear_port(daid, port)
  579. def update_card_dealer_and_type(self, cardNo, cardType = 'IC', isHaveBalance = True, balance = None):
  580. """
  581. 更新卡的状态 重写目的 如果卡不存在 立即下发停止充电指令 而不是新建一张卡
  582. :param cardNo:
  583. :param cardType:
  584. :param isHaveBalance:
  585. :param balance:
  586. :return:
  587. """
  588. dealer = Dealer.objects.get(id = self.device["ownerId"])
  589. agent = Agent.objects.get(id = dealer.agentId)
  590. if not agent:
  591. logger.error('agent is not found, agentId=%s' % dealer.agentId)
  592. return
  593. try:
  594. card = Card.objects.get(cardNo = cardNo, agentId = dealer.agentId)
  595. # 如果卡没有被绑定,这个时候检查下绑定关系。如果卡已经被某个经销商认领了,就不要刷新,不要动了
  596. if card.cardType and card.dealerId and card.devNo and (card.dealerId == self.device['ownerId']):
  597. return card
  598. card.dealerId = self.device['ownerId']
  599. card.devNo = self.device['devNo']
  600. card.cardType = cardType
  601. card.devTypeCode = self.device['devType']['code']
  602. card.isHaveBalance = isHaveBalance
  603. return card.save()
  604. except DoesNotExist:
  605. return
  606. except Exception as e:
  607. logger.exception(e)
  608. return
  609. def notify_invalid_card_to_dealer(self, cardNo, card):
  610. logger.info('Illegal card <{}>, charging is about to stop'.format(cardNo))
  611. group = Group.get_group(self.device['groupId'])
  612. self.notify_dealer(
  613. 'device_fault',
  614. title = u'注意!注意!您当前的设备已被非法卡使用!',
  615. device = u'组号::%s, 二维码编号:%s' % (self.device['groupNumber'], self.device['logicalCode']),
  616. location = u'组名称:%s, 地址:%s' % (group['groupName'], group['address']),
  617. fault = u'在%s编号为%s的设备被非法卡卡启动,启动卡号为:%s 卡联系人 %s 卡联系电话 %s' % (
  618. group['address'], self.device['logicalCode'], card.cardNo if card else '',
  619. card.phone if card else '', card.cardName if card else ''),
  620. notifyTime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  621. )