jndz.py 93 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import logging
  5. import time
  6. from decimal import Decimal
  7. from mongoengine import DoesNotExist
  8. from apilib.monetary import RMB, VirtualCoin
  9. from apilib.utils_datetime import to_datetime
  10. from apilib.utils_string import make_title_from_dict
  11. from apps.web.agent.models import Agent
  12. from apps.web.api.models import APIStartDeviceRecord
  13. from apps.web.api.ykt_north.models import YiKaTongCard
  14. from apps.web.common.models import TempValues
  15. from apps.web.constant import Const, DEALER_CONSUMPTION_AGG_KIND
  16. from apps.web.core.device_define.jndz import CMD_CODE, SWIPE_CARD_PARAM_OP, SWIPE_CARD_RES, RESULT_CODE, CARD_TYPE, \
  17. CARD_DEDUCTTYPE,CARD_REFUNDTYPE
  18. from apps.web.core.exceptions import ServiceException
  19. from apps.web.dealer.models import Dealer
  20. from apps.web.device.models import Group, Device, DeviceDict
  21. from apps.web.eventer import EventBuilder
  22. from apps.web.eventer.base import FaultEvent, WorkEvent
  23. from apps.web.eventer.errors import InvalidOption, NoCommandHandlerAvailable, RequestInvalid
  24. from apps.web.south_intf.platform import notify_event_to_north, notify_event_to_north_v2
  25. from apps.web.south_intf.zhejiang_fire import send_event_to_zhejiang
  26. from apps.web.south_intf.zhongtian import report_zhongtian_service_complete, report_zhongtian_refund
  27. from apps.web.user.models import ServiceProgress, CardRechargeOrder, MyUser, \
  28. UserVirtualCard, Card, ConsumeRecord, VCardConsumeRecord, Redpack
  29. logger = logging.getLogger(__name__)
  30. class builder(EventBuilder):
  31. def __getEvent__(self, device_event):
  32. event_data = self.deviceAdapter.analyze_event_data(device_event)
  33. if event_data is None or 'cmdCode' not in event_data:
  34. return None
  35. if event_data['cmdCode'] in [
  36. CMD_CODE.DEVICE_SUBMIT_CHARGE_FINISHED_06,
  37. CMD_CODE.DEVICE_SUBMIT_CHARGE_FINISHED_v2_16
  38. ]:
  39. return ChargingJNDZWorkEvent(self.deviceAdapter, event_data)
  40. if event_data['cmdCode'] == CMD_CODE.DEVICE_SUBMIT_OFFLINE_COINS_20:
  41. # 如果支持一卡通,走一卡通的流程
  42. if self.device.support_dev_type_features('support_wvtiykt'):
  43. return JNDZYKTCardChargeEvent(self.deviceAdapter, event_data)
  44. else:
  45. return ChargingJNDZWorkEvent(self.deviceAdapter, event_data)
  46. if event_data['cmdCode'] in [
  47. # todo 0A的告警不要了,先注释掉,后续删除
  48. # CMD_CODE.DEVICE_SUBMIT_DEVICE_FAULT_0A,
  49. CMD_CODE.DEVICE_FAULT_TEMPERATURE,
  50. CMD_CODE.DEVICE_FAULT_POWER,
  51. CMD_CODE.DEVICE_FAULT_SMOKE,
  52. CMD_CODE.DEVICE_ELEC,
  53. CMD_CODE.DEVICE_FAULT_ALTER
  54. ]:
  55. return JNDZEventerFailure(self.deviceAdapter, event_data)
  56. # 只有和动的新的刷卡流程才支持此指令
  57. if event_data['cmdCode'] in [CMD_CODE.DEVICE_CARD_CHARGE_2D]:
  58. if self.device.support_dev_type_features('support_wvtiykt'):
  59. return JNDZYKTCardChargeEvent(self.deviceAdapter, event_data)
  60. else:
  61. return JNDZNewCardChargeEvent(self.deviceAdapter, event_data)
  62. # 对于0x10指令 新旧的处理流程是完全不一样的
  63. if event_data['cmdCode'] in [CMD_CODE.SWIPE_CARD_10]:
  64. if self.deviceAdapter.device.devTypeCode == Const.DEVICE_TYPE_CODE_CHARGING_HD:
  65. return JNDZNewCardChargeEvent(self.deviceAdapter, event_data)
  66. elif self.deviceAdapter.device.driverCode == Const.DEVICE_TYPE_CODE_CHARGE_DOUBLE_SERIAL_JH:
  67. return JNDZDoubleSerialCardChargeEvent(self.deviceAdapter, event_data)
  68. elif self.device.support_dev_type_features('support_wvtiykt'):
  69. return JNDZYKTCardChargeEvent(self.deviceAdapter, event_data)
  70. else:
  71. return ChargingJNDZWorkEvent(self.deviceAdapter, event_data)
  72. if event_data['cmdCode'] in [CMD_CODE.DEVICE_REAL_TIME_REPORT_21]:
  73. return ChargingJNDZReportEvent(self.deviceAdapter, event_data)
  74. if event_data['cmdCode'] in [CMD_CODE.SWIPE_CARD_50,CMD_CODE.SWIPE_CARD_52,CMD_CODE.SWIPE_CARD_56]:
  75. return JNDZVirtualCardWorkEvent(self.deviceAdapter, event_data)
  76. class JNDZEventerFailure(FaultEvent):
  77. def do(self, **args):
  78. cmdCode = self.event_data.get("cmdCode")
  79. faultType = self.event_data.get(u"fault")
  80. desc = self.event_data.get("desc", "")
  81. # todo 0A的告警不要了,先注释掉,后续删除
  82. # 保证原有的故障处理逻辑不变
  83. # if cmdCode == CMD_CODE.DEVICE_SUBMIT_DEVICE_FAULT_0A:
  84. # return self.handler_0A_fault()
  85. # 这些都是整机告警
  86. part = "0"
  87. warningData = {
  88. "warningStatus": 2,
  89. "warningDesc": desc,
  90. "warningTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  91. }
  92. Device.update_dev_warning_cache(self.device.devNo, {part: warningData})
  93. group = Group.get_group(self.device.groupId)
  94. titleList = [
  95. {u"告警名称": faultType},
  96. {u"地址名称": group["groupName"]}
  97. ]
  98. title = make_title_from_dict(titleList)
  99. # 接下来的都是整机告警,这个地方需要通知到经销商
  100. self.notify_dealer(
  101. "device_fault",
  102. title=title,
  103. device=u"{}号设备".format(self.device.logicalCode),
  104. fault=faultType,
  105. notifyTime=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  106. location=u"{}".format(group["groupName"])
  107. )
  108. # 记录错误故障
  109. fault_record = self.record(
  110. faultCode=cmdCode,
  111. description=desc,
  112. title=faultType,
  113. detail={"faultType": faultType, "errorCode": self.event_data.get("errorCode")}
  114. )
  115. self.north_to_liangxi(fault_record)
  116. # todo 0A的告警不要了,先注释掉,后续删除
  117. # def handler_0A_fault(self):
  118. # faultContent = self.event_data.get('faultContent', '')
  119. # level = self.event_data.get('level', '')
  120. # errCode = self.event_data.get('errCode')
  121. #
  122. # port = self.event_data.get('port')
  123. # if port and port != 255:
  124. # title = u'注意!您的设备{}号端口发出告警!'.format(port)
  125. # part = str(port)
  126. # else:
  127. # title = u'注意!您的设备发出告警!'
  128. # part = "0"
  129. #
  130. # if errCode in ['A3']: # 空载 无需显示在经销商后台
  131. # return
  132. #
  133. # elif errCode in ['00']: # 老设备上报继电器粘连 100206 不上报!!
  134. # return
  135. #
  136. # # 设备告警打入缓存
  137. # elif errCode in ['A0', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9', 'AA']:
  138. # warningData = {
  139. # "warningStatus": 2,
  140. # "warningDesc": faultContent,
  141. # "warningTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  142. # }
  143. # Device.update_dev_warning_cache(self.device.devNo, {part: warningData})
  144. #
  145. # # 设备告警清除
  146. # elif errCode in ['20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '2A']: # 恢复信号 不操作
  147. # Device.clear_part_warning_cache(self.device.devNo, part)
  148. # # ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  149. # # if 'statusInfo' in ctrInfo and 'errCode' in ctrInfo:
  150. # # if ctrInfo['errCode'][-1] == errCode[-1]:
  151. # # ctrInfo['status'] = None
  152. # # ctrInfo['errCode'] = None
  153. # # ctrInfo['faultContent'] = None
  154. # # ctrInfo['level'] = None
  155. # # ctrInfo['statusInfo'] = None
  156. # # ctrInfo['cmdCode'] = None
  157. # # Device.update_dev_control_cache(self.device['devNo'], ctrInfo)
  158. #
  159. # else:
  160. # pass
  161. # # Device.update_dev_control_cache(self.device['devNo'], self.event_data)
  162. #
  163. # # 记录错误故障
  164. # self.record(
  165. # title=title,
  166. # description=faultContent,
  167. # level=level
  168. # )
  169. #
  170. # group = Group.get_group(self.device.groupId)
  171. #
  172. # if self.is_notify_dealer:
  173. # self.notify_dealer(
  174. # templateName="device_fault",
  175. # title=title,
  176. # device=u'{}-{}'.format(self.device.devTypeName, self.device.logicalCode),
  177. # fault=faultContent,
  178. # location=u'{}-{}-{}号设备({})'.format(group["address"], group["groupName"], self.device["groupNumber"],
  179. # self.device["logicalCode"]),
  180. # notifyTime=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  181. # )
  182. def is_server_refund(billingType, dev, dealer, agent):
  183. # type:(str, DeviceDict, Dealer, Agent)->bool
  184. if billingType != 'time':
  185. if "jhCardElecRefund" in dealer.features:
  186. return False
  187. if dev.is_auto_refund:
  188. return True
  189. else:
  190. return False
  191. else:
  192. if 'huopo_card_time_refund' in agent.features:
  193. return True
  194. else:
  195. support_server_refund = dev.devType.get('features', {}).get(
  196. 'support_server_refund', False)
  197. if not support_server_refund:
  198. return False
  199. if dev.is_auto_refund:
  200. return True
  201. else:
  202. return False
  203. swipe_card_cache_key = lambda devNo: "swipe_card_{}".format(devNo)
  204. class ChargingJNDZWorkEvent(WorkEvent):
  205. def support_playback(self):
  206. return self.event_data['cmdCode'] in [
  207. CMD_CODE.DEVICE_SUBMIT_CHARGE_FINISHED_06,
  208. CMD_CODE.DEVICE_SUBMIT_CHARGE_FINISHED_v2_16
  209. ]
  210. def __get_swipe_card_cache(self):
  211. return TempValues.get(swipe_card_cache_key(self.device.devNo))
  212. def __set_swipe_card_cache(self, cache_info):
  213. logger.debug('swipe cache info is: {}'.format(cache_info))
  214. return TempValues.set(swipe_card_cache_key(self.device.devNo), cache_info, 900)
  215. def __can_used_virtual_card(self, card, package):
  216. dealer = self.device.owner
  217. agent = Agent.objects.get(id=dealer.agentId)
  218. features = agent.features
  219. billingType = self.device.my_obj.otherConf.get('billingType', 'time')
  220. if not is_server_refund(billingType, self.device, self.device.owner, agent):
  221. return False, None
  222. else:
  223. if "vCardNeedBind" in features:
  224. virtual_card = card.bound_virtual_card
  225. else:
  226. virtual_card = card.related_virtual_card
  227. if not virtual_card:
  228. return False, None
  229. if virtual_card.can_use_today(package):
  230. return True, virtual_card
  231. else:
  232. return False, virtual_card
  233. def __do_new_card_proc_10(self):
  234. """
  235. 刷卡扣费的 新流程 预扣费 实际只是检查扣费 并没有真正的扣除
  236. :return:
  237. """
  238. cardNo = self.event_data['cardNo']
  239. preFee = RMB(self.event_data['preFee'])
  240. card = self.update_card_dealer_and_type(cardNo) # type: Card
  241. if not card:
  242. return self.response_use_card(SWIPE_CARD_RES.INVALID_CARD_02, 0)
  243. card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id))
  244. result = self.recharge_id_card(
  245. card=card,
  246. rechargeType='append',
  247. order=card_recharge_order
  248. )
  249. card.reload()
  250. logger.info('JingNengDianZi cmdNo(10) - cardNo=%s, openId=%s, result=%s, preFee=%s, curinfo=%s' % (
  251. cardNo, card.openId, result, preFee, self.event_data))
  252. if card.openId == '' or card.frozen:
  253. return self.response_use_card(SWIPE_CARD_RES.INVALID_CARD_02, 0)
  254. once_coins = float(preFee)
  255. once_time = int(self.device.my_obj.otherConf.get('cardMin', 180))
  256. # 获取上一次的刷卡缓存
  257. swipe_cache_info = self.__get_swipe_card_cache()
  258. # 对比刷卡缓存的卡ID 更新金额
  259. if swipe_cache_info and swipe_cache_info['cardId'] == str(card.id):
  260. ts = swipe_cache_info['ts']
  261. if ts >= (int(time.time()) - 600):
  262. total_coins = once_coins + swipe_cache_info['coins']
  263. total_time = once_time + swipe_cache_info['time']
  264. else:
  265. total_coins = once_coins
  266. total_time = once_time
  267. else:
  268. total_coins = once_coins
  269. total_time = once_time
  270. # 判断当前的支付金额 是否能被虚拟卡支付
  271. is_enough, virtual_card = self.__can_used_virtual_card(card, {
  272. 'coins': float(total_coins),
  273. 'unit': u'分钟',
  274. 'time': total_time
  275. })
  276. if not is_enough:
  277. if card.balance >= RMB(total_coins):
  278. is_enough = True
  279. leftBalance = max(float(card.balance) - total_coins, 0)
  280. else:
  281. leftBalance = 0
  282. if is_enough:
  283. rv = {
  284. 'cardId': str(card.id),
  285. 'coins': total_coins,
  286. 'ts': int(time.time()),
  287. 'time': total_time
  288. }
  289. self.__set_swipe_card_cache(rv)
  290. logger.debug(
  291. '[card deduct]cardNo = {}; coins = {}; total time = {}'.format(
  292. card.cardNo, total_coins, total_time))
  293. return self.response_use_card(SWIPE_CARD_RES.SUCCESS_00, leftBalance)
  294. else:
  295. return self.response_use_card(SWIPE_CARD_RES.BALANCE_NOT_ENOUGH_01, leftBalance)
  296. def __do_new_card_proc_20(self):
  297. """
  298. 刷卡新流程真正扣费的地方
  299. :return:
  300. """
  301. swipe_cache_info = self.__get_swipe_card_cache()
  302. if not swipe_cache_info or 'cardId' not in swipe_cache_info:
  303. logger.warning('not find card id. ignore it.')
  304. return
  305. # 这一步暂时 不明白用意
  306. self.__set_swipe_card_cache({'cardId': swipe_cache_info['cardId'], 'ts': 0})
  307. card = Card.objects(id=str(swipe_cache_info['cardId'])).first() # type: Card
  308. if not card:
  309. logger.warning('card<id={}> is not exist.'.format(str(card.id)))
  310. return
  311. port = str(self.event_data['port'])
  312. package = {
  313. 'coins': self.event_data['coins'],
  314. 'unit': u'分钟',
  315. 'time': self.event_data['needTime'],
  316. }
  317. is_enough, virtual_card = self.__can_used_virtual_card(card, package)
  318. if virtual_card and is_enough:
  319. # 记录卡消费记录以及消费记录
  320. orderNo, cardConsumeOrderNo = self.record_consume_for_card(
  321. card=card,
  322. money=RMB(0.0),
  323. attachParas={
  324. 'chargeIndex': port
  325. },
  326. servicedInfo={
  327. 'needTime': self.event_data['needTime'],
  328. 'needElec': self.event_data['elec'],
  329. 'startUart': self.event_data.get('uartData', '')
  330. })
  331. vCardConsumeRecord = virtual_card.consume(openId=card.openId, group=self.device.group, dev=self.device,
  332. package=package, attachParas={}, nickname=card.cardName)
  333. if not vCardConsumeRecord:
  334. logger.error('use virtual card to consume failure. id = {}'.format(str(virtual_card.id)))
  335. return
  336. # 插入虚拟卡的ID 退费的时候使用
  337. self.event_data.update({'vCardId': str(virtual_card.id)})
  338. self.event_data.update({"consumeRcdId": str(vCardConsumeRecord.id)})
  339. self.notify_balance_has_consume_for_card(card, self.event_data['coins'], desc=u'使用绑定的虚拟卡')
  340. else:
  341. orderNo, cardConsumeOrderNo = self.record_consume_for_card(
  342. card=card,
  343. money=RMB(self.event_data['coins']),
  344. attachParas={
  345. "chargeIndex": port
  346. },
  347. servicedInfo={
  348. 'needTime': self.event_data['needTime'],
  349. 'needElec': self.event_data['elec'],
  350. 'startUart': self.event_data.get('uartData', '')
  351. })
  352. res, _ = self.consume_money_for_card(
  353. card=card,
  354. money=RMB(self.event_data['coins']))
  355. if res != 1:
  356. logger.warning("consume error!!!, cardNo is {}".format(card.cardNo))
  357. return
  358. self.notify_balance_has_consume_for_card(card, self.event_data['coins']) # 通知微信,已经扣费
  359. # 注册服务
  360. ServiceProgress.register_card_service(
  361. self.device,
  362. int(port),
  363. card,
  364. {
  365. 'orderNo': orderNo,
  366. 'money': self.event_data['coins'],
  367. 'coin': self.event_data['coins'],
  368. 'needTime': self.event_data['needTime'],
  369. 'cardOrderNo': cardConsumeOrderNo
  370. })
  371. #
  372. billingType = self.device.get("otherConf", dict()).get('billingType', 'time')
  373. self.event_data.update({'billingType': billingType})
  374. self.event_data.update({'cardId': str(card.id)})
  375. self.event_data.update({'openId': card.openId})
  376. cInfo = Device.get_dev_control_cache(self.device.devNo)
  377. lastPortInfo = cInfo.get(str(port), {})
  378. # 钱需要累计
  379. lastCoins = lastPortInfo.get('coins', 0.0)
  380. self.event_data.update({'coins': self.event_data['coins'] + lastCoins})
  381. # 电量需要累加
  382. lastNeedElec = lastPortInfo.get('needElec', 0.0)
  383. self.event_data.update({'needElec': self.event_data['elec'] + lastNeedElec})
  384. # 时间需要累加
  385. lastNeedTime = lastPortInfo.get('needTime', 0.0)
  386. self.event_data.update({'needTime': self.event_data['needTime'] + lastNeedTime})
  387. self.event_data.update({'startTime': datetime.datetime.now().strftime(Const.DATETIME_FMT)})
  388. self.event_data.update({'isStart': True, 'status': Const.DEV_WORK_STATUS_WORKING})
  389. Device.update_dev_control_cache(self.device['devNo'], {port: self.event_data})
  390. # self.__set_swipe_card_cache({})
  391. def __do_fault_record(self):
  392. """
  393. TODO 梁溪消防的 紧急处理 后续需要统一这个流程
  394. :return:
  395. """
  396. if self.event_data["reasonCode"] == "03":
  397. desc = u"设备超功率运行,当前已停止充电"
  398. title = u"端口功率超限"
  399. elif self.event_data["reasonCode"] == "0B":
  400. desc = u"设备或是端口出现问题,当前已停止充电"
  401. title = u"设备端口故障"
  402. else:
  403. return
  404. from apps.web.device.models import FaultRecord
  405. faultRecord = FaultRecord(
  406. logicalCode=self.device.logicalCode,
  407. imei=self.device.devNo,
  408. portNo=str(self.event_data["port"]),
  409. faultCode=self.event_data["reasonCode"],
  410. title=title,
  411. description="",
  412. dealerId=self.device.ownerId,
  413. groupName=self.device.group.get('groupName', ''),
  414. address=self.device.group.get('address', ''),
  415. detail={"faultType": desc, "errorCode": "16{}".format(self.event_data["reasonCode"])}
  416. ).save()
  417. from taskmanager.mediator import task_caller
  418. task_caller(
  419. 'send_to_xf_falut',
  420. devNo=self.device.devNo,
  421. faultId=str(faultRecord.id)
  422. )
  423. def do(self, **args):
  424. devNo = self.device.devNo
  425. logger.info('JingNengDianZi charging event detected, devNo=%s,curInfo=%s' % (devNo, self.event_data))
  426. cmdCode = self.event_data['cmdCode']
  427. #: 刷卡消费的,分为扣费和退费两种
  428. if cmdCode == CMD_CODE.SWIPE_CARD_10:
  429. # 简易配置 如果配置了新卡流程特性的 并且是扣费的指令的 走新的流程 这个判断的优先级 低于设备的特性
  430. if self.event_data['oper'] == SWIPE_CARD_PARAM_OP.DECR_00:
  431. if "support_new_card_proc" in self.dealer.features:
  432. return self.__do_new_card_proc_10()
  433. cardNo = self.event_data['cardNo']
  434. preFee = RMB(self.event_data['preFee'])
  435. #: 操作符,是充值还是减少 <- (00, 01)
  436. oper = self.event_data['oper']
  437. card = self.update_card_dealer_and_type(cardNo)
  438. #: 经销商限制ID卡, 如果满足直接return
  439. if not card:
  440. self.response_use_card(SWIPE_CARD_RES.INVALID_CARD_02, 0)
  441. return
  442. self.event_data['openId'] = card.openId
  443. self.event_data['cardId'] = str(card.id)
  444. self.event_data['startTime'] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  445. Device.update_dev_control_cache(devNo, self.event_data)
  446. #: 首先检查订单,并进行充值
  447. #: 用户首先在手机客户端充值,需要这个时刻刷卡上报事件将充值的订单同步上来
  448. card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id))
  449. result = self.recharge_id_card(card=card,
  450. rechargeType='append',
  451. order=card_recharge_order)
  452. card.reload()
  453. logger.info('JingNengDianZi cmdNo(10) - cardNo=%s, openId=%s, result=%s, preFee=%s, curinfo=%s' % (
  454. cardNo, card.openId, result, preFee, self.event_data))
  455. # TODO 这个地方为了同时满足劲能电子和霍珀的需求 先使用特性 vCardNeedBind 后续需要统一规则
  456. try:
  457. dealer = Dealer.get_dealer(card.dealerId)
  458. agent = Agent.objects.get(id=dealer.get("agentId"))
  459. features = agent.features
  460. except Exception as e:
  461. features = list()
  462. if "vCardNeedBind" in features:
  463. virtual_card = card.bound_virtual_card
  464. else:
  465. virtual_card = card.related_virtual_card
  466. # 如果虚拟卡已经绑定,需要检查下今天是否可用,如果可用,有限使用虚拟卡
  467. vCardCanUse = False
  468. package = {'coins': float(preFee), 'unit': u'分钟', 'time': 180}
  469. if virtual_card is not None and card.openId is not None:
  470. devObj = Device.objects.get(devNo=devNo)
  471. cardMin = devObj.otherConf.get('cardMin', None)
  472. if cardMin is not None:
  473. package = {'coins': float(preFee), 'unit': u'分钟', 'time': int(cardMin)}
  474. vCardCanUse = virtual_card.can_use_today(package)
  475. #: 扣费
  476. if oper == SWIPE_CARD_PARAM_OP.DECR_00:
  477. if card.openId == '' or card.frozen:
  478. res = SWIPE_CARD_RES.INVALID_CARD_02
  479. leftBalance = RMB(0)
  480. return self.response_use_card(res, leftBalance)
  481. # 如果虚拟卡可用,卡的费用不要扣掉,仅仅做记录,但是虚拟卡的额度需要扣掉
  482. elif vCardCanUse:
  483. # 记录卡消费记录以及消费记录
  484. orderNo, cardOrderNo = self.record_consume_for_card(card, money=RMB(0.0), desc=u'使用绑定的虚拟卡')
  485. group = Group.get_group(self.device['groupId'])
  486. consumeRcd = virtual_card.consume(openId=card.openId, group=group, dev=self.device,
  487. package=package,
  488. attachParas={}, nickname=card.cardName)
  489. # 记录当前服务的progress.没有上报端口号,所以先用-1标记,表示端口未知。端口等消费的时候会报上来
  490. ServiceProgress.register_card_service(self.device, -1, card,
  491. {
  492. 'orderNo': orderNo,
  493. 'money': self.event_data['preFee'],
  494. 'coin': self.event_data['preFee'], 'needTime': 0,
  495. 'cardOrderNo': cardOrderNo,
  496. 'consumeRcdId': str(consumeRcd.id)
  497. })
  498. self.consume_money_for_card(card, money=RMB(0.0))
  499. if consumeRcd is None: # 如果额度没有扣除成功,就用卡
  500. pass
  501. else:
  502. self.response_use_card(SWIPE_CARD_RES.SUCCESS_00, leftBalance=999)
  503. # 通知微信,已经扣费
  504. # self.notify_balance_has_consume_for_card(card, preFee, desc = u'使用绑定的虚拟卡')
  505. return
  506. elif result['balance'] < preFee:
  507. res = SWIPE_CARD_RES.BALANCE_NOT_ENOUGH_01
  508. leftBalance = result['balance']
  509. return self.response_use_card(res, leftBalance)
  510. else:
  511. # 记录卡消费记录以及消费记录
  512. orderNo, cardOrderNo = self.record_consume_for_card(card, preFee)
  513. # 记录当前服务的progress.没有上报端口号,所以先用-1标记,表示端口未知。端口等消费的时候会报上来
  514. ServiceProgress.register_card_service(self.device, -1, card,
  515. {
  516. 'orderNo': orderNo,
  517. 'money': self.event_data['preFee'],
  518. 'coin': self.event_data['preFee'], 'needTime': 0,
  519. 'cardOrderNo': cardOrderNo
  520. })
  521. res = SWIPE_CARD_RES.SUCCESS_00
  522. leftBalance = result['balance']
  523. self.consume_money_for_card(card, preFee)
  524. leftBalance -= preFee
  525. self.response_use_card(res, leftBalance)
  526. # 通知微信,已经扣费
  527. self.notify_balance_has_consume_for_card(card, preFee)
  528. # 退费.卡的退费比较特殊:如果需要按照电量进行扣费退费,需要在设备管理后台,设置成按电量方式计费。然后把卡的余额回收关闭掉。
  529. # 充电结束后,上报事件,然后把钱退到卡里。如果是按照时间计费(霍柏的特殊),完全由设备决定,设备告诉我退费,我就退。针对霍柏
  530. # 绑定虚拟卡的情况下,按照时间退费,需要直接取消掉余额回收,靠结束事件触发结束
  531. elif oper == SWIPE_CARD_PARAM_OP.INCR_01:
  532. if virtual_card:
  533. logger.debug('virtual card is not do device refund.')
  534. return
  535. devObj = Device.objects.get(devNo=self.device['devNo'])
  536. dealer = self.device.owner
  537. if not dealer:
  538. logger.error('dealer is not found, dealerId=%s' % self.device.ownerId)
  539. return
  540. agent = Agent.objects(id=dealer.agentId).first()
  541. if not agent:
  542. logger.error('agent is not found, agentId=%s' % dealer.agentId)
  543. return
  544. res = SWIPE_CARD_RES.SUCCESS_00
  545. device_refund = False
  546. billingType = devObj.otherConf.get('billingType', 'time')
  547. if is_server_refund(billingType, self.device, dealer, agent):
  548. logger.debug('{} in {} has card refund by server refund.'.format(repr(card), repr(self.device)))
  549. self.response_use_card(res, card.balance)
  550. return
  551. logger.debug('{} card refund by {}.'.format(repr(card), repr(self.device)))
  552. self.response_use_card(res, card.balance + preFee)
  553. self.refund_money_for_card(preFee, str(card.id))
  554. self.notify_user(card.managerialOpenId, 'refund_coins', **{
  555. 'title': u'退币完成!您的卡号是%s,卡别名:%s' % (card.cardNo, card.nickName),
  556. 'backCount': u'金币:%s' % preFee,
  557. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  558. })
  559. else:
  560. raise InvalidOption('oper has be to 00 or 01, %s was given' % (oper,))
  561. #: 结束的命令
  562. elif cmdCode in [CMD_CODE.DEVICE_SUBMIT_CHARGE_FINISHED_06, CMD_CODE.DEVICE_SUBMIT_CHARGE_FINISHED_v2_16]:
  563. # 结束的时候故障的上报 如果结束的原因是故障导致的 记录一条故障告警
  564. if self.event_data.get("reasonCode") in ["03", "0B"]:
  565. self.__do_fault_record()
  566. port = str(self.event_data['port'])
  567. lineInfo = Device.clear_port_control_cache(devNo, port)
  568. if not lineInfo:
  569. logger.debug('port<{}> of device<l={}> cache is null.'.format(port, self.device.logicalCode))
  570. return
  571. logger.debug('port<{}> of device<l={}> cache is: {}'.format(port, self.device.logicalCode, str(lineInfo)))
  572. # 红包退费的判断处理
  573. if 'redpackInfo' in lineInfo:
  574. for _info in lineInfo['redpackInfo']:
  575. redpack = Redpack.get_one(_info['redpackId'])
  576. redpackCoins = VirtualCoin(redpack['redpackCoins'])
  577. lineInfo['coins'] = float(VirtualCoin(lineInfo['coins']) - redpackCoins)
  578. redpackMoney = RMB(redpack['redpackMoney'])
  579. lineInfo['price'] = float(RMB(lineInfo['price']) - redpackMoney)
  580. logger.debug(
  581. 'redpack is <{}> redpack money is: {}; redpack coins is: {}'.format(redpack.get('id'),
  582. str(redpackMoney.amount),
  583. str(redpackCoins.amount)))
  584. recvTime = to_datetime(self.recvTime)
  585. group = Group.get_group(self.device['groupId'])
  586. dealer = Dealer.objects(id=group['ownerId']).first()
  587. if not dealer:
  588. logger.error('dealer is not found, dealerId=%s' % group['ownerId'])
  589. return
  590. agent = Agent.objects(id=dealer.agentId).first()
  591. if not agent:
  592. logger.error('agent is not found, agentId=%s' % dealer.agentId)
  593. return
  594. if 'coins' not in lineInfo:
  595. logger.debug('port cache has no coins. no order in port {}'.format(port))
  596. return
  597. if 'startTime' not in lineInfo:
  598. logger.debug('startTime is not in lineInfo')
  599. return
  600. startTime = to_datetime(lineInfo['startTime'])
  601. refundProtectionTime = int(self.device.get_other_conf_item('refundProtectionTime', 5))
  602. if startTime > recvTime: # 如果web服务器时间和事件监控服务器时间不一致,导致开始时间比事件时间还大
  603. logger.warning(
  604. 'finished event of dev<{}> error: start time<{}> is bigger than recv time<{}>'.format(
  605. self.device.devNo, startTime, recvTime))
  606. if startTime > (recvTime + refundProtectionTime):
  607. return
  608. else:
  609. recvTime = startTime
  610. usedTime = int(round(((recvTime - startTime).total_seconds() / 60.0)))
  611. if usedTime <= 0:
  612. usedTime = 1
  613. cardId = lineInfo.get('cardId', '')
  614. vCardId = lineInfo.get('vCardId', None)
  615. money = VirtualCoin(lineInfo['coins'])
  616. backCoins = VirtualCoin(0.0)
  617. price = RMB(lineInfo.get('price', 0.0))
  618. refundRMB = RMB('0.0')
  619. leftTime = self.event_data['leftTime']
  620. billingType = lineInfo.get('billingType', 'time')
  621. consumeType = lineInfo.get('consumeType', '')
  622. minUsedTime = self.device.get_other_conf_item('minUsedTime', 0)
  623. isTempPackage = lineInfo.get('isTempPackage', False)
  624. orderPower = self.event_data.get("orderPower")
  625. needTime = 0
  626. leftTimeStr = ''
  627. spendElec = 0.0
  628. leftElec = self.event_data.get('elec', 0.0)
  629. needElec = lineInfo.get('needElec', 0.0)
  630. try:
  631. #: 刷卡或者万一缓存重启了,出现needElec为空的,作为1000度电吧,就会一定使用设备上报的电量
  632. if leftElec < needElec:
  633. spendElec = round(needElec - leftElec, 2)
  634. backCoins_by_elec = round(leftElec / needElec * float(money), 2)
  635. else:
  636. spendElec = 0.0
  637. backCoins_by_elec = money
  638. if leftTime == 65535:
  639. actualNeedTime = 0
  640. backCoins = money
  641. refundRMB = price
  642. usedTime = 0
  643. spendElec = 0.0
  644. else:
  645. actualNeedTime = usedTime + leftTime
  646. leftTimeStr = leftTime
  647. if ('alt_tech_refund_mode' in agent.features or 'alt_tech_refund_mode' in dealer.features) and \
  648. billingType == 'time':
  649. needTime = lineInfo['needTime']
  650. # 剩余时间不满 60 按照 0 算, 不满 120 按照 60 算...
  651. calcleftTime = (int(leftTime) // 60) * 60
  652. backCoins = money * (float(calcleftTime) / float(actualNeedTime))
  653. elif billingType == 'time':
  654. needTime = lineInfo['needTime']
  655. backCoins = money * (float(leftTime) / float(actualNeedTime))
  656. if refundProtectionTime < usedTime < minUsedTime: # 最小使用时长判断(不足改时长 按该时常算)
  657. backCoins = money * (float(actualNeedTime - minUsedTime) / float(actualNeedTime))
  658. logger.info(
  659. 'usedTime less then minUsedTime (usedTime<{}> ==> minUsedTime<{}>) devNo=<{}>, port=<{}>, backCoins=<{}>'.format(
  660. usedTime, minUsedTime, devNo, port, backCoins))
  661. else:
  662. needElec, elec = Decimal(lineInfo.get('needElec', 1000)), Decimal(
  663. str(self.event_data.get('elec')))
  664. ratio = (needElec - elec) / needElec
  665. backCoins = VirtualCoin(money.amount - money.amount * ratio)
  666. if 'jiuheng_double_judgment_of_time_and_elec' in dealer.features:
  667. backCoins = money * (float(leftTime) / float(actualNeedTime))
  668. backCoins = VirtualCoin(min(float(backCoins), float(backCoins_by_elec)))
  669. isRefundProtection = False
  670. if usedTime <= refundProtectionTime:
  671. backCoins = money
  672. isRefundProtection = True
  673. logger.info(
  674. 'exec protection refund devNo=<{}>, port=<{}>, backCoins=<{}>'.format(devNo, port, backCoins))
  675. if backCoins > money:
  676. backCoins = money
  677. refundRMB = price * (float(backCoins) / float(money)) if money != VirtualCoin(0) else RMB(0)
  678. logger.debug(
  679. 'refund money is: {}; refund rmb is: {}'.format(str(backCoins.amount), str(refundRMB.amount)))
  680. #: 扫码的方式
  681. if consumeType == 'server' and (not vCardId):
  682. logger.info("finished with netPay")
  683. #: 这里需要考虑API调用的和普通使用场景
  684. if 'extOrderNo' in lineInfo:
  685. record = APIStartDeviceRecord.get_api_record(self.device['logicalCode'], lineInfo['extOrderNo'])
  686. if not record:
  687. logger.debug('cannot find api start device record')
  688. return
  689. if record.postActionTriggered:
  690. logger.debug('api({}) post action has done.'.format(lineInfo['extOrderNo']))
  691. return
  692. # 中天的结束状态匹配
  693. reasonCode = self.event_data['reasonCode']
  694. if reasonCode == '0B':
  695. reasonCode = '03'
  696. elif reasonCode == '03':
  697. reasonCode = '04'
  698. else:
  699. pass
  700. # 中天空载需要这样写
  701. if leftTime == 65535:
  702. leftTime = lineInfo['needTime']
  703. report_zhongtian_service_complete(
  704. event_code='16',
  705. record=record,
  706. orderNo=lineInfo['extOrderNo'],
  707. deviceCode=self.device['logicalCode'],
  708. groupName=group['groupName'],
  709. address=group['address'],
  710. actualNeedTime=lineInfo['needTime'],
  711. leftTime=leftTime,
  712. finishedState=reasonCode
  713. )
  714. record.update(servicedInfo={'spendElec': str(spendElec), 'backCoins': '0'})
  715. if self.device.is_auto_refund:
  716. coins = VirtualCoin(lineInfo['coins'])
  717. money = RMB(lineInfo['price'])
  718. backCoins = self.get_backCoins(coins=coins, leftTime=leftTime,
  719. actualNeedTime=lineInfo['needTime'])
  720. backMoney = self.get_backMoney(money=money, leftTime=leftTime,
  721. actualNeedTime=lineInfo['needTime'])
  722. report_zhongtian_refund(
  723. eventCode='16',
  724. record=record,
  725. orderNo=lineInfo['extOrderNo'],
  726. deviceCode=self.device['logicalCode'],
  727. groupName=group['groupName'],
  728. address=group['address'],
  729. backMoney=str(backMoney),
  730. backCoins=str(backCoins),
  731. actualNeedTime=lineInfo['needTime'],
  732. leftTime=leftTime,
  733. finishedState=reasonCode
  734. )
  735. record.update(servicedInfo={'spendElec': str(spendElec), 'backCoins': str(backCoins)})
  736. else:
  737. if 'openId' not in lineInfo or not lineInfo['openId']:
  738. logger.warning(
  739. 'openId not in cache of port<{}> in device<{}>'.format(port, self.device.devNo))
  740. return
  741. openId = lineInfo['openId']
  742. user = MyUser.objects(openId=openId, groupId=self.device['groupId']).first()
  743. extra = []
  744. consumeDict = {
  745. 'chargeIndex': port,
  746. 'reason': self.event_data['reason'],
  747. 'actualNeedTime': u'动态功率计算为%s分钟' % actualNeedTime,
  748. 'duration': usedTime,
  749. 'endUart': self.event_data.get('uartData', ''),
  750. }
  751. orderPower and consumeDict.update({"orderPower": orderPower})
  752. if billingType == 'time':
  753. leftTimeStr = leftTime if leftTime != 65535 else lineInfo['needTime']
  754. consumeDict.update(
  755. {'leftTime': leftTimeStr, 'needTime': u'扫码订购%s分钟' % lineInfo['needTime']})
  756. consumeDict.update({'elec': spendElec})
  757. else:
  758. consumeDict.update({'needElec': lineInfo['needElec']})
  759. consumeDict.update({'elec': spendElec})
  760. consumeDict.update({'elecFee': self.calc_elec_fee(spendElec)})
  761. # 判断是否需要退款 个人感觉目前的退款判断是有问题的 if 与 elif 之间非互斥条件
  762. # 这个地方 建议抽象出公共方法 ,做分层处理 避免相互之间有矛盾
  763. # 顺序如下: 经销商全局经营策略 < 地址经营策略 < 小于设备经营策略 < 订单经营策略
  764. # 也就是说 优先判断经销商总开关(目前没有)然后判断地址组的免费,地址组的免费开关(目前没有) 然后判断设备退费开关,设备特性 ,最后是订单相关(套餐等)
  765. # 这样的话 业务退款逻辑 大部分就可以放在device 的features里面进行处理
  766. """
  767. 伪代码
  768. if not dealer.support_refund: ---> switch true or false / features.support_refund
  769. return false
  770. if not group.support_refund: ---> group.isFree / switch true or false / features.support_refund
  771. return false
  772. if not device.support_refund: ---> device.is_auto_refund / device.features.support_refund
  773. return false
  774. if not order.support_refund: ---> isTempPackage / refund_production enable / ic or id card
  775. return false
  776. return true
  777. """
  778. need_refund = False
  779. if not group.get('isFree', False):
  780. if isTempPackage is True:
  781. if isRefundProtection is True:
  782. need_refund = True
  783. else:
  784. pass
  785. elif self.device.is_auto_refund:
  786. need_refund = True
  787. elif isRefundProtection is True:
  788. need_refund = True
  789. else:
  790. pass
  791. else:
  792. pass
  793. logger.info("netPay need refund is {}".format(need_refund))
  794. if not need_refund:
  795. ServiceProgress.update_progress_and_consume_rcd(
  796. self.device['ownerId'],
  797. {
  798. 'open_id': openId, 'device_imei': self.device['devNo'],
  799. 'port': int(port), 'isFinished': False
  800. },
  801. consumeDict
  802. )
  803. extra.append({u'消费明细': u'消费{}(金币)'.format(money)})
  804. else:
  805. if isTempPackage:
  806. backCoins = VirtualCoin(0)
  807. self.refund_net_pay(user, lineInfo, refundRMB, backCoins, consumeDict, True)
  808. else:
  809. self.refund_net_pay(user, lineInfo, refundRMB, backCoins, consumeDict,
  810. ('refundRMB_device_event' in dealer.features))
  811. ServiceProgress.update_progress_and_consume_rcd(
  812. self.device['ownerId'],
  813. {
  814. 'open_id': openId, 'device_imei': self.device['devNo'],
  815. 'port': int(port), 'isFinished': False
  816. },
  817. consumeDict)
  818. if billingType == 'elec':
  819. billAsService = self.device.bill_as_service_feature
  820. billAsServiceSwitch = billAsService.on
  821. else:
  822. billAsService = None
  823. billAsServiceSwitch = False
  824. if DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS in consumeDict:
  825. extra.append({u'消费明细': u'消费{}金币,退款{}金币'.format(money - backCoins, backCoins)})
  826. if billAsServiceSwitch:
  827. elecExpense, serviceExpense = self.calc_service_fee(
  828. VirtualCoin(money - backCoins), billAsService.elec_charge,
  829. billAsService.service_charge)
  830. extra.append({u'电费': u'{}金币'.format(elecExpense)})
  831. extra.append({u'服务费': u'{}金币'.format(serviceExpense)})
  832. elif DEALER_CONSUMPTION_AGG_KIND.REFUNDED_CASH in consumeDict:
  833. extra.append({u'消费金额': u'消费{}元,退款{}元'.format(price - refundRMB, refundRMB)})
  834. if billAsServiceSwitch:
  835. elecExpense, serviceExpense = self.calc_service_fee(
  836. RMB(price - refundRMB), billAsService.elec_charge, billAsService.service_charge)
  837. extra.append({u'电费': u'{}元'.format(elecExpense)})
  838. extra.append({u'服务费': u'{}元'.format(serviceExpense)})
  839. self.notify_to_user(self.get_managerialOpenId_by_openId(openId), extra)
  840. elif consumeType in ['server', 'card'] and vCardId:
  841. # 使用的是虚拟卡
  842. logger.info("finished with vCard!")
  843. try:
  844. vCard = UserVirtualCard.objects.get(id=vCardId)
  845. except DoesNotExist:
  846. logger.info('can not find the vCard id = %s' % vCardId)
  847. return
  848. billingType = lineInfo.get('billingType', 'time')
  849. extra = []
  850. extra.append({u'虚拟卡券': u'{}--{}'.format(vCard.cardName, vCard.cardNo)})
  851. if billingType == 'time':
  852. extra.append({u'消费明细': u'消费{}分钟'.format(usedTime)})
  853. else:
  854. extra.append({u'消耗明细': u'消费{}度'.format(spendElec)})
  855. self.notify_to_user(self.get_managerialOpenId_by_openId(lineInfo["openId"]), extra)
  856. consumeDict = {
  857. 'chargeIndex': port,
  858. 'reason': self.event_data['reason'],
  859. 'actualNeedTime': u'动态功率计算为%s分钟' % actualNeedTime,
  860. 'duration': usedTime,
  861. }
  862. orderPower and consumeDict.update({"orderPower": orderPower})
  863. consumeDict.update({'elec': spendElec})
  864. if billingType != 'time':
  865. consumeDict.update({'elec': spendElec})
  866. consumeDict.update({'elecFee': self.calc_elec_fee(spendElec)})
  867. ServiceProgress.update_progress_and_consume_rcd(
  868. self.device['ownerId'],
  869. {'open_id': lineInfo['openId'], 'device_imei': self.device['devNo'],
  870. 'port': int(port), 'isFinished': False}, consumeDict
  871. )
  872. consumeRcdId = lineInfo.get('consumeRcdId', None)
  873. if consumeRcdId is None:
  874. logger.info('can not find consume rcd id')
  875. return
  876. # 尝试进行虚拟卡退费
  877. try:
  878. vCardConsumeRcd = VCardConsumeRecord.objects.get(id=consumeRcdId)
  879. except DoesNotExist, e:
  880. logger.info('can not find the consume rcd id = %s' % consumeRcdId)
  881. else:
  882. vCard.refund_quota(vCardConsumeRcd, usedTime, spendElec, backCoins.mongo_amount)
  883. elif consumeType == 'card':
  884. # 刷的实体卡
  885. logger.info("finished with card")
  886. if not cardId:
  887. logger.warning('{} finished with card, but no cardId.'.format(self.device))
  888. return
  889. try:
  890. cardRefundProtectionTime = self.device.get('otherConf', {}).get('cardRefundProtectionTime', 0)
  891. if cardRefundProtectionTime > 0:
  892. if usedTime < cardRefundProtectionTime:
  893. backCoins = money
  894. logger.info(
  895. 'exec protection refund devNo=<{}>, port=<{}>, backCoins=<{}>'.format(devNo, port,
  896. backCoins))
  897. if backCoins > money:
  898. backCoins = money
  899. except Exception as e:
  900. logger.exception(e)
  901. card = Card.objects.get(id=cardId)
  902. virtual_card = card.bound_virtual_card # type: UserVirtualCard
  903. extra = []
  904. extra.append({u'实体卡': u'{}--{}'.format(card.cardName, card.cardNo)})
  905. if billingType == 'time':
  906. logger.info("billingType is time.")
  907. consumeDict = {
  908. 'chargeIndex': port,
  909. 'leftTime': leftTimeStr,
  910. 'needTime': u'刷卡订购%s分钟' % needTime if virtual_card is None else u'绑定虚拟卡订购%s分钟' % needTime,
  911. 'reason': self.event_data['reason'],
  912. 'duration': usedTime,
  913. 'actualNeedTime': u'动态功率计算为%s分钟' % actualNeedTime,
  914. 'elec': spendElec,
  915. 'elecFee': self.calc_elec_fee(spendElec),
  916. 'cardId': cardId,
  917. 'endUart': self.event_data.get('uartData', '')
  918. }
  919. orderPower and consumeDict.update({"orderPower": orderPower})
  920. try:
  921. if virtual_card:
  922. self.refund_virtual_card(backCoins, cardId, consumeDict, lineInfo, spendElec, usedTime,
  923. virtual_card)
  924. extra.append({u'虚拟卡券': u'{}--{}'.format(virtual_card.cardName, virtual_card.cardNo)})
  925. extra.append({u'消费明细': u'消费{}分钟'.format(usedTime)})
  926. else:
  927. if self.device.support_dev_type_features('support_wvtiykt'):
  928. tradeFare = str(int((money - backCoins) * 100))
  929. extra.append({u'消费明细': u'消费{}金币'.format(money)})
  930. teradeDate = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
  931. orderNo = lineInfo.get('orderNo', '')
  932. if (datetime.datetime.now() - startTime).total_seconds() < 24 * 60 * 60:
  933. resrult = self.notify_to_ykt_norther(card.cardNo, orderNo, tradeFare, teradeDate)
  934. if resrult.get("resultCode") != "000000":
  935. raise RequestInvalid('卡扣费失败,失败编号%s,失败原因%s' % (
  936. resrult.get("resultCode"), resrult.get("resultMsg")))
  937. card.balance = RMB(float(str(card.balance)) - int(money - backCoins))
  938. card.save()
  939. elif is_server_refund(billingType, self.device, dealer, agent):
  940. logger.info(
  941. 'ready to server refund money <{}> for user card <{}> in device<{}>'.format(
  942. backCoins, str(card.id), self.device.devNo))
  943. consumeDict.update(
  944. {DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: backCoins.mongo_amount})
  945. self.refund_money_for_card(backCoins, str(card.id))
  946. extra.append({u'消费明细': u'消费{}金币,退款{}金币'.format(money - backCoins, backCoins)})
  947. else:
  948. extra.append({u'消费明细': u'消费{}金币'.format(money)})
  949. finally:
  950. ServiceProgress.update_progress_and_consume_rcd(
  951. self.device['ownerId'],
  952. {
  953. 'cardId': cardId,
  954. 'device_imei': self.device['devNo'],
  955. 'port': int(port),
  956. 'isFinished': False
  957. }, consumeDict)
  958. else:
  959. logger.info("billingType is elec")
  960. consumeDict = {
  961. 'chargeIndex': port,
  962. 'leftTime': leftTimeStr,
  963. 'reason': self.event_data['reason'],
  964. 'elec': spendElec,
  965. 'elecFee': self.calc_elec_fee(spendElec),
  966. 'duration': usedTime,
  967. 'needElec': lineInfo['needElec'],
  968. 'cardId': cardId,
  969. 'endUart': self.event_data.get('uartData', '')
  970. }
  971. orderPower and consumeDict.update({"orderPower": orderPower})
  972. try:
  973. if virtual_card:
  974. self.refund_virtual_card(backCoins, cardId, consumeDict, lineInfo, spendElec, usedTime,
  975. virtual_card)
  976. extra = [
  977. {
  978. u'虚拟卡券': u'{}--{}'.format(virtual_card.cardName, virtual_card.cardNo)
  979. }, {
  980. u'消费明细': u'消费{}度'.format(spendElec)
  981. }
  982. ]
  983. elif is_server_refund(billingType, self.device, dealer, agent):
  984. logger.info(
  985. 'ready to server refund money <{}> for user card <{}> in device<{}>'.format(
  986. backCoins,
  987. str(card.id),
  988. self.device.devNo))
  989. self.refund_money_for_card(backCoins, str(card.id))
  990. consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: backCoins.mongo_amount})
  991. extra.append({u'消费明细': u'消费{}金币,退款{}金币'.format(money - backCoins, backCoins)})
  992. billAsServiceByCard = self.device.bill_as_service_feature
  993. if billAsServiceByCard:
  994. billAsServiceCardSwitch = billAsServiceByCard.on
  995. else:
  996. billAsServiceCardSwitch = False
  997. if billAsServiceCardSwitch:
  998. elecExpense, serviceExpense = self.calc_service_fee(
  999. VirtualCoin(money - backCoins), billAsServiceByCard.elec_charge,
  1000. billAsServiceByCard.service_charge)
  1001. extra.append({u'电费': u'{}金币'.format(elecExpense)})
  1002. extra.append({u'服务费': u'{}金币'.format(serviceExpense)})
  1003. else:
  1004. extra.append({u'消费明细': u'消费{}金币'.format(money)})
  1005. finally:
  1006. ServiceProgress.update_progress_and_consume_rcd(
  1007. self.device['ownerId'],
  1008. {'cardId': cardId, 'device_imei': self.device['devNo'],
  1009. 'port': int(port), 'isFinished': False}, consumeDict
  1010. )
  1011. self.notify_to_user(card.managerialOpenId, extra)
  1012. elif consumeType == 'coin': #: 消费类型为金币,则
  1013. logger.info("finished with coin")
  1014. CoinConsumeRcd = ConsumeRecord.objects.get(
  1015. orderNo=lineInfo['consumeOrderNo']) # type: ConsumeRecord
  1016. CoinConsumeRcd.update_for_end(serviceInfo={
  1017. DEALER_CONSUMPTION_AGG_KIND.ELEC: spendElec,
  1018. DEALER_CONSUMPTION_AGG_KIND.ELECFEE: self.calc_elec_fee(spendElec),
  1019. 'actualNeedTime': u'动态功率计算为%s分钟' % actualNeedTime,
  1020. 'needTime': u'投币订购%s分钟' % needTime,
  1021. 'reason': self.event_data['reason'],
  1022. 'chargeIndex': str(port),
  1023. DEALER_CONSUMPTION_AGG_KIND.DURATION: usedTime
  1024. }, finishedTime=recvTime.strftime('%Y-%m-%d %H:%M:%S'))
  1025. else:
  1026. logger.warning('{} has not invalid consume type<{}>'.format(self.device, consumeType))
  1027. return
  1028. except Exception as e:
  1029. logger.exception('deal with jingneng devNo=%s event e=%s' % (devNo, e))
  1030. finally:
  1031. dataDict = {'backMoney': str(refundRMB.mongo_amount), 'backCoins': str(backCoins.mongo_amount)}
  1032. if lineInfo.has_key('orderNo'):
  1033. dataDict.update({'orderNo': lineInfo['orderNo']})
  1034. notify_event_to_north(self.dealer, self.device, level=Const.EVENT_NORMAL,
  1035. desc=self.event_data['reason'], dataDict=dataDict)
  1036. self.event_data.update({'deviceCode': self.device["logicalCode"]})
  1037. self.event_data.update({'spendElec': spendElec})
  1038. notify_event_to_north_v2(self.device["devNo"], self.event_data)
  1039. send_event_to_zhejiang(self.dealer, self.device, self.event_data)
  1040. if self.device.owner.supports("supportBeiJingFengTai"):
  1041. orderNo = lineInfo.get('orderNo')
  1042. from apps.web.south_intf.bj_north.api import post_charging_record_info,delete_port_info,post_charging_meta_info
  1043. post_charging_record_info(orderNo)
  1044. delete_port_info(self.device.devNo,port)
  1045. post_charging_meta_info(self.device.devNo,port)
  1046. #: 启动了端口,主要记录下投币数据
  1047. elif cmdCode == CMD_CODE.DEVICE_SUBMIT_OFFLINE_COINS_20:
  1048. consumeType = self.event_data['consumeType']
  1049. # 简易配置 如果配置了新卡流程特性的 并且是扣费的指令的 走新的流程 这个判断的优先级 低于设备的特性
  1050. if consumeType == "card":
  1051. if "support_new_card_proc" in self.device.owner.features:
  1052. return self.__do_new_card_proc_20()
  1053. if consumeType == 'coin':
  1054. service_info = {
  1055. 'startTime': datetime.datetime.now().strftime(Const.DATETIME_FMT),
  1056. 'needTime': self.event_data['needTime'],
  1057. 'needElec': self.event_data['elec'],
  1058. 'consumeType': consumeType,
  1059. 'coins': self.event_data['coins']
  1060. }
  1061. consume_order_no = self.record_consume_for_coin(
  1062. money=RMB(self.event_data['coins']), remarks=u'投币或者刷卡消费', servicedInfo=service_info)
  1063. service_info.update({'consumeOrderNo': consume_order_no})
  1064. Device.update_dev_control_cache(self.device.devNo, {str(self.event_data['port']): service_info})
  1065. elif consumeType == 'card':
  1066. port = self.event_data['port']
  1067. consumeDict = {'chargeIndex': port, 'elec': self.event_data['elec'],
  1068. 'money': self.event_data['coins'], 'needTime': u'刷卡订购%s分钟' % self.event_data['needTime']}
  1069. queryDict = {'device_imei': self.device['devNo'],
  1070. 'port': -1, 'isFinished': False,
  1071. 'cardId': {'$ne': ''}, 'start_time': {'$gte': int(time.time()) - 3600}}
  1072. progressDict = {'port': port}
  1073. ServiceProgress.update_progress_and_consume_rcd(ownerId=self.device['ownerId'], queryDict=queryDict,
  1074. consumeDict=consumeDict, updateConsume=True,
  1075. progressDict=progressDict)
  1076. # 找出对应的卡的ID记录到端口内存数据
  1077. queryDict.update(progressDict)
  1078. rcds = ServiceProgress.get_collection().find(queryDict, {'cardId': 1, 'open_id': 1, "consumeOrder": 1},
  1079. sort=[('start_time', -1)])
  1080. if rcds.count() == 0:
  1081. return
  1082. rcd = rcds[0]
  1083. devObj = Device.objects.get(devNo=self.device['devNo'])
  1084. billingType = devObj.otherConf.get('billingType', 'time')
  1085. # 刷卡虚拟卡启动的时候,将consumeRcd 写入到缓存 退费的时候使用
  1086. consumeRcdId = rcd.get("consumeOrder", dict()).get("consumeRcdId")
  1087. if consumeRcdId:
  1088. self.event_data.update({"consumeRcdId": consumeRcdId})
  1089. try:
  1090. vRcd = VCardConsumeRecord.objects(id=consumeRcdId).first()
  1091. self.event_data.update({'vCardId': vRcd.cardId})
  1092. except Exception as e:
  1093. # 防止报错退钱给实体卡, 强制添加vCardId
  1094. self.event_data.update({'vCardId': 'true'})
  1095. self.event_data.update({'billingType': billingType})
  1096. self.event_data.update({'cardId': rcd['cardId']})
  1097. self.event_data.update({'openId': rcd['open_id']})
  1098. cInfo = Device.get_dev_control_cache(devNo)
  1099. lastPortInfo = cInfo.get(str(port), {})
  1100. # 钱需要累计
  1101. lastCoins = lastPortInfo.get('coins', 0.0)
  1102. self.event_data.update({'coins': self.event_data['coins'] + lastCoins})
  1103. # 电量需要累加
  1104. lastNeedElec = lastPortInfo.get('needElec', 0.0)
  1105. self.event_data.update({'needElec': self.event_data['elec'] + lastNeedElec})
  1106. # 时间需要累加
  1107. lastNeedTime = lastPortInfo.get('needTime', 0.0)
  1108. self.event_data.update({'needTime': self.event_data['needTime'] + lastNeedTime})
  1109. self.event_data.update({'startTime': datetime.datetime.now().strftime(Const.DATETIME_FMT)})
  1110. self.event_data.update({'isStart': True, 'status': Const.DEV_WORK_STATUS_WORKING})
  1111. #: 记录该端口累计需要的时间和钱,cardId
  1112. Device.update_port_control_cache(self.device['devNo'], self.event_data)
  1113. elif consumeType == 'server':
  1114. port = self.event_data['port']
  1115. #: 记录该端口累计需要的时间和钱
  1116. Device.update_port_control_cache(self.device['devNo'], self.event_data)
  1117. consumeDict = {'chargeIndex': self.event_data['port'], 'elec': self.event_data['elec'],
  1118. 'needTime': u'订购%s分钟' % self.event_data['needTime']}
  1119. queryDict = {'device_imei': self.device['devNo'],
  1120. 'port': port, 'isFinished': False,
  1121. 'start_time': {'$gte': int(time.time()) - 3600}}
  1122. progressDict = {'port': self.event_data['port']}
  1123. ServiceProgress.update_progress_and_consume_rcd(ownerId=self.device['ownerId'],
  1124. queryDict=queryDict, consumeDict=consumeDict,
  1125. updateConsume=True, progressDict=progressDict)
  1126. else:
  1127. raise NoCommandHandlerAvailable(
  1128. '[JNDZ]] no command handler for cmd %s, curDevInfo=%s' % (cmdCode, self.event_data))
  1129. def refund_virtual_card(self, backCoins, cardId, consumeDict, lineInfo, spendElec, usedTime, virtual_card):
  1130. logger.debug('server refund for virtual card<{}> in device<{}>'.format(str(virtual_card.id), self.device.devNo))
  1131. consumeRcdId = lineInfo.get('consumeRcdId', None)
  1132. # 不是虚拟卡启动的直接结束掉
  1133. if consumeRcdId:
  1134. # 尝试进行虚拟卡退费
  1135. try:
  1136. vCardConsumeRcd = VCardConsumeRecord.objects.get(id=consumeRcdId)
  1137. except DoesNotExist, e:
  1138. logger.info('can not find the consume rcd id = %s' % consumeRcdId)
  1139. else:
  1140. virtual_card.refund_quota(vCardConsumeRcd, usedTime, spendElec,
  1141. backCoins.mongo_amount)
  1142. else:
  1143. logger.info('can not find consume rcd id')
  1144. def response_use_card(self, res, leftBalance):
  1145. try:
  1146. self.deviceAdapter.response_use_card(res, leftBalance)
  1147. except ServiceException as e:
  1148. logger.exception(e)
  1149. def _get_virtual_card_by_card(self, card):
  1150. if not card.openId or float(card.balance) != 0:
  1151. return
  1152. try:
  1153. dealer = self.device.owner
  1154. agent = Agent.objects.get(id=dealer.agentId)
  1155. features = agent.features
  1156. except Exception as e:
  1157. features = []
  1158. return card.bound_virtual_card if "vCardNeedBind" in features else card.related_virtual_card
  1159. def _check_virtual_card_can_use_today(self, virtual_card, fee):
  1160. # 如果虚拟卡已经绑定,需要检查下今天是否可用,如果可用,有限使用虚拟卡
  1161. if virtual_card:
  1162. unit = self.device['washConfig'].get('1', {}).get('unit', '分钟')
  1163. if unit == '分钟':
  1164. cardMin = self.device["otherConf"].get('cardMin', 180)
  1165. package = {'coins': float(fee), 'unit': unit, 'time': int(cardMin)}
  1166. elif unit == '度':
  1167. cardMin = self.device["otherConf"].get('cardMin', 180)
  1168. package = {'coins': float(fee), 'unit': unit, 'time': int(cardMin)}
  1169. else:
  1170. return 0
  1171. if virtual_card.can_use_today(package):
  1172. dayKey = datetime.datetime.now().strftime("%Y-%m-%d")
  1173. leftDayQuota = virtual_card.calc_left_day_quota(dayKey)
  1174. left_count = virtual_card.find_match_unit_and_can_use_count(leftDayQuota, package)
  1175. return left_count
  1176. def _check_card_balance_can_use_today(self, card, fee):
  1177. if float(card.balance) >= fee:
  1178. return True
  1179. else:
  1180. return False
  1181. def notify_to_user(self, openId, extra):
  1182. """
  1183. 推送通知 由于不是订单机制的版本 订单编号比较难以寻找,可能会不准
  1184. 直接推送到消费记录里面 由客户自行定位
  1185. """
  1186. group = Group.get_group(self.device['groupId'])
  1187. self.notify_user_service_complete(
  1188. service_name='充电',
  1189. openid=openId,
  1190. port=self.event_data["port"],
  1191. address=group["address"],
  1192. reason=self.event_data.get('reason'),
  1193. finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  1194. extra=extra
  1195. )
  1196. class JNDZNewCardChargeEvent(WorkEvent):
  1197. """
  1198. 和动主板新的刷卡流程,设备类型不同,同时 协议流程也不相同
  1199. 这个新的流程基本是:
  1200. 1.用户刷卡,查询卡的余额,不进行扣费
  1201. 2.用户按下相应的端口按钮,进行启动设备,设备启动后上报20指令,同时服务器对卡进行扣费,然后回复主板,否则主板会一直报
  1202. """
  1203. def do(self, **args):
  1204. # 上传的失败指令
  1205. devNo = self.device.devNo
  1206. logger.info('JNDZNewCardChargeEvent charging event detected, devNo=%s,curInfo=%s' % (devNo, self.event_data))
  1207. cmdCode = self.event_data.get('cmdCode')
  1208. if cmdCode == CMD_CODE.SWIPE_CARD_10:
  1209. self._do_card_balance()
  1210. elif cmdCode == CMD_CODE.DEVICE_CARD_CHARGE_2D:
  1211. self._do_charge_consume()
  1212. else:
  1213. logger.info("undefined JNDZNewCardChargeEvent cmd <{}>".format(cmdCode))
  1214. def _do_card_balance(self):
  1215. """
  1216. 查询卡的余额
  1217. :return:
  1218. """
  1219. cardNo = self.event_data.get("cardNo", "")
  1220. cardCst = self.event_data.get("preFee", 25.6)
  1221. card = self.update_card_dealer_and_type(cardNo)
  1222. if not card or not card.openId or card.frozen:
  1223. return self.deviceAdapter.response_use_card(SWIPE_CARD_RES.INVALID_CARD_02, 0)
  1224. # 是否存在没有到账的余额 进行充值
  1225. card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id))
  1226. self.recharge_id_card(
  1227. card=card,
  1228. rechargeType='append',
  1229. order=card_recharge_order
  1230. )
  1231. card.reload()
  1232. # 检查卡的余额是否足够
  1233. if RMB(card.balance) >= RMB(cardCst):
  1234. res = SWIPE_CARD_RES.SUCCESS_00
  1235. else:
  1236. res = SWIPE_CARD_RES.BALANCE_NOT_ENOUGH_01
  1237. return self.deviceAdapter.response_use_card(res, card.balance)
  1238. def _do_charge_consume(self):
  1239. if self.event_data.get("result", RESULT_CODE.FAILURE) == RESULT_CODE.FAILURE:
  1240. logger.info("receive failure card charge data <{}>".format(self.event_data.get("sourceData", '')))
  1241. return
  1242. # 回复主板是正常的2D指令
  1243. sessionId = self.event_data.get("sessionId")
  1244. self.deviceAdapter._response_to_2D("{:0>10}".format(sessionId))
  1245. # 判断是否这个 sessionId 是否已经被执行 判断方式是该端口中是否有这个 sessionId
  1246. portStr = self.event_data.get("portStr")
  1247. if not portStr:
  1248. logger.info("receive card charge data <{}> without port !".format(self.event_data.get("sourceData", '')))
  1249. return
  1250. portCache = Device.get_dev_control_cache(self.device.devNo).get(portStr, dict())
  1251. if portCache.get("status", Const.DEV_WORK_STATUS_WORKING) == Const.DEV_WORK_STATUS_WORKING and portCache.get(
  1252. "sessionId") == sessionId:
  1253. logger.info(
  1254. "receive card charge data <{}> has been handle, <{}>!".format(self.event_data.get("sourceData", ''),
  1255. sessionId))
  1256. return
  1257. # 接下来判断上传成功启动的类别 由硬币/卡类型决定
  1258. chargeType = self.event_data.get("chargeType")
  1259. if chargeType == "00":
  1260. self._charge_with_coin()
  1261. elif chargeType == "01":
  1262. self._charge_with_card()
  1263. elif chargeType == "02":
  1264. self._charge_with_remote()
  1265. else:
  1266. logger.info("card recharge type <{}> undefined! <{}>".format(chargeType, self.event_data.get("sourceData")))
  1267. def _charge_with_card(self):
  1268. """
  1269. 充值卡 的充值
  1270. :return:
  1271. """
  1272. cardType = self.event_data.get("cardType")
  1273. if cardType == CARD_TYPE.OFFLINE_CARD:
  1274. self._charge_with_offline_card()
  1275. elif cardType == CARD_TYPE.ONLINE_CARD:
  1276. self._charge_with_online_card()
  1277. elif cardType == CARD_TYPE.MONTHLY_CARD:
  1278. self._charge_with_monthly_card()
  1279. elif cardType == CARD_TYPE.FULL_CARD:
  1280. self._charge_with_full_card()
  1281. else:
  1282. logger.info(
  1283. "card recharge card type <{}> undefined! <{}>".format(cardType, self.event_data.get("sourceData")))
  1284. def _charge_with_coin(self):
  1285. """
  1286. 投币的上报
  1287. :return:
  1288. """
  1289. pass
  1290. def _charge_with_remote(self):
  1291. logger.info("not supper charge type! {}".format(self.event_data))
  1292. def _charge_with_online_card(self):
  1293. """
  1294. 在线卡启动充值
  1295. :return:
  1296. """
  1297. portStr = self.event_data.get("portStr")
  1298. needElec = self.event_data.get("elec", 0)
  1299. cardNo = self.event_data.get("cardNo")
  1300. cardCst = self.event_data.get("cardCst")
  1301. card = self.update_card_dealer_and_type(cardNo)
  1302. if not card or not card.openId or card.frozen:
  1303. logger.warning("error card, cardNo is {}".format(cardNo))
  1304. return
  1305. res, cardBalance = self.consume_money_for_card(card, RMB(cardCst))
  1306. if res != 1:
  1307. logger.warning("consume error!!!, cardNo is {}".format(cardNo))
  1308. return
  1309. consumeDict = {
  1310. "chargeIndex": portStr,
  1311. "needElec": needElec
  1312. }
  1313. orderNo, cardOrderNo = self.record_consume_for_card(card, RMB(cardCst), servicedInfo=consumeDict)
  1314. portCache = {
  1315. "isStart": True,
  1316. "status": Const.DEV_WORK_STATUS_WORKING,
  1317. "openId": card.openId,
  1318. "price": cardCst,
  1319. "coins": cardCst,
  1320. "needElec": needElec,
  1321. "startTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  1322. "startTimeStamp": int(time.time()),
  1323. "consumeType": "card",
  1324. }
  1325. Device.update_dev_control_cache(self.device.devNo, {portStr: portCache})
  1326. ServiceProgress.register_card_service(
  1327. self.device,
  1328. int(portStr),
  1329. card,
  1330. consumeOrder={
  1331. "orderNo": orderNo,
  1332. "cardOrderNo": cardOrderNo,
  1333. }
  1334. )
  1335. def _charge_with_offline_card(self):
  1336. """
  1337. 离线卡启动充值
  1338. :return:
  1339. """
  1340. pass
  1341. def _charge_with_monthly_card(self):
  1342. """
  1343. 包月在线卡启动
  1344. :return:
  1345. """
  1346. logger.info("not supper card charge type! {}".format(self.event_data))
  1347. def _charge_with_full_card(self):
  1348. """
  1349. 充满自停卡
  1350. :return:
  1351. """
  1352. logger.info("not supper card charge type! {}".format(self.event_data))
  1353. class JNDZDoubleSerialCardChargeEvent(ChargingJNDZWorkEvent):
  1354. def response_use_card(self, res, leftBalance):
  1355. sourceData = self.event_data.get("sourceData", "")
  1356. # 与模块驱动约定 最后两个字节为uart_id
  1357. uartId = sourceData[-2:]
  1358. # 发送指令使用221 主要考虑的是帧头的问题
  1359. self.deviceAdapter.response_use_card(res, leftBalance, uartId)
  1360. class ChargingJNDZReportEvent(WorkEvent):
  1361. def do(self, **args):
  1362. if self.device.owner.supports("supportBeiJingFengTai"):
  1363. from apps.web.south_intf.bj_north.api import get_update_port_num
  1364. get_update_port_num(self.device.devNo)
  1365. class JNDZVirtualCardWorkEvent(WorkEvent):
  1366. """
  1367. 处理新增的在线卡和虚拟卡指令
  1368. """
  1369. def _do_52(self):
  1370. # 回复ak
  1371. ackId = self.event_data['ack_id']
  1372. self.deviceAdapter.response_ak_5X(ackId)
  1373. # 解析参数
  1374. cardNo = self.event_data['cardNo']
  1375. coins = self.event_data['coins']
  1376. needTime = self.event_data['needTime']
  1377. port = self.event_data['port']
  1378. deductType = self.event_data['deductType']
  1379. # 组装package
  1380. package = {
  1381. 'coins': coins,
  1382. 'unit': u'分钟',
  1383. 'time': needTime,
  1384. }
  1385. # 找卡 虚拟卡
  1386. card = self.update_card_dealer_and_type(cardNo) # type: Card
  1387. virtual_card = card.bound_virtual_card # type: UserVirtualCard
  1388. # 非使用虚拟卡的逻辑
  1389. if deductType == CARD_DEDUCTTYPE.DEDUCT_BALANCE:
  1390. # 不需要组装package 直接card 扣除balance
  1391. orderNo, cardConsumeOrderNo = self.record_consume_for_card(
  1392. card=card,
  1393. money=RMB(coins),
  1394. attachParas={
  1395. "chargeIndex": port
  1396. },
  1397. servicedInfo={
  1398. 'needTime': needTime
  1399. })
  1400. res, _ = self.consume_money_for_card(
  1401. card=card,
  1402. money=RMB(coins))
  1403. # 记录开始充电时间
  1404. self.event_data.update({'startTime': time.time()})
  1405. if res != 1:
  1406. logger.warning("consume error!!!, cardNo is {}".format(card.cardNo))
  1407. return
  1408. elif deductType == CARD_DEDUCTTYPE.DEDUCT_TIME:
  1409. # 使用虚拟卡的逻辑
  1410. # 建立订单
  1411. orderNo, cardConsumeOrderNo = self.record_consume_for_card(
  1412. card=card,
  1413. money=RMB(0.0),
  1414. attachParas={
  1415. 'chargeIndex': port
  1416. },
  1417. servicedInfo={
  1418. 'needTime': needTime
  1419. })
  1420. # 进行扣费
  1421. vCardConsumeRecord = virtual_card.consume(openId=card.openId, group=self.device.group, dev=self.device,
  1422. package=package, attachParas={}, nickname=card.cardName)
  1423. # 记录开始充电时间
  1424. self.event_data.update({'startTime': time.time()})
  1425. if not vCardConsumeRecord:
  1426. logger.error('use virtual card to consume failure. id = {}'.format(str(virtual_card.id)))
  1427. return
  1428. # 记录开始充电时间
  1429. self.event_data.update({'startTime':time.time()})
  1430. # 插入虚拟卡的ID 退费的时候使用
  1431. self.event_data.update({'vCardId': str(virtual_card.id)})
  1432. self.event_data.update({"consumeRcdId": str(vCardConsumeRecord.id)})
  1433. # 建立sp 建立缓存
  1434. # 注册服务
  1435. ServiceProgress.register_card_service(
  1436. self.device,
  1437. int(port),
  1438. card,
  1439. {
  1440. 'orderNo': orderNo,
  1441. 'money': coins,
  1442. 'coin': coins,
  1443. 'needTime': needTime,
  1444. 'cardOrderNo': cardConsumeOrderNo
  1445. })
  1446. billingType = self.device.get("otherConf", dict()).get('billingType', 'time')
  1447. self.event_data.update({'billingType': billingType})
  1448. self.event_data.update({'cardId': str(card.id)})
  1449. self.event_data.update({'openId': card.openId})
  1450. Device.update_port_control_cache(self.device['devNo'],self.event_data)
  1451. # 通知用户卡启动
  1452. self.notify_user(card.managerialOpenId,'start_device', **{
  1453. 'title': u'启动成功!您的卡号是%s,卡别名:%s' % (card.cardNo, card.nickName),
  1454. 'startTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  1455. })
  1456. self.deviceAdapter.response_use_card_52(cardNo)
  1457. def do(self, **args):
  1458. devNo = self.device.devNo
  1459. logger.info('JingNengDianZi charging event detected, devNo=%s,curInfo=%s' % (devNo, self.event_data))
  1460. cmdCode = self.event_data['cmdCode']
  1461. if cmdCode == CMD_CODE.SWIPE_CARD_50:
  1462. cardNo = self.event_data['cardNo']
  1463. card = self.update_card_dealer_and_type(cardNo)
  1464. virtual_card = card.bound_virtual_card
  1465. if card == None:
  1466. status = '00'
  1467. balance = '0000'
  1468. leftDayQuota = '0000'
  1469. self.deviceAdapter.response_balance_inquiry_50(balance, leftDayQuota, cardNo, status)
  1470. return
  1471. if virtual_card:
  1472. dayKey = datetime.datetime.now()
  1473. leftDayQuota = None
  1474. leftDayQuotaList = virtual_card.calc_left_day_quota(dayKey.strftime("%Y-%m-%d"))
  1475. for leftDayQuotaDict in leftDayQuotaList:
  1476. if leftDayQuotaDict['unit'] == u'分钟':
  1477. leftDayQuota = leftDayQuotaDict['count']
  1478. if leftDayQuota > 0:
  1479. status = '01'
  1480. else:
  1481. leftTotalQuotaList = virtual_card.calc_left_total_quota()
  1482. for leftTotalQuotaDict in leftTotalQuotaList:
  1483. if leftTotalQuotaDict['unit'] == u'分钟':
  1484. leftTotalQuota = leftTotalQuotaDict['count']
  1485. if leftTotalQuota > 0:
  1486. status = '04'
  1487. else:
  1488. status = '05'
  1489. if leftDayQuota == None:
  1490. logger.debug('leftDayQuota not have unit 分钟')
  1491. status = '00'
  1492. balance = '0000'
  1493. leftDayQuota = '0000'
  1494. power = virtual_card.power
  1495. self.deviceAdapter.response_balance_inquiry_50(balance, leftDayQuota, cardNo, status,power)
  1496. return
  1497. if dayKey > virtual_card.expiredTime:
  1498. status = '06'
  1499. elif virtual_card.status == 'freeze':
  1500. status = '03'
  1501. power = virtual_card.power
  1502. self.deviceAdapter.response_balance_inquiry_50('0000', leftDayQuota, cardNo, status,power)
  1503. else:
  1504. if int(card.balance) > 0:
  1505. status = '02'
  1506. else:
  1507. status = '07'
  1508. if card.status == 'freeze':
  1509. status = '03'
  1510. power = '0000'
  1511. self.deviceAdapter.response_balance_inquiry_50(max(float(card.balance),0), '0000', cardNo, status,power)
  1512. elif cmdCode == CMD_CODE.SWIPE_CARD_52:
  1513. self._do_52()
  1514. elif cmdCode == CMD_CODE.SWIPE_CARD_56:
  1515. # 回复ak
  1516. ackId = self.event_data['ack_id']
  1517. self.deviceAdapter.response_ak_5X(ackId)
  1518. if self.event_data.get("reasonCode") in ["03", "0B"]:
  1519. self.__do_fault_record()
  1520. # 解析参数
  1521. port = self.event_data['port']
  1522. leftTime = self.event_data['usedTime']
  1523. leftElec = self.event_data['usedElec']
  1524. refundMoney = RMB(self.event_data['refundMoney']) * 0.1
  1525. refundTime = self.event_data['refundTime']
  1526. refundType = self.event_data['refundType']
  1527. # 找到缓存
  1528. lineInfo = Device.get_port_control_cache(devNo, port)
  1529. # 找到卡
  1530. cardId = lineInfo.get('cardId', '')
  1531. if not cardId:
  1532. logger.error('cardId is not found')
  1533. return
  1534. card = Card.objects.get(id=cardId)
  1535. virtualCard = card.related_virtual_card
  1536. money = RMB(lineInfo['coins'])
  1537. logger.debug('port<{}> cache is: {}'.format(port, str(lineInfo)))
  1538. group = Group.get_group(self.device['groupId'])
  1539. dealer = Dealer.objects(id=group['ownerId']).first()
  1540. if not dealer:
  1541. logger.error('dealer is not found, dealerId=%s' % group['ownerId'])
  1542. return
  1543. agent = Agent.objects(id=dealer.agentId).first()
  1544. if not agent:
  1545. logger.error('agent is not found, agentId=%s' % dealer.agentId)
  1546. return
  1547. if 'coins' not in lineInfo:
  1548. logger.debug('port cache has no coins. no order in port {}'.format(port))
  1549. return
  1550. extra = []
  1551. consumeDict = {
  1552. 'chargeIndex': port,
  1553. 'leftTime': leftTime,
  1554. 'leftElec': leftElec,
  1555. 'reason': self.event_data['reason'],
  1556. }
  1557. if self.device.is_auto_refund:
  1558. # 非使用虚拟卡的逻辑
  1559. if refundType == CARD_REFUNDTYPE.REFUND_BALANCE:
  1560. self.refund_money_for_card(refundMoney, str(card.id))
  1561. usedTime = int((time.time() - int(lineInfo['startTime'])) / 60)
  1562. consumeDict.update({"usedTime":usedTime})
  1563. consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: refundMoney.mongo_amount})
  1564. extra.append({u'消费明细': u'消费{}金币,退款{}金币'.format(money - refundMoney, refundMoney)})
  1565. # 使用虚拟卡的逻辑
  1566. elif refundType == CARD_REFUNDTYPE.REFUND_TIME:
  1567. vCardId = lineInfo['vCardId']
  1568. spendElec = 0
  1569. consumeRcdId = lineInfo.get('consumeRcdId', None)
  1570. if consumeRcdId is None:
  1571. logger.info('can not find consume rcd id')
  1572. return
  1573. usedTime = int(lineInfo['needTime']) - int(refundTime)
  1574. consumeDict.update({'usedTime':usedTime})
  1575. extra.append({u'虚拟卡券': u'{}--{}'.format(virtualCard.cardName, virtualCard.cardNo)})
  1576. extra.append({u'消费明细': u'消费{}分钟'.format(usedTime)})
  1577. try:
  1578. virtualCardConsumeRcd = VCardConsumeRecord.objects.get(id=consumeRcdId)
  1579. vCard = UserVirtualCard.objects.get(id=vCardId)
  1580. except DoesNotExist, e:
  1581. logger.info('can not find the consume rcd id = %s' % consumeRcdId)
  1582. else:
  1583. vCard.refund_quota(virtualCardConsumeRcd, usedTime, spendElec, RMB(refundMoney).mongo_amount)
  1584. ServiceProgress.update_progress_and_consume_rcd(
  1585. self.device['ownerId'],
  1586. {'open_id': lineInfo['openId'], 'device_imei': self.device['devNo'],
  1587. 'port': int(port), 'isFinished': False}, consumeDict
  1588. )
  1589. self.notify_user(self.get_managerialOpenId_by_openId(lineInfo["openId"]), extra)
  1590. self.deviceAdapter.respone_use_card_finished_56(card.cardNo)
  1591. if self.device.owner.supports("supportBeiJingFengTai"):
  1592. orderNo = lineInfo.get('orderNo')
  1593. from apps.web.south_intf.bj_north import post_charging_record_info
  1594. post_charging_record_info(orderNo)
  1595. Device.clear_port_control_cache(self.device.devNo, port)
  1596. class JNDZYKTCardChargeEvent(WorkEvent):
  1597. def __get_swipe_card_cache(self):
  1598. return TempValues.get(swipe_card_cache_key(self.device.devNo))
  1599. def __set_swipe_card_cache(self, cache_info):
  1600. logger.debug('swipe cache info is: {}'.format(cache_info))
  1601. return TempValues.set(swipe_card_cache_key(self.device.devNo), cache_info, 900)
  1602. def do(self, **args):
  1603. devNo = self.device.devNo
  1604. logger.info('JNDZNewCardChargeEvent charging event detected, devNo=%s,curInfo=%s' % (devNo, self.event_data))
  1605. cmdCode = self.event_data.get('cmdCode')
  1606. if cmdCode == CMD_CODE.SWIPE_CARD_10:
  1607. self._do_YKT_card_proc_10()
  1608. elif cmdCode == CMD_CODE.DEVICE_SUBMIT_OFFLINE_COINS_20:
  1609. self._do_YKT_card_start_20()
  1610. def _do_YKT_card_proc_10(self):
  1611. cardNo = hex(int(self.event_data['cardNo'])).upper()[2:]
  1612. preFee = RMB(self.event_data['preFee'])
  1613. devNo = self.device.devNo
  1614. cardYKT = self.query_to_ykt_norther(cardNo)
  1615. if cardYKT["code"] != "000000":
  1616. return self.deviceAdapter.response_use_card(SWIPE_CARD_RES.INVALID_CARD_02, 0)
  1617. if cardYKT["cardInfo"].get("custStatus") != 1:
  1618. return self.deviceAdapter.response_use_card(SWIPE_CARD_RES.INVALID_CARD_02, 0)
  1619. # 先把卡信息更新到YiKaTongCard
  1620. cardInfo = cardYKT.get('cardInfo')
  1621. YiKaTongCard.update_card(cardInfo)
  1622. card = self.update_card_dealer_and_type(cardNo)
  1623. if not card:
  1624. pastCardNo = YiKaTongCard.get_cardNo(cardNo)
  1625. card = self.update_card_dealer_and_type(pastCardNo)
  1626. if not card:
  1627. return self.deviceAdapter.response_use_card(SWIPE_CARD_RES.INVALID_CARD_02, 0)
  1628. card.cardNo = cardNo
  1629. card.save()
  1630. card.reload()
  1631. balance = cardYKT['cardInfo']['oddFare'] * 0.01
  1632. if balance <= 0:
  1633. return self.deviceAdapter.response_use_card(SWIPE_CARD_RES.BALANCE_NOT_ENOUGH_01, balance)
  1634. card.balance = RMB(balance)
  1635. card.save()
  1636. if card.openId == '' or card.frozen:
  1637. return self.response_use_card(SWIPE_CARD_RES.INVALID_CARD_02, RMB(0))
  1638. # 记录卡消费记录以及消费记录
  1639. orderNo, cardOrderNo = self.record_consume_for_card(card, preFee)
  1640. # 记录当前服务的progress.没有上报端口号,所以先用-1标记,表示端口未知。端口等消费的时候会报上来
  1641. ServiceProgress.register_card_service(self.device, -1, card,
  1642. {
  1643. 'orderNo': orderNo,
  1644. 'money': self.event_data['preFee'],
  1645. 'coin': self.event_data['preFee'], 'needTime': 0,
  1646. 'cardOrderNo': cardOrderNo
  1647. })
  1648. self.consume_money_for_card(card, preFee)
  1649. self.deviceAdapter.response_use_card(SWIPE_CARD_RES.SUCCESS_00, balance)
  1650. self.notify_balance_has_consume_for_card(card, preFee)
  1651. def _do_YKT_card_start_20(self):
  1652. """
  1653. 启动设备
  1654. :return:
  1655. """
  1656. port = self.event_data['port']
  1657. consumeDict = {'chargeIndex': port, 'elec': self.event_data['elec'],
  1658. 'money': self.event_data['coins'], 'needTime': u'刷卡订购%s分钟' % self.event_data['needTime']}
  1659. queryDict = {'device_imei': self.device['devNo'],
  1660. 'port': -1, 'isFinished': False,
  1661. 'cardId': {'$ne': ''}, 'start_time': {'$gte': int(time.time()) - 3600}}
  1662. progressDict = {'port': port}
  1663. ServiceProgress.update_progress_and_consume_rcd(ownerId=self.device['ownerId'], queryDict=queryDict,
  1664. consumeDict=consumeDict, updateConsume=True,
  1665. progressDict=progressDict)
  1666. # 找出对应的卡的ID记录到端口内存数据
  1667. queryDict.update(progressDict)
  1668. rcds = ServiceProgress.get_collection().find(queryDict, {'cardId': 1, 'open_id': 1, "consumeOrder": 1},
  1669. sort=[('start_time', -1)])
  1670. if rcds.count() == 0:
  1671. return
  1672. rcd = rcds[0]
  1673. devObj = Device.objects.get(devNo=self.device['devNo'])
  1674. billingType = devObj.otherConf.get('billingType', 'time')
  1675. # 刷卡虚拟卡启动的时候,将consumeRcd 写入到缓存 退费的时候使用
  1676. consumeRcdId = rcd.get("consumeOrder", dict()).get("consumeRcdId")
  1677. if consumeRcdId:
  1678. self.event_data.update({"consumeRcdId": consumeRcdId})
  1679. try:
  1680. vRcd = VCardConsumeRecord.objects(id=consumeRcdId).first()
  1681. self.event_data.update({'vCardId': vRcd.cardId})
  1682. except Exception as e:
  1683. # 防止报错退钱给实体卡, 强制添加vCardId
  1684. self.event_data.update({'vCardId': 'true'})
  1685. self.event_data.update({'billingType': billingType})
  1686. self.event_data.update({'cardId': rcd['cardId']})
  1687. self.event_data.update({'openId': rcd['open_id']})
  1688. cInfo = Device.get_dev_control_cache(self.device.devNo)
  1689. lastPortInfo = cInfo.get(str(port), {})
  1690. # 钱需要累计
  1691. lastCoins = lastPortInfo.get('coins', 0.0)
  1692. self.event_data.update({'coins': self.event_data['coins'] + lastCoins})
  1693. # 电量需要累加
  1694. lastNeedElec = lastPortInfo.get('needElec', 0.0)
  1695. self.event_data.update({'needElec': self.event_data['elec'] + lastNeedElec})
  1696. # 时间需要累加
  1697. lastNeedTime = lastPortInfo.get('needTime', 0.0)
  1698. self.event_data.update({'needTime': self.event_data['needTime'] + lastNeedTime})
  1699. self.event_data.update({'startTime': datetime.datetime.now().strftime(Const.DATETIME_FMT)})
  1700. self.event_data.update({'isStart': True, 'status': Const.DEV_WORK_STATUS_WORKING})
  1701. #: 记录该端口累计需要的时间和钱,cardId
  1702. Device.update_port_control_cache(self.device['devNo'], self.event_data)