changyuanSix.py 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import logging
  5. import random
  6. import time
  7. import simplejson as json
  8. from bson import ObjectId
  9. from mongoengine import DoesNotExist, ValidationError
  10. from typing import TYPE_CHECKING
  11. from apilib.monetary import RMB, VirtualCoin, Ratio
  12. from apilib.utils_datetime import to_datetime
  13. from apilib.utils_sys import memcache_lock
  14. from apps import serviceCache
  15. from apps.web.agent.models import Agent
  16. from apps.web.common.models import TempValues
  17. from apps.web.constant import FAULT_CODE, FAULT_LEVEL, Const, DEALER_CONSUMPTION_AGG_KIND, USER_RECHARGE_TYPE
  18. from apps.web.core.accounting import Accounting
  19. from apps.web.core.payment import WithdrawGateway
  20. from apps.web.dealer.models import Dealer
  21. from apps.web.device.models import Group, Device
  22. from apps.web.eventer import EventBuilder
  23. from apps.web.eventer.base import WorkEvent, FaultEvent
  24. from apps.web.report.ledger import Ledger
  25. from apps.web.user.models import CardRechargeOrder, Card, RechargeRecord, CardRechargeRecord, ServiceProgress, \
  26. ConsumeRecord, CardConsumeRecord, MyUser, UserVirtualCard
  27. if TYPE_CHECKING:
  28. from apps.web.device.models import GroupDict
  29. logger = logging.getLogger(__name__)
  30. class PayTypeEnum(object):
  31. COUNT_CARD = '00'
  32. NET_PAY = '01'
  33. OFFLINE_COIN = '02'
  34. PREPAID_CARD = '03'
  35. class builder(EventBuilder):
  36. def __getEvent__(self, device_event):
  37. eventData = self.deviceAdapter.analyze_event_data(device_event.get('data'))
  38. if eventData is None or 'cmdCode' not in eventData:
  39. return None
  40. if "ack_id" in device_event:
  41. eventData["ack_id"] = device_event["ack_id"]
  42. if eventData['cmdCode'] == 'D7':
  43. return ChangYuanFaultEventer(self.deviceAdapter, eventData)
  44. else:
  45. return ChangYuanWorkEventer(self.deviceAdapter, eventData)
  46. class ChangYuanFaultEventer(FaultEvent):
  47. def do(self, **args):
  48. if "ack_id" in self.event_data:
  49. self.deviceAdapter._ack(self.event_data["ack_id"])
  50. self.event_data.update({
  51. 'faultName': u'设备火灾预警',
  52. 'faultCode': FAULT_CODE.FIRE_ALARM,
  53. 'level': FAULT_LEVEL.CRITICAL,
  54. 'desc': u'主板上报设备火警'
  55. })
  56. warningData = {
  57. "warningStatus": 2,
  58. "warningDesc": "设备温度超限警告",
  59. "warningTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  60. }
  61. # 整机告警
  62. Device.update_dev_warning_cache(self.device.devNo, {"0": warningData})
  63. super(ChangYuanFaultEventer, self).do(**args)
  64. class ChangYuanWorkEventer(WorkEvent):
  65. def do(self, **args):
  66. if "ack_id" in self.event_data:
  67. self.deviceAdapter._ack(self.event_data['ack_id'])
  68. cmdCode = self.event_data.get('cmdCode')
  69. cacheKey = '{}-{}-event'.format(self.device.devNo, cmdCode)
  70. with memcache_lock(key=cacheKey, value='1', expire=120) as acquired:
  71. if acquired:
  72. if cmdCode == 'D3':
  73. self._do_recharge_card_result()
  74. elif cmdCode == 'D4':
  75. self._do_recharge_card()
  76. elif cmdCode == 'D9':
  77. self._do_start_charge()
  78. elif cmdCode == 'DA':
  79. self._do_finish()
  80. elif cmdCode == 'DE':
  81. self._do_card_refund()
  82. else:
  83. logger.warning(
  84. 'cyp_event is doing!, dev is <{}>, cacheKey is <{}>, cmd is <{}>, data is <{}>'.format(
  85. self.device.devNo,
  86. cacheKey,
  87. cmdCode,
  88. self.event_data
  89. )
  90. )
  91. def _do_card_refund(self):
  92. beforeRefund = self.event_data.get('beforeRefund')
  93. refund = self.event_data.get('refund')
  94. afterRefund = self.event_data.get('afterRefund')
  95. cardNo = str(self.event_data.get('cardNo'))
  96. # 指令上传错误
  97. if VirtualCoin(beforeRefund) + VirtualCoin(refund) != VirtualCoin(afterRefund):
  98. logger.info('bad refund event, beforeRefund is {}, refund is {} afterRefund is {}'.format(
  99. beforeRefund, refund, afterRefund
  100. ))
  101. return
  102. # 查找卡的消息 一定要是预付费卡
  103. card = self._update_card_dealer_and_type(cardNo, "01")
  104. if not card:
  105. return
  106. # 查看返费前的金额是否是相等的
  107. if VirtualCoin(card.balance) != VirtualCoin(beforeRefund):
  108. logger.info(
  109. 'beforeRefund is not equal card balance, cardNo is {}, beforeRefund is {}, card balance is {}'.format(
  110. cardNo, beforeRefund, card.balance
  111. ))
  112. return
  113. # 退费
  114. self.refund_money_for_card(RMB(refund), str(card.id))
  115. # 通知用户
  116. self.notify_user(
  117. card.managerialOpenId or "",
  118. 'refund_coins',
  119. **{
  120. 'title': u'预付卡充电桩返费',
  121. 'backCount': u'%s' % refund,
  122. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  123. }
  124. )
  125. @staticmethod
  126. def _do_update_ConsumeRecord_CardConsumeRecord_service_progress(portCache, updateMoney):
  127. portCache['payCount'] = portCache.get('payCount') + 1
  128. portCache['allPayMoney'] = updateMoney
  129. portCache['coins'] = updateMoney
  130. orderNo = portCache.get('orderNo')
  131. cardOrderNo = portCache.get('cardOrderNo')
  132. try:
  133. order = ConsumeRecord.objects.get(orderNo=orderNo)
  134. card_order = CardConsumeRecord.objects.get(orderNo=cardOrderNo)
  135. service_pogress = ServiceProgress.objects(__raw__={'consumeOrder.orderNo': orderNo}).first()
  136. order.coin = updateMoney
  137. order.money = updateMoney
  138. card_order.money = updateMoney
  139. service_pogress.consumeOrder['money'] = updateMoney
  140. service_pogress.consumeOrder['coin'] = updateMoney
  141. order.save()
  142. card_order.save()
  143. service_pogress.save()
  144. except Exception as e:
  145. logger.debug('%s' % e)
  146. finally:
  147. pass
  148. def _do_recharge_card(self):
  149. """
  150. 同步卡的请求
  151. :return:
  152. """
  153. cardNo = self.event_data.get('cardNo')
  154. cardBalance = self.event_data.get('cardBalance')
  155. cardType = self.event_data.get('cardType')
  156. # 查询卡 包括卡的类型
  157. card = self._update_card_dealer_and_type(cardNo, billing_method=cardType)
  158. if not card:
  159. return
  160. # 更新卡的余额 以设备侧的为准
  161. if RMB(cardBalance) != card.balance:
  162. logger.info('Card<{}> balance<{}> needs to be sync !!!'.format(cardNo, card.balance))
  163. card.balance = RMB(cardBalance)
  164. card = card.save()
  165. # orderNos 表示充值的订单 cardOrderNos 表示卡充值的订单
  166. money, coins, orderNos, cardOrderNos = CardRechargeOrder.get_to_do_list(str(card.id))
  167. # 一旦卡被冻结 立即下发 创建一张为 负数的金额订单 将卡的余额清空
  168. if card.frozen:
  169. group = Group.get_group(card.groupId)
  170. CardRechargeOrder.new_one(
  171. openId=card.openId,
  172. cardId=str(card.id),
  173. cardNo=card.cardNo,
  174. money=RMB(0),
  175. coins=VirtualCoin(0) - VirtualCoin(coins) - VirtualCoin(card.balance),
  176. group=group,
  177. rechargeId=ObjectId(),
  178. rechargeType='sendCoin'
  179. )
  180. money, coins, orderNos, cardOrderNos = CardRechargeOrder.get_to_do_list(str(card.id))
  181. # 没有需要同步的订单的情况下 直接退出
  182. if not orderNos:
  183. logger.info('Not card recharge record!, card is <{}>'.format(cardNo))
  184. # self._do_offline_recharge_by_dealer(cardNo, card, cardType)
  185. return
  186. orderNos = [str(item) for item in orderNos]
  187. sid = str(random.randint(0, 0xFFFF))
  188. TempValues.set('%s-%s' % (self.device.devNo, sid), value=json.dumps(orderNos))
  189. TempValues.set('%s-%s-%s' % (self.device.devNo, sid, cardNo), value=json.dumps(cardOrderNos))
  190. TempValues.set('%s-%s' % (self.device.devNo, cardNo), value=str(sid))
  191. # 计算总的需要同步的金额
  192. asyncMoney = RMB(cardBalance) + RMB(coins)
  193. logger.info('ready to recharge card, card is <{}>, money is <{}>'.format(cardNo, coins))
  194. # 已经下发金额后,先将订单的状态改为结束。如果主板上报失败,再将订单状态迁移回来否则就不再变化订单状态了
  195. try:
  196. CardRechargeOrder.update_card_order_has_finished(str(card.id))
  197. except Exception as e:
  198. logger.debug('%s' % e)
  199. else:
  200. self.deviceAdapter._async_card_balance(cardType, cardNo, asyncMoney)
  201. def _do_recharge_card_result(self):
  202. """
  203. 确认卡的同步结果
  204. :return:
  205. """
  206. asyncStatus = self.event_data.get('result')
  207. answerSid = self.event_data.get('sid')
  208. cardBalance = self.event_data.get('cardBalance')
  209. cardNo = str(self.event_data.get('cardNo'))
  210. cardType = self.event_data.get('cardType')
  211. # 先找卡 卡都不存在的话 直接推掉
  212. card = self._update_card_dealer_and_type(cardNo, cardType)
  213. if not card:
  214. return
  215. sid = TempValues.get('{}-{}'.format(self.device.devNo, cardNo))
  216. if sid != answerSid:
  217. logger.error('answer sid is not equal sid <{}>-<{}>'.format(sid, answerSid))
  218. return
  219. # 根据sid 以及卡号 查询出此次充值的所有的订单
  220. orderNos = TempValues.get('{}-{}'.format(self.device.devNo, sid))
  221. cardOrderNos = TempValues.get('{}-{}-{}'.format(self.device.devNo, sid, cardNo))
  222. # 明确接收到了主板同步失败的情况下 直接退出 不在转换充值订单的问题 防止出错
  223. if not asyncStatus:
  224. logger.error("card async not success! event data is <{}>".format(self.event_data))
  225. return
  226. # 下面的都不会更改订单的状态 最多走售后
  227. money, coins = RMB(0), VirtualCoin(0)
  228. # 校验金额是否是相等的
  229. orderNos = [ObjectId(item) for item in orderNos]
  230. rds = RechargeRecord.objects.filter(ownerId=card.dealerId, id__in=orderNos)
  231. for item in rds:
  232. money += item.money
  233. coins += item.coins
  234. # 这个地方就是异常值处理了
  235. if VirtualCoin(coins + card.balance) != VirtualCoin(cardBalance):
  236. logger.error('card pre balance not equal now balance! event is <{}>'.format(self.event_data))
  237. return
  238. # 依次更改卡充值的订单 并创建充值订单
  239. cardOrders = CardRechargeOrder.objects.filter(orderNo__in=cardOrderNos)
  240. for _order in cardOrders: # type: CardRechargeOrder
  241. _order.update_after_recharge_ic_card(
  242. device=self.device,
  243. sendMoney=RMB(_order.coins),
  244. preBalance=card.balance
  245. )
  246. preBalance = card.balance
  247. card.balance = card.balance + _order.coins
  248. # 创建充值记录
  249. CardRechargeRecord.add_record(
  250. card=card,
  251. group=Group.get_group(_order.groupId),
  252. order=_order,
  253. device=self.device
  254. )
  255. # 保存
  256. card.save()
  257. # 完成之后将TempValue 的key value 清空
  258. TempValues.remove('%s-%s' % (self.device['devNo'], sid))
  259. TempValues.remove('%s-%s' % (self.device['devNo'], cardNo))
  260. TempValues.remove("%s-%s-%s" % (self.device["devNo"], sid, cardNo))
  261. def _do_start_charge(self):
  262. '''
  263. 充电开始指令, 主要是更新各种缓存等等,
  264. 对于扫码的启动基本在adapter缓存已经处理完成,主要需要处理的是刷卡的启动
  265. 根据 需求方要求 刷卡的可以做合单处理
  266. :return:
  267. '''
  268. logger.info(
  269. 'do_start_charge() is run!! event_data:{}'.format(self.event_data))
  270. portStr = str(self.event_data.get('port'))
  271. ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  272. lineInfo = ctrInfo.get(portStr, {'port': portStr})
  273. payType = self.event_data.get('payType')
  274. if payType == PayTypeEnum.NET_PAY: # 线上支付
  275. if lineInfo.get('status', Const.DEV_WORK_STATUS_IDLE) == Const.DEV_WORK_STATUS_IDLE:
  276. lineInfo['power'] = self.event_data.get('power')
  277. lineInfo['payType'] = self.event_data.get('payType')
  278. lineInfo['needTime'] = self.event_data.get('needTime')
  279. logger.info("NET_PAY is ok!!")
  280. else:
  281. lineInfo['power'] = self.event_data.get('power')
  282. lineInfo['needTime'] = self.event_data.get('needTime')
  283. logger.info("NET_PAY is ok!!")
  284. Device.update_port_control_cache(self.device.devNo, lineInfo)
  285. return
  286. elif payType == PayTypeEnum.OFFLINE_COIN:
  287. Accounting.recordOfflineCoin(self.device,
  288. int(time.time()),
  289. int(self.event_data['coins']))
  290. orderNo = self.record_consume_for_coin(RMB(self.event_data["coins"]))
  291. Device.update_port_control_cache(
  292. self.device["devNo"],
  293. {'port': portStr,
  294. "isStart": True,
  295. "coins": self.event_data["coins"],
  296. "needTime": self.event_data["needTime"],
  297. "power": self.event_data["power"],
  298. "payType": self.event_data["payType"],
  299. "startTime": datetime.datetime.now().strftime(Const.DATETIME_FMT),
  300. "status": Const.DEV_WORK_STATUS_WORKING,
  301. "consumeOrderNo": orderNo,
  302. "payInfo": [{
  303. "coins": float(self.event_data["coins"]),
  304. "price": float(self.event_data["coins"]),
  305. "needTIme": self.event_data["needTime"],
  306. "consumeOrderNo": orderNo
  307. }]
  308. })
  309. else:
  310. cardNo = self.event_data.get('cardNo')
  311. if cardNo == '00000000':
  312. # 可能是虚拟卡 暂不处理
  313. return
  314. cardBalance = RMB(self.event_data.get('cardBalance'))
  315. coins = self.event_data.get('coins')
  316. card = self._update_card_dealer_and_type(cardNo, billing_method=self.event_data["cardType"])
  317. needTime = self.event_data.get('needTime')
  318. needBindCard = self.device['otherConf'].get('needBindCard', True)
  319. if not card:
  320. if needBindCard: # 需要绑卡却没有绑卡 直接停止
  321. return self.deviceAdapter.stop_charging_port(portStr)
  322. else:
  323. return
  324. if card.frozen:
  325. self.notify_invalid_card_to_dealer(cardNo, card)
  326. # 停止此离线卡的充电行为
  327. self.deviceAdapter.stop_charging_port(portStr)
  328. return
  329. if not Group.is_currency_mode_group(card.groupId, self.device.groupId): # 地址组通用校验
  330. logger.info(
  331. 'is_currency_mode_group is fail <{}> card_group=<{}> dev_group=<{}>'.format(cardNo, card.groupId,
  332. self.device.groupId))
  333. return self.deviceAdapter.stop_charging_port(portStr)
  334. # 离线卡 以设备上报过来的数据为准
  335. card.balance = cardBalance.mongo_amount
  336. card.save()
  337. card.reload()
  338. if lineInfo.get('status',
  339. Const.DEV_WORK_STATUS_IDLE) != Const.DEV_WORK_STATUS_IDLE and cardNo == lineInfo.get(
  340. 'cardNo'):
  341. logger.info('Card<{}> Swipe to pay again!!'.format(cardNo))
  342. orderNo = lineInfo.get('orderNo')
  343. cardOrderNo = lineInfo.get('cardOrderNo')
  344. lineInfo['coins'] += coins
  345. lineInfo['payCount'] += 1
  346. if not needTime:
  347. needTime = 0
  348. # lineInfo['needTime'] = needTime
  349. ConsumeRecord.objects.filter(orderNo=orderNo).update_one(inc__coin=coins, inc__money=coins)
  350. CardConsumeRecord.objects.filter(orderNo=cardOrderNo).update_one(inc__money=coins, dec__balance=coins)
  351. ServiceProgress.objects.filter(device_imei=self.device.devNo, port=int(portStr),
  352. consumeOrder__orderNo=orderNo, isFinished=False).update_one(
  353. inc__consumeOrder__coin=coins, inc__finished_time=(time.time() + needTime * 60))
  354. # 第一次刷卡
  355. else:
  356. logger.info('Card<{}> start charge the once!!'.format(cardNo))
  357. # 记录ID卡的消费
  358. attachParas = {
  359. 'chargeIndex': portStr
  360. }
  361. servicedInfo = {
  362. 'cardNo': cardNo,
  363. 'chargeIndex': portStr
  364. }
  365. orderNo, cardOrderNo = self.record_consume_for_card(card, RMB(coins), servicedInfo=servicedInfo,
  366. attachParas=attachParas)
  367. # 记录缓存信息 可以在个人中心以及经销商的端口管理显示
  368. lineInfo = {
  369. 'port': portStr,
  370. 'cardNo': cardNo,
  371. 'openId': card.openId,
  372. 'payCount': 1,
  373. 'coins': coins,
  374. 'status': Const.DEV_WORK_STATUS_WORKING,
  375. 'startTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  376. 'orderNo': orderNo,
  377. 'cardOrderNo': cardOrderNo,
  378. 'payType': payType,
  379. 'consumeType': 'card',
  380. }
  381. finishedTime = None
  382. if needTime:
  383. finishedTime = int(time.time()) + needTime * 60
  384. # lineInfo['needTime'] = needTime
  385. ServiceProgress.register_card_service(
  386. self.device,
  387. int(portStr),
  388. card,
  389. {
  390. 'orderNo': orderNo,
  391. 'coin': VirtualCoin(coins).mongo_amount,
  392. 'cardOrderNo': cardOrderNo
  393. },
  394. finishedTime)
  395. logger.info('port:{} is running, order:{} cardOrder:{}'.format(portStr, orderNo, cardOrderNo))
  396. # 更新端口缓存
  397. Device.update_port_control_cache(self.device.devNo, lineInfo)
  398. # 通知卡消费成功
  399. self.notify_balance_has_consume_for_card(card, coins)
  400. def _do_finish(self):
  401. '''
  402. 充电结束的指令
  403. :return:
  404. '''
  405. portStr = str(self.event_data.get('port'))
  406. FINISHED_COUNT_KEY = 'device_port_finish_{}_{}'.format(self.device.devNo, portStr)
  407. count = serviceCache.get(FINISHED_COUNT_KEY, 0)
  408. ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  409. lineInfo = ctrInfo.get(portStr)
  410. if not lineInfo or lineInfo.get('payType') is None:
  411. logger.info('cache is missing ,DA is over')
  412. return
  413. duration = ((datetime.datetime.now() - to_datetime(lineInfo.get('startTime'))).total_seconds() + 59) // 60
  414. if duration < 3:
  415. count += 1
  416. serviceCache.set(FINISHED_COUNT_KEY, count, 60 * 60 * 24)
  417. if count >= 3:
  418. self.notify_fault_to_dealer()
  419. serviceCache.delete(FINISHED_COUNT_KEY)
  420. payType = lineInfo.get('payType')
  421. try:
  422. if payType == PayTypeEnum.NET_PAY:
  423. self._do_netpay_finish(portStr, lineInfo)
  424. elif payType == PayTypeEnum.OFFLINE_COIN:
  425. self._do_offline_coin_finish(portStr, lineInfo)
  426. else:
  427. self._do_card_finish(portStr, lineInfo)
  428. except Exception as e:
  429. logger.error('DA is over error:{}'.format(e))
  430. Device.clear_port_control_cache(self.device.devNo, portStr)
  431. def _do_card_finish(self, portStr, lineInfo):
  432. logger.info('do card finished lineInfo={} ||| event_data={}'.format(lineInfo, self.event_data))
  433. extra = []
  434. coins = lineInfo.get('coins')
  435. cardNo = lineInfo.get('cardNo')
  436. card = self.update_card_dealer_and_type(cardNo, cardType='IC')
  437. if not card:
  438. logger.debug('server no find card!!! cardNo=<{}>'.format(cardNo))
  439. return
  440. extra.append({u'使用卡片': u'{}--{}'.format(card.cardName, cardNo)})
  441. consumeDict = {
  442. 'port': portStr,
  443. 'cardNo': cardNo,
  444. 'elec': self.event_data.get('usedElec'),
  445. 'reason': self.event_data.get('reason'),
  446. }
  447. payType = lineInfo.get('payType')
  448. if payType == PayTypeEnum.COUNT_CARD:
  449. # leftTime = self.event_data.get('left')
  450. now = datetime.datetime.now()
  451. # consumeDict['leftTime'] = leftTime
  452. consumeDict['duration'] = ((now - to_datetime(lineInfo.get('startTime'))).total_seconds() + 59) // 60
  453. # consumeDict['actualNeedTime'] = '动态计算时间为{}分钟'.format(consumeDict['duration'] + leftTime)
  454. # extra.append({u"使用详情": u"本次使用{}分钟,剩余{}分钟".format(consumeDict['duration'], consumeDict['leftTime'])})
  455. extra.append({u"使用详情": u"本次使用{}分钟".format(consumeDict['duration'])})
  456. elif payType == PayTypeEnum.PREPAID_CARD:
  457. leftMoney = self.event_data.get('leftMoney')
  458. if leftMoney:
  459. leftMoney = float(leftMoney)
  460. coins = float(coins)
  461. if leftMoney > coins:
  462. leftMoney = coins
  463. refundedMoney = round(leftMoney, 2)
  464. consumeDict['spendMoney'] = round(coins - leftMoney, 2)
  465. extra.append(
  466. {u"消费明细": u"消费{}金币,剩余{}金币(等待靠卡退费)".format(consumeDict['spendMoney'], refundedMoney)})
  467. else:
  468. consumeDict['spendMoney'] = round(coins, 2)
  469. extra.append(
  470. {u"消费明细": u"消费{}金币".format(coins)})
  471. ServiceProgress.update_progress_and_consume_rcd(
  472. self.device['ownerId'],
  473. {
  474. 'device_imei': self.device['devNo'],
  475. 'cardId': str(card.id),
  476. 'port': int(portStr),
  477. 'isFinished': False
  478. },
  479. consumeDict
  480. )
  481. self.notify_to_user(card.managerialOpenId, extra)
  482. logger.info('do_card_finish is finished!!')
  483. def _do_netpay_finish(self, portStr, lineInfo):
  484. logger.info('netpay_finish is running !! event_data:{}'.format(
  485. json.dumps(self.event_data, ensure_ascii=False)))
  486. usedElec = self.event_data.get('spendElec')
  487. reason = self.event_data.get('reason')
  488. leftMoney = self.event_data.get('leftMoney')
  489. extra = []
  490. openId = lineInfo.get('openId')
  491. coins = VirtualCoin(lineInfo.get('coins', 0))
  492. money = RMB(lineInfo.get('price', 0))
  493. payInfo = lineInfo.get('payInfo')
  494. vCardId = lineInfo.get('vCardId')
  495. now = datetime.datetime.now()
  496. duration = (now - to_datetime(lineInfo.get('startTime'))).total_seconds() // 60
  497. refundMoney = RMB(leftMoney)
  498. if duration <= 5:
  499. refundMoney = money
  500. # 如果没有openId则为经销商远程上分
  501. if not openId:
  502. logger.info('Remote activation by dealer')
  503. return
  504. else:
  505. user = MyUser.objects.filter(openId=openId, groupId=self.device['groupId']).first()
  506. consumeDict = {
  507. 'port': portStr,
  508. 'elec': usedElec,
  509. 'duration': duration,
  510. 'reason': reason,
  511. }
  512. logger.info('ChangYuanPower net pay finish and start to notify user! {}-{}'.format(self.device['devNo'],
  513. portStr))
  514. if duration > 5:
  515. extra.append({u'充电时长': u'{}分钟'.format(duration)})
  516. else:
  517. extra.append({u'充电时长': u'0分钟'})
  518. if self.device.is_auto_refund or duration <= 5:
  519. if payInfo:
  520. # 先做现金分账
  521. for item in payInfo:
  522. self.do_ledger(item['rechargeRcdId'])
  523. # 现金退款
  524. self.refund_net_pay(user, lineInfo, refundedMoney=refundMoney, refundCoins=VirtualCoin(0),
  525. consumeDict=consumeDict, is_cash=True)
  526. extra.append({u'消费明细': u'支付{}元,退款{}元'.format(money, refundMoney)})
  527. elif vCardId:
  528. try:
  529. vCard = UserVirtualCard.objects.get(id=vCardId)
  530. extra.append({u'虚拟卡券': u'{}--{}'.format(vCard.cardName, vCard.cardNo)})
  531. except DoesNotExist:
  532. logger.info('can not find the vCard id = %s' % vCardId)
  533. pass
  534. pass
  535. else:
  536. self.refund_net_pay(user, lineInfo, refundedMoney=RMB(0), refundCoins=VirtualCoin(refundMoney),
  537. consumeDict=consumeDict, is_cash=False)
  538. extra.append({u'消费明细': u'消费{}金币,退款{}金币'.format(coins, VirtualCoin(refundMoney))})
  539. else:
  540. extra.append({u'消费明细': u'消费{}金币'.format(coins)})
  541. if payInfo:
  542. for item in payInfo:
  543. self.do_ledger(item['rechargeRcdId'])
  544. ServiceProgress.update_progress_and_consume_rcd(
  545. self.device['ownerId'],
  546. {
  547. 'open_id': openId,
  548. 'device_imei': self.device['devNo'],
  549. 'port': int(portStr),
  550. 'isFinished': False
  551. },
  552. consumeDict
  553. )
  554. self.notify_to_user(openId=user.managerialOpenId, extra=extra)
  555. logger.info('do_netpay_finish() is over !!')
  556. def _update_card_dealer_and_type(self, cardNo, billing_method):
  557. """
  558. 昌原的都是IC卡 并且需要标注卡类型
  559. :param cardNo: 卡号
  560. :param billing_method: 卡类型
  561. :return:
  562. """
  563. card = self.update_card_dealer_and_type(cardNo, "IC") # type: Card
  564. if not card:
  565. logger.error("not found card <{}>, event is <{}>".format(cardNo, self.event_data))
  566. return None
  567. # 没有卡的计费类型的 说明是经销商新添加的卡 添加上计费类型 有了卡计费类型的 需要比较卡计费类型是否正确 如果不正确的 说明卡不正确
  568. if not card.billing_method:
  569. card.billing_method = billing_method
  570. if not card.billing_method_equal(billing_method):
  571. logger.error("card <{}> billing_method not equal!, event is <{}>".format(cardNo, self.event_data))
  572. return None
  573. return card
  574. def update_card_dealer_and_type(self, cardNo, cardType='ID', isHaveBalance=True, balance=None):
  575. """
  576. 更新卡的状态 重写目的 如果卡不存在 立即下发停止充电指令 而不是新建一张卡
  577. :param cardNo:
  578. :param cardType:
  579. :param isHaveBalance:
  580. :param balance:
  581. :return:
  582. """
  583. dealer = Dealer.objects.get(id=self.device['ownerId'])
  584. agent = Agent.objects.get(id=dealer.agentId)
  585. if not agent:
  586. logger.error('agent is not found, agentId=%s' % dealer.agentId)
  587. return
  588. try:
  589. card = Card.objects.filter(cardNo=cardNo, agentId=dealer.agentId).first()
  590. if not card:
  591. return None
  592. # 如果卡没有被绑定,这个时候检查下绑定关系。如果卡已经被某个经销商认领了,就不要刷新,不要动了
  593. if card.cardType and card.dealerId and card.devNo and (card.dealerId == self.device['ownerId']):
  594. return card
  595. else:
  596. card.dealerId = self.device['ownerId']
  597. card.devNo = self.device['devNo']
  598. card.cardType = cardType
  599. card.devTypeCode = self.device['devType']['code']
  600. card.isHaveBalance = isHaveBalance
  601. card.save()
  602. return card
  603. except Exception as e:
  604. logger.exception(e)
  605. return
  606. def notify_to_user(self, openId, extra):
  607. group = Group.get_group(self.device['groupId'])
  608. self.notify_user_service_complete(
  609. service_name='充电',
  610. openid=openId,
  611. port=self.event_data['port'],
  612. address=group['address'],
  613. reason=self.event_data.get('reason'),
  614. finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  615. extra=extra)
  616. def notify_invalid_card_to_dealer(self, cardNo, card):
  617. logger.info('Illegal card <{}>, charging is about to stop'.format(cardNo))
  618. group = Group.get_group(self.device['groupId'])
  619. self.notify_dealer(
  620. 'device_fault',
  621. title=u'注意!注意!您当前的设备已被非法卡使用!',
  622. device=u'组号::%s, 二维码编号:%s' % (self.device['groupNumber'], self.device['logicalCode']),
  623. location=u'组名称:%s, 地址:%s' % (group['groupName'], group['address']),
  624. fault=u'在%s编号为%s的设备被非法卡卡启动,启动卡号为:%s 卡联系人 %s 卡联系电话 %s' % (
  625. group['address'], self.device['logicalCode'], card.cardNo if card else '',
  626. card.phone if card else '', card.cardName if card else ''),
  627. notifyTime=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  628. )
  629. def _do_offline_coin_finish(self, portStr, lineInfo):
  630. CoinConsumeRcd = ConsumeRecord.objects.get(orderNo=lineInfo.get('consumeOrderNo'))
  631. startTime = lineInfo.get('startTime')
  632. startTime = to_datetime(startTime)
  633. nowTime = datetime.datetime.now()
  634. if startTime > nowTime:
  635. usedTime = 0
  636. else:
  637. usedTime = int(round(((nowTime - startTime).total_seconds() / 60.0)))
  638. consumeDict = {
  639. 'chargeIndex': portStr,
  640. 'reason': self.event_data.get('reason'),
  641. 'leftTime': self.event_data.get('left', 0),
  642. 'actualNeedTime': usedTime + self.event_data.get('left', 0),
  643. DEALER_CONSUMPTION_AGG_KIND.DURATION: usedTime,
  644. DEALER_CONSUMPTION_AGG_KIND.ELEC: self.event_data.get('usedElec'),
  645. DEALER_CONSUMPTION_AGG_KIND.TOTAL_COUNT: lineInfo.get('coins'),
  646. }
  647. CoinConsumeRcd.update_service_info(consumeDict)
  648. CoinConsumeRcd.update(status='finished', finishedTime=nowTime)
  649. def notify_fault_to_dealer(self):
  650. group = self.device.group
  651. self.notify_dealer(
  652. templateName='device_fault',
  653. title=u'端口连续停止超过三次',
  654. device=u'{groupNumber}组-{logicalCode}-端口{port}'.format(groupNumber=self.device['groupNumber'],
  655. logicalCode=self.device['logicalCode'],
  656. port=self.event_data.get('port')),
  657. location=u'{address}-{groupName}'.format(address=group['address'], groupName=group['groupName']),
  658. fault=u'端口连续停止超过三次',
  659. notifyTime=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  660. )
  661. def anonymous_user_start(self, lineInfo):
  662. coins = self.event_data.get('coins')
  663. cardNo = self.event_data.get('cardNo')
  664. port = self.event_data.get('port')
  665. needTime = self.event_data.get('needTime')
  666. cardBalance = self.event_data.get('cardBalance')
  667. payType = self.event_data.get('payType')
  668. if lineInfo.get('status', Const.DEV_WORK_STATUS_IDLE) != Const.DEV_WORK_STATUS_IDLE and cardNo == lineInfo.get(
  669. 'cardNo'):
  670. logger.info('anonymous_user card<{}> Swipe to pay again!!'.format(cardNo))
  671. orderNo = lineInfo.get('orderNo')
  672. cardOrderNo = lineInfo.get('cardOrderNo')
  673. lineInfo['coins'] += coins
  674. lineInfo['payCount'] += 1
  675. if not needTime:
  676. needTime = 0
  677. # lineInfo['needTime'] = needTime
  678. ConsumeRecord.objects.filter(orderNo=orderNo).update_one(inc__coin=coins, inc__money=coins)
  679. CardConsumeRecord.objects.filter(orderNo=cardOrderNo).update_one(inc__money=coins, dec__balance=coins)
  680. ServiceProgress.objects.filter(device_imei=self.device.devNo, port=port,
  681. consumeOrder__orderNo=orderNo, isFinished=False).update_one(
  682. inc__consumeOrder__coin=coins, inc__finished_time=(time.time() + needTime * 60))
  683. # 第一次刷卡
  684. else:
  685. logger.info('anonymous_user card<{}> start charge the once!!'.format(cardNo))
  686. card = Card(
  687. cardNo=cardNo,
  688. dealerId=self.device.ownerId,
  689. agentId=self.device.owner.agentId,
  690. groupId=self.device.groupId,
  691. openId=Const.DEFAULT_CARD_OPENID,
  692. balance=RMB(cardBalance),
  693. ).save()
  694. # 记录ID卡的消费
  695. attachParas = {
  696. 'chargeIndex': port
  697. }
  698. servicedInfo = {
  699. 'cardNo': cardNo,
  700. 'chargeIndex': port,
  701. }
  702. orderNo, cardOrderNo = self.record_consume_for_card(card, RMB(coins), servicedInfo=servicedInfo,
  703. attachParas=attachParas)
  704. # 记录缓存信息 可以在个人中心以及经销商的端口管理显示
  705. lineInfo = {
  706. 'port': port,
  707. 'cardNo': cardNo,
  708. 'openId': card.openId,
  709. 'payCount': 1,
  710. 'coins': coins,
  711. 'status': Const.DEV_WORK_STATUS_WORKING,
  712. 'startTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  713. 'orderNo': orderNo,
  714. 'cardOrderNo': cardOrderNo,
  715. 'payType': payType,
  716. 'consumeType': 'card',
  717. }
  718. finishedTime = None
  719. if needTime:
  720. finishedTime = int(time.time()) + (needTime + 5) * 60
  721. # lineInfo['needTime'] = needTime
  722. ServiceProgress.register_card_service(
  723. self.device,
  724. port,
  725. card,
  726. {
  727. 'orderNo': orderNo,
  728. 'coin': VirtualCoin(coins).mongo_amount,
  729. 'cardOrderNo': cardOrderNo
  730. },
  731. finishedTime)
  732. logger.info('port:{} is running, order:{} cardOrder:{}'.format(port, orderNo, cardOrderNo))
  733. # 更新端口缓存
  734. Device.update_port_control_cache(self.device.devNo, lineInfo)