zhongshanlvzhi.py 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import time
  5. import simplejson as json
  6. import logging
  7. from typing import TYPE_CHECKING
  8. from apilib.monetary import RMB, VirtualCoin
  9. from apilib.utils_datetime import to_datetime
  10. from apps.web.common.models import TempValues
  11. from apps.web.constant import Const, DEALER_CONSUMPTION_AGG_KIND
  12. from apps.web.core.accounting import Accounting
  13. from apps.web.device.models import Group, Device
  14. from apps.web.eventer.base import WorkEvent, FaultEvent
  15. from apps.web.eventer import EventBuilder
  16. from apps.web.report.utils import record_consumption_stats
  17. from apps.web.user.transaction_deprecated import refund_money
  18. from apps.web.user.models import ServiceProgress, CardRechargeOrder, MyUser, Card, \
  19. ConsumeRecord
  20. if TYPE_CHECKING:
  21. from apps.web.device.models import GroupDict
  22. logger = logging.getLogger(__name__)
  23. class builder(EventBuilder):
  24. def __getEvent__(self, device_event):
  25. if "ack_event_id" in device_event:
  26. self.deviceAdapter.ack_05_event(device_event.get("ack_event_id"))
  27. event_data = self.deviceAdapter.analyze_event_data(device_event)
  28. if "FaultCode" in event_data:
  29. return LVZHIFaultEvent(self.deviceAdapter, event_data)
  30. elif event_data.get('cmd') == '99':
  31. return LVZHIOnline(self.deviceAdapter, event_data)
  32. else:
  33. return ChargingLVZHIWorkEvent(self.deviceAdapter, event_data)
  34. class ChargingLVZHIWorkEvent(WorkEvent):
  35. def __parse_device_finished_data(self, event_data):
  36. duration = event_data.get("duration", -1)
  37. if duration != -1:
  38. if "v" in event_data:
  39. duration = ((duration + 59) / 60)
  40. elec = event_data.get("elec", -1)
  41. if elec != -1:
  42. if "v" in event_data:
  43. elec = round(elec / (10000.0 * 3600.0), 3)
  44. else:
  45. elec = round(elec / 3600.0, 3)
  46. logger.debug("lz_device duration is {}, device elec is {}".format(duration, elec))
  47. return duration, elec
  48. def do(self, **args):
  49. logger.info("lz_charging event to do devNo=%s,info=%s" % (
  50. self.device.devNo, json.dumps(self.event_data, ensure_ascii=False)))
  51. cmd = self.event_data.get("cmd")
  52. if cmd == "03":
  53. coins = self.event_data.get("coins")
  54. port = self.event_data.get("port")
  55. port_control_cache = Device.get_dev_control_cache(self.device.devNo)
  56. lineInfo = port_control_cache.get(port, {})
  57. offlineCoin = float(coins) - float(lineInfo.get("coins", 0))
  58. if offlineCoin:
  59. Accounting.recordOfflineCoin(self.device,
  60. int(time.time()),
  61. int(offlineCoin))
  62. else:
  63. Accounting.recordOfflineCoin(self.device,
  64. int(time.time()),
  65. int(float(coins)))
  66. consumeDict = {
  67. "totalCount": offlineCoin,
  68. "chargeIndex": port
  69. }
  70. orderNo = self.record_consume_for_coin(RMB(coins), servicedInfo=consumeDict)
  71. # 更新端口缓存
  72. billingType = self.event_data.get("billingType")
  73. portInfo = {
  74. "port": port,
  75. "status": Const.DEV_WORK_STATUS_WORKING,
  76. "coins": coins,
  77. "billingType": billingType,
  78. "startTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  79. "consumeType": "coin",
  80. "orderNo": orderNo,
  81. }
  82. deviceConfigs = self.device['otherConf'].get('deviceConfigs', {})
  83. if not deviceConfigs:
  84. deviceConfigs = self.deviceAdapter.get_consumption_rules()
  85. if billingType == "time":
  86. coinTime = deviceConfigs.get("coinTime", 240)
  87. needTime = int(float(coinTime) * float(coins))
  88. portInfo.update({"needTime": needTime})
  89. elif billingType == 'billAsService':
  90. coinElec = deviceConfigs.get("coinElec", 0.6)
  91. serviceCharge = self.device.bill_as_service_feature.service_charge
  92. elecCharge = self.device.bill_as_service_feature.elec_charge
  93. if float(coinElec) == 0.0:
  94. coinElec = elecCharge
  95. unitFee = float(serviceCharge) + float(coinElec)
  96. needElec = round(float(coins) / unitFee, 2)
  97. portInfo.update({"needElec": needElec})
  98. else:
  99. coinElec = deviceConfigs.get("coinElec", 0.6)
  100. needElec = round(float(coinElec) * float(coins), 2)
  101. portInfo.update({"needElec": needElec})
  102. if lineInfo.get("startTime"):
  103. lastOrder = lineInfo.get("orderNo")
  104. historicalOrders = lastOrder
  105. portInfo["startTime"] = lineInfo.get("startTime")
  106. portInfo["coins"] = coins
  107. portInfo["historicalOrders"] = historicalOrders
  108. try:
  109. order = ConsumeRecord.objects.filter(orderNo=historicalOrders).first()
  110. order.finishedTime = datetime.datetime.now()
  111. order.status = "finished"
  112. order.servicedInfo.update({"reason": "再次投币使用,当前订单金额({})累计到下一单(消费订单号:{})".format(order.coin, orderNo),
  113. "duration": ((order.finishedTime - order.dateTimeAdded).total_seconds() + 59) / 60,
  114. })
  115. order.coin = VirtualCoin(0).mongo_amount
  116. order.money = RMB(0).mongo_amount
  117. order.save()
  118. ServiceProgress.objects(device_imei=self.device.devNo,
  119. port=int(port), isFinished=False).update_one(upsert=False,
  120. consumeOrder={
  121. "orderNo": orderNo,
  122. "coins": portInfo[
  123. "coins"]})
  124. except Exception:
  125. pass
  126. Device.update_port_control_cache(self.device.devNo, portInfo)
  127. # 如果是投币,直接把端口状态刷新下
  128. try:
  129. self.deviceAdapter.get_all_port_status()
  130. except Exception, e:
  131. logger.info("lz_some err=%s" % e)
  132. elif cmd == "04": # 只有离线卡 离线卡记录数据更新
  133. cardNo = self.event_data["cardNo"]
  134. fee = RMB(self.event_data["fee"])
  135. cardType = self.event_data["cardType"]
  136. cardBalance = RMB(self.event_data["cardBalance"])
  137. port = self.event_data["port"]
  138. eventCode = self.event_data["eventCode"]
  139. card = self.update_card_dealer_and_type(cardNo, cardType=cardType, balance=cardBalance)
  140. if not card:
  141. card = Card(cardNo=cardNo, remarks="中山智能离线卡", agentId=self.dealer.agentId,
  142. openId=Const.DEFAULT_CARD_OPENID, dealerId=str(self.dealer.id), cardType=cardType, isHaveBalance=False,
  143. devNo=self.device.devNo, balance=cardBalance)
  144. card.save()
  145. if cardType == "IC":
  146. if eventCode == "01": # 刷卡上报事件 01 代表扣费 02 代表退费
  147. billingType = self.event_data.get("billingType")
  148. port_control_cache = Device.get_dev_control_cache(self.device.devNo)
  149. portInfo = {
  150. "port": port,
  151. "status": Const.DEV_WORK_STATUS_WORKING,
  152. "cardType": cardType,
  153. "cardNo": cardNo,
  154. "coins": str(fee),
  155. "billingType": billingType,
  156. "startTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  157. "consumeType": "card",
  158. }
  159. # 如果是绑定了卡的
  160. if card.cardNo != card.openId:
  161. portInfo.update({"openId": card.openId})
  162. deviceConfigs = self.device['otherConf'].get('deviceConfigs', {})
  163. if not deviceConfigs:
  164. deviceConfigs = self.deviceAdapter.get_consumption_rules()
  165. if billingType == "time":
  166. cardTime = deviceConfigs.get("cardTime", 240)
  167. needTime = int(float(cardTime) * float(fee))
  168. portInfo.update({"needTime": needTime})
  169. elif billingType == 'billAsService':
  170. cardElec = deviceConfigs.get("cardElec", 0.6)
  171. serviceCharge = self.device.bill_as_service_feature.service_charge
  172. elecCharge = self.device.bill_as_service_feature.elec_charge
  173. if float(cardElec) == 0.0:
  174. cardElec = elecCharge
  175. unitFee = float(serviceCharge) + float(cardElec)
  176. needElec = round(float(fee) / unitFee, 2)
  177. portInfo.update({"needElec": needElec})
  178. else:
  179. cardElec = deviceConfigs.get("cardElec", 0.6)
  180. needElec = round(float(cardElec) * float(fee), 2)
  181. portInfo.update({"needElec": needElec})
  182. servicedInfo = {"卡号": cardNo, "chargeIndex": port}
  183. # 记录卡消费记录以及消费记录
  184. orderNo, cardOrderNo = self.record_consume_for_card(card, fee, servicedInfo=servicedInfo)
  185. portInfo.update({"orderNo": orderNo})
  186. lineInfo = port_control_cache.get(port, {})
  187. if lineInfo.get("startTime"):
  188. lastOrder = lineInfo.get("orderNo")
  189. historicalOrders = lastOrder
  190. portInfo["startTime"] = lineInfo.get("startTime")
  191. portInfo["coins"] = str(fee)
  192. portInfo["historicalOrders"] = historicalOrders
  193. try:
  194. order = ConsumeRecord.objects.filter(orderNo=historicalOrders).first()
  195. order.finishedTime = datetime.datetime.now()
  196. order.status = "finished"
  197. order.servicedInfo.update(
  198. {"reason": "再次刷卡使用,当前订单金额({})累计到下一单(消费订单号:{})".format(order.coin, orderNo),
  199. "duration": ((order.finishedTime - order.dateTimeAdded).total_seconds() + 59) / 60,
  200. })
  201. order.coin = VirtualCoin(0).mongo_amount
  202. order.money = RMB(0).mongo_amount
  203. order.save()
  204. ServiceProgress.objects(device_imei=self.device.devNo,
  205. port=int(port), isFinished=False).update_one(upsert=False,
  206. consumeOrder={
  207. "orderNo": orderNo,
  208. "coins": portInfo[
  209. "coins"]})
  210. except Exception:
  211. pass
  212. else:
  213. # 记录当前服务的progress.没有上报端口号,所以先用-1标记,表示端口未知。端口等消费的时候会报上来
  214. ServiceProgress.register_card_service(self.device, int(port), card,
  215. {"orderNo": orderNo,
  216. "money": self.event_data["fee"],
  217. "coin": self.event_data["fee"],
  218. "needTime": 0,
  219. "cardOrderNo": cardOrderNo})
  220. Device.update_port_control_cache(self.device.devNo, portInfo)
  221. # 卡号与openId相同,则为未绑定卡不用发送
  222. if card.cardNo == card.openId:
  223. return
  224. # 通知微信,已经扣费
  225. self.notify_balance_has_consume_for_card(card, fee)
  226. # 离线卡退费事件 更新卡余额
  227. else:
  228. self.update_card_dealer_and_type(cardNo, balance=cardBalance)
  229. elif cmd == "05":
  230. devNo = self.device.devNo
  231. port = self.event_data["port"]
  232. try:
  233. # 获取充电的模式
  234. ctrInfo = Device.get_dev_control_cache(devNo)
  235. lineInfo = ctrInfo.get(port)
  236. if not lineInfo:
  237. logger.debug("lz_get null control cache from {}".format(repr(self.device)))
  238. return
  239. billingType = self.event_data.get("billingType")
  240. if billingType == "elec":
  241. return self.do_elec_finish(devNo, port, lineInfo, self.event_data)
  242. elif billingType == "billAsService":
  243. return self.do_billAsService_finish(devNo, port, lineInfo, self.event_data)
  244. else:
  245. return self.do_time_finish(devNo, port, lineInfo, self.event_data)
  246. finally:
  247. Device.clear_port_control_cache(devNo, str(port))
  248. elif cmd == "1D":
  249. cardNo = self.event_data["cardNo"]
  250. cardType = self.event_data["cardType"]
  251. cardBalance = self.event_data["cardBalance"]
  252. if cardType == "ID":
  253. logger.info("lz_cardType is IdCard return!!!")
  254. card = self.update_card_dealer_and_type(cardNo, cardType, balance=RMB(cardBalance)) # type: Card
  255. if not card:
  256. card = Card(cardNo=cardNo, remarks="中山智能离线卡", agentId=self.dealer.agentId,
  257. openId=Const.DEFAULT_CARD_OPENID, dealerId=str(self.dealer.id), cardType=cardType, isHaveBalance=False,
  258. devNo=self.device.devNo, balance=RMB(cardBalance))
  259. card.save()
  260. logger.info("lz_ no such card now create!! cardNo:%s" % cardNo)
  261. money, coins, orderNos, cardOrderNos = CardRechargeOrder.get_to_do_list(str(card.id))
  262. if not orderNos:
  263. logger.info("lz_not card recharge record!, card is <{}>".format(cardNo))
  264. # 缓存里面更新低余额卡号
  265. card.reload()
  266. self.deviceAdapter.updata_no_balance_list(self.device.devNo, card)
  267. return
  268. orderNos = [str(item) for item in orderNos]
  269. CardRechargeInfo = {
  270. "rechargeRecord": orderNos,
  271. "cardRechargeOrder": cardOrderNos,
  272. "asyncMoney": str(coins),
  273. "cardBalance": str(cardBalance)
  274. }
  275. tempKey = "%s-%s" % (self.device["devNo"], cardNo)
  276. TempValues.set(key=tempKey, value=json.dumps(CardRechargeInfo))
  277. if card.frozen:
  278. asyncMoney = 0
  279. else:
  280. asyncMoney = str(coins)
  281. try:
  282. CardRechargeOrder.objects.filter(orderNo__in=cardOrderNos).update(status="finished")
  283. except Exception as e:
  284. logger.debug("lz_%s" % e)
  285. else:
  286. self.deviceAdapter.recharge_ic_card(cardNo, asyncMoney)
  287. logger.info("send recharge cmd is done,wait 1F return cardNo:%s asyncMoney:%s tempKey:%s" % (
  288. cardNo, asyncMoney, tempKey))
  289. elif cmd == "1F":
  290. cardNo = str(self.event_data["cardNo"])
  291. cardType = self.event_data["cardType"]
  292. ayscMoney = RMB(self.event_data["ayscMoney"])
  293. nowBalance = RMB(self.event_data["cardBalance"])
  294. status = self.event_data["status"]
  295. card = self.update_card_dealer_and_type(cardNo, cardType) # type: Card
  296. if not card:
  297. return
  298. if card.frozen:
  299. logger.debug("lz_{} has been frozen.".format(repr(card)))
  300. return
  301. # 明确接收到了主板同步失败的情况下,将订单的状态回传
  302. if status != "01":
  303. CardRechargeInfoKey = "%s-%s" % (self.device["devNo"], cardNo)
  304. CardRechargeInfo = TempValues.get(CardRechargeInfoKey)
  305. cardRechargeOrders = CardRechargeInfo.get("cardRechargeOrder")
  306. logger.info("lz_card recharge receive fail status from device, card is <{}>".format(cardNo))
  307. try:
  308. CardRechargeOrder.objects.filter(orderNo__in=cardRechargeOrders).update(status="finishedPay")
  309. except Exception as e:
  310. logger.debug("lz_%s" % e)
  311. return
  312. else:
  313. self.update_card_dealer_and_type(cardNo, cardType=cardType, balance=nowBalance)
  314. logger.info("IcCard recharge completed cardNo:%s asyncMoney:%s nowBalance%s" % (
  315. cardNo, str(ayscMoney), str(nowBalance)))
  316. elif cmd == "F1":
  317. self.deviceAdapter.send_F1_heartbeat()
  318. def do_elec_finish(self, devNo, port, lineInfo, msgDict=None):
  319. leftElec = float(self.event_data.get("leftElec", 0))
  320. reasonStr = self.event_data.get("reason")
  321. cardNo = self.event_data.get("cardNo")
  322. startTime = lineInfo.get("startTime")
  323. startTime = to_datetime(startTime)
  324. nowTime = datetime.datetime.now()
  325. coins = VirtualCoin(lineInfo.get("coins"))
  326. needElec = lineInfo.get("needElec")
  327. if leftElec == 65535:
  328. leftElec = needElec
  329. elif leftElec > needElec:
  330. logger.error("lz_left elec is bigger than need elec. something is wrong")
  331. leftElec = 0
  332. spendElec = needElec - leftElec
  333. if startTime > nowTime:
  334. usedTime = 0
  335. else:
  336. usedTime = int(round(((nowTime - startTime).total_seconds() / 60.0)))
  337. # 获取组信息
  338. group = Group.get_group(self.device["groupId"])
  339. consumeTypeCode = self.event_data["consumeTypeCode"]
  340. if consumeTypeCode == "01": # 投币结束上报 记录到表中
  341. CoinConsumeRcd = ConsumeRecord.objects.get(orderNo=lineInfo["orderNo"])
  342. CoinConsumeRcd.finishedTime = datetime.datetime.now()
  343. consumeDict = {
  344. "chargeIndex": port,
  345. "reason": reasonStr,
  346. "leftElec": leftElec,
  347. "leftMoney": self.event_data["backMoney"],
  348. DEALER_CONSUMPTION_AGG_KIND.DURATION: usedTime,
  349. DEALER_CONSUMPTION_AGG_KIND.ELEC: spendElec,
  350. }
  351. servicedInfo = CoinConsumeRcd.servicedInfo
  352. servicedInfo.update(consumeDict)
  353. servicedInfo["rechargeCode"] = self.event_data["rechargeCode"]
  354. CoinConsumeRcd.servicedInfo = servicedInfo
  355. CoinConsumeRcd.save()
  356. valueDict = {
  357. DEALER_CONSUMPTION_AGG_KIND.TOTAL_COUNT: servicedInfo.get("totalCount"),
  358. DEALER_CONSUMPTION_AGG_KIND.DURATION: servicedInfo.get("duration")
  359. }
  360. status = CoinConsumeRcd.update_agg_info(valueDict)
  361. if status:
  362. record_consumption_stats(CoinConsumeRcd)
  363. ServiceProgress.objects(device_imei=self.device.devNo, port=int(port), isFinished=False).update_one(
  364. upsert=False, isFinished=True, expireAt = datetime.datetime.now())
  365. elif consumeTypeCode == "04": # 扫码的结束
  366. refundCoins = VirtualCoin(0)
  367. consumeDict = {
  368. "reason": reasonStr,
  369. "chargeIndex": port,
  370. "duration": usedTime,
  371. "leftElec": leftElec,
  372. DEALER_CONSUMPTION_AGG_KIND.COIN: coins.mongo_amount,
  373. DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: coins.mongo_amount,
  374. DEALER_CONSUMPTION_AGG_KIND.ELEC: spendElec,
  375. }
  376. refundSwitch = self.device.is_auto_refund
  377. refundProtection = int(lineInfo.get("refundProtection", 0))
  378. refundProtectionTime = int(lineInfo.get("refundProtectionTime", 5))
  379. if refundSwitch:
  380. refundCoins = VirtualCoin(self.event_data["backMoney"])
  381. if (refundProtection == 1 and usedTime < refundProtectionTime) and refundCoins != VirtualCoin(0):
  382. refundCoins = coins
  383. if refundCoins > coins:
  384. refundCoins = coins
  385. if "openId" in lineInfo:
  386. # 扫码使用金币启动
  387. openId = lineInfo["openId"]
  388. extra = []
  389. extra.append({u"消费金额": "{}(金币)".format(coins)})
  390. try:
  391. if refundCoins > VirtualCoin(0):
  392. consumeDict.update({
  393. DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS: refundCoins.mongo_amount,
  394. DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: (coins - refundCoins).mongo_amount
  395. })
  396. refund_money(self.device, refundCoins, openId)
  397. extra.append({u"退款金额": "{}(金币)".format(refundCoins)})
  398. ServiceProgress.update_progress_and_consume_rcd(
  399. self.device.ownerId,
  400. {
  401. "open_id": lineInfo["openId"],
  402. "port": int(port),
  403. "device_imei": self.device.devNo,
  404. "isFinished": False
  405. },
  406. consumeDict
  407. )
  408. finally:
  409. user = MyUser.objects(openId=lineInfo["openId"],
  410. groupId=self.device.groupId).first()
  411. self.notify_user_service_complete(
  412. service_name="充电",
  413. openid=user.managerialOpenId if user else "",
  414. port=port,
  415. address=group["address"],
  416. reason=self.event_data["reason"],
  417. finished_time=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  418. extra=extra
  419. )
  420. else:
  421. logger.error("lz_not net pay rather user virtual card pay. something is wrong.")
  422. # 离线卡结束 直接结束服务进程
  423. elif consumeTypeCode == "02": # 离线卡结束
  424. consumeDict = {
  425. "chargeIndex": port,
  426. "reason": self.event_data["reason"],
  427. "leftElec": leftElec,
  428. "duration": usedTime,
  429. # "finishedTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  430. "cardNo": cardNo,
  431. DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: coins.mongo_amount,
  432. DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: coins.mongo_amount,
  433. DEALER_CONSUMPTION_AGG_KIND.ELEC: spendElec,
  434. }
  435. backMoney = VirtualCoin(self.event_data.get("backMoney", 0))
  436. if backMoney > VirtualCoin(0):
  437. consumeDict.update({
  438. DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: backMoney.mongo_amount,
  439. DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: (coins - backMoney).mongo_amount,
  440. })
  441. card = self.update_card_dealer_and_type(cardNo)
  442. if not card:
  443. logger.info("end charge but no cardNo!!! cardNo=%s" % cardNo)
  444. openId = lineInfo.get("openId", cardNo)
  445. ServiceProgress.update_progress_and_consume_rcd(
  446. self.device.ownerId,
  447. {
  448. "open_id": openId,
  449. "port": int(port),
  450. "device_imei": self.device.devNo,
  451. "isFinished": False,
  452. },
  453. consumeDict
  454. )
  455. # 未绑定用户的离线卡
  456. if card.cardNo == card.openId:
  457. return
  458. extra = [{u"实体卡号": cardNo}]
  459. if backMoney > VirtualCoin(0):
  460. extra.append({
  461. u"等待靠卡返回金额": u"{}(金币)".format(backMoney.mongo_amount)
  462. })
  463. else:
  464. extra.append({
  465. u"消耗金额": u"{}(金币)".format(lineInfo.get("coins",0))
  466. })
  467. self.notify_user_service_complete(
  468. service_name=u"充电",
  469. openid=self.get_managerialOpenId_by_openId(lineInfo.get("openId")),
  470. port=port,
  471. address=group["address"],
  472. reason=self.event_data["reason"],
  473. finished_time=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  474. extra=extra
  475. )
  476. def do_time_finish(self, devNo, port, lineInfo, msgDict=None):
  477. try:
  478. refundProtection = lineInfo.get("refundProtection", 0)
  479. refundProtectionTime = int(lineInfo.get("refundProtectionTime", 5))
  480. startTime = to_datetime(lineInfo["startTime"])
  481. nowTime = datetime.datetime.now()
  482. if startTime > nowTime:
  483. logger.error("lz_start time is bigger than now time,devNo={}".format(devNo))
  484. usedTime = 0
  485. else:
  486. usedTime = (((nowTime - startTime).total_seconds() + 59) / 60)
  487. leftTime = int(self.event_data["leftTime"])
  488. actualNeedTime = usedTime + leftTime
  489. group = self.device.group # type: GroupDict
  490. coins = VirtualCoin(lineInfo.get("coins"))
  491. consumeTypeCode = self.event_data["consumeTypeCode"]
  492. if consumeTypeCode == "01": # 投币结束上报 记录到表中
  493. CoinConsumeRcd = ConsumeRecord.objects.get(orderNo=lineInfo["orderNo"])
  494. CoinConsumeRcd.finishedTime = datetime.datetime.now()
  495. consumeDict = {
  496. "reason": self.event_data["reason"],
  497. "leftTime": leftTime,
  498. "duration": usedTime,
  499. # "finishedTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  500. "leftMoney": self.event_data["backMoney"],
  501. }
  502. servicedInfo = CoinConsumeRcd.servicedInfo
  503. servicedInfo.update(consumeDict)
  504. servicedInfo["rechargeCode"] = self.event_data["rechargeCode"]
  505. CoinConsumeRcd.servicedInfo = servicedInfo
  506. CoinConsumeRcd.save()
  507. valueDict = {
  508. DEALER_CONSUMPTION_AGG_KIND.TOTAL_COUNT: servicedInfo.get("totalCount"),
  509. DEALER_CONSUMPTION_AGG_KIND.DURATION: servicedInfo.get("duration")
  510. }
  511. status = CoinConsumeRcd.update_agg_info(valueDict)
  512. if status:
  513. record_consumption_stats(CoinConsumeRcd)
  514. ServiceProgress.objects(device_imei=self.device.devNo, port=int(port), isFinished=False).update_one(
  515. upsert=False, isFinished=True)
  516. elif consumeTypeCode == "02": # 离线卡
  517. # 服务消息
  518. consumeDict = {
  519. "reason": self.event_data["reason"],
  520. "leftTime": leftTime,
  521. "chargeIndex": port,
  522. "duration": usedTime,
  523. "cardNo": self.event_data["cardNo"],
  524. DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: lineInfo['coins'],
  525. DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: lineInfo['coins'],
  526. }
  527. card = Card.objects.filter(cardNo=lineInfo["cardNo"], agentId=self.dealer.agentId).first()
  528. if not card:
  529. logger.error("lz_not exist this card no.")
  530. return
  531. openId = lineInfo.get("openId", card.cardNo)
  532. backMoney = VirtualCoin(self.event_data.get("backMoney"))
  533. if backMoney > VirtualCoin(0):
  534. consumeDict.update({
  535. DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: VirtualCoin(self.event_data["backMoney"]).mongo_amount,
  536. DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: (coins-backMoney).mongo_amount
  537. })
  538. ServiceProgress.update_progress_and_consume_rcd(self.device.ownerId,
  539. {"open_id": openId, "port": int(port),
  540. "device_imei": self.device.devNo,
  541. "isFinished": False}, consumeDict)
  542. # 判断是否绑卡 绑卡就推送,不绑卡就不推送
  543. if card.cardNo == card.openId:
  544. return
  545. extra = [{
  546. u"实体卡号": lineInfo["cardNo"]
  547. }]
  548. if VirtualCoin(self.event_data["backMoney"]) > VirtualCoin(0):
  549. extra.append({
  550. u"等待靠卡返回金额": u"{}(金币)".format(self.event_data["backMoney"])
  551. })
  552. self.notify_user_service_complete(
  553. service_name="充电",
  554. openid=card.managerialOpenId if card else "",
  555. port=port,
  556. address=group["address"],
  557. reason=self.event_data["reason"],
  558. finished_time=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  559. extra=extra)
  560. elif consumeTypeCode == "04": # 扫码支付
  561. extra = []
  562. consumeDict = {
  563. "reason": self.event_data["reason"],
  564. "leftTime": leftTime,
  565. "chargeIndex": port,
  566. "duration": usedTime,
  567. DEALER_CONSUMPTION_AGG_KIND.COIN: coins.mongo_amount,
  568. DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: coins.mongo_amount,
  569. }
  570. coins = VirtualCoin(lineInfo["coins"])
  571. backCoins = VirtualCoin(0)
  572. if self.device.is_auto_refund:
  573. backCoins = VirtualCoin(self.event_data.get("backMoney", 0))
  574. if (refundProtection == 1 and usedTime < refundProtectionTime) and backCoins != VirtualCoin(0):
  575. backCoins = coins
  576. if backCoins > coins:
  577. backCoins = coins
  578. logger.debug(
  579. "lz_lefttime = {}, usedTime = {}, need time = {}, back coins = {}".format(leftTime, usedTime,
  580. actualNeedTime,
  581. backCoins))
  582. if "openId" in lineInfo:
  583. try:
  584. if backCoins > VirtualCoin(0):
  585. consumeDict.update({
  586. DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS: backCoins.mongo_amount,
  587. DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: (coins- backCoins).mongo_amount
  588. })
  589. refund_money(self.device, backCoins, lineInfo["openId"])
  590. extra.append({u"消费金额": "{}(金币)".format(coins)})
  591. extra.append({u"退款金额": "{}(金币)".format(backCoins)})
  592. else:
  593. extra.append({u"消费金额": "{}(金币)".format(coins)})
  594. ServiceProgress.update_progress_and_consume_rcd(self.device.ownerId,
  595. {"open_id": lineInfo["openId"],
  596. "port": int(port),
  597. "device_imei": self.device.devNo,
  598. "isFinished": False}, consumeDict)
  599. user = MyUser.objects(openId=lineInfo["openId"],
  600. groupId=self.device.groupId).first()
  601. self.notify_user_service_complete(
  602. service_name="充电",
  603. openid=user.managerialOpenId if user else "",
  604. port=port,
  605. address=group["address"],
  606. reason=self.event_data["reason"],
  607. finished_time=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  608. extra=extra
  609. )
  610. except Exception as e:
  611. logger.info(e)
  612. else: # 后续在线卡或是其他
  613. pass
  614. except Exception as e:
  615. logger.exception(e)
  616. def do_billAsService_finish(self,devNo, port, lineInfo, msgDict=None):
  617. serviceFee = float(self.event_data.get("serviceFee", 0))
  618. reasonStr = self.event_data.get("reason")
  619. cardNo = self.event_data.get("cardNo")
  620. startTime = lineInfo.get("startTime")
  621. startTime = to_datetime(startTime)
  622. nowTime = datetime.datetime.now()
  623. coins = VirtualCoin(lineInfo.get("coins"))
  624. if serviceFee == 65535:
  625. serviceFee = 0
  626. usedElec = 0
  627. usedElecFee = 0
  628. else:
  629. usedElec = float(serviceFee) / float(self.device.devTypeFeatures.get("billAsService").get("serviceCharge"))
  630. usedElecFee = float(str(coins)) - float(self.event_data.get("backMoney")) - serviceFee
  631. if startTime > nowTime:
  632. usedTime = 0
  633. else:
  634. usedTime = int(round(((nowTime - startTime).total_seconds() / 60.0)))
  635. # 获取组信息
  636. group = Group.get_group(self.device["groupId"])
  637. consumeTypeCode = self.event_data["consumeTypeCode"]
  638. if consumeTypeCode == "01": # 投币结束上报 记录到表中
  639. CoinConsumeRcd = ConsumeRecord.objects.get(orderNo=lineInfo["orderNo"])
  640. CoinConsumeRcd.finishedTime = datetime.datetime.now()
  641. consumeDict = {
  642. "chargeIndex": port,
  643. "reason": reasonStr,
  644. "leftMoney": self.event_data["backMoney"],
  645. DEALER_CONSUMPTION_AGG_KIND.DURATION: usedTime,
  646. }
  647. servicedInfo = CoinConsumeRcd.servicedInfo
  648. servicedInfo.update(consumeDict)
  649. servicedInfo["rechargeCode"] = self.event_data["rechargeCode"]
  650. CoinConsumeRcd.servicedInfo = servicedInfo
  651. CoinConsumeRcd.save()
  652. valueDict = {
  653. DEALER_CONSUMPTION_AGG_KIND.TOTAL_COUNT: servicedInfo.get("totalCount"),
  654. DEALER_CONSUMPTION_AGG_KIND.DURATION: servicedInfo.get("duration"),
  655. DEALER_CONSUMPTION_AGG_KIND.ELECFEE: usedElecFee,
  656. DEALER_CONSUMPTION_AGG_KIND.SERVICEFEE: serviceFee,
  657. }
  658. status = CoinConsumeRcd.update_agg_info(valueDict)
  659. if status:
  660. record_consumption_stats(CoinConsumeRcd)
  661. ServiceProgress.objects(device_imei=self.device.devNo, port=int(port), isFinished=False).update_one(
  662. upsert=False, isFinished=True, expireAt = datetime.datetime.now())
  663. elif consumeTypeCode == "02": # 离线卡结束
  664. consumeDict = {
  665. "chargeIndex": port,
  666. "reason": self.event_data["reason"],
  667. "duration": usedTime,
  668. "cardNo": cardNo,
  669. DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: VirtualCoin(coins).mongo_amount,
  670. DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: VirtualCoin(coins).mongo_amount,
  671. DEALER_CONSUMPTION_AGG_KIND.SERVICEFEE: serviceFee,
  672. DEALER_CONSUMPTION_AGG_KIND.ELECFEE: usedElecFee,
  673. }
  674. backMoney = VirtualCoin(self.event_data.get("backMoney", 0))
  675. if backMoney > VirtualCoin(0):
  676. consumeDict.update({
  677. DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: backMoney.mongo_amount,
  678. DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: (coins - backMoney).mongo_amount,
  679. })
  680. card = self.update_card_dealer_and_type(cardNo)
  681. if not card:
  682. logger.info("end charge but no cardNo!!! cardNo=%s" % cardNo)
  683. openId = lineInfo.get("openId", cardNo)
  684. ServiceProgress.update_progress_and_consume_rcd(
  685. self.device.ownerId,
  686. {
  687. "open_id": openId,
  688. "port": int(port),
  689. "device_imei": self.device.devNo,
  690. "isFinished": False,
  691. },
  692. consumeDict
  693. )
  694. # 未绑定用户的离线卡
  695. if card.cardNo == card.openId:
  696. return
  697. extra = [{u"实体卡号": cardNo}]
  698. if backMoney > VirtualCoin(0):
  699. extra.append({
  700. u"等待靠卡返回金额": u"{}(金币)".format(backMoney.mongo_amount)
  701. })
  702. else:
  703. extra.append({
  704. u"消耗金额": u"{}(金币)".format(lineInfo.get("coins",0)),
  705. u"服务费金额": "{}(金币)".format(serviceFee),
  706. u"电费金额": "{}(金币)".format(float(lineInfo.get("coins",0)) - serviceFee)
  707. })
  708. self.notify_user_service_complete(
  709. service_name=u"充电",
  710. openid=self.get_managerialOpenId_by_openId(lineInfo.get("openId")),
  711. port=port,
  712. address=group["address"],
  713. reason=self.event_data["reason"],
  714. finished_time=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  715. extra=extra
  716. )
  717. elif consumeTypeCode == "03":
  718. pass
  719. elif consumeTypeCode == "04": # 扫码的结束
  720. refundCoins = VirtualCoin(0)
  721. consumeDict = {
  722. "reason": reasonStr,
  723. "chargeIndex": port,
  724. "duration": usedTime,
  725. DEALER_CONSUMPTION_AGG_KIND.COIN: coins.mongo_amount,
  726. DEALER_CONSUMPTION_AGG_KIND.ELEC: usedElec,
  727. DEALER_CONSUMPTION_AGG_KIND.ELECFEE: usedElecFee,
  728. DEALER_CONSUMPTION_AGG_KIND.SERVICEFEE: serviceFee
  729. }
  730. refundSwitch = self.device.is_auto_refund
  731. refundProtection = int(lineInfo.get("refundProtection", 0))
  732. refundProtectionTime = int(lineInfo.get("refundProtectionTime", 5))
  733. if refundSwitch:
  734. refundCoins = VirtualCoin(self.event_data["backMoney"])
  735. if (refundProtection == 1 and usedTime < refundProtectionTime) and refundCoins != VirtualCoin(0):
  736. refundCoins = coins
  737. if refundCoins > coins:
  738. refundCoins = coins
  739. if "openId" in lineInfo:
  740. # 扫码使用金币启动
  741. openId = lineInfo["openId"]
  742. extra = []
  743. extra.append({u"消费金额": "{}(金币)".format(coins)})
  744. extra.append({u"服务费金额": "{}(金币)".format(serviceFee)})
  745. extra.append({u"电费金额": "{}(金币)".format(usedElecFee)})
  746. try:
  747. if refundCoins > VirtualCoin(0):
  748. consumeDict.update({
  749. DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS: refundCoins.mongo_amount,
  750. DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: (coins - refundCoins).mongo_amount
  751. })
  752. refund_money(self.device, refundCoins, openId)
  753. extra.append({u"退款金额": "{}(金币)".format(refundCoins)})
  754. ServiceProgress.update_progress_and_consume_rcd(
  755. self.device.ownerId,
  756. {
  757. "open_id": lineInfo["openId"],
  758. "port": int(port),
  759. "device_imei": self.device.devNo,
  760. "isFinished": False
  761. },
  762. consumeDict
  763. )
  764. finally:
  765. user = MyUser.objects(openId=lineInfo["openId"],
  766. groupId=self.device.groupId).first()
  767. self.notify_user_service_complete(
  768. service_name="充电",
  769. openid=user.managerialOpenId if user else "",
  770. port=port,
  771. address=group["address"],
  772. reason=self.event_data["reason"],
  773. finished_time=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  774. extra=extra
  775. )
  776. else:
  777. logger.error("lz_not net pay rather user virtual card pay. something is wrong.")
  778. # 离线卡结束 直接结束服务进程
  779. class LVZHIFaultEvent(FaultEvent):
  780. def do(self, **args):
  781. group = Group.get_group(self.device["groupId"])
  782. port = self.event_data.get("port")
  783. faultContent = self.event_data.get("statusInfo")
  784. if port:
  785. if self.event_data.get("FaultCode") == "01":
  786. faultContent = "当前设备第%s号端口警告:发生非法充电" % (port)
  787. else:
  788. faultContent = "当前设备第%s号端口发生故障: %s" % (port, faultContent)
  789. if self.event_data.get("FaultCode") == "04":
  790. self.notify_dealer(
  791. templateName="device_fault",
  792. title="注意!设备火警预告!",
  793. device=u"{groupNumber}组-{logicalCode}".format(groupNumber=self.device["groupNumber"],
  794. logicalCode=self.device["logicalCode"]),
  795. location=u"{address}-{groupName}".format(address=group["address"], groupName=group["groupName"]),
  796. fault=faultContent,
  797. notifyTime=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  798. )
  799. self.record(
  800. faultCode=self.event_data.get("FaultCode"),
  801. description=faultContent,
  802. title="注意!设备火警预告!",
  803. # detail=self.event_data.get("statusInfo"),
  804. )
  805. elif self.event_data.get("FaultCode") == "01":
  806. self.notify_dealer(
  807. templateName="device_fault",
  808. title="注意!非法充电警告!",
  809. device=u"{groupNumber}组-{logicalCode}".format(groupNumber=self.device["groupNumber"],
  810. logicalCode=self.device["logicalCode"]),
  811. location=u"{address}-{groupName}".format(address=group["address"], groupName=group["groupName"]),
  812. fault=faultContent,
  813. notifyTime=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  814. )
  815. self.record(
  816. faultCode=self.event_data.get("FaultCode"),
  817. description=faultContent,
  818. title="注意!非法充电警告!",
  819. )
  820. # 端口管理页面需要看到非法充电端口的端口状态,所以需要在非法充电信息报上来的时候将状态记录到缓存中
  821. if port:
  822. portInfo = {
  823. "port": str(int(port)),
  824. "billingType": "faultCharge",
  825. "startTime" : datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  826. "statusErrorInfo": "非法充电"
  827. }
  828. Device.update_port_control_cache(self.device.devNo,portInfo)
  829. else:
  830. self.notify_dealer(
  831. templateName="device_fault",
  832. title="注意!您的设备当前发生故障!",
  833. device=u"{groupNumber}组-{logicalCode}".format(groupNumber=self.device["groupNumber"],
  834. logicalCode=self.device["logicalCode"]),
  835. location=u"{address}-{groupName}".format(address=group["address"], groupName=group["groupName"]),
  836. fault=faultContent,
  837. notifyTime=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  838. )
  839. self.record(
  840. faultCode=self.event_data.get("FaultCode"),
  841. description=faultContent,
  842. title="注意!您的设备当前发生故障!",
  843. # detail=self.event_data.get("statusInfo"),
  844. )
  845. class LVZHIOnline(WorkEvent):
  846. def do(self, **args):
  847. ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  848. if ctrInfo:
  849. for k , v in ctrInfo.items():
  850. if isinstance(v,dict):
  851. if 'statusErrorInfo' in v:
  852. if v['statusErrorInfo'] == "非法充电":
  853. Device.clear_port_control_cache(self.device.devNo, str(k))