changyuanFive.py 40 KB

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