dc_plug.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. # -*- coding: utf-8 -*-
  2. #!/usr/bin/env python
  3. import datetime
  4. import logging
  5. from mongoengine import DoesNotExist
  6. from apilib.monetary import RMB, VirtualCoin
  7. from apilib.utils_string import make_title_from_dict
  8. from apilib.utils_sys import memcache_lock
  9. from apps.web.constant import Const
  10. from apps.web.dealer.models import Dealer
  11. from apps.web.device.models import WeifuleDeviceOrder, Group, Device
  12. from apps.web.eventer import EventBuilder
  13. from apps.web.eventer.base import WorkEvent
  14. from apps.web.user.models import ServiceProgress, UserVirtualCard, VCardConsumeRecord, \
  15. ConsumeRecord, Card, CardConsumeRecord, CardRechargeOrder, MyUser
  16. from apps.web.user.transaction_deprecated import refund_money
  17. logger = logging.getLogger(__name__)
  18. created_order_32 = 32
  19. executing_order_33 = 33
  20. finished_order_34 = 34
  21. id_card_request_35 = 35
  22. card_is_normal = 1
  23. card_not_in_db = 2
  24. card_is_forzen = 3
  25. card_has_not_order = 4 #IC卡适用
  26. card_less_balance = 5 #ID卡适用
  27. card_type_is_ic = 6#IC卡不允许当做ID卡使用
  28. class builder(EventBuilder):
  29. def __getEvent__(self, device_event):
  30. event_data = self.deviceAdapter.analyze_event_data(device_event['data'])
  31. if event_data is None:
  32. return None
  33. if event_data['fun_code'] in [created_order_32,executing_order_33,finished_order_34,id_card_request_35]:
  34. return ChargingWorkEvent(self.deviceAdapter, event_data)
  35. class ChargingWorkEvent(WorkEvent):
  36. #微付乐的板子:根据设备上报上来的订单,创建正在服务的信息
  37. def create_progress_for_weifule_order(self,dev,consumeRcd,order):
  38. try:
  39. progress = ServiceProgress.objects.filter(weifuleOrderNo = order['id']).first()
  40. if progress:
  41. return
  42. port = int(order['port_id'])
  43. attachParas = consumeRcd.attachParas
  44. needKind,needValue,unit = self.analyse_need_from_package(order)
  45. consumeOrder = {
  46. 'orderNo': consumeRcd.orderNo,
  47. 'coin': consumeRcd.coin.mongo_amount,
  48. 'money': consumeRcd.money.mongo_amount,
  49. 'unit': unit,
  50. 'consumeType':'mobile_vcard' if u'虚拟卡' in consumeRcd.remarks else 'mobile'
  51. }
  52. if needKind:
  53. consumeOrder.update({needKind:needValue})
  54. new_service_progress = ServiceProgress(
  55. open_id=consumeRcd.openId,
  56. device_imei=consumeRcd.devNo,
  57. devTypeCode=dev['devType']['code'],
  58. port=port,
  59. attachParas=attachParas if attachParas else {},
  60. start_time=int(order['create_time']),
  61. finished_time=24*60*60,
  62. consumeOrder = consumeOrder,
  63. expireAt = datetime.datetime.now() + datetime.timedelta(days = 91)
  64. )
  65. new_service_progress.save()
  66. except Exception as e:
  67. logger.exception(e)
  68. def consume_money_for_ID_card(self,agentId,cardNo,balance,money):
  69. card = Card.objects.get(agentId=agentId,cardNo = cardNo)
  70. if card.balance < money:
  71. card.balance = RMB(0)
  72. else:
  73. card.balance = (card.balance - money)
  74. # card.showBalance = card.balance #只有微付乐是创建订单成功后才真正扣费
  75. try:
  76. card.save()
  77. except Exception as e:
  78. logger.exception(e)
  79. return card
  80. def update_balance_for_IC_card(self,card,balance):
  81. card.balance = balance
  82. try:
  83. card.save()
  84. except Exception,e:
  85. pass
  86. def is_order_accepted(self,orderNo,funCode):
  87. return True if WeifuleDeviceOrder.objects.filter(orderNo = orderNo,funCode = funCode).count() > 0 else False
  88. def record_order_event(self,order,funCode):
  89. newObj = WeifuleDeviceOrder(orderNo = order['id'],funCode = funCode,order = order)
  90. try:
  91. newObj.save()
  92. except Exception,e:
  93. logger.exception('save order event error=%s' % e)
  94. pass
  95. def record_consume_for_card(self, card, money, orderNo):
  96. group = Group.get_group(self.device['groupId'])
  97. address = group['address']
  98. group_number = self.device['groupNumber']
  99. now = datetime.datetime.now()
  100. new_record = {
  101. 'orderNo': orderNo,
  102. 'time': now.strftime("%Y-%m-%d %H:%M:%S"),
  103. 'dateTimeAdded': now,
  104. 'openId': card.openId,
  105. 'ownerId': self.device['ownerId'],
  106. 'coin': money.mongo_amount,
  107. 'money': money.mongo_amount,
  108. 'devNo': self.device['devNo'],
  109. 'logicalCode': self.device['logicalCode'],
  110. 'groupId': self.device['groupId'],
  111. 'address': address,
  112. 'groupNumber': group_number,
  113. 'groupName': group['groupName'],
  114. 'devTypeCode': self.device.devTypeCode,
  115. 'devTypeName': self.device.devTypeName,
  116. 'isNormal': True,
  117. 'status': ConsumeRecord.Status.RUNNING,
  118. 'remarks': u'刷卡消费',
  119. 'errorDesc': '',
  120. 'sequanceNo': '',
  121. 'desc': '',
  122. 'attachParas': {},
  123. 'servicedInfo': {}
  124. }
  125. ConsumeRecord.get_collection().insert_one(new_record)
  126. # 刷卡消费也记录一条数据
  127. new_card_record = {
  128. 'orderNo': orderNo,
  129. 'openId': card.openId,
  130. 'cardId': str(card.id),
  131. 'money': money.mongo_amount,
  132. 'balance': card.balance.mongo_amount,
  133. 'devNo': self.device['devNo'],
  134. 'devType': self.device['devType']['name'],
  135. 'logicalCode': self.device['logicalCode'],
  136. 'groupId': self.device['groupId'],
  137. 'address': address,
  138. 'groupNumber': group_number,
  139. 'groupName': group['groupName'],
  140. 'result': 'success',
  141. 'remarks': u'刷卡消费',
  142. 'sequanceNo': '',
  143. 'dateTimeAdded': datetime.datetime.now(),
  144. 'desc': '',
  145. 'servicedInfo': {},
  146. 'linkedConsumeRcdOrderNo':str(new_record['orderNo'])
  147. }
  148. CardConsumeRecord.get_collection().insert(new_card_record)
  149. return new_record['orderNo'], new_card_record['orderNo']
  150. def __translate_reason(self,cause,chrmt):
  151. if cause == 7:
  152. if chrmt == 0:
  153. return u'订购时间已经用完。'
  154. elif chrmt == 2:
  155. return u'订购电量已经用完。'
  156. elif cause == 5:
  157. return u'支付的金额已经使用完毕'
  158. elif cause == 6:
  159. return u'用户手工停止了充电'
  160. elif cause == 8:
  161. return u'故障导致充电停止'
  162. elif cause == 9:
  163. return u'本端口功率过载,主动关闭'
  164. elif cause == 10:
  165. return u'刷卡结束'
  166. elif cause == 11:
  167. return u'没有连接充电器,主动关闭'
  168. elif cause == 12:
  169. return u'远程关闭'
  170. elif cause == 13:
  171. return u'检测到烟雾告警,主动关闭'
  172. elif cause == 20:
  173. return u'端口功率过小。可能是电池已经充满,也可能是所接负载功率太小'
  174. return u'充电结束。'
  175. def response_id_card(self,msgDict):
  176. data = msgDict
  177. cardNo = str(data['card_no'])
  178. Card.record_dev_card_no(self.device['devNo'],cardNo)
  179. card = self.find_card_by_card_no(cardNo)
  180. # 如果没有卡,直接返回
  181. # cardNo,portId,balance,result
  182. if card is None:
  183. return self.deviceAdapter.response_card_event(cardNo=data['card_no'], portId = data['port_id'], balance = 0,result=card_not_in_db)
  184. elif card.frozen:
  185. return self.deviceAdapter.response_card_balance(cardNo=data['card_no'], portId = data['port_id'],balance=0, result=card_is_forzen)
  186. if card.cardType == 'IC':#如果以前是离线卡,只能用于离线卡,需要提醒用户
  187. return self.deviceAdapter.response_card_balance(cardNo=data['card_no'], portId = data['port_id'],balance=0, result=card_is_forzen)
  188. card = self.update_card_dealer_and_type(cardNo, 'ID')
  189. #: 首先检查订单,并进行充值
  190. #: 用户首先在手机客户端充值,需要这个时刻刷卡上报事件将充值的订单同步上来
  191. card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id))
  192. if card_recharge_order:
  193. result = self.recharge_id_card(card = card,
  194. rechargeType = 'append',
  195. order = card_recharge_order)
  196. card.reload()
  197. self.deviceAdapter.response_card_event(data['card_no'], data['port_id'], card.balance)#返回的cardNo为16进制
  198. #确定发送成功了,然后再保存到数据库中
  199. card.save()
  200. def analyse_need_from_package(self,order):
  201. needKind,needValue,unit = 'needElec',0,u'度'
  202. if order['chrmt'] == 0:
  203. needKind = 'needTime'
  204. needValue = order['amount']/60
  205. unit = u'分钟'
  206. elif order['chrmt'] == 2:
  207. needValue = order['amount']/1000000.0
  208. else:
  209. needKind = 'needPower'
  210. needValue = order['amount']/60
  211. unit = u'分钟'
  212. return needKind,needValue,unit
  213. def do(self, **args):
  214. devNo = self.device['devNo']
  215. funCode = self.event_data['fun_code']
  216. if funCode == id_card_request_35:
  217. self.response_id_card(self.event_data)
  218. else:#处理任务事件
  219. order = self.event_data['order']
  220. logger.info('weifule charging event detected, devNo=%s' % (devNo,))
  221. with memcache_lock(key = '%s-%s-%s-finished' % (devNo, order['id'], funCode), value = '1',
  222. expire = 120) as acquired:
  223. if acquired:
  224. self.deal_order(devNo, funCode, order, self.event_data)
  225. def deal_order(self,devNo,funCode,order,msgDict):
  226. dealer = Dealer.get_dealer(self.device['ownerId'])
  227. #开始充电最重要的功能核对订单,以及记录订单。
  228. #扫码情况下,需要检查订单,如果订单成功了,就不需要核查,如果订单失败的,需要核查,然后把没有扣的钱扣掉
  229. #刷卡情况下,正式把刷卡的钱扣掉,并产生消费的订单。
  230. #投币情况下,把投币扣费的记录记录下来即可
  231. #首先,统一回复确认报文
  232. if funCode in [created_order_32,executing_order_33,finished_order_34,id_card_request_35]:
  233. try:
  234. self.deviceAdapter.ack_event(order['id'], funCode)
  235. except Exception,e:
  236. logger.info('ack event devNo=%s err=%s' % (devNo,e))
  237. pass
  238. if funCode == created_order_32 :#订单创建成功,开始收单,并检查账单
  239. #回收余额也会有订单,但是没有端口,不需要处理,直接pass掉
  240. if order['order_type'] == 'card_refund':
  241. return
  242. #首先把端口状态给刷新掉(虽然是任务在设备上排队的通知,端口肯定是要忙起来的,先搞成忙)
  243. Device.update_dev_control_cache(self.device['devNo'],{
  244. str(order['port_id']):{'status':Const.DEV_WORK_STATUS_WORKING}
  245. }
  246. )
  247. if order['order_type'] == 'card_start':#刷卡
  248. cardNo = str(order['card_no'])
  249. #登记下卡相关信息
  250. onceCard = Device.objects.get(devNo = devNo).otherConf.get('onceCard',100)
  251. card = self.update_card_dealer_and_type(cardNo, 'ID')
  252. balance = card.balance - RMB(order['coins']/100.0)
  253. self.update_card_balance(card, balance)
  254. #卡的显示余额同步下
  255. card.showBalance = card.balance
  256. card.save()
  257. fee = RMB(onceCard/10.0)
  258. # 记录卡消费记录以及消费记录
  259. orderNo, cardOrderNo = self.record_consume_for_card(card, fee,order['id'])
  260. # 记录当前服务的progress,便于手机界面查询进度
  261. needKind,needValue,unit = self.analyse_need_from_package(order)
  262. consumeDict = {'orderNo': orderNo,'money': fee,'coin': onceCard/10.0,'cardOrderNo': cardOrderNo,needKind:needValue,'unit':unit}
  263. ServiceProgress.register_card_service_for_weifule(self.device, order['port_id'], card, consumeDict)
  264. # 通知用户,已经扣费
  265. self.notify_balance_has_consume_for_card(card, fee)
  266. elif order['order_type'] == 'apps_start':#扫码
  267. #检查订单是否失败,如果是失败的,需要纠正为成功,并把用户的余额扣除掉
  268. consumeRcd = ConsumeRecord.objects.filter(orderNo = order['id']).first()
  269. if consumeRcd is None or consumeRcd.isNormal:
  270. return
  271. consumeRcd.isNormal = True
  272. consumeRcd.save()
  273. if u'虚拟卡' in consumeRcd.remarks:
  274. pass#虚拟卡,不管成功与否都已经先扣掉了配额(有些不合理),这里什么都不处理
  275. else:
  276. user = MyUser.objects.get(openId = consumeRcd.openId,groupId = consumeRcd.groupId)
  277. user.pay(VirtualCoin(order['coins']))
  278. self.create_progress_for_weifule_order(self.device,consumeRcd,order)
  279. #如果是启动了订单,需要更新端口状态,需要刷新服务状态
  280. elif funCode == executing_order_33:
  281. progress = ServiceProgress.objects.filter(weifuleOrderNo = order['id']).first()
  282. if (not progress) or (not order.has_key('port_id')):#只有卡充值的时候,order才没有port
  283. return
  284. Device.update_dev_control_cache(self.device['devNo'],{
  285. str(order['port_id']):{
  286. 'status':Const.DEV_WORK_STATUS_WORKING,
  287. 'openId':progress.open_id,
  288. 'startTime':datetime.datetime.now().strftime(Const.DATETIME_FMT)
  289. }
  290. }
  291. )
  292. progress.status = 'running'
  293. progress.runningTime = datetime.datetime.now()
  294. progress.save()
  295. #订单结束,需要把进行账单核对、记录更新、通知、退费等;
  296. elif funCode == finished_order_34:
  297. #如果是离线卡的余额回收,修改下余额,然后直接返回即可
  298. if order['order_type'] == 'card_refund':
  299. card = Card.objects.filter(agentId = dealer['agentId'],cardNo = str(order['card_no'])).first()
  300. if card is None:
  301. return
  302. else:
  303. card.balance = RMB(order['balance']/100.0)
  304. card.save()
  305. return
  306. order['reason'] = self.__translate_reason(order['cause'],order['chrmt'])
  307. progress = ServiceProgress.objects.filter(weifuleOrderNo = order['id']).first()
  308. if progress is None:
  309. return
  310. progress.status = 'finished'
  311. progress.isFinished = True
  312. progress.save()
  313. consumeRcd = ConsumeRecord.objects.filter(orderNo = order['id']).first()
  314. if not consumeRcd:#投币的不会生成订单
  315. return
  316. try:
  317. group = Group.get_group(self.device['groupId'])
  318. consumeDict = {
  319. 'reason': order['reason'],
  320. 'chargeIndex': order['port_id'],
  321. 'duration': round(order['time']/60.0,1),
  322. 'elec':round(order['elec']/1000000.0,2)
  323. }
  324. notifyDict = {
  325. 'service': u'充电服务完成',
  326. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  327. 'remark': u'谢谢您的支持'
  328. }
  329. refundDict = {
  330. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  331. }
  332. coins = consumeRcd.coin
  333. if order['chrmt'] in [0,1]:#按照时间计费
  334. needTime = order['amount']/60
  335. usedTime = order['time']/60
  336. leftTime = order['left_time']/60 if order['left_time'] > 0 else 0
  337. leftTimeStr = leftTime
  338. actualNeedTime = usedTime + leftTime
  339. consumeDict.update({
  340. 'needTime':needTime,
  341. 'leftTime': leftTimeStr,
  342. 'actualNeedTime': u'动态功率计算为%s分钟' % actualNeedTime,
  343. })
  344. titleDictList = [
  345. {u'设备编号':self.device['logicalCode']},
  346. {u'端口':order['port_id']},
  347. {u'地址':group['address']},
  348. {u'结束原因':order['reason']},
  349. {u'订购时间':u'%s分钟' % needTime},
  350. {u'应充时间':u'%s分钟' % actualNeedTime},
  351. {u'充电时间':u'%s分钟' % usedTime},
  352. {u'剩余时间':u'%s分钟' % leftTimeStr}
  353. ]
  354. notifyDict.update(
  355. {
  356. 'title': make_title_from_dict(titleDictList)
  357. }
  358. )
  359. backCoins = coins * (float(leftTime) / float(actualNeedTime))
  360. titleDictList = [
  361. {u'设备编号':self.device['logicalCode']},
  362. {u'端口':order['port_id']},
  363. {u'付款':u'%s元' % coins},
  364. {u'预定时间':u'%s分钟' % needTime},
  365. {u'应充时间':u'%s分钟' % actualNeedTime},
  366. {u'充电时间':u'%s分钟' % usedTime},
  367. {u'剩余时间':u'%s分钟' % leftTimeStr},
  368. ]
  369. refundDict.update(
  370. {
  371. 'title':make_title_from_dict(titleDictList),
  372. 'backCount': u'金币:%s' % backCoins
  373. }
  374. )
  375. else:
  376. needElec = order['amount']/1000000.0
  377. usedElec = order['elec']/1000000.0
  378. leftElec = needElec-usedElec if needElec > usedElec else 0.0
  379. titleDictList = [
  380. {u'设备编号':self.device['logicalCode']},
  381. {u'端口':order['port_id']},
  382. {u'结束原因':order['reason']},
  383. {u'订购电量':u'%s度' % needElec},
  384. {u'消耗电量':u'%s度' % usedElec},
  385. {u'剩余电量':u'%s分钟' % leftElec},
  386. ]
  387. notifyDict.update(
  388. {
  389. 'title': make_title_from_dict(titleDictList)
  390. }
  391. )
  392. backCoins = coins * (float(leftElec) / float(needElec))
  393. titleDictList = [
  394. {u'设备编号':self.device['logicalCode']},
  395. {u'端口':order['port_id']},
  396. {u'结束原因':order['reason']},
  397. {u'订购电量':u'%s度' % needElec},
  398. {u'消耗电量':u'%s度' % usedElec},
  399. {u'剩余电量':u'%s度' % leftElec},
  400. ]
  401. refundDict.update(
  402. {
  403. 'title':make_title_from_dict(titleDictList),
  404. 'backCount': u'金币:%s' % backCoins
  405. }
  406. )
  407. if u'虚拟卡' in consumeRcd.remarks:
  408. #退额度
  409. try:
  410. vRcd = VCardConsumeRecord.objects.get(orderNo = order['id'])
  411. vCard = UserVirtualCard.objects.get(id = vRcd.cardId)
  412. except DoesNotExist,e:
  413. logger.info('can not find the vCard id = %s' % vRcd.cardId)
  414. return
  415. #通知服务结束
  416. notifyOpenId = self.get_managerialOpenId_by_openId(vRcd.openId) if vCard else ''
  417. self.notify_user(notifyOpenId, 'service_complete',**notifyDict)
  418. #不需要退款,直接返回,不通知
  419. if self.device.is_auto_refund:
  420. if order['chrmt'] in [0,1]:
  421. vCard.refund_quota(vRcd,usedTime,0.0,backCoins.mongo_amount)
  422. else:
  423. vCard.refund_quota(vRcd,0.0,usedElec,backCoins.mongo_amount)
  424. #刷卡的方式
  425. elif order['order_type'] == 'card_start' and order['card_no']:
  426. dealer = Dealer.get_dealer(ownerId = self.device['ownerId'])
  427. card = Card.objects.filter(agentId = dealer['agentId'],cardNo = str(order['card_no'])).first()
  428. if card is None:#离线卡没有绑定或者在线卡被解绑了
  429. return
  430. #先通知
  431. notifyOpenId = card.managerialOpenId if card else ''
  432. self.notify_user(notifyOpenId, 'service_complete',**notifyDict)
  433. #不需要退款,直接返回.在线卡需要退费,离线卡只登记使用记录就OK
  434. if self.device.is_auto_refund and card.cardType == 'ID':
  435. card = self.refund_money_for_card(RMB(backCoins), card.id)
  436. card.showBalance = card.balance
  437. card.save()
  438. consumeDict.update({'refundedMoney': str(backCoins)})
  439. self.notify_user(notifyOpenId, 'refund_coins', **refundDict)
  440. else:#扫码的
  441. user = MyUser.objects(openId = consumeRcd.openId, groupId = self.device['groupId']).first()
  442. if not user:
  443. return
  444. #通知服务结束
  445. notifyOpenId = user.managerialOpenId if user else ''
  446. self.notify_user(notifyOpenId, 'service_complete',**notifyDict)
  447. #如果需要退款,计算退款数据.
  448. if self.device.is_auto_refund:
  449. consumeDict.update({'refundedMoney': str(backCoins)})
  450. refund_money(self.device, backCoins, consumeRcd.openId)
  451. self.notify_user(notifyOpenId, 'refund_coins', **refundDict)
  452. ServiceProgress.update_progress_and_consume_rcd(self.device['ownerId'],{'weifuleOrderNo':order['id']}, consumeDict)
  453. except Exception,e:
  454. logger.exception('some exception happed,devNo=%s,e=%s' % (devNo,e))
  455. finally:
  456. Device.clear_port_control_cache(devNo,order['port_id'])
  457. self.deviceAdapter.get_port_status_from_dev()
  458. else:
  459. pass