chazuo.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. 'status': ConsumeRecord.Status.RUNNING,
  166. 'remarks': u'刷卡消费',
  167. 'errorDesc': '',
  168. 'sequanceNo': '',
  169. 'desc': '',
  170. 'attachParas': {},
  171. 'servicedInfo': {}
  172. }
  173. ConsumeRecord.get_collection().insert_one(new_record)
  174. # 刷卡消费也记录一条数据
  175. new_card_record = {
  176. 'orderNo': orderNo,
  177. 'openId': card.openId,
  178. 'cardId': str(card.id),
  179. 'money': money.mongo_amount,
  180. 'balance': card.balance.mongo_amount,
  181. 'devNo': self.device['devNo'],
  182. 'devType': self.device['devType']['name'],
  183. 'logicalCode': self.device['logicalCode'],
  184. 'groupId': self.device['groupId'],
  185. 'address': address,
  186. 'groupNumber': group_number,
  187. 'groupName': group['groupName'],
  188. 'result': 'success',
  189. 'remarks': u'刷卡消费',
  190. 'sequanceNo': '',
  191. 'dateTimeAdded': datetime.datetime.now(),
  192. 'desc': '',
  193. 'servicedInfo': {},
  194. 'linkedConsumeRcdOrderNo': str(new_record['orderNo'])
  195. }
  196. CardConsumeRecord.get_collection().insert(new_card_record)
  197. return new_record['orderNo'], new_card_record['orderNo']
  198. def _do_ack_order(self, order):
  199. funCode = self.event_data['fun_code']
  200. if funCode in [created_order_32, finished_order_34]:
  201. try:
  202. self.deviceAdapter.ack_event(order['id'], funCode)
  203. except Exception, e:
  204. logger.info('ack event devNo=%s err=%s' % (self.device.devNo, e))
  205. pass
  206. def _do_created_order_32(self, order):
  207. if order['order_type'] == 'apps_start': # 扫码
  208. self.pay_apps_start_by_32(order)
  209. consumeRcd = ConsumeRecord.objects.filter(orderNo=order['id']).first()
  210. if consumeRcd is None:
  211. return
  212. self.create_progress_for_socket_order(consumeRcd, order)
  213. elif order['order_type'] == 'card_start':
  214. cardNo = order['card_no']
  215. # 登记下卡相关信息
  216. if order['card_type'] == 'IC':
  217. card = self.update_card_dealer_and_type(cardNo, 'IC')
  218. if card:
  219. self.update_balance_for_IC_card(card, RMB(order['balance'] / 100.0))
  220. else:
  221. return
  222. # 如果卡冻结了,马上把端口关闭
  223. if card.frozen:
  224. logger.info('this card=%s is frozen,so shut down the port which opened by the card' % cardNo)
  225. self.deviceAdapter.stop_charging_port(order['port'])
  226. else:
  227. card = self.update_card_dealer_and_type(cardNo, 'ID')
  228. if not card:
  229. logger.info('no find card=<{}>'.format(cardNo))
  230. return
  231. balance = card.balance - RMB(order['amount'] / 100.0)
  232. card.balance = balance
  233. # 卡的显示余额同步下
  234. card.showBalance = balance
  235. card.save()
  236. fee = RMB(order['amount'] / 100.0)
  237. # 记录卡消费记录以及消费记录
  238. orderNo, cardOrderNo = self.record_consume_for_card(card, fee, order['id'])
  239. consumeRcd = ConsumeRecord.objects.filter(orderNo=orderNo).first()
  240. self.create_progress_for_socket_order(consumeRcd, order)
  241. # 通知用户,已经扣费
  242. title = make_title_from_dict([
  243. {u'设备地址': u'{}'.format(self.device.group.address)},
  244. {u'设备编号': u'{}'.format(self.device['logicalCode'])},
  245. {u'实体卡': u'{}--No:{}'.format(card.cardName or card.nickName, card.cardNo)},
  246. {u'本次消费': u'{} 元'.format(fee)},
  247. {u'卡余额': u'{} 元'.format(RMB(order['balance'] / 100.0))},
  248. ])
  249. start_time_stamp = order.get('create_time')
  250. start_time = datetime.datetime.fromtimestamp(start_time_stamp)
  251. self.notify_user(
  252. card.managerialOpenId,
  253. 'dev_start',
  254. **{
  255. 'title': title,
  256. 'things': u'刷卡消费',
  257. 'remark': u'感谢您的支持!',
  258. 'time': start_time.strftime(Const.DATETIME_FMT)
  259. }
  260. )
  261. # 缓存切换
  262. ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  263. lineInfo = ctrInfo.get(str(order['port']), {})
  264. if not lineInfo or lineInfo.get('status') == Const.DEV_WORK_STATUS_IDLE:
  265. lineInfo = {
  266. 'port': str(order['port']),
  267. 'status': Const.DEV_WORK_STATUS_WORKING,
  268. 'order_type': 'card_start',
  269. 'startTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  270. 'orderNo': orderNo,
  271. }
  272. Device.update_port_control_cache(self.device.devNo, lineInfo)
  273. else:
  274. pass
  275. def _do_exec_order_33(self, order):
  276. now = datetime.datetime.fromtimestamp(order['exec_time'])
  277. consumeRcd = ConsumeRecord.objects.filter(orderNo=order['id']).first()
  278. if not consumeRcd:
  279. logger.info('no this order <{}> '.format(order['id']))
  280. return
  281. consumeRcd.startTime = now
  282. consumeRcd.save()
  283. ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  284. lineInfo = ctrInfo.get(str(order['port']), {})
  285. if lineInfo.get('status') == Const.DEV_WORK_STATUS_WORKING and lineInfo.get('orderNo') == order['id']:
  286. lineInfo.update({'chrmt': order['chrmt']})
  287. if 'card_no' in order:
  288. lineInfo.update({'cardType': order.get('card_type'),'cardNo': order.get('card_no')})
  289. Device.update_port_control_cache(self.device.devNo, lineInfo)
  290. logger.info('now to exec order <{}>'.format(lineInfo.get('orderNo')))
  291. return
  292. consumeRcd = ConsumeRecord.objects.filter(orderNo=order['id']).first()
  293. lineInfo = {
  294. 'port': str(order['port']),
  295. 'status': Const.DEV_WORK_STATUS_WORKING,
  296. 'order_type': order['order_type'],
  297. 'startTime': now.strftime('%Y-%m-%d %H:%M:%S'),
  298. 'orderNo': consumeRcd.orderNo,
  299. 'chrmt': order['chrmt']
  300. }
  301. if 'card_no' in order:
  302. lineInfo.update({'cardType': order.get('card_type'), 'cardNo': order.get('card_no')})
  303. Device.update_port_control_cache(self.device.devNo, lineInfo)
  304. logger.info('now to exec next order <{}>'.format(lineInfo.get('orderNo')))
  305. def _do_finished_order_34(self, order):
  306. # 如果是离线卡的余额回收,修改下余额,然后直接返回即可
  307. if order['order_type'] == 'card_refund':
  308. return self.update_card_dealer_and_type(cardNo=order['card_no'], cardType='IC', balance=RMB(order['balance'] / 100.0))
  309. consumeRcd = ConsumeRecord.objects.filter(orderNo=order['id'], status__ne='finished').first() # type: ConsumeRecord
  310. if not consumeRcd:
  311. logger.info('Repeat processing!! orderNo<{}>'.format(order['id']))
  312. return
  313. progress = ServiceProgress.objects.filter(device_imei=self.device.devNo, port=int(order['port']), consumes__contains=order['id']).first()
  314. if progress is None:
  315. pass
  316. else:
  317. progress.consumes.remove(order['id'])
  318. if len(progress.consumes) > 0: # 还有其他的单未处理完成
  319. progress.save()
  320. else:
  321. progress.status = 'finished' # 此时为本次服务的最后一单
  322. progress.isFinished = True
  323. progress.save()
  324. try:
  325. order['reason'] = self.__translate_reason(order['cause'], order['chrmt'])
  326. group = Group.get_group(self.device['groupId'])
  327. consumeDict = {
  328. 'reason': order['reason'],
  329. 'chargeIndex': order['port'],
  330. 'duration': round(order['time'] / 60.0, 1),
  331. 'elec': round(order['elec'] / 1000000.0, 2)
  332. }
  333. notifyDict = {
  334. 'service': u'充电服务完成',
  335. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  336. 'remark': u'谢谢您的支持'
  337. }
  338. refundDict = {
  339. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  340. }
  341. refundProtection = self.device['otherConf'].get('refundProtection', 5)
  342. package = consumeRcd.package
  343. if package.get('name') == '充满自停' and package.get('coins') == 0 and package.get('price') == 0: # 后付费
  344. usedFee = VirtualCoin(order['money'] * 0.01)
  345. usedTime = order['time'] / 60
  346. cause = int(order.get(u'cause', 0))
  347. if cause == no_load_code or usedTime <= refundProtection:
  348. usedFee = VirtualCoin(0)
  349. consumeDict.update({
  350. DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: usedFee.mongo_amount
  351. })
  352. user = MyUser.objects(openId=consumeRcd.openId, groupId=self.device['groupId']).first()
  353. if not user:
  354. return
  355. try:
  356. user.pay(usedFee)
  357. consumeRcd.update(coin=usedFee.mongo_amount, status='finished')
  358. except Exception:
  359. pass
  360. notifyDict.update(
  361. {'title': make_title_from_dict([
  362. {u'设备编号': self.device['logicalCode']},
  363. {u'端口': order['port']},
  364. {u'地址': group['address']},
  365. {u'结束原因': order['reason']},
  366. {u'充电时间': u'%s分钟' % round(usedTime, 1)},
  367. {u'本次服务费用': u'%s币' % usedFee},
  368. ])})
  369. # 通知服务结束
  370. notifyOpenId = user.managerialOpenId if user else ''
  371. self.notify_user(notifyOpenId, 'service_complete', **notifyDict)
  372. consumeRcd.update_service_info(consumeDict)
  373. consumeRcd.update(status='finished', finishedTime=datetime.datetime.now(),
  374. servicedInfo__source=json.dumps(order))
  375. logger.info('orderNo<{}> finished!!'.format(consumeRcd.orderNo))
  376. return
  377. else:
  378. coins = consumeRcd.coin
  379. backCoins = RMB(0)
  380. if order['chrmt'] == 'TIME': # 按照时间计费
  381. needTime = round(order['amount_time'] / 60.0, 1)
  382. usedTime = round(order['time'] / 60.0, 1)
  383. leftTime = round(order['left_time'] / 60 if order['left_time'] > 0 else 0, 1)
  384. leftTimeStr = leftTime
  385. actualNeedTime = round(usedTime + leftTime, 1)
  386. consumeDict.update({
  387. 'needTime': needTime,
  388. 'leftTime': leftTimeStr,
  389. 'actualNeedTime': u'动态功率计算为%s分钟' % actualNeedTime,
  390. })
  391. notifyDict.update({'title': make_title_from_dict([
  392. {u'设备编号': self.device['logicalCode']},
  393. {u'端口': order['port']},
  394. {u'地址': group['address']},
  395. {u'结束原因': order['reason']},
  396. {u'订购时间': u'%s分钟' % needTime},
  397. {u'应充时间': u'%s分钟' % actualNeedTime},
  398. {u'充电时间': u'%s分钟' % usedTime},
  399. {u'剩余时间': u'%s分钟' % leftTimeStr}
  400. ])})
  401. refundTitleDictList = [
  402. {u'设备编号': self.device['logicalCode']},
  403. {u'端口': order['port']},
  404. {u'付款': u'%s元' % coins},
  405. {u'预定时间': u'%s分钟' % needTime},
  406. {u'应充时间': u'%s分钟' % actualNeedTime},
  407. {u'充电时间': u'%s分钟' % usedTime},
  408. {u'剩余时间': u'%s分钟' % leftTimeStr},
  409. ]
  410. else:
  411. needElec = round(order['amount_elec'] / 1000000.0, 2)
  412. usedTime = round(order['time'] / 60.0, 1)
  413. usedElec = round(order['elec'] / 1000000.0, 2)
  414. leftElec = round(needElec - usedElec if needElec > usedElec else 0.0, 2)
  415. consumeDict.update({
  416. 'needElec': needElec,
  417. 'leftElec': leftElec,
  418. })
  419. notifyDict.update({'title': make_title_from_dict([
  420. {u'设备编号': self.device['logicalCode']},
  421. {u'端口': order['port']},
  422. {u'结束原因': order['reason']},
  423. {u'充电时间': u'%s分钟' % usedTime},
  424. {u'订购电量': u'%s度' % needElec},
  425. {u'消耗电量': u'%s度' % usedElec},
  426. {u'剩余电量': u'%s度' % leftElec},
  427. ])})
  428. refundTitleDictList = [
  429. {u'设备编号': self.device['logicalCode']},
  430. {u'端口': order['port']},
  431. {u'结束原因': order['reason']},
  432. {u'充电时间': u'%s分钟' % usedTime},
  433. {u'订购电量': u'%s度' % needElec},
  434. {u'消耗电量': u'%s度' % usedElec},
  435. {u'剩余电量': u'%s度' % leftElec},
  436. ]
  437. if usedTime <= refundProtection:
  438. backCoins = coins
  439. else:
  440. if self.device.is_auto_refund:
  441. backCoins = coins * (float(order['left_money']) / float(order['amount']))
  442. refundDict.update({
  443. 'title': make_title_from_dict(refundTitleDictList),
  444. 'backCount': u'金币:%s' % backCoins
  445. })
  446. logger.info(
  447. 'REFUND STATUS orderNo=<{}>, refundSwitch={}, refundProtectionTime=<{}>, backCoins=<{}>'.format(
  448. consumeRcd.orderNo, self.device.is_auto_refund, refundProtection, backCoins.mongo_amount
  449. ))
  450. if u'虚拟卡' in consumeRcd.remarks:
  451. # 退额度
  452. try:
  453. vRcd = VCardConsumeRecord.objects.get(orderNo=order['id'])
  454. vCard = UserVirtualCard.objects.get(id=vRcd.cardId)
  455. except DoesNotExist, e:
  456. logger.info('can not find the vCard id = %s' % vRcd.cardId)
  457. return
  458. # 通知服务结束
  459. notifyOpenId = self.get_managerialOpenId_by_openId(vRcd.openId) if vCard else ''
  460. self.notify_user(notifyOpenId, 'service_complete', **notifyDict)
  461. # 不需要退款,直接返回,不通知
  462. if self.device.is_auto_refund and backCoins > RMB(0):
  463. if order['chrmt'] == 'TIME':
  464. vCard.refund_quota(vRcd, usedTime, 0.0, backCoins.mongo_amount)
  465. else:
  466. vCard.refund_quota(vRcd, 0.0, usedElec, backCoins.mongo_amount)
  467. # 刷卡的方式
  468. elif order['order_type'] == 'card_start' and order['card_no']:
  469. dealer = Dealer.get_dealer(ownerId=self.device['ownerId'])
  470. card = Card.objects.filter(agentId=dealer['agentId'], cardNo=order['card_no']).first()
  471. if card is None: # 离线卡没有绑定或者在线卡被解绑了
  472. return
  473. # 先通知
  474. notifyOpenId = card.managerialOpenId if card else ''
  475. self.notify_user(notifyOpenId, 'service_complete', **notifyDict)
  476. # 不需要退款,直接返回.在线卡需要退费,离线卡只登记使用记录就OK
  477. if backCoins > RMB(0) and card.cardType == 'ID' and backCoins > RMB(0):
  478. card = self.refund_money_for_card(RMB(backCoins), card.id)
  479. card.showBalance = card.balance
  480. card.save()
  481. consumeDict.update({'refundedMoney': str(backCoins)})
  482. self.notify_user(notifyOpenId, 'refund_coins', **refundDict)
  483. else: # 扫码的
  484. user = MyUser.objects(openId=consumeRcd.openId, groupId=self.device['groupId']).first()
  485. if not user:
  486. return
  487. # 通知服务结束
  488. notifyOpenId = user.managerialOpenId if user else ''
  489. self.notify_user(notifyOpenId, 'service_complete', **notifyDict)
  490. # 如果需要退款,计算退款数据.
  491. if backCoins > RMB(0):
  492. refundCash = 'refundRMB_device_event' in self.device.owner.features
  493. rechargeRcdId = consumeRcd.recharge_record_id
  494. if rechargeRcdId:
  495. rechargeRcd = RechargeRecord.objects.filter(id=rechargeRcdId, isQuickPay=True).first()
  496. else:
  497. rechargeRcd = None
  498. if refundCash is True and rechargeRcd is not None: # 退现金特征 + 有充值订单
  499. refundRMB = rechargeRcd.money * (backCoins.amount / coins.amount)
  500. self.refund_net_pay(user, {'rechargeRcdId': rechargeRcdId, 'openId': user.openId},
  501. refundRMB, VirtualCoin(0), consumeDict, True)
  502. refundDict.update({'backCount': u'金额:%s' % refundRMB})
  503. self.notify_user(notifyOpenId, 'refund_coins', **refundDict)
  504. else:
  505. self.refund_net_pay(user, {'openId': user.openId}, RMB(0), backCoins, consumeDict,
  506. False)
  507. self.notify_user(notifyOpenId, 'refund_coins', **refundDict)
  508. consumeRcd.update_service_info(consumeDict)
  509. consumeRcd.update(status='finished', finishedTime=datetime.datetime.now(),servicedInfo__source=json.dumps(order))
  510. except Exception as e:
  511. logger.exception('some exception happed,devNo=%s,e=%s' % (self.device.devNo, e))
  512. finally:
  513. ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  514. lineInfo = ctrInfo.get(str(order['port']), {})
  515. # 如果缓存中要结束的端口是本单的 就清空缓存 否则不处理
  516. if order['id'] == lineInfo.get('orderNo'):
  517. Device.clear_port_control_cache(self.device.devNo, order['port'])
  518. # 这里采用更新端口 不操作缓存
  519. self.deviceAdapter.get_port_status_from_dev()
  520. def deal_with_ic_charge_event(self):
  521. cardNo = self.event_data['card_no']
  522. card = self.update_card_dealer_and_type(cardNo, 'IC')
  523. if not card:
  524. return self.deviceAdapter.response_card_charge_result(self.event_data['card_no'], card_not_in_db)
  525. elif card.frozen:
  526. return self.deviceAdapter.response_card_charge_result(self.event_data['card_no'], card_is_forzen)
  527. preBalance = card.balance
  528. rechargeOrder = CardRechargeOrder.get_last_to_do_one(str(card.id))
  529. if rechargeOrder:
  530. self.recharge_ic_card(card=card,
  531. preBalance=preBalance,
  532. rechargeType='append',
  533. order=rechargeOrder,
  534. need_verify=False)
  535. else:
  536. return self.deviceAdapter.response_card_charge_result(self.event_data['card_no'], card_has_not_order)
  537. def recharge_ic_card(self, card, preBalance, rechargeType, order, need_verify=True):
  538. # type:(Card, RMB, str, CardRechargeOrder, bool)->bool
  539. """
  540. # rechargeType有两种,一种是用直接覆写overwrite的方式,一种是append追加钱的方式。
  541. # 不同的的设备,充值的方式还不一样.注意:money是实际用户付的钱,coins是给用户充值的钱,比如付10块(money),充15(coins)。
  542. :param card:
  543. :param preBalance:
  544. :param rechargeType:
  545. :param order:
  546. :return:
  547. """
  548. if not order or order.coins == RMB(0):
  549. return False
  550. status = Card.get_card_status(str(card.id))
  551. if status == 'busy':
  552. return False
  553. Card.set_card_status(str(card.id), 'busy')
  554. try:
  555. # IC卡需要下发到设备,设备写卡,把余额打入卡中
  556. if rechargeType == 'overwrite':
  557. sendMoney = preBalance + order.coins
  558. else:
  559. sendMoney = order.coins
  560. # 先判断order最近一次充值是否OK. 满足三个条件才认为上次充值成功:
  561. # 1、操作时间已经超过三天
  562. # 2、操作结果是串口超时, 即result == 1
  563. # 3、当前余额大于最后一次充值操作的充值前余额
  564. if need_verify and len(order.operationLog) > 0:
  565. log = order.operationLog[-1]
  566. result = log['result']
  567. time_delta = (datetime.datetime.now() - log['time']).total_seconds()
  568. last_pre_balance = RMB(log['preBalance'])
  569. if (result == ErrorCode.DEVICE_CONN_FAIL or result == ErrorCode.BOARD_UART_TIMEOUT) \
  570. and (time_delta > 3 * 24 * 3600 or preBalance > last_pre_balance):
  571. logger.debug('{} recharge verify result is finished.'.format(repr(card)))
  572. order.update_after_recharge_ic_card(device=self.device,
  573. sendMoney=sendMoney,
  574. preBalance=preBalance,
  575. result=ErrorCode.SUCCESS,
  576. description=u'充值校验结束')
  577. CardRechargeRecord.add_record(
  578. card=card,
  579. group=Group.get_group(order.groupId),
  580. order=order,
  581. device=self.device)
  582. return False
  583. try:
  584. operation_result, balance = self.deviceAdapter.recharge_card(card.cardNo, sendMoney,
  585. orderNo=str(order.id))
  586. order.update_after_recharge_ic_card(device=self.device,
  587. sendMoney=sendMoney,
  588. preBalance=preBalance,
  589. syncBalance=balance,
  590. result=operation_result['result'],
  591. description=operation_result['description'])
  592. if operation_result['result'] != ErrorCode.SUCCESS:
  593. return False
  594. if not balance:
  595. balance = preBalance + order.coins
  596. CardRechargeRecord.add_record(
  597. card=card,
  598. group=Group.get_group(order.groupId),
  599. order=order,
  600. device=self.device)
  601. # 刷新卡里面的余额
  602. card.balance = balance
  603. card.lastMaxBalance = balance
  604. card.save()
  605. return True
  606. except Exception as e:
  607. order.update_after_recharge_ic_card(device=self.device,
  608. sendMoney=sendMoney,
  609. preBalance=preBalance,
  610. syncBalance=balance,
  611. result=ErrorCode.EXCEPTION,
  612. description=e.message)
  613. return False
  614. except Exception as e:
  615. logger.exception(e)
  616. return False
  617. finally:
  618. Card.set_card_status(str(card.id), 'idle')
  619. def response_id_card(self):
  620. cardNo = self.event_data['card_no']
  621. Card.record_dev_card_no(self.device['devNo'], cardNo)
  622. card = Card.objects.filter(cardNo=cardNo, agentId=self.device.owner.agentId).first()
  623. # 如果没有卡,直接返回
  624. if card is None:
  625. return self.deviceAdapter.response_card_balance(cardNo=cardNo, balance=0, result=card_not_in_db, amount=0)
  626. if card.cardType == '':
  627. card.update(cardType='ID')
  628. elif card.cardType != 'ID': # 如果以前是离线卡,只能用于离线卡,需要提醒用户
  629. return self.deviceAdapter.response_card_balance(cardNo=cardNo, balance=0, result=card_type_is_ic, amount=0)
  630. if card.frozen:
  631. return self.deviceAdapter.response_card_balance(cardNo=cardNo, balance=0, result=card_is_forzen, amount=0)
  632. card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id))
  633. if card_recharge_order:
  634. result = self.recharge_id_card(card=card,
  635. rechargeType='append',
  636. order=card_recharge_order)
  637. card.reload()
  638. oper = self.event_data.get('reduce')
  639. fee = RMB(self.device['otherConf'].get('id_card_oncefee', 1))
  640. if oper:
  641. if card.balance >= fee:
  642. result = card_is_normal
  643. balance = card.balance - fee
  644. # balance = card.showBalance * 100
  645. else:
  646. result = card_less_balance
  647. # balance = card.showBalance * 100
  648. balance = card.balance
  649. self.deviceAdapter.response_card_balance(cardNo, fee, balance, result) # 返回的cardNo为16进制
  650. else:
  651. self.deviceAdapter.response_card_balance(cardNo, fee, card.balance, card_is_normal)