weifuleCar.py 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import json
  4. import logging
  5. from copy import deepcopy
  6. import datetime
  7. import time
  8. from typing import TYPE_CHECKING, Tuple, Optional
  9. from apilib.monetary import RMB, VirtualCoin, Ratio
  10. from apilib.utils_string import make_title_from_dict
  11. from apilib.utils_sys import memcache_lock
  12. from apps.web.constant import Const, FAULT_LEVEL, DEALER_CONSUMPTION_AGG_KIND
  13. from apps.web.device.models import DeviceUploadInfo, Group, DeviceDict
  14. from apps.web.eventer import EventBuilder
  15. from apps.web.eventer.base import WorkEvent, FaultEvent
  16. from apps.web.eventer.weifuleCommon import WeiFuLeStatusEvent
  17. from apps.web.user.models import ConsumeRecord, CardConsumeRecord, RefundMoneyRecord
  18. from apps.web.user.models import ServiceProgress, RechargeRecord, MyUser, CardRechargeOrder
  19. from apps.web.utils import concat_user_login_entry_url
  20. if TYPE_CHECKING:
  21. from apps.web.user.models import Card
  22. logger = logging.getLogger(__name__)
  23. def log_obj(obj):
  24. obj = deepcopy(obj)
  25. if isinstance(obj, dict):
  26. for k, v in obj.items():
  27. if isinstance(v, object):
  28. obj[k] = str(v)
  29. if isinstance(obj, list) or isinstance(obj, tuple) or isinstance(obj, set):
  30. obj = map(lambda x: str(x) if isinstance(x, object) else x, obj)
  31. if isinstance(obj, unicode):
  32. obj = str(obj)
  33. # print('\33[33m' + json.dumps(obj,ensure_ascii=True,encoding='utf-8') + '\33[0m')
  34. return '\33[33m' + json.dumps(obj, ensure_ascii=False, encoding='utf-8') + '\33[0m'
  35. card_is_normal = 1
  36. card_not_in_db = 2
  37. card_is_forzen = 3
  38. card_has_not_order = 4 # IC卡适用
  39. CREATED = 'created'
  40. RUNNING = 'running'
  41. WAITING = 'wait'
  42. FINISHED = 'finished'
  43. END = 'end'
  44. TIMEOUT = 'timeout'
  45. FAILURE = 'failure'
  46. class builder(EventBuilder):
  47. def __getEvent__(self, device_event):
  48. logger.info('reve event, event_data:{}'.format(log_obj(device_event)))
  49. event_data = device_event.get('data')
  50. fun_code = event_data.get('fun_code')
  51. if not event_data:
  52. return
  53. if fun_code in [44]: # 44状态变更
  54. return ChargingWeiFuLeCarStatusEvent(self.deviceAdapter, device_event)
  55. if fun_code in [40, 41, 42, 43]: # 40温度告警/41电流超限/42电压超限/43功率过载
  56. return ChargingWeiFuLeCarFaultEvent(self.deviceAdapter, event_data)
  57. else:
  58. return ChargingWeiFuLeCarWorkEvent(self.deviceAdapter, event_data)
  59. class ChargingWeiFuLeCarStatusEvent(WeiFuLeStatusEvent):
  60. def parse_status(self, data):
  61. return {'1': data.get('result')}
  62. class ChargingWeiFuLeCarWorkEvent(WorkEvent):
  63. def do(self, **args):
  64. fun_code = self.event_data.get('fun_code')
  65. order_id = self.event_data.get('order', {}).get('id')
  66. key = '{}.{}.event'.format(self.device.devNo, order_id)
  67. if fun_code == 37: # 处理卡充值
  68. self.deal_with_ic_charge_event()
  69. elif fun_code == 45: # 上传要链接
  70. self.response_the_link()
  71. else:
  72. with memcache_lock(key = key, value = '1', expire = 15) as acquired:
  73. if acquired:
  74. if fun_code == 32 or fun_code == 34 or fun_code == 33:
  75. self._do_order_change_event_32_33_34()
  76. self._do_ack_order_32_or_remove_order_from_device_34()
  77. else:
  78. logger.debug('fun_code<{}> is doing. cache_key:{}'.format(repr(fun_code), key))
  79. return
  80. def deal_with_ic_charge_event(self):
  81. cardNo = self.event_data.get('card_no')
  82. card = self.update_card_dealer_and_type(cardNo, cardType='IC')
  83. logger.info(log_obj('Start card recharge operation'))
  84. if not card:
  85. logger.info(log_obj('Start card recharge operation --> no such card !!! '))
  86. return self.deviceAdapter.send_mqtt({
  87. 'fun_code': 37,
  88. 'card_no': cardNo,
  89. 'result': card_not_in_db,
  90. })
  91. elif card.frozen:
  92. logger.info(log_obj('Start card recharge operation --> card is frozen !!! '))
  93. return self.deviceAdapter.send_mqtt({
  94. 'fun_code': 37,
  95. 'card_no': cardNo,
  96. 'result': card_is_forzen,
  97. })
  98. rechargeOrder = CardRechargeOrder.get_last_to_do_one(str(card.id))
  99. if rechargeOrder:
  100. preBalance = RMB(self.event_data.get('balance', 0) / 100.0)
  101. logger.info(log_obj('Start card recharge operation --> to recharg rechargeAmount:{}'.format(preBalance)))
  102. self.recharge_ic_card(card=card,
  103. preBalance=preBalance,
  104. rechargeType='append',
  105. order=rechargeOrder,
  106. need_verify=False)
  107. else:
  108. logger.info(log_obj('Start card recharge operation --> no rechargeOrder !! '))
  109. self.deviceAdapter.send_mqtt({
  110. 'card_no': cardNo,
  111. 'fun_code': 37,
  112. 'result': card_has_not_order,
  113. })
  114. def _do_order_change_event_32_33_34(self):
  115. order_info = self.event_data.get('order')
  116. if not order_info:
  117. logger.info(log_obj('no order info,do over!!'))
  118. else:
  119. order_processing = OrderProcessing(self)
  120. order_type = order_info.get('order_type')
  121. self.save_upload_log()
  122. if hasattr(order_processing, order_type):
  123. event = getattr(order_processing, order_type)
  124. try:
  125. event()
  126. except Exception:
  127. import traceback
  128. logger.info(traceback.format_exc())
  129. else:
  130. logger.info(log_obj('no this order_type'))
  131. def _do_ack_order_32_or_remove_order_from_device_34(self):
  132. order_info = self.event_data.get('order')
  133. order_id = order_info.get('id')
  134. fun_code = self.event_data.get('fun_code')
  135. if (time.time() - order_info.get('create_time', 0)) < 300 and order_info.get('order_type') == 'apps_start':
  136. order = ConsumeRecord.objects.filter(orderNo=order_id).first()
  137. if order: # 正常流程 第一次上报先不处理, 否则则为发mqtt命令测试直接回复
  138. return
  139. if fun_code == 32:
  140. self.deviceAdapter.do_ack_order_32(order_id)
  141. elif fun_code == 34:
  142. self.deviceAdapter.do_ack_remove_order_from_device_34(order_id)
  143. else:
  144. pass
  145. def save_upload_log(self):
  146. order_info = self.event_data.get('order')
  147. order_info = deepcopy(order_info)
  148. order_info['order_id'] = order_info.pop('id')
  149. DeviceUploadInfo(**order_info).save()
  150. def response_the_link(self):
  151. qr_code_url = concat_user_login_entry_url(l=self.device['logicalCode'])
  152. data = {
  153. 'fun_code': 45,
  154. 'qrcode': qr_code_url
  155. }
  156. self.deviceAdapter.send_mqtt(data)
  157. def record_consume_for_card(self, card, money, desc=u'', servicedInfo=None, sid=None, attachParas=None):
  158. # type: (Card, RMB, unicode, Optional[dict], Optional[str], Optional[dict])->Tuple[str, str]
  159. servicedInfo = {} if servicedInfo is None else servicedInfo
  160. attachParas = {} if attachParas is None else attachParas
  161. group = Group.get_group(self.device['groupId'])
  162. address = group['address']
  163. group_number = self.device['groupNumber']
  164. now = datetime.datetime.now()
  165. orderNo = attachParas.get('orderNo')
  166. new_record = {
  167. 'orderNo': orderNo,
  168. 'time': now.strftime('%Y-%m-%d %H:%M:%S'),
  169. 'dateTimeAdded': now,
  170. 'openId': card.openId,
  171. 'ownerId': self.device['ownerId'],
  172. 'coin': money.mongo_amount,
  173. 'money': money.mongo_amount,
  174. 'devNo': self.device['devNo'],
  175. 'logicalCode': self.device['logicalCode'],
  176. 'groupId': self.device['groupId'],
  177. 'address': address,
  178. 'groupNumber': group_number,
  179. 'groupName': group['groupName'],
  180. 'devTypeCode': self.device.devTypeCode,
  181. 'devTypeName': self.device.devTypeName,
  182. 'isNormal': True,
  183. 'remarks': u'刷卡消费',
  184. 'errorDesc': '',
  185. 'sequanceNo': '',
  186. 'desc': desc,
  187. 'attachParas': attachParas,
  188. 'servicedInfo': servicedInfo
  189. }
  190. ConsumeRecord.get_collection().insert_one(new_record)
  191. # 刷卡消费也记录一条数据
  192. new_card_record = {
  193. 'orderNo': orderNo,
  194. 'openId': card.openId,
  195. 'cardId': str(card.id),
  196. 'money': money.mongo_amount,
  197. 'balance': card.balance.mongo_amount,
  198. 'devNo': self.device['devNo'],
  199. 'devType': self.device['devType']['name'],
  200. 'logicalCode': self.device['logicalCode'],
  201. 'groupId': self.device['groupId'],
  202. 'address': address,
  203. 'groupNumber': group_number,
  204. 'groupName': group['groupName'],
  205. 'result': 'success',
  206. 'remarks': u'刷卡消费',
  207. 'sequanceNo': '',
  208. 'dateTimeAdded': datetime.datetime.now(),
  209. 'desc': desc,
  210. 'servicedInfo': servicedInfo,
  211. 'linkedConsumeRcdOrderNo': str(new_record['orderNo'])
  212. }
  213. if sid is not None:
  214. new_card_record.update({'sid': sid})
  215. CardConsumeRecord.get_collection().insert(new_card_record)
  216. return new_record['orderNo'], new_card_record['orderNo']
  217. class ChargingWeiFuLeCarFaultEvent(FaultEvent):
  218. def do(self, **args):
  219. # 40温度告警/41电流超限/42电压超限/43功率过载
  220. group = Group.get_group(self.device['groupId'])
  221. fun_code = self.event_data.get('fun_code')
  222. if fun_code == 40:
  223. item = self.event_data.get('temp')
  224. faultName = r'设备火灾预警'
  225. desc = r'主板上报设备过热,设备温度超限(设备温度:{} 度)'.format(item)
  226. title = r'告警名称:\t\t{}\n\n地址名称:\t\t{}-{}\n'.format(faultName, group['address'], group['groupName'])
  227. elif fun_code == 41:
  228. item = self.event_data.get('ampr')
  229. faultName = r'设备电流超过最大限制'
  230. desc = r'主板上报电流超限,设备电流超限(设备电流:{} 安)'.format(item)
  231. title = r'告警名称:\t\t{}\n\n地址名称:\t\t{}-{}\n'.format(faultName, group['address'], group['groupName'])
  232. elif fun_code == 42:
  233. item = self.event_data.get('volt')
  234. faultName = r'设备电压超过最限制'
  235. desc = r'主板上报电压超限,设备电压超限(设备电压:{} 伏)'.format(item)
  236. title = r'告警名称:\t\t{}\n\n地址名称:\t\t{}-{}\n'.format(faultName, group['address'], group['groupName'])
  237. elif fun_code == 43:
  238. item = self.event_data.get('watt')
  239. faultName = r'设备功率超过最限制'
  240. desc = r'主板上报功率超限,设备功率超限(设备电压:{} 瓦)'.format(item)
  241. title = r'告警名称:\t\t{}\n\n地址名称:\t\t{}-{}\n'.format(faultName, group['address'], group['groupName'])
  242. else:
  243. return
  244. self.notify_dealer(
  245. templateName='device_fault',
  246. title=title,
  247. device=r'{}号设备({})\n'.format(self.device['groupNumber'], self.device['logicalCode']),
  248. location=r'设备告警\n',
  249. notifyTime=desc + r'\n',
  250. fault=r'%s\n' % datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  251. )
  252. self.record(
  253. faultCode=self.event_data.get('FaultCode'),
  254. description=desc,
  255. title=faultName,
  256. level=self.event_data.get('level', FAULT_LEVEL.NORMAL),
  257. # detail=self.event_data.get('statusInfo'),
  258. )
  259. class OrderProcessing:
  260. desc_map = {
  261. 0: '订购已用完',
  262. 1: '管理员停止',
  263. 2: '设备故障',
  264. 3: '充电枪拔除',
  265. 4: '紧急停止按钮',
  266. 5: '计量故障',
  267. 6: '功率过载',
  268. 7: '电压过载',
  269. 8: '电流过载',
  270. 9: '超过单次充电最大时长',
  271. 10: '设备启动失败',
  272. 11: '继电器故障',
  273. 12: '刷卡停止',
  274. 13: '用户远程停止',
  275. 14: '经销商远程停止'
  276. }
  277. def __init__(self, base):
  278. self.devNo = base.device.devNo
  279. self.fun_code = base.event_data.get('fun_code')
  280. self.order_info = base.event_data.get('order')
  281. self.dev = base.device # type: DeviceDict
  282. self.adapter = base.deviceAdapter
  283. self.base = base
  284. self.PORT = '1'
  285. def _exec_order_33(self):
  286. start_time = self.order_info.get('exec_time')
  287. start_time = datetime.datetime.fromtimestamp(start_time)
  288. order_id = self.order_info.get('id')
  289. consumeOrder = ConsumeRecord.objects.filter(orderNo=order_id, status__ne=ConsumeRecord.Status.FINISHED).first()
  290. if not consumeOrder:
  291. logger.info(log_obj('no this order'))
  292. return
  293. consumeOrder.update(startTime=start_time)
  294. def apps_start(self):
  295. """
  296. 'order_type' : 'apps_start',
  297. 'money' : 1.2388653318201,
  298. 'id' : '86743505437857520201225084915001',
  299. 'left_money' : 98.76113466818,
  300. 'last_clock' : 607.31,
  301. 'create_time' : 1608857355,
  302. 'time' : 475.525,
  303. 'amount' : 100,
  304. 'elec' : 12388.653318201,
  305. 'exec_time' : 1608857355,
  306. 'status' : 'running',
  307. 'last_ecnt' : 16777128
  308. """
  309. dev_stat = self.order_info.get('status')
  310. order = ConsumeRecord.objects.filter(orderNo=self.order_info['id']).first()
  311. if not order:
  312. logger.info('no this order')
  313. return
  314. if dev_stat == 'running':
  315. self._apps_start_do_record_running(dev_stat, order)
  316. elif dev_stat == 'waiting':
  317. self._apps_start_do_record_waiting(dev_stat, order)
  318. elif dev_stat == 'finished':
  319. self._apps_start_do_record_finished(dev_stat, order)
  320. def _apps_start_do_record_running(self, dev_stat, order):
  321. rechargeRcdId = order.rechargeRcdId or ''
  322. if order.status == CREATED: # 让adapter处理
  323. if 'adapter' in self.order_info: # 服务器过来直接处理
  324. order.update(status=RUNNING)
  325. else:
  326. if (time.time() - self.order_info.get('create_time', 0)) > 300:
  327. ConsumeRecord.objects.filter(id=order.id, status=CREATED).update(status=RUNNING, isNormal=True)
  328. elif order.status == RUNNING: # 设备上报
  329. start_time = self.order_info.get('exec_time')
  330. start_time = datetime.datetime.fromtimestamp(start_time)
  331. order.update(
  332. status=RUNNING,
  333. startTime=start_time
  334. )
  335. elif order.status == WAITING: # 33 上来的wait单变running
  336. start_time = self.order_info.get('exec_time')
  337. start_time = datetime.datetime.fromtimestamp(start_time)
  338. order.update(
  339. status=RUNNING,
  340. startTime=start_time,
  341. )
  342. elif order.status in [TIMEOUT, FAILURE]: # 此时已经退费,标记为后支付,不做处理
  343. start_time = self.order_info.get('exec_time') or self.order_info.get('create_time')
  344. start_time = datetime.datetime.fromtimestamp(start_time)
  345. order.update(
  346. status=RUNNING,
  347. coin=VirtualCoin(0),
  348. money=RMB(0),
  349. startTime=start_time,
  350. attachParas__payAfterUse=True,
  351. rechargeRcdId='',
  352. attachParas__last_refund_rechargeRcdId=rechargeRcdId,
  353. isNormal=True,
  354. errorDesc='设备启动已退费, 此单标记为后支付<dev_stat:{}>'.format(dev_stat),
  355. )
  356. consumeOrder = {
  357. 'orderNo': order.orderNo,
  358. 'coin': 0,
  359. 'consumeType': 'postpaid',
  360. }
  361. self.adapter.register_service_progress(order.openId, order.orderNo, consumeOrder, order.attachParas)
  362. title = make_title_from_dict([
  363. {u'设备地址': u'{}'.format(self.dev.group.address)},
  364. {u'设备编号': u'{}'.format(self.dev['logicalCode'])},
  365. ])
  366. self.base.notify_user(
  367. order.user.managerialOpenId,
  368. 'dev_start',
  369. **{
  370. 'title': title,
  371. 'things': u'您的汽车充电服务已启动',
  372. 'remark': u'感谢您的支持!',
  373. 'time': start_time.strftime(Const.DATETIME_FMT)
  374. }
  375. )
  376. elif order.status in [FINISHED, END]:
  377. pass
  378. else:
  379. logger.info('error order status devStat=<{}>, order=<{}>'.format(dev_stat, order.orderNo))
  380. def _apps_start_do_record_waiting(self, dev_stat, order):
  381. # 兼容系统 全改为running单
  382. rechargeRcdId = order.rechargeRcdId or ''
  383. if order.status == CREATED: # 启动过程服务器中断停留在
  384. if (time.time() - self.order_info.get('create_time', 0)) > 300:
  385. ConsumeRecord.objects.filter(id=order.id, status=CREATED).update(status=RUNNING, isNormal=True)
  386. elif order.status == RUNNING: # 服务器成功启动置为running
  387. order.update(
  388. status=RUNNING
  389. )
  390. elif order.status == WAITING: # 重复
  391. pass
  392. elif order.status in [TIMEOUT, FAILURE]: # 此时已经退费,标记为后支付,不做处理
  393. order.update(
  394. status=RUNNING,
  395. coin=VirtualCoin(0),
  396. money=RMB(0),
  397. attachParas__payAfterUse=True,
  398. rechargeRcdId='',
  399. attachParas__last_refund_rechargeRcdId=rechargeRcdId,
  400. isNormal=True,
  401. errorDesc='设备启动已退费, 此单标记为后支付<dev_stat:{}>'.format(dev_stat),
  402. )
  403. consumeOrder = {
  404. 'orderNo': order.orderNo,
  405. 'coin': 0,
  406. 'consumeType': 'postpaid',
  407. }
  408. self.adapter.register_service_progress(order.openId, order.orderNo, consumeOrder, order.attachParas)
  409. elif order.status in [FINISHED, END]:
  410. pass
  411. def _apps_start_do_record_finished(self, dev_stat, order):
  412. if order.status == CREATED: # 启动过程中服务器出错
  413. ConsumeRecord.objects.filter(id=order.id, status=CREATED).update(status=RUNNING, isNormal=True)
  414. order.reload()
  415. self.pre_pay(order)
  416. elif order.status in [RUNNING, WAITING]: # 正常状态的 或者修正状态的单
  417. payAfterUse = order.attachParas.get('payAfterUse')
  418. if payAfterUse:
  419. self.after_pay(order)
  420. else:
  421. self.pre_pay(order)
  422. elif order.status == TIMEOUT: # 设备启动后,100事件先上(该状态为running),210回复改为timeout,后付费处理
  423. order.update(
  424. status=RUNNING,
  425. coin=VirtualCoin(0),
  426. money=RMB(0),
  427. attachParas__payAfterUse=True,
  428. rechargeRcdId='',
  429. attachParas__last_refund_rechargeRcdId=order.rechargeRcdId,
  430. isNormal=True,
  431. errorDesc='设备启动已退费, 此单标记为后支付<dev_stat:{}>'.format(dev_stat),
  432. )
  433. order.reload()
  434. self.after_pay(order)
  435. elif order.status == FAILURE: # 状态位置不处理
  436. pass
  437. elif order.status in [FINISHED, END]:
  438. logger.info(
  439. log_obj('do apps finished --> no order found or repeated submit orderNo:{}'.format(order.orderNo)))
  440. return
  441. logger.info(log_obj('apps start event orderNo:{} is over'.format(order)))
  442. def pre_pay(self, order):
  443. openId = order.openId
  444. # leftMoney = RMB.fen_to_yuan(self.order_info.get('left_money', 0)) # 传过来的这个值有问题
  445. amount = RMB.fen_to_yuan(self.order_info.get('amount', 0))
  446. usedMoney = RMB.fen_to_yuan(self.order_info.get('money'))
  447. leftMoney = amount - usedMoney
  448. used_time = round(self.order_info.get('time') / 60.0, 2)
  449. used_elec = self.order_info.get('elec') / 10.0 ** 6
  450. reason = self.desc_map.get(self.order_info.get('cause'))
  451. execTimeStamp = self.order_info.get('exec_time')
  452. finishedTimeStamp = self.order_info.get('over_time')
  453. finishedTime = datetime.datetime.fromtimestamp(finishedTimeStamp)
  454. if not execTimeStamp:
  455. startTime = finishedTime
  456. else:
  457. startTime = datetime.datetime.fromtimestamp(execTimeStamp)
  458. status = self.order_info.get('status')
  459. consumeDict = {
  460. DEALER_CONSUMPTION_AGG_KIND.DURATION: '%d' % used_time,
  461. DEALER_CONSUMPTION_AGG_KIND.ELEC: '%.2f' % used_elec,
  462. DEALER_CONSUMPTION_AGG_KIND.COIN: amount.mongo_amount,
  463. 'reason': reason
  464. }
  465. order.status = status
  466. order.startTime = startTime
  467. order.finishedTime = finishedTime
  468. order.save()
  469. user = MyUser.objects(openId=order.openId,
  470. groupId=self.dev.groupId).first()
  471. group = Group.get_group(self.dev['groupId'])
  472. extra = [
  473. # {u'订单号': '{}'.format(order.orderNo)},
  474. {u'本次使用时长': '{}(分钟)'.format(used_time)},
  475. ]
  476. self.base.notify_user_service_complete(
  477. service_name='充电',
  478. openid=user.managerialOpenId if user else '',
  479. port=self.PORT,
  480. address=group['address'],
  481. reason=reason,
  482. finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  483. extra=extra
  484. )
  485. if self.dev.is_auto_refund:
  486. recharge_record = None
  487. if order.rechargeRcdId:
  488. recharge_record = RechargeRecord.objects(
  489. id=order.rechargeRcdId, isQuickPay=True).first() # type: RechargeRecord
  490. if recharge_record:
  491. refundMoneyRecord = RefundMoneyRecord.objects.filter(openId=openId,
  492. rechargeObjId=recharge_record.id).first()
  493. if not refundMoneyRecord:
  494. self.__do_refund_money(recharge_record, order, leftMoney, consumeDict)
  495. else:
  496. self.__do_refund_coin(order, leftMoney, consumeDict)
  497. order.update_service_info(consumeDict)
  498. ServiceProgress.objects.filter(device_imei=self.devNo, weifuleOrderNo=order.orderNo).update(
  499. isFinished=True, finished_time=int(time.time())
  500. )
  501. def after_pay(self, order):
  502. """
  503. 只针对设备下发之后,没有回复的故障单为后付费
  504. :return:
  505. """
  506. used_money = RMB.fen_to_yuan(self.order_info.get('money', 0))
  507. amount = RMB.fen_to_yuan(self.order_info.get('amount', 0))
  508. used_time = round(self.order_info.get('time') / 60.0, 2)
  509. used_elec = self.order_info.get('elec') / 10.0 ** 6
  510. reason = self.desc_map.get(self.order_info.get('cause'))
  511. execTimeStamp = self.order_info.get('exec_time')
  512. finishedTimeStamp = self.order_info.get('over_time')
  513. finishedTime = datetime.datetime.fromtimestamp(finishedTimeStamp)
  514. if not execTimeStamp:
  515. startTime = finishedTime
  516. else:
  517. startTime = datetime.datetime.fromtimestamp(execTimeStamp)
  518. consumeDict = {
  519. DEALER_CONSUMPTION_AGG_KIND.DURATION: '%d' % used_time,
  520. DEALER_CONSUMPTION_AGG_KIND.ELEC: '%.2f' % used_elec,
  521. 'reason': reason,
  522. }
  523. order.startTime = startTime
  524. order.finishedTime = finishedTime
  525. coins = VirtualCoin(order.package.get('coins', 0))
  526. price = RMB(order.package.get('price'))
  527. ratio = Ratio(float(used_money) / float(amount))
  528. order.coin = coins * ratio
  529. order.money = price * ratio
  530. if order.money == RMB(0):
  531. order.coin = VirtualCoin(0)
  532. order.save()
  533. extra = [
  534. # {u'订单号': '{}'.format(order.orderNo)},
  535. {u'本次使用时长': '{}(分钟)'.format(used_time)},
  536. ]
  537. if order.money > RMB(0):
  538. self._pay_by_coins(order)
  539. else:
  540. order.update(status=FINISHED)
  541. order.reload()
  542. if order.status == FINISHED:
  543. extra.append({u'本单消费金额': '{}金币(已使用账户余额自动结算本次消费)'.format(order.coin.mongo_amount)})
  544. consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.COIN: order.coin.mongo_amount})
  545. else:
  546. extra.append({u'本单消费金额': '{}元((您的账户余额已不足以抵扣本次消费,请前往账单中心进行支付))'.format(order.money.mongo_amount)})
  547. consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: order.money.mongo_amount})
  548. self.base.notify_user_service_complete(
  549. service_name='充电结束',
  550. openid=order.user.managerialOpenId,
  551. port=self.PORT,
  552. address=self.dev.group['address'],
  553. reason=reason,
  554. finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  555. extra=extra
  556. )
  557. order.update_service_info(consumeDict)
  558. ServiceProgress.objects.filter(device_imei=self.devNo, weifuleOrderNo=order.orderNo).update(
  559. isFinished=True, finished_time=int(time.time())
  560. )
  561. def _pay_by_coins(self, order):
  562. try:
  563. order.s_to_e()
  564. except Exception:
  565. return False
  566. def __do_refund_money(self, recharge_record, consumeOrder, leftMoney, consumeDict):
  567. logger.info(log_obj('now do refund money!'))
  568. coins = consumeOrder.coin
  569. openId = consumeOrder.openId
  570. price = recharge_record.money
  571. user = MyUser.objects.filter(openId=openId, groupId=self.dev['groupId']).first()
  572. refundMoney = RMB(price) * Ratio(float(leftMoney) / float(coins))
  573. if refundMoney > RMB(price):
  574. refundMoney = RMB(price)
  575. if refundMoney > RMB(0):
  576. self.base.refund_net_pay(user, {'rechargeRcdId': str(recharge_record.id), 'openId': user.openId},
  577. refundMoney,
  578. VirtualCoin(0), consumeDict, True)
  579. title = make_title_from_dict([
  580. {u'设备地址': u'{}'.format(self.dev.group.address)},
  581. {u'设备编号': u'{}'.format(self.dev['logicalCode'])},
  582. ])
  583. self.base.notify_user(self.base.get_managerialOpenId_by_openId(openId), 'refund_coins', **{
  584. 'title': title,
  585. 'backCount': u'金额:%s' % refundMoney,
  586. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')})
  587. logger.info(log_obj('refund cash is successful refundMoney:{}'.format(refundMoney)))
  588. def __do_refund_coin(self, consumeOrder, leftMoney, consumeDict):
  589. logger.info(log_obj('now do refund coin !'))
  590. coins = consumeOrder.coin
  591. openId = consumeOrder.openId
  592. user = MyUser.objects.filter(openId=openId, groupId=self.dev['groupId']).first()
  593. backCoins = VirtualCoin(coins) * Ratio(float(leftMoney) / float(coins))
  594. if backCoins > VirtualCoin(coins):
  595. backCoins = VirtualCoin(coins)
  596. if backCoins > RMB(0):
  597. self.base.refund_net_pay(user, {'openId': openId}, RMB(0), backCoins, consumeDict, False)
  598. title = make_title_from_dict([
  599. {u'设备地址': u'{}'.format(self.dev.group.address)},
  600. {u'设备编号': u'{}'.format(self.dev['logicalCode'])},
  601. ])
  602. self.base.notify_user(self.base.get_managerialOpenId_by_openId(openId), 'refund_coins', **{
  603. 'title': title,
  604. 'backCount': u'金币:%s' % backCoins,
  605. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')})
  606. # ---------------------刷卡处理-------------------------
  607. def card_start(self):
  608. status = self.order_info.get('status')
  609. if self.fun_code == 32:
  610. self._card_start_do_record_running_32()
  611. elif self.fun_code == 34:
  612. if status == 'finished':
  613. self._card_start_do_record_finished_34()
  614. else:
  615. pass
  616. elif self.fun_code == 33:
  617. self._exec_order_33()
  618. def card_refund(self):
  619. """
  620. 离线卡:上报退费 做一次卡余额同步
  621. """
  622. status = self.order_info.get('status')
  623. if self.fun_code == 32:
  624. if status == 'finished':
  625. self._card_refund_do_record_finished()
  626. else:
  627. pass
  628. elif self.fun_code == 34:
  629. if status == 'finished':
  630. pass
  631. # self._card_refund_do_record_finished()
  632. else:
  633. pass
  634. def _card_start_do_record_running_32(self):
  635. self._charge_by_card()
  636. def _card_start_do_record_finished_34(self):
  637. used_time = round(self.order_info.get('time') / 60.0, 2)
  638. finishedTimeStamp = self.order_info.get('over_time')
  639. finishedTime = datetime.datetime.fromtimestamp(finishedTimeStamp)
  640. execTimeStamp = self.order_info.get('exec_time')
  641. if not execTimeStamp:
  642. startTime = finishedTime
  643. else:
  644. startTime = datetime.datetime.fromtimestamp(execTimeStamp)
  645. reason = self.desc_map.get(self.order_info.get('cause'))
  646. card_no = self.order_info.get('card_no')
  647. used_elec = self.order_info.get('elec') / 10 ** 6
  648. card_balance = RMB.fen_to_yuan(self.order_info.get('balance'))
  649. amount = RMB.fen_to_yuan(self.order_info.get('amount'))
  650. order_id = self.order_info.get('id')
  651. # left_money = RMB.fen_to_yuan(self.order_info.get('left_money')) #这个值有问题
  652. used_money = RMB.fen_to_yuan(self.order_info.get('money'))
  653. left_money = amount - used_money
  654. consumeOrder = ConsumeRecord.objects.filter(devNo=self.devNo, orderNo=order_id,
  655. status__ne=ConsumeRecord.Status.FINISHED).first()
  656. if not consumeOrder:
  657. logger.info(
  658. log_obj('do card finished --> no order found or repeated submit orderNo:{}'.format(order_id)))
  659. return
  660. openId = consumeOrder.openId
  661. consumeOrder.servicedInfo.update({
  662. DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: left_money.mongo_amount,
  663. DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: amount.mongo_amount,
  664. DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: (amount - left_money).mongo_amount,
  665. DEALER_CONSUMPTION_AGG_KIND.DURATION: '%d' % used_time,
  666. DEALER_CONSUMPTION_AGG_KIND.ELEC: '%.2f' % used_elec,
  667. 'cardBalance': card_balance.mongo_amount,
  668. 'reason': reason,
  669. 'cardNo': card_no
  670. })
  671. consumeOrder.startTime = startTime
  672. consumeOrder.finishedTime = finishedTime
  673. consumeOrder.save()
  674. ServiceProgress.update_progress_and_consume_rcd(
  675. self.dev['ownerId'],
  676. {
  677. 'open_id': openId,
  678. 'device_imei': self.devNo,
  679. 'isFinished': False,
  680. 'weifuleOrderNo': order_id,
  681. },
  682. consumeOrder.servicedInfo,
  683. progressDict={'isFinished': True, 'finished_time': finishedTimeStamp}
  684. )
  685. user = MyUser.objects(openId=consumeOrder.openId,
  686. groupId=self.dev.groupId).first()
  687. group = Group.get_group(self.dev['groupId'])
  688. extra = [
  689. # {u'订单号': '{}'.format(consumeOrder.orderNo)},
  690. {u'本次使用时长': '{}(分钟)'.format(used_time)},
  691. ]
  692. self.base.notify_user_service_complete(
  693. service_name=u'刷卡充电',
  694. openid=user.managerialOpenId if user else '',
  695. port=self.PORT,
  696. address=group['address'],
  697. reason=reason,
  698. finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  699. extra=extra
  700. )
  701. logger.info(log_obj('card start event orderNo:{} is over'.format(order_id)))
  702. def _register_card_service_progress(self, card, consumeOrder, weifuleOrderNo=None, attachParas=None):
  703. return ServiceProgress.objects.create(
  704. open_id=card.openId,
  705. device_imei=self.devNo,
  706. devTypeCode='100257',
  707. port=1,
  708. cardId=str(card.id),
  709. attachParas=attachParas if attachParas else {},
  710. start_time=int(time.time()),
  711. finished_time=int(time.time() + 24 * 60 * 60),
  712. consumeOrder=consumeOrder if consumeOrder else {},
  713. weifuleOrderNo=weifuleOrderNo,
  714. ).id
  715. def _charge_by_card(self):
  716. # used_money = RMB.fen_to_yuan(self.order_info.get('money', 0))
  717. # left_money = RMB.fen_to_yuan(self.order_info.get('left_money'))
  718. amount = RMB.fen_to_yuan(self.order_info.get('amount', 0))
  719. card_balance = RMB.fen_to_yuan(self.order_info.get('balance'))
  720. start_time_stamp = self.order_info.get('create_time')
  721. start_time = datetime.datetime.fromtimestamp(start_time_stamp)
  722. # used_time = self.order_info.get('time') / 60
  723. # used_elec = self.order_info.get('elec') / 10 ** 6
  724. # status = self.order_info.get('status')
  725. order_id = self.order_info.get('id')
  726. card_no = self.order_info.get('card_no')
  727. card = self.base.update_card_dealer_and_type(card_no, cardType='IC', balance=card_balance)
  728. if not card:
  729. logger.info(log_obj('no this card cardNo:{}'.format(card_no)))
  730. return
  731. servicedInfo = {
  732. 'cardNo': card_no,
  733. 'cardBalance': card_balance.mongo_amount
  734. }
  735. attachParas = {
  736. 'orderNo': order_id
  737. }
  738. # 记录卡消费记录以及消费记录
  739. orderNo, cardOrderNo = self.base.record_consume_for_card(card, amount, servicedInfo=servicedInfo,
  740. attachParas=attachParas)
  741. consumeOrder = {
  742. 'orderNo': orderNo,
  743. 'cardOrderNo': cardOrderNo,
  744. 'coin': str(amount),
  745. 'consumeType': 'card',
  746. }
  747. self._register_card_service_progress(card, consumeOrder, order_id)
  748. title = make_title_from_dict([
  749. {u'设备地址': u'{}'.format(self.dev.group.address)},
  750. {u'设备编号': u'{}'.format(self.dev['logicalCode'])},
  751. {u'实体卡': u'{}--No:{}'.format(card.cardName or card.nickName, card.cardNo)},
  752. {u'本次消费': u'{} 元'.format(amount)},
  753. {u'卡余额': u'{} 元'.format(card_balance)},
  754. ])
  755. self.base.notify_user(
  756. card.managerialOpenId,
  757. 'dev_start',
  758. **{
  759. 'title': title,
  760. 'things': u'刷卡消费',
  761. 'remark': u'感谢您的支持!',
  762. 'time': start_time.strftime(Const.DATETIME_FMT)
  763. }
  764. )
  765. def _card_refund_do_record_finished(self):
  766. card_no = self.order_info.get('card_no')
  767. card_balance = RMB.fen_to_yuan(self.order_info.get('balance'))
  768. refundMoney = RMB.fen_to_yuan(self.order_info.get('refund'))
  769. finishedTime = self.order_info.get('over_time')
  770. finishedTime = datetime.datetime.fromtimestamp(finishedTime).strftime('%Y-%m-%d %H:%M:%S')
  771. card = self.base.update_card_dealer_and_type(card_no, cardType='IC', balance=card_balance)
  772. if card:
  773. logger.info(log_obj(
  774. 'Card balance sync completed, cardNo:{} , refundMoney:{} ,finishedTime:{}'.format(
  775. card_no, refundMoney.mongo_amount, finishedTime)))
  776. title = make_title_from_dict([
  777. {u'设备地址': u'{}'.format(self.dev.group.address)},
  778. {u'设备编号': u'{}'.format(self.dev['logicalCode'])},
  779. {u'实体卡': u'{}--No:{}'.format(card.cardName, card.cardNo)},
  780. {u'卡余额': u'{} 元'.format(card_balance)},
  781. ])
  782. self.base.notify_user(
  783. card.managerialOpenId,
  784. 'refund_coins',
  785. **{
  786. 'title': title,
  787. 'backCount': u'%s 元' % refundMoney,
  788. 'finishTime': finishedTime
  789. }
  790. )
  791. else:
  792. logger.info(log_obj(
  793. 'Card balance sync failed, cardNo:{} , refundMoney:{} ,finishedTime:{}'.format(
  794. card_no, refundMoney.mongo_amount, finishedTime)))