xiaokedou.py 64 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import json
  5. import logging
  6. import time
  7. from arrow import Arrow
  8. from django.conf import settings
  9. from apilib.monetary import RMB, VirtualCoin, Ratio
  10. from apilib.systypes import StrEnum
  11. from apilib.utils_string import make_title_from_dict
  12. from apps.web.agent.models import Agent
  13. from apps.web.constant import Const, DEALER_CONSUMPTION_AGG_KIND, FAULT_LEVEL, CONSUMETYPE, DeviceCmdCode
  14. from apps.web.core.adapter.xiaokedou import ChargingXiaoKeDouBox
  15. from apps.web.core.exceptions import ServiceException
  16. from apps.web.core.networking import MessageSender
  17. from apps.web.dealer.models import Dealer
  18. from apps.web.device.models import Group, Device, DeviceDict, GroupDict
  19. from apps.web.eventer.base import FaultEvent, WorkEvent, ComNetPayAckEvent, AckEventProcessorIntf, \
  20. IcStartAckEvent, IcRechargeAckEvent, CardRefundAckEvent, IdStartAckEvent, VirtualCardStartAckEvent
  21. from apps.web.eventer import EventBuilder
  22. from apps.web.user.models import CardRechargeOrder, UserVirtualCard, Card, ConsumeRecord, RechargeRecord, \
  23. VCardConsumeRecord, MyUser
  24. from apps.web.user.utils import freeze_user_balance
  25. from apilib.utils_sys import memcache_lock
  26. logger = logging.getLogger(__name__)
  27. class SWIPE_CARD_PARAM_OP(StrEnum):
  28. """
  29. 卡的操作
  30. 格式 描述_命令码
  31. """
  32. #: 减少 (扣费)
  33. DECR_00 = '00'
  34. #: 增加 (充值, 退费)
  35. INCR_01 = '01'
  36. class SWIPE_CARD_RES(StrEnum):
  37. """
  38. 回应卡的操作
  39. 格式 描述_命令码
  40. """
  41. SUCCESS_00 = '00'
  42. BALANCE_NOT_ENOUGH_01 = '01'
  43. INVALID_CARD_02 = '02'
  44. class CARD_TYPE(StrEnum):
  45. OFFLINE_CARD = "00"
  46. ONLINE_CARD = "01"
  47. MONTHLY_CARD = "02"
  48. FULL_CARD = "04"
  49. class builder(EventBuilder):
  50. def __getEvent__(self, device_event):
  51. if 'order_id' in device_event:
  52. if device_event['order_type'] == 'com_start':
  53. return MyComNetPayAckEvent(self.deviceAdapter, device_event)
  54. elif device_event['order_type'] == 'ic_recharge':
  55. return MyIcRechargeAckEvent(self.deviceAdapter, device_event)
  56. elif device_event['order_type'] == 'ic_start':
  57. return MyIcStartAckEvent(self.deviceAdapter, device_event)
  58. elif device_event['order_type'] == 'id_start':
  59. return MyIdStartAckEvent(self.deviceAdapter, device_event)
  60. elif device_event['order_type'] == 'vir_start':
  61. return MyVirtualCardStartAckEvent(self.deviceAdapter, device_event)
  62. elif device_event['order_type'] == 'card_refund':
  63. return MyCardRefundAckEvent(self.deviceAdapter, device_event)
  64. if 'event_type' in device_event:
  65. if device_event['event_type'] == 'card':
  66. return CardEvent(self.deviceAdapter, device_event)
  67. else:
  68. return None
  69. event_data = self.analyze_event_data(device_event['data'])
  70. if event_data is None or 'cmdCode' not in event_data:
  71. return None
  72. if event_data['cmdCode'] in ['0A']:
  73. return JNDZEventerFailure(self.deviceAdapter, event_data)
  74. if event_data['cmdCode'] in ['21']:
  75. return ChargingJNDZReportEvent(self.deviceAdapter, event_data)
  76. def analyze_event_data(self, data):
  77. cmdCode = data[4:6]
  78. #: 上传设备故障
  79. if cmdCode == '0A':
  80. port = int(data[8:10], 16)
  81. errCode = data[10:12]
  82. FaultDict = {
  83. 'A0': ['继电器粘连,请到现场排查', FAULT_LEVEL.FATAL],
  84. 'A1': ['高温', FAULT_LEVEL.CRITICAL],
  85. 'A2': ['低温', FAULT_LEVEL.NORMAL],
  86. 'A3': ['空载,充电头脱落、充电器拔出', FAULT_LEVEL.NORMAL],
  87. 'A4': ['消防(烟感)', FAULT_LEVEL.CRITICAL],
  88. 'A5': ['总功率过载' if port == 255 else '{}号端口过载'.format(port), FAULT_LEVEL.FATAL],
  89. 'A6': ['倾斜', FAULT_LEVEL.FATAL],
  90. 'A7': ['水压高', FAULT_LEVEL.NORMAL],
  91. 'A8': ['水压低', FAULT_LEVEL.NORMAL],
  92. 'A9': ['过压', FAULT_LEVEL.NORMAL],
  93. 'AA': ['欠压', FAULT_LEVEL.NORMAL],
  94. '27': ['水压高恢复', FAULT_LEVEL.NORMAL],
  95. }
  96. return {
  97. 'status': Const.DEV_WORK_STATUS_FAULT,
  98. 'statusInfo': FaultDict.get(errCode, ['设备故障', FAULT_LEVEL.NORMAL])[0],
  99. 'cmdCode': cmdCode,
  100. 'port': port,
  101. 'errCode': errCode,
  102. 'faultContent': FaultDict.get(errCode, [errCode, FAULT_LEVEL.NORMAL])[0],
  103. 'level:': FaultDict.get(errCode, [errCode, FAULT_LEVEL.NORMAL])[1]
  104. }
  105. elif cmdCode == '21':
  106. _result = data[6: 8]
  107. port_num = int(data[8:10], 16)
  108. port_info_urat_info = data[10:170]
  109. port_status_desc = {
  110. "01": "端口空闲",
  111. "02": "端口正在使用",
  112. "03": "端口禁用",
  113. "04": "端口故障",
  114. }
  115. port_info = {}
  116. for index in xrange(0, 160, 16):
  117. item = port_info_urat_info[index: index + 16]
  118. one_port = {}
  119. one_port["port"] = int(item[:2], 16)
  120. one_port["portStatus"] = item[2:4]
  121. # one_port["portStatusDesc"] = port_status_desc.get(item[2:4])
  122. one_port["leftTime"] = int(item[4:8], 16)
  123. one_port["power"] = int(item[8:12], 16)
  124. one_port["usedElec"] = int(item[12:16], 16) * 0.01 # 转换为度要因为DevicePortReport表中的elec为,乘以 0.01
  125. port_info[str(one_port["port"])] = one_port
  126. return {"cmdCode": cmdCode, "result": _result, "portNum": port_num,
  127. "portInfo": port_info, "sourceData": data}
  128. else:
  129. logger.info("receive data <{}>, cmd is invalid".format(data))
  130. class CardEvent(WorkEvent):
  131. def do(self):
  132. if self.event_data['funCode'] == '10':
  133. self._do_get_balance()
  134. def _do_get_balance(self):
  135. RATIO_CONVERSION = 100
  136. smartbox = self.deviceAdapter # type: ChargingXiaoKeDouBox
  137. cardNo = str(int(self.event_data["card_id"], 16))
  138. logger.info('[_do_get_balance] receive cardNo = {}'.format(cardNo))
  139. card = self.update_card_dealer_and_type(cardNo) # type: Card
  140. data = {"funCode": '10', "card_id": self.event_data["card_id"], 'coins': 0} # 十六进制卡号
  141. dealer = self.device.owner
  142. # 无主卡或者是卡被冻结
  143. if not card or not card.openId or card.frozen or not dealer:
  144. logger.info('[_do_get_balance] receive cardNo = {}, card invalid!'.format(cardNo))
  145. data.update({"result": '02'})
  146. return self.send_mqtt(data=data)
  147. # 是否存在没有到账的余额 进行充值
  148. card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id))
  149. self.recharge_id_card(
  150. card=card,
  151. rechargeType='append',
  152. order=card_recharge_order
  153. )
  154. card.reload()
  155. ongoingList = getattr(card, 'ongoingList', []) # 有冻结未结束的订单
  156. # 先不给做一张卡只能开启一单的限制
  157. if card.balance <= RMB(0) or ongoingList:
  158. logger.info('[_do_get_balance] receive cardNo = {}, card balance = {} not enough!'.format(cardNo, card.balance))
  159. data.update({"result": '01'})
  160. return self.send_mqtt(data=data)
  161. server_configs = smartbox.get_server_configs()
  162. onlineCardMinStartCoins = RMB(server_configs.get('onlineCardMinStartCoins', 0))
  163. onlineCardTime = server_configs.get('onlineCardTime', 720.0) # 单位: 分钟
  164. onlineCardELec = server_configs.get('onlineCardELec', 3.0) # 单位: 度
  165. need_time = int(float(onlineCardTime))
  166. need_elec = int(float(onlineCardELec) * 100)
  167. data.update({
  168. 'coins': int(RMB(card.balance) * 10), # 单位: 角
  169. 'need_time': need_time,
  170. 'need_elec': need_elec,
  171. 'attach_paras': {'openId': card.openId}
  172. })
  173. if float(self.device['driverVersion'].split('.')[-1]) <= 9:
  174. data.update({'account_rule': smartbox.get_account_rule()})
  175. else:
  176. data.update({'account_rule': smartbox.get_account_rule_new()})
  177. data['attach_paras'].update({'precision': 3600})
  178. if card.balance > onlineCardMinStartCoins:
  179. data.update({"result": '00'})
  180. else:
  181. data.update({"result": '01'})
  182. if self.device.bill_as_service_feature.on:
  183. serviceFee = int(self.device.bill_as_service_feature.service_charge * RATIO_CONVERSION)
  184. elecFee = int(self.device.bill_as_service_feature.elec_charge * RATIO_CONVERSION)
  185. data.update({"serviceFee": serviceFee,
  186. "elecFee": elecFee,
  187. "RATIO_CONVERSION": RATIO_CONVERSION,
  188. "billingMethod": "billAsService"})
  189. self.send_mqtt(data=data)
  190. def send_mqtt(self, data=None, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, otherData=None):
  191. '''
  192. 发送mqtt 指令默认210 返回data
  193. '''
  194. result = MessageSender.send(self.device, cmd,
  195. data)
  196. if 'rst' in result and result['rst'] != 0:
  197. if result['rst'] == -1:
  198. raise ServiceException(
  199. {'result': 2, 'description': u'该设备正在玩命找网络,请您稍候再试', 'rst': -1})
  200. elif result['rst'] == 1:
  201. raise ServiceException(
  202. {'result': 2, 'description': u'该设备忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能', 'rst': 1})
  203. else:
  204. if cmd in [DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE]:
  205. return
  206. return result.get('data', 'ok')
  207. class JNDZEventerFailure(FaultEvent):
  208. def do(self, **args):
  209. faultContent = self.event_data.get('faultContent', '')
  210. level = self.event_data.get('level', '')
  211. errCode = self.event_data.get('errCode')
  212. port = self.event_data.get('port')
  213. if port and port != 255:
  214. title = u'注意!您的设备{}号端口发出告警!'.format(port)
  215. else:
  216. title = u'注意!您的设备发出告警!'
  217. if errCode in ['A3']: # 空载 无需显示在经销商后台
  218. return
  219. # else:
  220. # Device.update_dev_control_cache(self.device['devNo'], self.event_data)
  221. group = Group.get_group(self.device.groupId)
  222. if self.is_notify_dealer:
  223. self.notify_dealer(
  224. templateName="device_fault",
  225. title=title,
  226. device=u'{}-{}'.format(self.device.devTypeName, self.device.logicalCode),
  227. fault=faultContent,
  228. location=u'{address}-{groupName}-{groupNumber}号设备({logicalCode})'.format(address=group["address"],
  229. groupName=group["groupName"],
  230. groupNumber=self.device[
  231. "groupNumber"],
  232. logicalCode=self.device[
  233. "logicalCode"]),
  234. notifyTime=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  235. )
  236. # 记录错误故障
  237. self.record(
  238. title=title,
  239. description=faultContent,
  240. level=level
  241. )
  242. def is_server_refund(billingType, dev, dealer, agent):
  243. # type:(str, DeviceDict, Dealer, Agent)->bool
  244. if billingType != 'time':
  245. if 'jhCardElecRefund' in dealer.features:
  246. return False
  247. if dev.is_auto_refund:
  248. return True
  249. else:
  250. return False
  251. else:
  252. if 'huopo_card_time_refund' in agent.features:
  253. return True
  254. else:
  255. support_server_refund = dev.devType.get('features', {}).get(
  256. 'support_server_refund', False)
  257. if not support_server_refund:
  258. return False
  259. if dev.is_auto_refund:
  260. return True
  261. else:
  262. return False
  263. class StartAckEventPreProcessor(AckEventProcessorIntf):
  264. def analysis_reason(self, reason):
  265. FINISHED_CHARGE_REASON_MAP = {
  266. '00': u'购买的充电时间或者电量已经用完',
  267. '01': u'系统判断为异常断电(插头被拔或者松动,或者电瓶已经充满),电瓶车充电器种类繁多,可能存在误差',
  268. '02': u'电池已经充满',
  269. '03': u'警告!您的电池功率超过本机最大限制。为了公共安全,不建议您在该充电桩充电',
  270. '04': u'远程断电',
  271. '11': u'设备或是端口出现问题,被迫停止',
  272. # 服务器定义的停止事件
  273. '90': u'订单异常,设备使用期间可能存在离线时长超过一小时',
  274. '91': u'系统检测到充电已结束, 平台结单', # 主板轮询出来的停止(无结束上报)
  275. '92': u'管理员远程关闭订单',
  276. '95': u'继电器粘连、短路,停止充电',
  277. '96': u'空载,充电头脱落、充电器拔出',
  278. '97': u'用户远程手动停止订单',
  279. '98': u'您购买的时间已经用完了',
  280. '99': u'您购买的电量已经用完了',
  281. '100': u'金额已用完了',
  282. 'EE': u'经销商强制结束',
  283. }
  284. return FINISHED_CHARGE_REASON_MAP.get(reason, '')
  285. def pre_processing(self, device, event_data):
  286. # type:(DeviceDict, dict)->dict
  287. source = json.dumps(event_data, indent=4)
  288. event_data['source'] = source
  289. if 'duration' in event_data and event_data['duration'] != 0:
  290. duration1 = event_data.get('duration')
  291. event_data['duration'] = round(duration1 / 60.0, 1)
  292. if 'fts' in event_data and 'sts' in event_data:
  293. duration2 = event_data['fts'] - event_data['sts']
  294. if abs(duration2 - duration1) < 300:
  295. duration = max(duration1, duration2)
  296. event_data['duration'] = round(duration / 60.0, 1)
  297. if 'elec' in event_data:
  298. elec = event_data.get('elec')
  299. event_data['elec'] = round(elec / 3600000.0, 3)
  300. if 'need_time' in event_data:
  301. event_data['needTime'] = event_data.pop('need_time') / 60.0
  302. if 'need_elec' in event_data:
  303. event_data['needElec'] = event_data.pop('need_elec') / 3600000.0
  304. if 'port' in event_data:
  305. pass
  306. if event_data['status'] == 'finished':
  307. event_data['reasonDesc'] = self.analysis_reason(event_data.get('reason', '91'))
  308. if 'need_pay' in event_data:
  309. precision = event_data.get('attach_paras', {}).get('precision', 1000.0)
  310. event_data['needPay'] = VirtualCoin(event_data.get('need_pay', 0) / (100.0 * precision)) # 单位(分 精度(老的设备为1000 新的设备为3600))
  311. if event_data.get('billingMethod') == 'prepaid' or event_data.get('billingMethod') == 'billAsService':
  312. if event_data.get('reason') in ['00', '98', '99']:
  313. event_data['reasonDesc'] = '订购的套餐已用完'
  314. if 'sub' in event_data:
  315. pass
  316. return event_data
  317. class ChargingJNDZReportEvent(WorkEvent):
  318. def do(self, **args):
  319. ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  320. logger.info('ctrInfo:<{}> upload_21:<{}>'.format(ctrInfo, self.event_data))
  321. class MyComNetPayAckEvent(ComNetPayAckEvent):
  322. def __init__(self, smartBox, event_data):
  323. super(MyComNetPayAckEvent, self).__init__(smartBox, event_data, StartAckEventPreProcessor())
  324. def post_before_start(self, order=None):
  325. # 记录处理的源数据报文
  326. uart_source = getattr(order, 'uart_source', [])
  327. uart_source.append({
  328. 'rece_running': self.event_data.get('source'),
  329. 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  330. })
  331. order.uart_source = uart_source
  332. order.save()
  333. def post_after_start(self, order=None):
  334. pass
  335. def post_before_finish(self, order=None):
  336. # 记录处理的源数据报文
  337. uart_source = getattr(order, 'uart_source', [])
  338. uart_source.append({
  339. 'rece_finished': self.event_data.get('source'),
  340. 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  341. })
  342. order.uart_source = uart_source
  343. order.save()
  344. def post_after_finish(self, order=None):
  345. pass
  346. def merge_order(self, master_order, sub_orders):
  347. # type:(ConsumeRecord, list)->dict
  348. package = master_order.package
  349. needTime, needElec, coins = self.deviceAdapter._check_package(package)
  350. start_time = Arrow.fromdatetime(master_order.startTime, tzinfo=settings.TIME_ZONE)
  351. portDict = {'estimatedTs': int(start_time.timestamp + (needTime + 5) * 60)}
  352. if package.get('billingMethod') == CONSUMETYPE.POSTPAID or package.get('billingMethod') == CONSUMETYPE.BILL_AS_SERVICE_POSTPAID:
  353. portDict['needKind'] = 'needTime'
  354. portDict['needValue'] = needTime
  355. portDict['unit'] = u'分钟'
  356. portDict['coins'] = 0
  357. portDict['consumeType'] = package.get('billingMethod')
  358. else:
  359. unit = package.get('unit')
  360. portDict['coins'] = coins
  361. portDict['consumeType'] = CONSUMETYPE.MOBILE
  362. if unit == '度':
  363. portDict['needKind'] = 'needElec'
  364. portDict['needValue'] = needElec
  365. else:
  366. portDict['needKind'] = 'needTime'
  367. portDict['needValue'] = needTime
  368. for sub_order in sub_orders:
  369. needTime, needElec, coins = self.deviceAdapter._check_package(sub_order.package)
  370. portDict['coins'] += coins
  371. if unit == '度':
  372. portDict['needValue'] += needElec
  373. else:
  374. portDict['needValue'] += needTime
  375. return portDict
  376. # TODO 结束事件处理
  377. def do_finished_event(self, master_order, sub_orders, merge_order_info):
  378. # type: (ConsumeRecord, [ConsumeRecord], dict)->None
  379. if merge_order_info['consumeType'] == CONSUMETYPE.POSTPAID or merge_order_info['consumeType'] == CONSUMETYPE.BILL_AS_SERVICE_POSTPAID:
  380. self.do_postpaid_time_finished(master_order, sub_orders, merge_order_info)
  381. else:
  382. self.do_prepaid_time_finished(master_order, sub_orders, merge_order_info)
  383. @property
  384. def is_postpaid(self):
  385. return True
  386. def do_postpaid_time_finished(self, master_order, sub_orders=None, merge_order_info=None):
  387. duration = self.event_data.get('duration', 0)
  388. elec = self.event_data.get('elec', 0)
  389. # 做一个时间溢出的保护
  390. try:
  391. dealer = self.device.owner
  392. agent = Agent.objects.get(id=dealer.agentId)
  393. features = agent.features
  394. if 'time_overflow_protection' in features:
  395. if 'needKind' in merge_order_info and merge_order_info['needKind'] == 'needTime':
  396. if abs(duration - merge_order_info['needValue']) <= 10:
  397. duration = merge_order_info['needValue']
  398. except:
  399. pass
  400. needPay = self.event_data.get('needPay', 0)
  401. consumeDict = {
  402. 'reason': self.event_data.get('reasonDesc'),
  403. 'chargeIndex': str(master_order.used_port),
  404. DEALER_CONSUMPTION_AGG_KIND.DURATION: duration,
  405. DEALER_CONSUMPTION_AGG_KIND.ELEC: elec,
  406. }
  407. if self.device.bill_as_service_feature.on:
  408. ratio_conversion = self.event_data.get('ratio_conversion', 1)
  409. precision = self.event_data.get('precision', 3600)
  410. needPay = VirtualCoin(round(float(needPay) / ratio_conversion / 10, 2))
  411. serviceFee = round(self.event_data.get("service_fee", 0) / ratio_conversion / (1000.0 * precision), 2)
  412. elecFee = round(self.event_data.get("elec_fee", 0) / ratio_conversion / (1000.0 * precision), 2)
  413. consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.ELECFEE:elecFee})
  414. consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.SERVICEFEE:serviceFee})
  415. # 没有真正的结束, 等待状态机执行
  416. master_order.servicedInfo.update(consumeDict)
  417. logger.debug(
  418. 'orderNo = {}, orderType = isPostpaid, usedTime = {}, needPayMoney = {}'.format(master_order.orderNo,
  419. duration,
  420. needPay))
  421. # 保护时间判断
  422. refundProtectionTime = int(self.device.otherConf.get('serverConfigs', {}).get('refundProtectionTime', 5))
  423. if duration < refundProtectionTime:
  424. needPay = VirtualCoin(0)
  425. # 先尝试用金币付款
  426. # 1 获取金币汇率
  427. coinRatio = float(self.device.otherConf.get('serverConfigs', {}).get('coinRatio', 1))
  428. # 2 更新订单金额
  429. master_order.coin = (needPay * coinRatio).mongo_amount
  430. master_order.money = needPay
  431. master_order.save()
  432. # 3 使用金币支付
  433. self.pay_order(master_order)
  434. master_order.reload()
  435. extra = [{u'使用详情': '{}号端口, {}(分钟)'.format(master_order.used_port, duration)}]
  436. if self.device.bill_as_service_feature.on:
  437. extra.append({
  438. u'电费金额': u'{}(金币)'.format(serviceFee),
  439. u'服务费金额': u'{}(金币)'.format(elecFee)
  440. })
  441. if master_order.status == ConsumeRecord.Status.FINISHED:
  442. if needPay == VirtualCoin(0):
  443. desc = ''
  444. else:
  445. if master_order.paymentInfo.get('via') == 'virtualCard':
  446. desc = u'(已使用优惠卡券抵扣本次消费)'
  447. master_order.update(coin=VirtualCoin(0), money=RMB(0))
  448. # 结算完了进行退虚拟卡额度处理:
  449. try:
  450. if "rcdId" in master_order.paymentInfo:
  451. consumeRcdId = master_order.paymentInfo['rcdId']
  452. else:
  453. consumeRcdId = master_order.paymentInfo['itemId']
  454. vCardConsumeRcd = VCardConsumeRecord.objects.get(id=consumeRcdId)
  455. vCard = UserVirtualCard.objects.get(id=vCardConsumeRcd.cardId)
  456. vCard.refund_quota(vCardConsumeRcd, duration, 0.0, VirtualCoin(0).mongo_amount)
  457. except:
  458. pass
  459. else:
  460. desc = u'(已使用账户余额自动结算本次消费)'
  461. extra.append({u'消费金额': u'{}(金币)'.format((needPay * coinRatio).mongo_amount)})
  462. if self.device.bill_as_service_feature.on:
  463. extra.append({
  464. u'电费金额': u'{}(金币)'.format(serviceFee),
  465. u'服务费金额': u'{}(金币)'.format(elecFee)
  466. })
  467. else:
  468. desc = u'(您的账户余额已不足以抵扣本次消费,请前往账单中心进行支付)'
  469. extra.append({u'消费金额': u'{}(元)'.format(master_order.money)})
  470. if self.device.bill_as_service_feature.on:
  471. extra.append({
  472. u'电费金额': u'{}(金币)'.format(serviceFee),
  473. u'服务费金额': u'{}(金币)'.format(elecFee)
  474. })
  475. extra.append({u'用户余额': u'{}(金币)'.format(master_order.user.calc_currency_balance(self.device.owner, self.device.group))})
  476. self.event_data['reasonDesc'] += desc
  477. self.notify_to_user(master_order.user.managerialOpenId, extra)
  478. def do_dealer_feature_processing(self, master_order):
  479. '''
  480. 处理一些更具经销商特征要求显示或则隐藏的关于订单的信息
  481. '''
  482. pass
  483. def notify_to_user(self, openId, extra):
  484. group = Group.get_group(self.device['groupId'])
  485. self.notify_user_service_complete(
  486. service_name='充电',
  487. openid=openId,
  488. port='',
  489. address=group['address'],
  490. reason=self.event_data.get('reasonDesc'),
  491. finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  492. extra=extra)
  493. def pay_order(self, order):
  494. order.status = 'running'
  495. order.attachParas['packageId'] = order.package.get('packageId')
  496. order.save()
  497. order.s_to_e()
  498. def do_prepaid_time_finished(self, master_order, sub_orders, merge_order_info):
  499. duration, elec = self.event_data.get('duration', 0), self.event_data.get('elec', 0)
  500. needKind = merge_order_info['needKind']
  501. needValue = merge_order_info['needValue']
  502. coins = VirtualCoin(merge_order_info['coins'])
  503. # 保护时间判断
  504. refundProtectionTime = int(self.device.otherConf.get('serverConfigs', {}).get('refundProtectionTime', 5))
  505. if duration <= refundProtectionTime:
  506. usedRatio = 0
  507. else:
  508. if not self.device.is_auto_refund: # 没有开启退费开关 直接退费
  509. usedRatio = 1
  510. else:
  511. if needKind == 'needTime':
  512. # 做一个时间溢出的保护
  513. duration = min(merge_order_info['needValue'], duration)
  514. usedRatio = duration / (needValue * 1.0)
  515. else:
  516. # 做一个电量溢出保护
  517. elec = min(merge_order_info['needValue'], elec)
  518. usedRatio = elec / (needValue * 1.0)
  519. usedFee = coins * usedRatio
  520. backCoins = coins - usedFee
  521. logger.debug(
  522. 'orderNo = {}, orderType = isPostpaid, usedTime = {},coins = {}, usedFee = {}, backCoins = {}'.format(
  523. master_order.orderNo,
  524. duration, coins,
  525. usedFee, backCoins))
  526. user = master_order.user # type: MyUser
  527. extra = [{u'使用详情': '{}号端口, {}(分钟)'.format(master_order.used_port, duration)}]
  528. if self.device.bill_as_service_feature.on:
  529. ratio_conversion = self.event_data.get('ratio_conversion', 1)
  530. precision = self.event_data.get('precision', 3600)
  531. serviceFee = round(self.event_data.get("service_fee", 0) / ratio_conversion / (1000.0 * precision), 2)
  532. elecFee = round(self.event_data.get("elec_fee", 0) / ratio_conversion / (1000.0 * precision), 2)
  533. extra.append({
  534. u'电费金额': u'{}(金币)'.format(serviceFee),
  535. u'服务费金额': u'{}(金币)'.format(elecFee)
  536. })
  537. if master_order.paymentInfo['via'] == 'free':
  538. extra.append({u'消费金额': u'当前设备免费使用'})
  539. elif master_order.paymentInfo['via'] in ['netPay', 'coins', 'cash', 'coin']:
  540. all_money = RMB(0)
  541. all_refund_money = RMB(0)
  542. orders = [master_order] + sub_orders
  543. refundCash = 'refundRMB_device_event' in self.device.owner.features
  544. for _order in orders[::-1]:
  545. consumeDict = {
  546. 'reason': self.event_data.get('reasonDesc'),
  547. 'chargeIndex': str(master_order.used_port),
  548. }
  549. need_back_coins, need_consume_coins, backCoins = self._calc_refund_info(backCoins, _order.coin)
  550. rechargeRcdId = _order.attachParas.get('linkedRechargeRecordId', '')
  551. if rechargeRcdId != '':
  552. rechargeRcd = RechargeRecord.objects.filter(id=rechargeRcdId, isQuickPay=True).first()
  553. else:
  554. rechargeRcd = None
  555. if refundCash and rechargeRcd: # 退现金特征 + 有充值订单
  556. # 退现金部分
  557. user.clear_frozen_balance(str(_order.id), _order.paymentInfo['deduct'],
  558. back_coins=VirtualCoin(0),
  559. consume_coins=VirtualCoin(_order.coin))
  560. refundRMB = rechargeRcd.money * (float(need_back_coins) / float(_order.coin))
  561. self.refund_net_pay(user, {'rechargeRcdId': rechargeRcdId, 'openId': user.openId},
  562. refundRMB, VirtualCoin(0), consumeDict, True)
  563. all_money += RMB(rechargeRcd.money)
  564. all_refund_money += RMB(refundRMB)
  565. else:
  566. user.clear_frozen_balance(str(_order.id), _order.paymentInfo['deduct'], back_coins=need_back_coins,
  567. consume_coins=need_consume_coins)
  568. consumeDict.update({
  569. DEALER_CONSUMPTION_AGG_KIND.COIN: _order.coin.mongo_amount,
  570. DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS: need_back_coins.mongo_amount})
  571. if _order.orderNo == master_order.orderNo:
  572. consumeDict.update({
  573. DEALER_CONSUMPTION_AGG_KIND.DURATION: duration,
  574. DEALER_CONSUMPTION_AGG_KIND.ELEC: elec,
  575. })
  576. if self.device.bill_as_service_feature.on:
  577. consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.ELECFEE: elecFee})
  578. consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.SERVICEFEE: serviceFee})
  579. _order.update_service_info(consumeDict)
  580. if refundCash and all_money > RMB(0):
  581. extra.append({u'消费金额': '{}元'.format(all_money - all_refund_money)})
  582. if all_refund_money > RMB(0):
  583. extra.append({u'退款金额': '{}元'.format(all_refund_money)})
  584. else:
  585. extra.append({u'消费金额': '{}金币'.format(usedFee)})
  586. if (coins - usedFee) > VirtualCoin(0):
  587. extra.append({u'退款金额': '{}金币(当充电金币数量大于或等于扫码充电金额时可抵现金,金币不可提现)'.format(coins - usedFee)})
  588. self.notify_to_user(master_order.user.managerialOpenId, extra)
  589. else:
  590. logger.error('not net pay rather user virtual card pay. something is wrong.')
  591. return
  592. def _calc_refund_info(self, backCoins, orderCoin):
  593. if backCoins >= orderCoin:
  594. need_back_coins = orderCoin
  595. need_consume_coins = VirtualCoin(0)
  596. backCoins -= orderCoin
  597. else:
  598. need_back_coins = backCoins
  599. need_consume_coins = orderCoin - need_back_coins
  600. backCoins = VirtualCoin(0)
  601. return need_back_coins, need_consume_coins, backCoins
  602. class MyIcStartAckEvent(IcStartAckEvent):
  603. def __init__(self, smartBox, event_data):
  604. super(MyIcStartAckEvent, self).__init__(smartBox, event_data, StartAckEventPreProcessor())
  605. def post_after_start(self, order=None):
  606. pass
  607. def post_after_finish(self, order=None):
  608. pass
  609. def merge_order(self, master_order, sub_orders):
  610. # type:(ConsumeRecord, list)->dict
  611. consumeModule = self.device.get('otherConf', dict()).get('consumeModule', 0)
  612. # 按时间计费
  613. if consumeModule == 0:
  614. billingType = 'time'
  615. else:
  616. billingType = 'elec'
  617. start_time = Arrow.fromdatetime(master_order.startTime, tzinfo=settings.TIME_ZONE)
  618. portDict = {
  619. 'billingType': billingType
  620. }
  621. if master_order.paymentInfo['via'] == 'virtualCard':
  622. portDict.update({
  623. 'vCardId': master_order.paymentInfo['itemId']
  624. })
  625. all_coins = master_order.coin
  626. all_money = master_order.money
  627. for sub_order in sub_orders:
  628. all_coins += sub_order.coin
  629. all_money += sub_order.money
  630. portDict['coins'] = str(all_coins)
  631. portDict['money'] = str(all_money)
  632. portDict['estimatedTs'] = int(start_time.timestamp + 12 * 60 * 60)
  633. return portDict
  634. def do_time_finished(self, card, order, merge_order_info):
  635. # type: (Card, ConsumeRecord, dict)->None
  636. duration, elec = self.event_data['duration'], self.event_data['elec']
  637. coins = VirtualCoin(merge_order_info['coins'])
  638. left = self.event_data['left']
  639. consumeDict = {
  640. 'reason': self.event_data['reason'],
  641. 'chargeIndex': str(order.used_port),
  642. 'duration': duration,
  643. 'elec': elec,
  644. 'elecFee': self.deviceAdapter.calc_elec_fee(elec)
  645. }
  646. # backMoney = VirtualCoin(self.event_data['backMoney'])
  647. group = self.device.group # type: GroupDict
  648. extra = [{
  649. u'实体卡号': self.event_data['cardNo']
  650. }]
  651. consumeDict.update({'balance': str(card.balance)})
  652. order.update_service_info(consumeDict)
  653. self.notify_user_service_complete(
  654. service_name='充电',
  655. openid=card.managerialOpenId if card else '',
  656. port=order.used_port,
  657. address=group.address,
  658. reason=self.event_data['reason'],
  659. finished_time=order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'),
  660. extra=extra)
  661. def do_elec_finished(self, card, order, merge_order_info):
  662. # type:(Card, ConsumeRecord, dict)->None
  663. '''
  664. 电川的板子 按电量退费
  665. :return:
  666. '''
  667. leftElec = self.event_data.get('left', 0) / 100.0
  668. duration, elec = self.event_data['duration'], self.event_data['elec']
  669. try:
  670. consumeDict = {
  671. 'reason': self.event_data['reasonDesc'],
  672. 'chargeIndex': order.used_port,
  673. 'duration': duration,
  674. 'elec': elec,
  675. 'elecFee': self.deviceAdapter.calc_elec_fee(elec)
  676. }
  677. # backMoney = self.event_data.get('backMoney')
  678. extra = [{u'实体卡号': self.event_data['cardNo']}]
  679. # if self.event_data['cardType'] == 'ID':
  680. # if backMoney > 0:
  681. # self.refund_money_for_card(RMB(backMoney), card.id)
  682. # extra.append({
  683. # u'退款金额': u'{}(金币)'.format(backMoney)
  684. # })
  685. #
  686. # consumeDict.update({'balance': str(card.balance + VirtualCoin(self.event_data['backMoney']))})
  687. # else:
  688. consumeDict.update({'balance': str(card.balance)})
  689. order.update_service_info(consumeDict)
  690. self.notify_user_service_complete(
  691. service_name='充电',
  692. openid=self.get_managerialOpenId_by_openId(order.openId),
  693. port=order.used_port,
  694. address=self.device.group.address,
  695. reason=self.event_data['reason'],
  696. finished_time=order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'),
  697. extra=extra)
  698. except Exception as e:
  699. logger.exception(e)
  700. def do_finished_event(self, card, order, merge_order_info):
  701. # type:(Card, ConsumeRecord, dict)->None
  702. if 'backMoney' in self.event_data and self.event_data['backMoney'] > RMB(0):
  703. refund_order = RechargeRecord.objects(orderNo=self.event_data['order_id']).first()
  704. if not refund_order:
  705. self.refund_money_for_card(RMB(self.event_data['backMoney']), card.id, self.event_data['order_id'])
  706. billing_type = merge_order_info['billingType']
  707. if billing_type == 'time':
  708. self.do_time_finished(card, order, merge_order_info)
  709. else:
  710. self.do_elec_finished(card, order, merge_order_info)
  711. class MyIcRechargeAckEvent(IcRechargeAckEvent):
  712. def __init__(self, smartBox, event_data):
  713. super(MyIcRechargeAckEvent, self).__init__(smartBox, event_data, StartAckEventPreProcessor())
  714. class MyCardRefundAckEvent(CardRefundAckEvent):
  715. def __init__(self, smartBox, event_data):
  716. super(MyCardRefundAckEvent, self).__init__(smartBox, event_data, CardRefundAckEventPreProcessor())
  717. class CardRefundAckEventPreProcessor(AckEventProcessorIntf):
  718. def pre_processing(self, device, event_data):
  719. # type:(DeviceDict, dict)->dict
  720. if 'cardType' in event_data:
  721. if event_data['cardType'] == 'AA33':
  722. event_data['cardType'] = 'ID'
  723. else:
  724. event_data['cardType'] = 'IC'
  725. if 'cardNo' in event_data:
  726. event_data['cardNo'] = str(event_data['cardNo'])
  727. event_data['backMoney'] = (int(event_data['backMoney']) / 10.0)
  728. return event_data
  729. class IdStartAckEventPreProcessor(AckEventProcessorIntf):
  730. def analysis_reason(self, reason):
  731. FINISHED_CHARGE_REASON_MAP = {
  732. '00': u'购买的充电时间或者电量已经用完',
  733. '01': u'系统判断为异常断电(插头被拔或者松动,或者电瓶已经充满),电瓶车充电器种类繁多,可能存在误差',
  734. '02': u'电池已经充满',
  735. '03': u'警告!您的电池功率超过本机最大限制。为了公共安全,不建议您在该充电桩充电',
  736. '04': u'远程断电',
  737. '11': u'设备或是端口出现问题,被迫停止',
  738. # 服务器定义的停止事件
  739. '90': u'订单异常,设备使用期间可能存在离线时长超过一小时',
  740. '91': u'主板异常停止,未知错误', # 主板轮询出来的停止(无结束上报)
  741. '92': u'管理员远程关闭订单',
  742. '95': u'继电器粘连、短路,停止充电',
  743. '96': u'空载,充电头脱落、充电器拔出',
  744. '97': u'用户远程手动停止订单',
  745. '98': u'您购买的时间已经用完了',
  746. '99': u'您购买的电量已经用完了',
  747. 'EE': u'经销商强制结束',
  748. }
  749. return FINISHED_CHARGE_REASON_MAP.get(reason)
  750. def pre_processing(self, device, event_data):
  751. # type:(DeviceDict, dict)->dict
  752. source = json.dumps(event_data, indent=4)
  753. event_data['source'] = source
  754. if 'duration' in event_data and event_data['duration'] != 0:
  755. duration = event_data.get('duration')
  756. event_data['duration'] = round(duration / 60.0, 1)
  757. if 'fts' in event_data and 'sts' in event_data:
  758. duration = event_data['fts'] - event_data['sts']
  759. event_data['duration'] = round(duration / 60.0, 1)
  760. if 'elec' in event_data:
  761. elec = event_data.get('elec')
  762. event_data['elec'] = round(elec / 3600000.0, 3)
  763. if 'need_time' in event_data:
  764. event_data['needTime'] = event_data.pop('need_time') / 60.0
  765. if 'need_elec' in event_data:
  766. event_data['needElec'] = event_data.pop('need_elec') / 3600000.0
  767. if 'port' in event_data:
  768. pass
  769. if event_data['status'] == 'finished':
  770. event_data['reasonDesc'] = self.analysis_reason(event_data.get('reason', '91'))
  771. if 'need_pay' in event_data:
  772. precision = event_data.get('attach_paras', {}).get('precision', 1000.0)
  773. event_data['needPay'] = VirtualCoin(
  774. event_data.get('need_pay', 0) / (100.0 * precision)) # 单位(分 精度(老的设备为1000 新的设备为3600
  775. if 'sub' in event_data:
  776. subs = event_data.get('sub', [])
  777. for item in subs:
  778. if 'need_time' in event_data:
  779. event_data['needTime'] = item.pop('need_time')
  780. if 'need_elec' in event_data:
  781. event_data['needElec'] = item.pop('need_elec')
  782. if 'card_id' in event_data:
  783. event_data['cardNo'] = str(int(event_data.get('card_id'), 16))
  784. event_data['fee'] = 0.0
  785. return event_data
  786. class MyIdStartAckEvent(IdStartAckEvent):
  787. def __init__(self, smartBox, event_data):
  788. super(MyIdStartAckEvent, self).__init__(smartBox, event_data, IdStartAckEventPreProcessor())
  789. def post_before_finish(self, order=None):
  790. # 记录处理的源数据报文
  791. uart_source = getattr(order, 'uart_source', [])
  792. uart_source.append({
  793. 'rece_finished': self.event_data.get('source'),
  794. 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  795. })
  796. order.uart_source = uart_source
  797. order.save()
  798. def post_after_start(self, order=None):
  799. # 通知用户,已经扣费
  800. title = make_title_from_dict([
  801. {u'设备地址': u'{}'.format(self.device.group.address)},
  802. {u'设备编号': u'{}'.format(self.device['logicalCode'])},
  803. {u'实体卡': u'{}--No:{}'.format(self.card.cardName or self.card.nickName, self.card.cardNo)},
  804. ])
  805. start_time_stamp = self.event_data.get('sts', time.time())
  806. start_time = datetime.datetime.fromtimestamp(start_time_stamp)
  807. self.notify_user(
  808. self.card.managerialOpenId,
  809. 'dev_start',
  810. **{
  811. 'title': title,
  812. 'things': u'刷卡启动充电',
  813. 'remark': u'感谢您的支持!',
  814. 'time': start_time.strftime(Const.DATETIME_FMT)
  815. }
  816. )
  817. def post_after_finish(self, order=None):
  818. pass
  819. def merge_order(self, master_order, sub_orders):
  820. # type:(ConsumeRecord, list)->dict
  821. billingType = self.device.get('otherConf', dict()).get('billingType', 'time')
  822. start_time = Arrow.fromdatetime(master_order.startTime, tzinfo=settings.TIME_ZONE)
  823. portDict = {
  824. 'billingType': billingType,
  825. 'consumeType': 'postpaid'
  826. }
  827. if self.device.bill_as_service_feature.on:
  828. billingType = 'elec'
  829. portDict.update({
  830. 'billingType': billingType,
  831. 'consumeType': "billAsServicePostpaid"
  832. })
  833. all_coins = master_order.coin
  834. all_money = master_order.money
  835. all_consume_time = self.event_data.get('needTime', 0)
  836. all_consume_elec = self.event_data.get('needElec', 0)
  837. for sub_order in sub_orders:
  838. all_coins += sub_order.coin
  839. all_money += sub_order.money
  840. if self.event_data.get('sub', []):
  841. for _ in self.event_data['sub']:
  842. all_consume_time += _.get('needTime', 0)
  843. all_consume_elec += _.get('needElec', 0)
  844. portDict['coins'] = str(all_coins)
  845. portDict['money'] = str(all_money)
  846. if billingType == 'time':
  847. portDict['needKind'] = 'needTime'
  848. portDict['needValue'] = all_consume_time
  849. portDict['unit'] = u'分钟'
  850. portDict['estimatedTs'] = int(start_time.timestamp + all_consume_time * 60)
  851. elif billingType == 'elec':
  852. portDict['needKind'] = 'needElec'
  853. portDict['needValue'] = round(all_consume_elec / 100.0, 2)
  854. portDict['unit'] = u'度'
  855. portDict['estimatedTs'] = int(start_time.timestamp + 12 * 60 * 60)
  856. else:
  857. pass
  858. return portDict
  859. def do_time_finished(self, order, merge_order_info):
  860. # type: (ConsumeRecord, dict)->None
  861. duration, elec = self.event_data['duration'], self.event_data['elec']
  862. coins = VirtualCoin(merge_order_info['coins'])
  863. left = self.event_data['leftTime']
  864. group = Group.get_group(self.device['groupId'])
  865. dealer = Dealer.objects(id=group['ownerId']).first()
  866. if not dealer:
  867. logger.error('dealer is not found, dealerId=%s' % group['ownerId'])
  868. return
  869. agent = Agent.objects(id=dealer.agentId).first()
  870. if not agent:
  871. logger.error('agent is not found, agentId=%s' % dealer.agentId)
  872. return
  873. consumeDict = {
  874. 'reason': self.event_data['reason'],
  875. 'chargeIndex': str(order.used_port),
  876. 'duration': duration,
  877. 'elec': elec,
  878. 'cardNo': self.event_data['cardNo'],
  879. 'elecFee': self.deviceAdapter.calc_elec_fee(elec)
  880. }
  881. auto_refund = self.device.is_auto_refund
  882. refundProtectionTime = int(self.device['otherConf'].get('serverConfigs', {}).get('refundProtectionTime', 5))
  883. logger.debug('{} auto refund enable switch is {}. refund protect time = {}'.format(
  884. repr(self.device), str(auto_refund), refundProtectionTime))
  885. if left > merge_order_info['needValue']:
  886. left = merge_order_info['needValue']
  887. real_back_coins = coins * Ratio(float(left) / float(merge_order_info['needValue']))
  888. if auto_refund:
  889. if duration < refundProtectionTime:
  890. backMoney = coins
  891. else:
  892. backMoney = real_back_coins
  893. else:
  894. backMoney = VirtualCoin(0)
  895. if backMoney and backMoney > RMB(0):
  896. if is_server_refund('time', self.device, dealer, agent):
  897. logger.info(
  898. 'ready to server refund money <{}> for user card <{}> in device<{}>'.format(
  899. backMoney,
  900. str(self.card.id),
  901. self.device.devNo))
  902. consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: backMoney.mongo_amount})
  903. self.refund_money_for_card(backMoney, self.card.id, order.orderNo)
  904. self.notify_user(self.card.managerialOpenId, 'refund_coins', **{
  905. 'title': u'退币完成!您的卡号是%s,卡别名:%s' % (self.card.cardNo, self.card.nickName),
  906. 'backCount': u'金币:%s' % backMoney,
  907. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  908. })
  909. extra = [{
  910. u'实体卡号': self.event_data['cardNo']
  911. }]
  912. order.update_service_info(consumeDict)
  913. self.notify_user_service_complete(
  914. service_name='充电',
  915. openid=self.card.managerialOpenId if self.card else '',
  916. port=order.used_port,
  917. address=group.address,
  918. reason=self.event_data['reasonDesc'],
  919. finished_time=order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'),
  920. extra=extra)
  921. def do_elec_finished(self, order, merge_order_info):
  922. # type:(ConsumeRecord, dict)->None
  923. '''
  924. 电川的板子 按电量退费
  925. :return:
  926. '''
  927. leftElec = self.event_data.get('leftElec', 0)
  928. duration, elec = self.event_data['duration'], self.event_data['elec']
  929. coins = VirtualCoin(merge_order_info['coins'])
  930. group = Group.get_group(self.device['groupId'])
  931. dealer = Dealer.objects(id=group['ownerId']).first()
  932. if not dealer:
  933. logger.error('dealer is not found, dealerId=%s' % group['ownerId'])
  934. return
  935. agent = Agent.objects(id=dealer.agentId).first()
  936. if not agent:
  937. logger.error('agent is not found, agentId=%s' % dealer.agentId)
  938. return
  939. try:
  940. consumeDict = {
  941. 'reason': self.event_data['reasonDesc'],
  942. 'chargeIndex': order.used_port,
  943. 'duration': duration,
  944. 'elec': elec,
  945. 'elecFee': self.deviceAdapter.calc_elec_fee(elec)
  946. }
  947. if leftElec > merge_order_info['needValue']:
  948. leftElec = merge_order_info['needValue']
  949. backMoney = coins * Ratio(leftElec / merge_order_info['needValue'])
  950. if backMoney and backMoney > RMB(0):
  951. if is_server_refund('elec', self.device, dealer, agent):
  952. logger.info(
  953. 'ready to server refund money <{}> for user card <{}> in device<{}>'.format(
  954. backMoney,
  955. str(self.card.id),
  956. self.device.devNo))
  957. consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: backMoney.mongo_amount})
  958. self.refund_money_for_card(backMoney, self.card.id, order.orderNo)
  959. self.notify_user(self.card.managerialOpenId, 'refund_coins', **{
  960. 'title': u'退币完成!您的卡号是%s,卡别名:%s' % (self.card.cardNo, self.card.nickName),
  961. 'backCount': u'金币:%s' % backMoney,
  962. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  963. })
  964. extra = [{u'实体卡号': self.event_data['cardNo']}]
  965. consumeDict.update({'balance': str(self.card.balance)})
  966. order.update_service_info(consumeDict)
  967. self.notify_user_service_complete(
  968. service_name='充电',
  969. openid=self.get_managerialOpenId_by_openId(order.openId),
  970. port=order.used_port,
  971. address=self.device.group.address,
  972. reason=self.event_data['reasonDesc'],
  973. finished_time=order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'),
  974. extra=extra)
  975. except Exception as e:
  976. logger.exception(e)
  977. def do_finished_event(self, order, merge_order_info):
  978. # type:(ConsumeRecord, dict)->None
  979. self.do_postpaid_time_finished(order, merge_order_info)
  980. def do_postpaid_time_finished(self, master_order, merge_order_info):
  981. extra = []
  982. duration = self.event_data.get('duration', 0)
  983. elec = self.event_data.get('elec', 0)
  984. needPay = self.event_data.get('needPay', 0)
  985. consumeDict = {
  986. 'reason': self.event_data.get('reasonDesc'),
  987. 'chargeIndex': str(master_order.used_port),
  988. DEALER_CONSUMPTION_AGG_KIND.DURATION: duration,
  989. DEALER_CONSUMPTION_AGG_KIND.ELEC: elec,
  990. }
  991. if self.device.bill_as_service_feature.on:
  992. ratio_conversion = self.event_data.get('ratio_conversion', 100)
  993. precision = self.event_data.get('precision', 3600)
  994. needPay = VirtualCoin(round(float(needPay) / ratio_conversion / 10, 2))
  995. serviceFee = round(self.event_data.get("service_fee", 0) / ratio_conversion / (1000.0 * precision), 2)
  996. elecFee = round(self.event_data.get("elec_fee", 0) / ratio_conversion / (1000.0 * precision), 2)
  997. consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.ELECFEE: elecFee})
  998. consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.SERVICEFEE: serviceFee})
  999. extra.append({
  1000. u'电费金额': u'{}(金币)'.format(serviceFee),
  1001. u'服务费金额': u'{}(金币)'.format(elecFee)
  1002. })
  1003. logger.debug(
  1004. 'orderNo = {}, orderType = isPostpaid, usedTime = {}, needPayMoney = {}'.format(master_order.orderNo,
  1005. duration,
  1006. needPay))
  1007. # 保护时间判断
  1008. refundProtectionTime = int(self.device.otherConf.get('serverConfigs', {}).get('refundProtectionTime', 5))
  1009. if duration < refundProtectionTime:
  1010. needPay = VirtualCoin(0)
  1011. master_order.coin = needPay
  1012. master_order.money = needPay
  1013. master_order.save()
  1014. # 3 使用金币支付()
  1015. self.pay_by_card(master_order)
  1016. self.card.reload()
  1017. extra.append({u'使用详情': '{}号端口, {}(分钟)'.format(master_order.used_port, duration)})
  1018. consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: master_order.coin.mongo_amount})
  1019. master_order.update_service_info(consumeDict)
  1020. extra.append({u'消费金额': u'{}(金币)'.format(needPay.mongo_amount)})
  1021. if self.card.balance >= RMB(0):
  1022. extra.append({u'卡余额': u'{}(金币)'.format(self.card.balance)})
  1023. else:
  1024. extra.append({u'卡余额': u'已欠费{}(金币)'.format(abs(self.card.balance))})
  1025. self.notify_to_user(self.card.managerialOpenId, extra)
  1026. def notify_to_user(self, openId, extra):
  1027. group = Group.get_group(self.device['groupId'])
  1028. self.notify_user_service_complete(
  1029. service_name='充电',
  1030. openid=openId,
  1031. port=self.event_data.get('port'),
  1032. address=group['address'],
  1033. reason=self.event_data.get('reasonDesc'),
  1034. finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  1035. extra=extra)
  1036. def checkout_order(self, order):
  1037. #冻结卡不让其他设备使用
  1038. self.card.freeze_balance(str(order.id), VirtualCoin(0))
  1039. self.card.reload()
  1040. def pay_by_card(self, order):
  1041. # 1 接触冻结状态
  1042. self.card.clear_frozen_balance(str(order.id), VirtualCoin(0))
  1043. # 2 扣除费用
  1044. self.card.update(dec__balance=round(order.coin, 2))
  1045. class MyVirtualCardStartAckEvent(VirtualCardStartAckEvent):
  1046. def __init__(self, smartBox, event_data):
  1047. super(MyVirtualCardStartAckEvent, self).__init__(smartBox, event_data, StartAckEventPreProcessor())
  1048. def post_after_start(self, order=None, card=None):
  1049. pass
  1050. def post_after_finish(self, order=None):
  1051. pass
  1052. def merge_order(self, master_order, sub_orders):
  1053. # type:(ConsumeRecord, list)->dict
  1054. billingType = self.device.get('otherConf', dict()).get('billingType', 'time')
  1055. start_time = Arrow.fromdatetime(master_order.startTime, tzinfo=settings.TIME_ZONE)
  1056. portDict = {
  1057. 'billingType': billingType
  1058. }
  1059. all_coins = master_order.coin
  1060. all_money = master_order.money
  1061. all_consume_time = self.event_data.get('needTime', 0)
  1062. all_consume_elec = self.event_data.get('needElec', 0)
  1063. if self.event_data.get('sub', []):
  1064. for _ in self.event_data['sub']:
  1065. all_consume_time += _.get('needTime', 0)
  1066. all_consume_elec += _.get('needElec', 0)
  1067. portDict['coins'] = str(all_coins)
  1068. portDict['money'] = str(all_money)
  1069. if billingType == 'time':
  1070. portDict['needKind'] = 'needTime'
  1071. portDict['needValue'] = all_consume_time
  1072. portDict['unit'] = u'分钟'
  1073. portDict['estimatedTs'] = int(start_time.timestamp + all_consume_time * 60)
  1074. elif billingType == 'elec':
  1075. portDict['needKind'] = 'needElec'
  1076. portDict['needValue'] = round(all_consume_elec / 100.0, 2)
  1077. portDict['unit'] = u'度'
  1078. portDict['estimatedTs'] = int(start_time.timestamp + 12 * 60 * 60)
  1079. else:
  1080. pass
  1081. return portDict
  1082. def checkout_order(self, order):
  1083. group = Group.get_group(self.device.groupId) # type:GroupDict
  1084. freeze_user_balance(self.device, group, order, self.virtualCard)
  1085. def do_time_finished(self, order, sub_orders, merge_order_info):
  1086. # type: (ConsumeRecord, list, dict)->None
  1087. duration, elec = self.event_data['duration'], self.event_data['elec']
  1088. consumeDict = {
  1089. 'reason': self.event_data['reasonDesc'],
  1090. 'chargeIndex': str(order.used_port),
  1091. 'duration': duration,
  1092. 'elec': elec,
  1093. 'cardNo': self.event_data['cardNo'],
  1094. 'elecFee': self.deviceAdapter.calc_elec_fee(elec)
  1095. }
  1096. auto_refund = self.device.is_auto_refund
  1097. refundProtectionTime = int(self.device['otherConf'].get('serverConfigs', {}).get('refundProtectionTime', 5))
  1098. logger.debug('{} auto refund enable switch is {}. refund protect time = {}'.format(
  1099. repr(self.device), str(auto_refund), refundProtectionTime))
  1100. if auto_refund:
  1101. order_need_time = order.package.get('time')
  1102. if order_need_time >= duration:
  1103. usedTime = duration
  1104. duration = 0
  1105. else:
  1106. usedTime = order_need_time
  1107. duration -= order_need_time
  1108. success, consumeTotal, consumeDay = self.virtualCard.clear_frozen_quota(transaction_id=str(order.id),
  1109. usedTime=usedTime, spendElec=0,
  1110. backCoins=VirtualCoin(
  1111. 0).mongo_amount)
  1112. self.insert_vCard_consume_record(self.virtualCard, order, success, consumeTotal, consumeDay)
  1113. for sub_order in sub_orders:
  1114. order_need_time = sub_order.package.get('time')
  1115. if order_need_time >= duration:
  1116. usedTime = duration
  1117. duration = 0
  1118. else:
  1119. usedTime = order_need_time
  1120. duration -= order_need_time
  1121. success, consumeTotal, consumeDay = self.virtualCard.clear_frozen_quota(
  1122. transaction_id=str(sub_order.id),
  1123. usedTime=usedTime, spendElec=0,
  1124. backCoins=VirtualCoin(0).mongo_amount)
  1125. self.insert_vCard_consume_record(self.virtualCard, sub_order, success, consumeTotal, consumeDay)
  1126. else:
  1127. order_need_time = order.package.get('time')
  1128. success, consumeTotal, consumeDay = self.virtualCard.clear_frozen_quota(transaction_id=str(order.id),
  1129. usedTime=order_need_time,
  1130. spendElec=0,
  1131. backCoins=VirtualCoin(
  1132. 0).mongo_amount)
  1133. self.insert_vCard_consume_record(self.virtualCard, order, success, consumeTotal, consumeDay)
  1134. for sub_order in sub_orders:
  1135. order_need_time = sub_order.package.get('time')
  1136. success, consumeTotal, consumeDay = self.virtualCard.clear_frozen_quota(
  1137. transaction_id=str(sub_order.id),
  1138. usedTime=order_need_time, spendElec=0,
  1139. backCoins=VirtualCoin(0).mongo_amount)
  1140. self.insert_vCard_consume_record(self.virtualCard, sub_order, success, consumeTotal, consumeDay)
  1141. group = self.device.group # type: GroupDict
  1142. extra = [{
  1143. u'虚拟卡号': self.virtualCard.cardNo
  1144. }]
  1145. order.update_service_info(consumeDict)
  1146. self.notify_user_service_complete(
  1147. service_name='充电',
  1148. openid=self.card.managerialOpenId if self.card else '',
  1149. port=order.used_port,
  1150. address=group.address,
  1151. reason=self.event_data['reasonDesc'],
  1152. finished_time=order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'),
  1153. extra=extra)
  1154. def do_elec_finished(self, order, sub_orders, merge_order_info):
  1155. # type:(ConsumeRecord, list, dict)->None
  1156. duration, elec = self.event_data['duration'], self.event_data['elec']
  1157. consumeDict = {
  1158. 'reason': self.event_data['reasonDesc'],
  1159. 'chargeIndex': str(order.used_port),
  1160. 'duration': duration,
  1161. 'elec': elec,
  1162. 'cardNo': self.event_data['cardNo'],
  1163. 'elecFee': self.deviceAdapter.calc_elec_fee(elec),
  1164. }
  1165. auto_refund = self.device.is_auto_refund
  1166. refundProtectionTime = int(self.device['otherConf'].get('serverConfigs', {}).get('refundProtectionTime', 5))
  1167. logger.debug('{} auto refund enable switch is {}. refund protect time = {}'.format(
  1168. repr(self.device), str(auto_refund), refundProtectionTime))
  1169. if auto_refund:
  1170. order_need_time = order.package.get('time')
  1171. if order_need_time >= elec:
  1172. usedTime = duration
  1173. duration = 0
  1174. else:
  1175. usedTime = order_need_time
  1176. elec -= order_need_time
  1177. success, consumeTotal, consumeDay = self.virtualCard.clear_frozen_quota(transaction_id=str(order.id),
  1178. usedTime=usedTime, spendElec=0,
  1179. backCoins=VirtualCoin(
  1180. 0).mongo_amount)
  1181. self.insert_vCard_consume_record(self.virtualCard, order, success, consumeTotal, consumeDay)
  1182. for sub_order in sub_orders:
  1183. order_need_time = order.package.get('time')
  1184. if order_need_time >= duration:
  1185. usedTime = duration
  1186. duration = 0
  1187. else:
  1188. usedTime = order_need_time
  1189. duration -= order_need_time
  1190. success, consumeTotal, consumeDay = self.virtualCard.clear_frozen_quota(
  1191. transaction_id=str(sub_order.id),
  1192. usedTime=usedTime, spendElec=0,
  1193. backCoins=VirtualCoin(0).mongo_amount)
  1194. self.insert_vCard_consume_record(self.virtualCard, sub_order, success, consumeTotal, consumeDay)
  1195. else:
  1196. order_need_time = order.package.get('time')
  1197. success, consumeTotal, consumeDay = self.virtualCard.clear_frozen_quota(
  1198. transaction_id=str(order.id),
  1199. usedTime=order_need_time, spendElec=0,
  1200. backCoins=VirtualCoin(
  1201. 0).mongo_amount)
  1202. self.insert_vCard_consume_record(self.virtualCard, order, success, consumeTotal, consumeDay)
  1203. for sub_order in sub_orders:
  1204. order_need_time = sub_order.package.get('time')
  1205. success, consumeTotal, consumeDay = self.virtualCard.clear_frozen_quota(
  1206. transaction_id=str(sub_order.id),
  1207. usedTime=order_need_time, spendElec=0,
  1208. backCoins=VirtualCoin(0).mongo_amount)
  1209. self.insert_vCard_consume_record(self.virtualCard, sub_order, success, consumeTotal, consumeDay)
  1210. extra = [{
  1211. u'虚拟卡号': self.virtualCard.cardNo
  1212. }]
  1213. order.update_service_info(consumeDict)
  1214. self.notify_user_service_complete(
  1215. service_name='充电',
  1216. openid=self.get_managerialOpenId_by_openId(order.openId),
  1217. port=order.used_port,
  1218. address=self.device.group.address,
  1219. reason=self.event_data['reason'],
  1220. finished_time=order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'),
  1221. extra=extra)
  1222. def do_finished_event(self, order, sub_orders, merge_order_info):
  1223. # type:(ConsumeRecord, list, dict)->None
  1224. billing_type = merge_order_info['billingType']
  1225. if billing_type == 'time':
  1226. self.do_time_finished(order, sub_orders, merge_order_info)
  1227. else:
  1228. self.do_elec_finished(order, sub_orders, merge_order_info)
  1229. def insert_vCard_consume_record(self, vCard, order, success, consumeTotal, consumeDay):
  1230. try:
  1231. if success and consumeDay['count'] > 0:
  1232. record = VCardConsumeRecord(
  1233. orderNo=VCardConsumeRecord.make_no(order.logicalCode),
  1234. openId=order.openId,
  1235. nickname=order.nickname,
  1236. cardId=str(vCard.id),
  1237. dealerId=vCard.dealerId,
  1238. devNo=order.devNo,
  1239. devTypeCode = self.device.devTypeCode,
  1240. devTypeName = self.device.devTypeName,
  1241. logicalCode=order.logicalCode,
  1242. groupId=order.groupId,
  1243. address=order.address,
  1244. groupNumber=order.groupNumber,
  1245. groupName=order.groupName,
  1246. attachParas=order.attachParas,
  1247. consumeData=consumeTotal,
  1248. consumeDayData=consumeDay
  1249. )
  1250. record.save()
  1251. except Exception as e:
  1252. logger.exception(e)