weifule_touch.py 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import json
  4. import logging
  5. import datetime
  6. import time
  7. from mongoengine import DoesNotExist
  8. from apilib.monetary import RMB, VirtualCoin
  9. from apilib.utils_string import make_title_from_dict
  10. from apilib.utils_sys import memcache_lock
  11. from apps.web.constant import Const, ErrorCode, DEALER_CONSUMPTION_AGG_KIND
  12. from apps.web.dealer.models import Dealer
  13. from apps.web.device.models import Group, Device
  14. from apps.web.eventer import EventBuilder
  15. from apps.web.eventer.base import WorkEvent
  16. from apps.web.eventer.weifuleCommon import WeiFuLeStatusEvent, WeiFuLeCommonEvent
  17. from apps.web.user.models import ServiceProgress, UserVirtualCard, VCardConsumeRecord, \
  18. ConsumeRecord, Card, CardConsumeRecord, CardRechargeOrder, MyUser, CardRechargeRecord, RechargeRecord
  19. logger = logging.getLogger(__name__)
  20. created_order_32 = 32
  21. exec_order_33 = 33
  22. finished_order_34 = 34
  23. id_card_request_35 = 35
  24. card_recharge_order_36 = 36
  25. status_change_event_44 = 44
  26. ic_consume_event_48 = 48 # ic卡花钱的时候,会上报此事件
  27. part_sn_event = 49 # 组件上报相关信息
  28. card_is_normal = 1
  29. card_not_in_db = 2
  30. card_is_forzen = 3
  31. card_has_not_order = 4 # IC卡适用
  32. card_less_balance = 5 # ID卡适用
  33. card_type_is_ic = 6 # IC卡不允许当做ID卡使用
  34. no_load_code = 7
  35. class builder(EventBuilder):
  36. def __getEvent__(self, device_event):
  37. event_data = self.deviceAdapter.analyze_event_data(device_event['data'])
  38. if event_data is None:
  39. return None
  40. if event_data['fun_code'] in [created_order_32, exec_order_33, finished_order_34, id_card_request_35,
  41. card_recharge_order_36]:
  42. return ChargingSocketWorkEvent(self.deviceAdapter, event_data)
  43. if event_data['fun_code'] in [status_change_event_44]:
  44. return WeiFuLeStatusEvent(self.deviceAdapter, device_event)
  45. class ChargingSocketWorkEvent(WorkEvent, WeiFuLeCommonEvent):
  46. def analyse_need_from_package(self, order):
  47. needKind, needValue, unit = 'needElec', 0, u'度'
  48. if order['chrmt'] == 'TIME':
  49. needKind = 'needTime'
  50. needValue = order['amount_time'] / 60
  51. unit = u'分钟'
  52. else:
  53. needValue = order['amount_elec'] / 1000000.0
  54. return needKind, needValue, unit
  55. def create_progress_for_socket_order(self, consumeRcd, order):
  56. try:
  57. port = int(order['port'])
  58. progress = ServiceProgress.objects.filter(device_imei=self.device.devNo, port=port).first()
  59. if not progress:
  60. progress = ServiceProgress(device_imei=self.device.devNo, port=port)
  61. progress.start_time = order['create_time']
  62. progress.finished_time = order['create_time'] + order.get('amount_time', 60 * 60 * 12)
  63. progress.isFinished = False
  64. else:
  65. if order['id'] in progress.consumes:
  66. return
  67. if progress.isFinished == False:
  68. progress.finished_time = progress.finished_time + order.get('amount_time', 60 * 60 * 12)
  69. else:
  70. progress.consumes = []
  71. progress.start_time = order['create_time']
  72. progress.finished_time = order['create_time'] + order.get('amount_time', 60 * 60 * 12)
  73. progress.isFinished = False
  74. progress.open_id = consumeRcd.openId
  75. progress.consumes.append(order['id'])
  76. progress.status = 'running'
  77. progress.save()
  78. except Exception as e:
  79. logger.exception(e)
  80. def do(self, **args):
  81. devNo = self.device['devNo']
  82. funCode = self.event_data.get('fun_code')
  83. order = self.event_data.get('order', {})
  84. logger.info('weifule charging event detected, devNo=%s' % (devNo,))
  85. with memcache_lock(key = '%s-%s-%s-finished' % (devNo, order.get('id'), funCode), value = '1',
  86. expire = 120) as acquired:
  87. if acquired:
  88. self._do_ack_order(order)
  89. try:
  90. if funCode == created_order_32:
  91. self._do_created_order_32(order)
  92. elif funCode == exec_order_33:
  93. self._do_exec_order_33(order)
  94. elif funCode == finished_order_34 and order['order_type'] != 'card_charge':
  95. self._do_finished_order_34(order)
  96. elif funCode == finished_order_34 and order['order_type'] == 'card_charge':
  97. self.update_card_recharge_for_success_event(order['id'], RMB(order['balance'] / 100.0))
  98. elif funCode == id_card_request_35:
  99. self.response_id_card()
  100. elif funCode == card_recharge_order_36: # 如果是卡充值,直接回复订单
  101. self.deal_with_ic_charge_event()
  102. except Exception:
  103. import traceback
  104. logger.warn(traceback.format_exc())
  105. logger.info('deal order is finished < funCode: {} order: {} >'.format(funCode, order.get('id')))
  106. else:
  107. logger.info('weifule charging event is doing !!!, devNo=%s' % (devNo,))
  108. def update_balance_for_IC_card(self, card, balance):
  109. card.balance = balance
  110. try:
  111. card.save()
  112. except Exception, e:
  113. pass
  114. def __translate_reason(self, cause, chrmt):
  115. if cause == 1:
  116. if chrmt == 'TIME':
  117. return u'订购时间已经用完。'
  118. else:
  119. return u'订购电量已经用完。'
  120. elif cause == 2:
  121. return u'用户远程停止。'
  122. elif cause == 3:
  123. return u'管理员操作停止。'
  124. elif cause == 4:
  125. return u'经销商远程停止。'
  126. elif cause == 5:
  127. return u'用户拔掉充电器,或者插座脱落。'
  128. elif cause == 6:
  129. return u'端口功率过载,系统为了安全,关闭此充电端口。'
  130. elif cause == 7:
  131. return u'整机电压过载,系统为了安全,关闭所有充电端口。'
  132. elif cause == 8:
  133. return u'端口电流过载,系统为了安全,关闭此充电端口。'
  134. elif cause == 9:
  135. return u'整机功率超限,系统为了安全,关闭所有充电端口。'
  136. elif cause == 10:
  137. return u'检测到温度超限,系统为了安全,关闭所有充电端口。'
  138. elif cause == 11:
  139. return u'充满自停'
  140. elif cause == 20:
  141. return u'端口功率过小。可能是电池已经充满,也可能是所接负载功率太小'
  142. return u'充电结束。'
  143. def record_consume_for_card(self, card, money, orderNo):
  144. group = Group.get_group(self.device['groupId'])
  145. address = group['address']
  146. group_number = self.device['groupNumber']
  147. now = datetime.datetime.now()
  148. new_record = {
  149. 'orderNo': orderNo,
  150. 'time': now.strftime('%Y-%m-%d %H:%M:%S'),
  151. 'dateTimeAdded': now,
  152. 'openId': card.openId,
  153. 'ownerId': self.device['ownerId'],
  154. 'coin': money.mongo_amount,
  155. 'money': money.mongo_amount,
  156. 'devNo': self.device['devNo'],
  157. 'logicalCode': self.device['logicalCode'],
  158. 'groupId': self.device['groupId'],
  159. 'address': address,
  160. 'groupNumber': group_number,
  161. 'groupName': group['groupName'],
  162. 'devTypeCode': self.device.devTypeCode,
  163. 'devTypeName': self.device.devTypeName,
  164. 'isNormal': True,
  165. 'remarks': u'刷卡消费',
  166. 'errorDesc': '',
  167. 'sequanceNo': '',
  168. 'desc': '',
  169. 'attachParas': {},
  170. 'servicedInfo': {}
  171. }
  172. ConsumeRecord.get_collection().insert_one(new_record)
  173. # 刷卡消费也记录一条数据
  174. new_card_record = {
  175. 'orderNo': orderNo,
  176. 'openId': card.openId,
  177. 'cardId': str(card.id),
  178. 'money': money.mongo_amount,
  179. 'balance': card.balance.mongo_amount,
  180. 'devNo': self.device['devNo'],
  181. 'devType': self.device['devType']['name'],
  182. 'logicalCode': self.device['logicalCode'],
  183. 'groupId': self.device['groupId'],
  184. 'address': address,
  185. 'groupNumber': group_number,
  186. 'groupName': group['groupName'],
  187. 'result': 'success',
  188. 'remarks': u'刷卡消费',
  189. 'sequanceNo': '',
  190. 'dateTimeAdded': datetime.datetime.now(),
  191. 'desc': '',
  192. 'servicedInfo': {},
  193. 'linkedConsumeRcdOrderNo': str(new_record['orderNo'])
  194. }
  195. CardConsumeRecord.get_collection().insert(new_card_record)
  196. return new_record['orderNo'], new_card_record['orderNo']
  197. def _do_ack_order(self, order):
  198. funCode = self.event_data['fun_code']
  199. if funCode in [created_order_32, finished_order_34]:
  200. try:
  201. self.deviceAdapter.ack_event(order['id'], funCode)
  202. except Exception, e:
  203. logger.info('ack event devNo=%s err=%s' % (self.device.devNo, e))
  204. pass
  205. def _do_created_order_32(self, order):
  206. if order['order_type'] == 'apps_start': # 扫码
  207. self.pay_apps_start_by_32(order)
  208. consumeRcd = ConsumeRecord.objects.filter(orderNo=order['id']).first()
  209. if consumeRcd is None:
  210. return
  211. self.create_progress_for_socket_order(consumeRcd, order)
  212. elif order['order_type'] == 'card_start':
  213. cardNo = order['card_no']
  214. # 登记下卡相关信息
  215. if order['card_type'] == 'IC':
  216. card = self.update_card_dealer_and_type(cardNo, 'IC')
  217. if card:
  218. self.update_balance_for_IC_card(card, RMB(order['balance'] / 100.0))
  219. else:
  220. return
  221. # 如果卡冻结了,马上把端口关闭
  222. if card.frozen:
  223. logger.info('this card=%s is frozen,so shut down the port which opened by the card' % cardNo)
  224. self.deviceAdapter.stop_charging_port(order['port'])
  225. else:
  226. card = self.update_card_dealer_and_type(cardNo, 'ID')
  227. if not card:
  228. logger.info('no find card=<{}>'.format(cardNo))
  229. return
  230. balance = card.balance - RMB(order['amount'] / 100.0)
  231. card.balance = balance
  232. # 卡的显示余额同步下
  233. card.showBalance = balance
  234. card.save()
  235. fee = RMB(order['amount'] / 100.0)
  236. # 记录卡消费记录以及消费记录
  237. orderNo, cardOrderNo = self.record_consume_for_card(card, fee, order['id'])
  238. consumeRcd = ConsumeRecord.objects.filter(orderNo=orderNo).first()
  239. self.create_progress_for_socket_order(consumeRcd, order)
  240. # 通知用户,已经扣费
  241. title = make_title_from_dict([
  242. {u'设备地址': u'{}'.format(self.device.group.address)},
  243. {u'设备编号': u'{}'.format(self.device['logicalCode'])},
  244. {u'实体卡': u'{}--No:{}'.format(card.cardName or card.nickName, card.cardNo)},
  245. {u'本次消费': u'{} 元'.format(fee)},
  246. {u'卡余额': u'{} 元'.format(RMB(order['balance'] / 100.0))},
  247. ])
  248. start_time_stamp = order.get('create_time')
  249. start_time = datetime.datetime.fromtimestamp(start_time_stamp)
  250. self.notify_user(
  251. card.managerialOpenId,
  252. 'dev_start',
  253. **{
  254. 'title': title,
  255. 'things': u'刷卡消费',
  256. 'remark': u'感谢您的支持!',
  257. 'time': start_time.strftime(Const.DATETIME_FMT)
  258. }
  259. )
  260. # 缓存切换
  261. ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  262. lineInfo = ctrInfo.get(str(order['port']), {})
  263. if not lineInfo or lineInfo.get('status') == Const.DEV_WORK_STATUS_IDLE:
  264. lineInfo = {
  265. 'port': str(order['port']),
  266. 'status': Const.DEV_WORK_STATUS_WORKING,
  267. 'order_type': 'card_start',
  268. 'startTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  269. 'orderNo': orderNo,
  270. }
  271. Device.update_port_control_cache(self.device.devNo, lineInfo)
  272. else:
  273. pass
  274. def _do_exec_order_33(self, order):
  275. now = datetime.datetime.fromtimestamp(order['exec_time'])
  276. consumeRcd = ConsumeRecord.objects.filter(orderNo=order['id']).first()
  277. if not consumeRcd:
  278. logger.info('no this order <{}> '.format(order['id']))
  279. return
  280. consumeRcd.startTime = now
  281. consumeRcd.save()
  282. ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  283. lineInfo = ctrInfo.get(str(order['port']), {})
  284. if lineInfo.get('status') == Const.DEV_WORK_STATUS_WORKING and lineInfo.get('orderNo') == order['id']:
  285. lineInfo.update({'chrmt': order['chrmt']})
  286. if 'card_no' in order:
  287. lineInfo.update({'cardType': order.get('card_type'),'cardNo': order.get('card_no')})
  288. Device.update_port_control_cache(self.device.devNo, lineInfo)
  289. logger.info('now to exec order <{}>'.format(lineInfo.get('orderNo')))
  290. return
  291. consumeRcd = ConsumeRecord.objects.filter(orderNo=order['id']).first()
  292. lineInfo = {
  293. 'port': str(order['port']),
  294. 'status': Const.DEV_WORK_STATUS_WORKING,
  295. 'order_type': order['order_type'],
  296. 'startTime': now.strftime('%Y-%m-%d %H:%M:%S'),
  297. 'orderNo': consumeRcd.orderNo,
  298. 'chrmt': order['chrmt']
  299. }
  300. if 'card_no' in order:
  301. lineInfo.update({'cardType': order.get('card_type'), 'cardNo': order.get('card_no')})
  302. Device.update_port_control_cache(self.device.devNo, lineInfo)
  303. logger.info('now to exec next order <{}>'.format(lineInfo.get('orderNo')))
  304. def _do_finished_order_34(self, order):
  305. # 如果是离线卡的余额回收,修改下余额,然后直接返回即可
  306. if order['order_type'] == 'card_refund':
  307. return self.update_card_dealer_and_type(cardNo=order['card_no'], cardType='IC', balance=RMB(order['balance'] / 100.0))
  308. consumeRcd = ConsumeRecord.objects.filter(orderNo=order['id'], status__ne='finished').first()
  309. if not consumeRcd:
  310. logger.info('Repeat processing!! orderNo<{}>'.format(order['id']))
  311. return
  312. progress = ServiceProgress.objects.filter(device_imei=self.device.devNo, port=int(order['port']), consumes__contains=order['id']).first()
  313. if progress is None:
  314. pass
  315. else:
  316. progress.consumes.remove(order['id'])
  317. if len(progress.consumes) > 0: # 还有其他的单未处理完成
  318. progress.save()
  319. else:
  320. progress.status = 'finished' # 此时为本次服务的最后一单
  321. progress.isFinished = True
  322. progress.save()
  323. try:
  324. order['reason'] = self.__translate_reason(order['cause'], order['chrmt'])
  325. group = Group.get_group(self.device['groupId'])
  326. consumeDict = {
  327. 'reason': order['reason'],
  328. 'chargeIndex': order['port'],
  329. 'duration': round(order['time'] / 60.0, 1),
  330. 'elec': round(order['elec'] / 1000000.0, 2)
  331. }
  332. notifyDict = {
  333. 'service': u'充电服务完成',
  334. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  335. 'remark': u'谢谢您的支持'
  336. }
  337. refundDict = {
  338. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  339. }
  340. refundProtection = self.device['otherConf'].get('refundProtection', 5)
  341. package = consumeRcd.package
  342. if package.get('name') == '充满自停' and package.get('coins') == 0 and package.get('price') == 0: # 后付费
  343. usedFee = VirtualCoin(order['money'] * 0.01)
  344. usedTime = order['time'] / 60
  345. cause = int(order.get(u'cause', 0))
  346. if cause == no_load_code or usedTime <= refundProtection:
  347. usedFee = VirtualCoin(0)
  348. consumeDict.update({
  349. DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: usedFee.mongo_amount
  350. })
  351. user = MyUser.objects(openId=consumeRcd.openId, groupId=self.device['groupId']).first()
  352. if not user:
  353. return
  354. try:
  355. user.pay(usedFee)
  356. consumeRcd.update(coin=usedFee.mongo_amount, status='finished')
  357. except Exception:
  358. pass
  359. notifyDict.update(
  360. {'title': make_title_from_dict([
  361. {u'设备编号': self.device['logicalCode']},
  362. {u'端口': order['port']},
  363. {u'地址': group['address']},
  364. {u'结束原因': order['reason']},
  365. {u'充电时间': u'%s分钟' % round(usedTime, 1)},
  366. {u'本次服务费用': u'%s币' % usedFee},
  367. ])})
  368. # 通知服务结束
  369. notifyOpenId = user.managerialOpenId if user else ''
  370. self.notify_user(notifyOpenId, 'service_complete', **notifyDict)
  371. consumeRcd.update_service_info(consumeDict)
  372. consumeRcd.update(status='finished', finishedTime=datetime.datetime.now(),
  373. servicedInfo__source=json.dumps(order))
  374. logger.info('orderNo<{}> finished!!'.format(consumeRcd.orderNo))
  375. return
  376. else:
  377. coins = consumeRcd.coin
  378. backCoins = RMB(0)
  379. if order['chrmt'] == 'TIME': # 按照时间计费
  380. needTime = round(order['amount_time'] / 60.0, 1)
  381. usedTime = round(order['time'] / 60.0, 1)
  382. leftTime = round(order['left_time'] / 60 if order['left_time'] > 0 else 0, 1)
  383. leftTimeStr = leftTime
  384. actualNeedTime = round(usedTime + leftTime, 1)
  385. consumeDict.update({
  386. 'needTime': needTime,
  387. 'leftTime': leftTimeStr,
  388. 'actualNeedTime': u'动态功率计算为%s分钟' % actualNeedTime,
  389. })
  390. notifyDict.update({'title': make_title_from_dict([
  391. {u'设备编号': self.device['logicalCode']},
  392. {u'端口': order['port']},
  393. {u'地址': group['address']},
  394. {u'结束原因': order['reason']},
  395. {u'订购时间': u'%s分钟' % needTime},
  396. {u'应充时间': u'%s分钟' % actualNeedTime},
  397. {u'充电时间': u'%s分钟' % usedTime},
  398. {u'剩余时间': u'%s分钟' % leftTimeStr}
  399. ])})
  400. refundTitleDictList = [
  401. {u'设备编号': self.device['logicalCode']},
  402. {u'端口': order['port']},
  403. {u'付款': u'%s元' % coins},
  404. {u'预定时间': u'%s分钟' % needTime},
  405. {u'应充时间': u'%s分钟' % actualNeedTime},
  406. {u'充电时间': u'%s分钟' % usedTime},
  407. {u'剩余时间': u'%s分钟' % leftTimeStr},
  408. ]
  409. else:
  410. needElec = round(order['amount_elec'] / 1000000.0, 2)
  411. usedTime = round(order['time'] / 60.0, 1)
  412. usedElec = round(order['elec'] / 1000000.0, 2)
  413. leftElec = round(needElec - usedElec if needElec > usedElec else 0.0, 2)
  414. consumeDict.update({
  415. 'needElec': needElec,
  416. 'leftElec': leftElec,
  417. })
  418. notifyDict.update({'title': make_title_from_dict([
  419. {u'设备编号': self.device['logicalCode']},
  420. {u'端口': order['port']},
  421. {u'结束原因': order['reason']},
  422. {u'充电时间': u'%s分钟' % usedTime},
  423. {u'订购电量': u'%s度' % needElec},
  424. {u'消耗电量': u'%s度' % usedElec},
  425. {u'剩余电量': u'%s度' % leftElec},
  426. ])})
  427. refundTitleDictList = [
  428. {u'设备编号': self.device['logicalCode']},
  429. {u'端口': order['port']},
  430. {u'结束原因': order['reason']},
  431. {u'充电时间': u'%s分钟' % usedTime},
  432. {u'订购电量': u'%s度' % needElec},
  433. {u'消耗电量': u'%s度' % usedElec},
  434. {u'剩余电量': u'%s度' % leftElec},
  435. ]
  436. if usedTime <= refundProtection:
  437. backCoins = coins
  438. else:
  439. if self.device.is_auto_refund:
  440. backCoins = coins * (float(order['left_money']) / float(order['amount']))
  441. refundDict.update({
  442. 'title': make_title_from_dict(refundTitleDictList),
  443. 'backCount': u'金币:%s' % backCoins
  444. })
  445. logger.info(
  446. 'REFUND STATUS orderNo=<{}>, refundSwitch={}, refundProtectionTime=<{}>, backCoins=<{}>'.format(
  447. consumeRcd.orderNo, self.device.is_auto_refund, refundProtection, backCoins.mongo_amount
  448. ))
  449. if u'虚拟卡' in consumeRcd.remarks:
  450. # 退额度
  451. try:
  452. vRcd = VCardConsumeRecord.objects.get(orderNo=order['id'])
  453. vCard = UserVirtualCard.objects.get(id=vRcd.cardId)
  454. except DoesNotExist, e:
  455. logger.info('can not find the vCard id = %s' % vRcd.cardId)
  456. return
  457. # 通知服务结束
  458. notifyOpenId = self.get_managerialOpenId_by_openId(vRcd.openId) if vCard else ''
  459. self.notify_user(notifyOpenId, 'service_complete', **notifyDict)
  460. # 不需要退款,直接返回,不通知
  461. if self.device.is_auto_refund and backCoins > RMB(0):
  462. if order['chrmt'] == 'TIME':
  463. vCard.refund_quota(vRcd, usedTime, 0.0, backCoins.mongo_amount)
  464. else:
  465. vCard.refund_quota(vRcd, 0.0, usedElec, backCoins.mongo_amount)
  466. # 刷卡的方式
  467. elif order['order_type'] == 'card_start' and order['card_no']:
  468. dealer = Dealer.get_dealer(ownerId=self.device['ownerId'])
  469. card = Card.objects.filter(agentId=dealer['agentId'], cardNo=order['card_no']).first()
  470. if card is None: # 离线卡没有绑定或者在线卡被解绑了
  471. return
  472. # 先通知
  473. notifyOpenId = card.managerialOpenId if card else ''
  474. self.notify_user(notifyOpenId, 'service_complete', **notifyDict)
  475. # 不需要退款,直接返回.在线卡需要退费,离线卡只登记使用记录就OK
  476. if backCoins > RMB(0) and card.cardType == 'ID' and backCoins > RMB(0):
  477. card = self.refund_money_for_card(RMB(backCoins), card.id)
  478. card.showBalance = card.balance
  479. card.save()
  480. consumeDict.update({'refundedMoney': str(backCoins)})
  481. self.notify_user(notifyOpenId, 'refund_coins', **refundDict)
  482. else: # 扫码的
  483. user = MyUser.objects(openId=consumeRcd.openId, groupId=self.device['groupId']).first()
  484. if not user:
  485. return
  486. # 通知服务结束
  487. notifyOpenId = user.managerialOpenId if user else ''
  488. self.notify_user(notifyOpenId, 'service_complete', **notifyDict)
  489. # 如果需要退款,计算退款数据.
  490. if backCoins > RMB(0):
  491. refundCash = 'refundRMB_device_event' in self.device.owner.features
  492. rechargeRcdId = consumeRcd.rechargeRcdId
  493. if rechargeRcdId:
  494. rechargeRcd = RechargeRecord.objects.filter(id=rechargeRcdId, isQuickPay=True).first()
  495. else:
  496. rechargeRcd = None
  497. if refundCash is True and rechargeRcd is not None: # 退现金特征 + 有充值订单
  498. refundRMB = rechargeRcd.money * (backCoins.amount / coins.amount)
  499. self.refund_net_pay(user, {'rechargeRcdId': rechargeRcdId, 'openId': user.openId},
  500. refundRMB, VirtualCoin(0), consumeDict, True)
  501. refundDict.update({'backCount': u'金额:%s' % refundRMB})
  502. self.notify_user(notifyOpenId, 'refund_coins', **refundDict)
  503. else:
  504. self.refund_net_pay(user, {'openId': user.openId}, RMB(0), backCoins, consumeDict,
  505. False)
  506. self.notify_user(notifyOpenId, 'refund_coins', **refundDict)
  507. consumeRcd.update_service_info(consumeDict)
  508. consumeRcd.update(status='finished', finishedTime=datetime.datetime.now(),servicedInfo__source=json.dumps(order))
  509. except Exception as e:
  510. logger.exception('some exception happed,devNo=%s,e=%s' % (self.device.devNo, e))
  511. finally:
  512. ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  513. lineInfo = ctrInfo.get(str(order['port']), {})
  514. # 如果缓存中要结束的端口是本单的 就清空缓存 否则不处理
  515. if order['id'] == lineInfo.get('orderNo'):
  516. Device.clear_port_control_cache(self.device.devNo, order['port'])
  517. # 这里采用更新端口 不操作缓存
  518. self.deviceAdapter.get_port_status_from_dev()
  519. def deal_with_ic_charge_event(self):
  520. cardNo = self.event_data['card_no']
  521. card = self.update_card_dealer_and_type(cardNo, 'IC')
  522. if not card:
  523. return self.deviceAdapter.response_card_charge_result(self.event_data['card_no'], card_not_in_db)
  524. elif card.frozen:
  525. return self.deviceAdapter.response_card_charge_result(self.event_data['card_no'], card_is_forzen)
  526. preBalance = card.balance
  527. rechargeOrder = CardRechargeOrder.get_last_to_do_one(str(card.id))
  528. if rechargeOrder:
  529. self.recharge_ic_card(card=card,
  530. preBalance=preBalance,
  531. rechargeType='append',
  532. order=rechargeOrder,
  533. need_verify=False)
  534. else:
  535. return self.deviceAdapter.response_card_charge_result(self.event_data['card_no'], card_has_not_order)
  536. def recharge_ic_card(self, card, preBalance, rechargeType, order, need_verify=True):
  537. # type:(Card, RMB, str, CardRechargeOrder, bool)->bool
  538. """
  539. # rechargeType有两种,一种是用直接覆写overwrite的方式,一种是append追加钱的方式。
  540. # 不同的的设备,充值的方式还不一样.注意:money是实际用户付的钱,coins是给用户充值的钱,比如付10块(money),充15(coins)。
  541. :param card:
  542. :param preBalance:
  543. :param rechargeType:
  544. :param order:
  545. :return:
  546. """
  547. if not order or order.coins == RMB(0):
  548. return False
  549. status = Card.get_card_status(str(card.id))
  550. if status == 'busy':
  551. return False
  552. Card.set_card_status(str(card.id), 'busy')
  553. try:
  554. # IC卡需要下发到设备,设备写卡,把余额打入卡中
  555. if rechargeType == 'overwrite':
  556. sendMoney = preBalance + order.coins
  557. else:
  558. sendMoney = order.coins
  559. # 先判断order最近一次充值是否OK. 满足三个条件才认为上次充值成功:
  560. # 1、操作时间已经超过三天
  561. # 2、操作结果是串口超时, 即result == 1
  562. # 3、当前余额大于最后一次充值操作的充值前余额
  563. if need_verify and len(order.operationLog) > 0:
  564. log = order.operationLog[-1]
  565. result = log['result']
  566. time_delta = (datetime.datetime.now() - log['time']).total_seconds()
  567. last_pre_balance = RMB(log['preBalance'])
  568. if (result == ErrorCode.DEVICE_CONN_FAIL or result == ErrorCode.BOARD_UART_TIMEOUT) \
  569. and (time_delta > 3 * 24 * 3600 or preBalance > last_pre_balance):
  570. logger.debug('{} recharge verify result is finished.'.format(repr(card)))
  571. order.update_after_recharge_ic_card(device=self.device,
  572. sendMoney=sendMoney,
  573. preBalance=preBalance,
  574. result=ErrorCode.SUCCESS,
  575. description=u'充值校验结束')
  576. CardRechargeRecord.add_record(
  577. card=card,
  578. group=Group.get_group(order.groupId),
  579. order=order,
  580. device=self.device)
  581. return False
  582. try:
  583. operation_result, balance = self.deviceAdapter.recharge_card(card.cardNo, sendMoney,
  584. orderNo=str(order.id))
  585. order.update_after_recharge_ic_card(device=self.device,
  586. sendMoney=sendMoney,
  587. preBalance=preBalance,
  588. syncBalance=balance,
  589. result=operation_result['result'],
  590. description=operation_result['description'])
  591. if operation_result['result'] != ErrorCode.SUCCESS:
  592. return False
  593. if not balance:
  594. balance = preBalance + order.coins
  595. CardRechargeRecord.add_record(
  596. card=card,
  597. group=Group.get_group(order.groupId),
  598. order=order,
  599. device=self.device)
  600. # 刷新卡里面的余额
  601. card.balance = balance
  602. card.lastMaxBalance = balance
  603. card.save()
  604. return True
  605. except Exception as e:
  606. order.update_after_recharge_ic_card(device=self.device,
  607. sendMoney=sendMoney,
  608. preBalance=preBalance,
  609. syncBalance=balance,
  610. result=ErrorCode.EXCEPTION,
  611. description=e.message)
  612. return False
  613. except Exception as e:
  614. logger.exception(e)
  615. return False
  616. finally:
  617. Card.set_card_status(str(card.id), 'idle')
  618. def response_id_card(self):
  619. cardNo = self.event_data['card_no']
  620. Card.record_dev_card_no(self.device['devNo'], cardNo)
  621. card = Card.objects.filter(cardNo=cardNo, agentId=self.device.owner.agentId).first()
  622. # 如果没有卡,直接返回
  623. if card is None:
  624. return self.deviceAdapter.response_card_balance(cardNo=cardNo, balance=0, result=card_not_in_db, amount=0)
  625. if card.cardType == '':
  626. card.update(cardType='ID')
  627. elif card.cardType != 'ID': # 如果以前是离线卡,只能用于离线卡,需要提醒用户
  628. return self.deviceAdapter.response_card_balance(cardNo=cardNo, balance=0, result=card_type_is_ic, amount=0)
  629. if card.frozen:
  630. return self.deviceAdapter.response_card_balance(cardNo=cardNo, balance=0, result=card_is_forzen, amount=0)
  631. card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id))
  632. if card_recharge_order:
  633. result = self.recharge_id_card(card=card,
  634. rechargeType='append',
  635. order=card_recharge_order)
  636. card.reload()
  637. oper = self.event_data.get('reduce')
  638. fee = RMB(self.device['otherConf'].get('id_card_oncefee', 1))
  639. if oper:
  640. if card.balance >= fee:
  641. result = card_is_normal
  642. balance = card.balance - fee
  643. # balance = card.showBalance * 100
  644. else:
  645. result = card_less_balance
  646. # balance = card.showBalance * 100
  647. balance = card.balance
  648. self.deviceAdapter.response_card_balance(cardNo, fee, balance, result) # 返回的cardNo为16进制
  649. else:
  650. self.deviceAdapter.response_card_balance(cardNo, fee, card.balance, card_is_normal)