123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- import datetime
- import logging
- import random
- import time
- import uuid
- import simplejson as json
- from bson import ObjectId
- from apilib.monetary import Ratio, RMB, VirtualCoin
- from apilib.utils_sys import memcache_lock
- from apps.web.api.models import APIServiceStartRecord
- from apps.web.common.models import TempValues
- from apps.web.constant import Const, DEALER_CONSUMPTION_AGG_KIND
- from apps.web.core.accounting import Accounting
- from apps.web.core.device_define.changyuan import CYCardMixin, CYConstant
- from apps.web.device.models import Group, Device
- from apps.web.eventer import EventBuilder
- from apps.web.eventer.base import FaultEvent, WorkEvent
- from apps.web.report.utils import record_consumption_stats
- from apps.web.south_intf.platform import handle_and_notify_event_to_north_cy4, handle_and_notify_event_to_north_cy4_new
- from apps.web.user.models import RefundMoneyRecord
- from apps.web.user.models import ServiceProgress, RechargeRecord, CardRechargeOrder, CardRechargeRecord, Card, MyUser, \
- ConsumeRecord
- from apps.web.user.transaction_deprecated import refund_money, refund_cash
- logger = logging.getLogger(__name__)
- class builder(EventBuilder):
- def __getEvent__(self, device_event):
- event_data = self.deviceAdapter.analyze_event_data(device_event['data'])
- if event_data is None or 'cmdCode' not in event_data:
- return
- if event_data["cmdCode"] in ["D7"]:
- return ChargingCY4FaultEventer(self.deviceAdapter, event_data)
- if event_data['cmdCode'] in ['D8', 'D9', 'DA', 'D4', 'D3']:
- return ChargingCY4WorkEventer(self.deviceAdapter, event_data)
- return None
- class ChargingCY4FaultEventer(FaultEvent):
- def do(self, **args):
- # 收到指令之后优先回复
- self.deviceAdapter.ack_event()
- group = Group.get_group(self.device["groupId"])
- warningData = {
- "warningStatus": 2,
- "warningDesc": "设备温度超限警告",
- "warningTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
- }
- # 整机告警
- Device.update_dev_warning_cache(self.device.devNo, {"0": warningData})
- self.notify_dealer(
- templateName = "device_fault",
- title = "注意!设备火警预告!",
- device = u"{groupNumber}组-{logicalCode}".format(groupNumber = self.device["groupNumber"],
- logicalCode = self.device["logicalCode"]),
- location = u"{address}-{groupName}".format(address = group["address"], groupName = group["groupName"]),
- fault = u"设备当前温度过高,可能发生了火警!",
- notifyTime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
- )
- class ChargingCY4WorkEventer(CYCardMixin, WorkEvent):
- def do(self, **args):
- devNo = self.device['devNo']
- logger.info('ChangYuan4 charging event detected, devNo=%s,curInfo=%s' % (devNo, self.event_data))
- cmdCode = self.event_data['cmdCode']
- with memcache_lock(key = "{devNo}.{cmdCode}.event".format(devNo = devNo, cmdCode = cmdCode), expire=10, value = '1') as acquired:
- if acquired:
- # 收到某系指令之后先回复确认
- if self.event_data.get("cmdCode") in ["D3", "D9", "DA"]:
- self.deviceAdapter.ack_event()
- # 设备状态由主板主动上报,上报之后需要和之前的端口信息做对比,如果之前端口处于工作状态的,而现在出于空闲状态的,需要结束服务
- if cmdCode == 'D8':
- devCache = Device.get_dev_control_cache(devNo)
- portInfo = self.event_data.get("portInfo")
- newDevCache = self.compare_port_cache(portInfo, devCache)
- self.temperature_check(self.event_data.get("temperature"))
- self.main_board_check(portInfo)
- newDevCache.update({"temperature": self.event_data.get("temperature")})
- Device.update_dev_control_cache(self.device["devNo"], newDevCache)
- if "cy4EventApi" in self.dealer.features:
- for port, portData in newDevCache.items():
- if port.isdigit():
- orderNo = portData["consumeType"] + str(uuid.uuid4())
- dataDict = {"deviceCode":self.device.logicalCode,
- "port":port,
- "notifyType": "portInfo",
- "status":portData["status"],
- "portInfo":{}}
- if portData["status"] == Const.DEV_WORK_STATUS_WORKING:
- portDict = {
- "deviceCode": self.device.logicalCode,
- "status": Const.DEV_WORK_STATUS_WORKING,
- "power" : portData.get('power',0),
- "leftTime": portData.get('leftTime',None),
- "startTime":portData["startTime"],
- "consumeType":portData["consumeType"],
- "orderNo":orderNo,
- "isAPI":portData.get("isAPI",False),
- "port":port,
- "extOrderNo":portData.get("extOrderNo",""),
- }
- dataDict["portInfo"] = portDict
- handle_and_notify_event_to_north_cy4_new(self.device,portDict)
- # 开始充电 状态上报 启动方式分为 刷卡 扫码 投币 对于刷卡需要创建相应的消费记录 其余的更新端口状态
- elif cmdCode == 'D9':
- self.do_event_start()
- elif cmdCode == 'DA': # 充电停止
- self.do_event_finish()
- elif cmdCode == 'D4': # 卡充值
- self.do_event_recharge_card()
- return # D4充值,直接已经回应了报文,不需要再次回应报文。
- elif cmdCode == 'D3': # 卡充值的回执
- self.do_event_recharge_card_result()
- def compare_port_cache(self, portInfo, devCache):
- """
- 主板要求 对比 上报上来的端口信息 和 缓存中的端口信息 如果端口的信息由工作变为空闲 ,则处理该端口停止
- 同时更新端口缓存信息
- :param portInfo: 主板上报的信息
- :param devCache: 服务器记录的信息
- :return:
- """
- newDevCache = dict()
- for portStr, portData in portInfo.items():
- # 首先读取 服务器的缓存信息 如果存在等待D9的标记 则此次上报的数据不对 该端口信息产生任何影响
- portCache = devCache.get(portStr) or dict()
- if portCache.get("waitD9"):
- newDevCache[portStr] = portCache
- continue
- # 存在剩余时间以及使用时间
- if portData.get("leftTime", 0) and portCache.get("needTime", 0):
- portData["usedTime"] = portCache.get("needTime") - portData.get("leftTime")
- portCache.update(portData)
- newDevCache[portStr] = portCache
- # 非正常情况 清掉缓存
- if portData.get("status") == Const.DEV_WORK_STATUS_IDLE and portCache.get("status") == Const.DEV_WORK_STATUS_WORKING:
- Device.update_dev_control_cache(self.device.devNo, {portStr: {'status': Const.DEV_WORK_STATUS_IDLE}})
- return newDevCache
- def stop_port(self, portStr):
- """
- 停止端口 状态
- :param portStr:
- :return:
- """
- self.deviceAdapter.stop(portStr)
- def do_event_start(self):
- """
- 处理 D9 充电开始指令事件
- :return:
- """
- cardNo = self.event_data["cardNo"]
- portStr = self.event_data["port"]
- if not self.event_data["result"]:
- logger.info("start charging device reply error! devNo is %s, port is %s" % (self.device["devNo"], portStr))
- return
- if self.event_data["consumeType"] == "card":
- card = self.update_card_dealer_and_type(cardNo)
- if "cy4EventApi" not in self.dealer.features:
- if not self.check_card_can_use(card):
- logger.info("[ChargingCY4WorkEventer do_event_start] check card not can use devNo = {}".format(self.device.devNo))
- self.notify_invalid_card_to_dealer(self.event_data["cardNo"], card)
- return self.deviceAdapter.stop_charging_port(portStr)
- # 判断卡是否存在 如果不存在 直接更新缓存信息即可
- if not card:
- Device.update_dev_control_cache(self.device.devNo, {portStr: self.event_data})
- return
- # 更新卡的余额
- self.update_card_balance(card, RMB(self.event_data["balance"]))
- # 记录卡的消费
- attachParas = {"chargeIndex": portStr}
- servicedInfo = {"cardNo": cardNo, "chargeIndex": portStr}
- orderNo, cardOrderNo = self.record_consume_for_card(card, RMB(self.event_data["coins"]), servicedInfo=servicedInfo, attachParas=attachParas)
- # 记录当前的卡消费的 progress
- fee = RMB(self.event_data["coins"])
- ServiceProgress.register_card_service(
- self.device,
- int(portStr),
- card,
- {
- "orderNo": orderNo,
- "money": fee.mongo_amount,
- "coin": fee.mongo_amount,
- "needTime": self.event_data["needTime"],
- "cardOrderNo": cardOrderNo
- }
- )
- # 通知卡消费成功
- self.notify_balance_has_consume_for_card(card, fee)
- cacheDict = {
- "isStart": True,
- "status": Const.DEV_WORK_STATUS_WORKING,
- "coins": float(fee),
- "price": float(fee),
- "startTime": datetime.datetime.now().strftime(Const.DATETIME_FMT),
- "openId": card.openId,
- "needTime": self.event_data.get("needTime"),
- "power": self.event_data.get("power"),
- "consumeType": "card",
- "payInfo": [{
- "needTime": self.event_data.get("needTime"),
- "coins": float(fee),
- "price": float(fee),
- "cardId": str(card.id),
- "cardNo": self.event_data.get("cardNo")
- }]
- }
- Device.update_dev_control_cache(self.device['devNo'], {portStr: cacheDict}) # 记录该端口累计需要的时间和钱
- else:
- # dataDict = {
- # 'orderNo': orderNo,
- # 'port': portStr,
- # 'devNo': self.device['devNo'],
- # 'logicalCode': self.device['logicalCode'],
- # 'money': float(fee),
- # 'startTime': datetime.datetime.now().strftime(Const.DATETIME_FMT),
- # 'openId': card.openId,
- # 'cardNo': cardNo,
- # 'needTime': self.event_data.get('needTime'),
- # 'consumeType': 'card',
- # 'status': 'PENDING',
- # }
- dataDict = {
- "data": self.event_data.get('data'),
- "logicalCode":self.device['logicalCode'],
- "notifyType":"swipCard"
- }
- handle_and_notify_event_to_north_cy4_new(self.device, dataDict)
- elif self.event_data["consumeType"] == "mobile":
- # 如果是扫码启动的设备 可能会存在续充问题 payInfo needTime 需要特殊处理
- devCache = Device.get_dev_control_cache(self.device["devNo"])
- portCache = devCache.get(portStr, {})
- if portCache.get('isAPI', False) is False:
- # 没有 等待D9 的标记 说明已经处理过D9了 不需要再次处理
- if not portCache.get("waitD9"):
- return
- # 如果是虚拟卡消费,在service处理消费记录的时候一定会写入到端口缓存中
- consumeRcdId = portCache.pop("consumeRcdId", None)
- # 获取本阶段的充电时间
- needTime = self.get_need_time(portCache, self.event_data.get("needTime"))
- payInfo = portCache.get("payInfo")
- payInfo[-1].update({
- "needTime": needTime,
- })
- if consumeRcdId:
- payInfo[-1].update({"consumeRcdId": consumeRcdId})
- portCache.update({
- "needTime": portCache.get("needTime", 0) + needTime,
- "power": self.event_data["power"],
- "waitD9": False
- })
- Device.update_dev_control_cache(self.device["devNo"], {portStr: portCache})
- else:
- portCache.update({
- "needTime": self.event_data["needTime"] if self.event_data["needTime"] != 0 else 1,
- "power": self.event_data["power"],
- "waitD9": False
- })
- Device.update_dev_control_cache(self.device["devNo"], {portStr: portCache})
- dataDict = {
- 'orderNo': portCache.get('orderNo'),
- 'port': portStr,
- 'devNo': self.device.get("devNo"),
- 'logicalCode': self.device.get('logicalCode'),
- 'money': portCache.get('price'),
- 'startTime': datetime.datetime.now().strftime(Const.DATETIME_FMT),
- 'needTime': int(self.event_data.get('needTime', 0)),
- 'consumeType': 'mobile',
- 'status': 'PENDING',
- 'extOrderNo': portCache.get('extOrderNo')
- }
- handle_and_notify_event_to_north_cy4(self.device, dataDict)
- dataDict.update({"notifyType": "start"})
- handle_and_notify_event_to_north_cy4_new(self.device, dataDict)
- # 投币的暂时只是更新一下端口缓存
- elif self.event_data["consumeType"] == "coin":
- nowTime = datetime.datetime.now().strftime(Const.DATETIME_FMT)
- Accounting.recordOfflineCoin(
- self.device,
- int(time.time()),
- int(self.event_data["coins"]),
- port = portStr)
- orderNo = self.record_consume_for_coin(RMB(self.event_data["coins"]))
- Device.update_dev_control_cache(
- self.device["devNo"],
- {
- portStr: {
- "isStart": True,
- "coins": self.event_data["coins"],
- "needTime": self.event_data["needTime"],
- "power": self.event_data["power"],
- "consumeType": self.event_data["consumeType"],
- "startTime": datetime.datetime.now().strftime(Const.DATETIME_FMT),
- "status": Const.DEV_WORK_STATUS_WORKING,
- "consumeOrderNo": orderNo,
- "payInfo": [{
- "coins": float(self.event_data["coins"]),
- "price": float(self.event_data["coins"]),
- "needTIme": self.event_data["needTime"],
- "consumeOrderNo": orderNo
- }]
- }
- }
- )
- else:
- logger.error("error consumeType %s" % self.event_data["consumeType"])
- def do_event_finish(self):
- """
- 充电结束事件上报
- :return:
- """
- portStr = self.event_data["port"]
- leftTime = self.event_data["leftTime"]
- devCache = Device.get_dev_control_cache(self.device.devNo)
- portCache = devCache.get(portStr, {})
- if not portCache:
- logger.error("[ChargingCY4WorkEventer do_event_finish] portCache is None, device = {}, port = {}".format(self.device.devNo, portStr))
- return
- group = self.device.group
- dealer = self.device.owner
- if not dealer or not group:
- logger.error('[ChargingCY4WorkEventer do_event_finish] dealer is not found, dealerId = {}'.format(self.device.ownerId))
- return
- if "coins" not in portCache:
- logger.error("[ChargingCY4WorkEventer do_event_finish] no coins info, device = {}, event data = {}".format(self.device.devNo, self.event_data))
- return
- nowTime = datetime.datetime.now()
- openId = portCache.get("openId", None)
- consumeType = portCache.get("consumeType", None)
- if consumeType == "mobile":
- if portCache.get('isAPI', False) is False:
- if not openId:
- # 没有OPENID的情况下 仅仅是 清空端口缓存就OK
- Device.clear_port_control_cache(self.device.devNo, portStr)
- logger.info("[ChargingCY4WorkEventer do_event_finish] mobile consumeType finish but no openId, devNo = {}, port = {}".format(self.device.devNo, portStr))
- return
- user = MyUser.objects(openId=openId, groupId=self.device.groupId).first()
- consumeDict = {
- 'chargeIndex': portStr,
- 'reason': self.event_data["reason"],
- 'actualNeedTime': portCache.get("needTime"),
- 'duration': int(portCache.get("needTime", 0)) - int(leftTime),
- 'elec': self.event_data["spendElec"],
- 'elecFee': self.calc_elec_fee(self.event_data["spendElec"]),
- 'leftTime': leftTime,
- 'needTime': u'扫码订购%s分钟' % portCache.get("needTime"),
- DEALER_CONSUMPTION_AGG_KIND.COIN: VirtualCoin(portCache.get("coins")).mongo_amount
- }
- # 扫码退费的情况处理
- payInfo = portCache.get("payInfo", list())
- allNeedTime = portCache.get("needTime", 0)
- usedTime = allNeedTime - leftTime
- extra = [
- {u"订购时长": u"{} 分钟".format(allNeedTime)},
- {u"使用时长": u"{} 分钟".format(usedTime)}
- ]
- # 剩余时间比总订购时间还要长 则证明没有使用
- if usedTime <= 0:
- usedTime = 0
- if usedTime <= CYConstant.REFUND_PRODUCTION_TIME:
- usedTime = 0
- self.event_data.update({"reason": u"异常结束,如非自己停止,可能是插头松动"})
- self.notify_user_service_complete(
- service_name=u"充电",
- openid=user.managerialOpenId if user else "",
- port=self.event_data['port'],
- address=group.address,
- reason=self.event_data["reason"],
- finished_time=nowTime.strftime("%Y-%m-%d %H:%M:%S"),
- extra=extra
- )
- # 如果设备开启了退费并且结束原因在退费原因之列 或者时间不满5分钟
- if (self.device.is_auto_refund and self.event_data.get(
- "reasonCode") in CYConstant.NEED_REFUND) or usedTime <= CYConstant.REFUND_PRODUCTION_TIME:
- # 总的退还
- allRefundMoney = VirtualCoin(0)
- # 针对每一笔的支付信息不同进行退款
- for item in payInfo:
- usedTime, refundMoney = self.refund_user(item, usedTime, openId)
- allRefundMoney += refundMoney
- if allRefundMoney > VirtualCoin(0):
- consumeDict.update({
- DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS: allRefundMoney.mongo_amount,
- DEALER_CONSUMPTION_AGG_KIND.COIN: (
- VirtualCoin(portCache.get("coins")) - allRefundMoney).mongo_amount
- })
- refundTitle = u'您使用的%s号端口充电,共付款:%s元,充电预定时间为:%s分钟,剩余时间:%s分钟,给您退款:%s元(退款金额将于1-5个工作日内返还)' % (
- portStr, portCache.get("coins"), portCache.get('needTime'), leftTime, allRefundMoney)
- #
- self.notify_user(user.managerialOpenId if user else '', 'refund_coins', **{
- 'title': refundTitle,
- 'backCount': u'%s' % allRefundMoney.amount,
- 'finishTime': datetime.datetime.strftime(nowTime, "%Y-%m-%d %H:%M:%S")
- })
- # 没有退费的情况下 需要对经销商进行分账
- else:
- for item in payInfo:
- rechargeRcdId = item.get("rechargeRcdId")
- self.do_ledger(rechargeRcdId)
- ServiceProgress.update_progress_and_consume_rcd(
- self.device["ownerId"],
- {
- "open_id": openId,
- "device_imei": self.device["devNo"],
- "port": int(portStr),
- "isFinished": False
- },
- consumeDict
- )
- else:
- try:
- asr = APIServiceStartRecord.objects(orderNo=portCache['orderNo']).first()
- needTime = asr.needTime
- dataDict = {
- 'port': portStr,
- 'devNo': self.device['devNo'],
- 'logicalCode': self.device['logicalCode'],
- 'finishReason': self.event_data["reason"],
- 'duration': int(needTime) - int(leftTime),
- 'spendElec': self.event_data["spendElec"],
- 'leftTime': int(leftTime),
- 'status': 'FINISHED',
- 'consumeType': 'mobile',
- 'endTime': datetime.datetime.now().strftime(Const.DATETIME_FMT),
- 'extOrderNo': asr.extOrderNo
- }
- handle_and_notify_event_to_north_cy4(self.device, dataDict)
- dataDict.update({"notifyType": "finished"})
- handle_and_notify_event_to_north_cy4_new(self.device, dataDict)
- except Exception as e:
- logger.error('changyuan4 api finish error! e={}'.format(e))
- Device.clear_port_control_cache(self.device['devNo'], portStr)
- elif consumeType == "card":
- cardId = portCache.get("payInfo")[0].get("cardId")
- if not cardId:
- logger.info("[ChargingCY4WorkEventer do_event_finish] card consumeType finish but no cardId, devNo = {}, port = {}".format(self.device.devNo, portStr))
- else:
- card = Card.objects.get(id = cardId)
- extra = [
- {u"刷卡卡号": "{}".format(card.cardNo)},
- {u"订购时长": u"{} 分钟".format(portCache.get("needTime", 0))},
- {u"使用时长": u"{} 分钟".format(int(portCache.get("needTime", 0)) - int(leftTime))}
- ]
- self.notify_user_service_complete(
- service_name=u"充电",
- openid=card.managerialOpenId,
- port=self.event_data['port'],
- address=group.address,
- reason=self.event_data["reason"],
- finished_time=nowTime.strftime("%Y-%m-%d %H:%M:%S"),
- extra=extra
- )
- consumeDict = {
- 'chargeIndex': portStr,
- 'reason': self.event_data["reason"],
- 'actualNeedTime': portCache.get("needTime"),
- 'duration': int(portCache.get("needTime")) - int(leftTime),
- 'elec': self.event_data["spendElec"],
- 'elecFee': self.calc_elec_fee(self.event_data["spendElec"]),
- 'leftTime': leftTime,
- 'needTime': u'刷卡订购%s分钟' % portCache.get("needTime")
- }
- ServiceProgress.update_progress_and_consume_rcd(
- self.device["ownerId"],
- {
- "open_id": openId,
- "device_imei": self.device["devNo"],
- "port": int(portStr),
- "isFinished": False
- },
- consumeDict
- )
- dataDict = {
- 'port': portStr,
- 'cardNo': card.cardNo,
- 'devNo': self.device['devNo'],
- 'logicalCode': self.device['logicalCode'],
- 'finishReason': self.event_data["reason"],
- 'duration': int(portCache.get("needTime")) - int(leftTime),
- 'spendElec': self.event_data["spendElec"],
- 'leftTime': int(leftTime),
- 'status': 'FINISHED',
- 'consumeType': 'card',
- 'endTime': datetime.datetime.now().strftime(Const.DATETIME_FMT),
- }
- handle_and_notify_event_to_north_cy4(self.device, dataDict)
- dataDict.update({"notifyType": "finished"})
- handle_and_notify_event_to_north_cy4_new(self.device, dataDict)
- elif consumeType == "coin":
- CoinConsumeRcd = ConsumeRecord.objects.get(orderNo = portCache.get('consumeOrderNo')) # type: ConsumeRecord
- CoinConsumeRcd.servicedInfo['elec'] = portCache.get("spendElec")
- CoinConsumeRcd.servicedInfo['actualNeedTime'] = u'动态功率计算为%s分钟' % str(
- int(portCache.get("needTime")) - int(leftTime))
- CoinConsumeRcd.servicedInfo['needTime'] = u'投币订购%s分钟' % portCache.get("needTime")
- CoinConsumeRcd.servicedInfo['reason'] = self.event_data.get("reason")
- CoinConsumeRcd.servicedInfo['chargeIndex'] = portStr
- CoinConsumeRcd.finishedTime = datetime.datetime.strftime(nowTime, '%Y-%m-%d %H:%M:%S')
- CoinConsumeRcd.save()
- valueDict = {
- DEALER_CONSUMPTION_AGG_KIND.ELEC: portCache.get("spendElec"),
- DEALER_CONSUMPTION_AGG_KIND.ELECFEE: self.calc_elec_fee(portCache.get("spendElec"))
- }
- status = CoinConsumeRcd.update_agg_info(valueDict)
- if status:
- record_consumption_stats(CoinConsumeRcd)
- else:
- logger.error(
- '[update_progress_and_consume_rcd] failed to update_agg_info record=%r' % (CoinConsumeRcd,))
- else:
- logger.error("error consumeType! devNo is %s, port is %s" % (self.device["devNo"], portStr))
- # 事件结束之后清空端口的信息 更新设备的服务信息
- Device.clear_port_control_cache(self.device["devNo"], portStr)
- def do_event_recharge_card(self):
- """
- 主板发起 卡金额同步指令
- 服务器对照 相应卡的 余额 状态 充值记录等等进行相应的操作
- :return:
- """
- cardNo = self.event_data["cardNo"]
- cardBalance = self.event_data["balance"]
- card = self.update_card_dealer_and_type(cardNo)
- if not card:
- logger.error("no card exist, can not async card balance")
- return
- # 更新卡的余额 以设备侧的为准
- if RMB(cardBalance) != card.balance:
- logger.info('Card<{}> balance<{}> needs to be sync !!!'.format(cardNo, card.balance))
- card.balance = RMB(cardBalance)
- card = card.save()
- # orderNos 表示充值的订单 cardOrderNos 表示卡充值的订单
- money, coins, orderNos, cardOrderNos = CardRechargeOrder.get_to_do_list(str(card.id))
- # 一旦卡被冻结 立即下发 创建一张为 负数的金额订单 将卡的余额清空
- if card.frozen:
- group = Group.get_group(card.groupId)
- CardRechargeOrder.new_one(
- openId=card.openId,
- cardId=str(card.id),
- cardNo=card.cardNo,
- money=RMB(0),
- coins=VirtualCoin(0) - VirtualCoin(coins) - VirtualCoin(card.balance),
- group=group,
- rechargeId=ObjectId(),
- rechargeType=u"sendCoin"
- )
- money, coins, orderNos, cardOrderNos = CardRechargeOrder.get_to_do_list(str(card.id))
- # 没有需要同步的订单的情况下 直接退出
- if not orderNos:
- logger.info('Not card recharge record!, card is <{}>'.format(cardNo))
- # self._do_offline_recharge_by_dealer(cardNo, card, cardType)
- return
- orderNos = [str(item) for item in orderNos]
- sid = str(random.randint(0, 0xFFFF))
- TempValues.set('%s-%s' % (self.device.devNo, sid), value=json.dumps(orderNos))
- TempValues.set('%s-%s-%s' % (self.device.devNo, sid, cardNo), value=json.dumps(cardOrderNos))
- TempValues.set('%s-%s' % (self.device.devNo, cardNo), value=str(sid))
- # 计算总的需要同步的金额
- asyncMoney = RMB(cardBalance) + RMB(coins)
- logger.info('ready to recharge card, card is <{}>, money is <{}>'.format(cardNo, coins))
- # 已经下发金额后,先将订单的状态改为结束。如果主板上报失败,再将订单状态迁移回来否则就不再变化订单状态了
- try:
- CardRechargeOrder.update_card_order_has_finished(str(card.id))
- except Exception as e:
- logger.debug('%s' % e)
- else:
- self.deviceAdapter.recharge_card(cardNo, asyncMoney)
- def do_event_recharge_card_result(self):
- """
- 同步卡余额 D3指令的应答指令 表示主板已经确认卡的余额已经被更新
- :return:
- """
- result = self.event_data["result"]
- if not result:
- logger.error("async card balance error, device reply error")
- return
- answerSid = self.event_data["sid"]
- cardNo = self.event_data["cardNo"]
- cardBalance = self.event_data["balance"]
- sid = TempValues.get("%s-%s" % (self.device["devNo"], cardNo))
- if sid != answerSid:
- logger.error("answer sid is not equal sid! %s\t%s" % (answerSid, sid))
- card = self.update_card_dealer_and_type(cardNo)
- if not card:
- logger.error("not found card, event is <{}>".format(self.event_data))
- return
- # 根据sid 以及卡号 查询出此次充值的所有的订单
- orderNos = TempValues.get('{}-{}'.format(self.device.devNo, sid))
- cardOrderNos = TempValues.get('{}-{}-{}'.format(self.device.devNo, sid, cardNo))
- # 下面的都不会更改订单的状态 最多走售后
- money, coins = RMB(0), VirtualCoin(0)
- # 校验金额是否是相等的
- orderNos = [ObjectId(item) for item in orderNos]
- rds = RechargeRecord.objects.filter(ownerId=card.dealerId, id__in=orderNos)
- for item in rds:
- money += item.money
- coins += item.coins
- # 这个地方就是异常值处理了
- if VirtualCoin(coins + card.balance) != VirtualCoin(cardBalance):
- logger.error('card pre balance not equal now balance! event is <{}>'.format(self.event_data))
- return
- # 依次更改卡充值的订单 并创建充值订单
- cardOrders = CardRechargeOrder.objects.filter(orderNo__in=cardOrderNos)
- for _order in cardOrders: # type: CardRechargeOrder
- _order.update_after_recharge_ic_card(
- device=self.device,
- sendMoney=RMB(_order.coins),
- preBalance=card.balance
- )
- preBalance = card.balance
- card.balance = card.balance + _order.coins
- # 创建充值记录
- CardRechargeRecord.add_record(
- card=card,
- group=Group.get_group(_order.groupId),
- order=_order,
- device=self.device
- )
- # 保存
- card.save()
- # 完成之后将TempValue 的key value 清空
- TempValues.remove('%s-%s' % (self.device['devNo'], sid))
- TempValues.remove('%s-%s' % (self.device['devNo'], cardNo))
- TempValues.remove("%s-%s-%s" % (self.device["devNo"], sid, cardNo))
- def temperature_check(self, temperature):
- """
- 温度监测
- :param temperature
- :return:
- """
- # 黑龙江温度 -19 度 昌原让屏蔽低温告警
- if temperature > self.deviceAdapter.MAX_TEMPERATURE:
- group = Group.get_group(self.device["groupId"])
- # self.notify_dealer(
- # 'device_fault',
- # **{
- # 'title': u'注意!注意!设备温度不正常',
- # 'device': u'组号::%s, 二维码编号: %s, 当前温度: %s' % (self.device['groupNumber'], self.device['logicalCode'], temperature),
- # 'location': u'组名称:%s, 地址:%s' % (group['groupName'], group['address']),
- # 'fault': u"设备温度超限",
- # 'notifyTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
- # }
- # )
- self.notify_dealer(
- templateName = "device_fault",
- title = "注意!设备当前温度过高",
- device = u"{groupNumber}组-{logicalCode}".format(groupNumber = self.device["groupNumber"],
- logicalCode = self.device["logicalCode"]),
- location = u"{address}-{groupName}".format(address = group["address"], groupName = group["groupName"]),
- fault = u"设备当前温度过高,请小心火警发生!",
- notifyTime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
- )
- def main_board_check(self, portInfo):
- """
- 继电器粘连 监测
- :param portInfo:
- :return:
- """
- group = Group.get_group(self.device["groupId"])
- for portStr, portData in portInfo.items():
- if portData.get("status") == Const.DEV_WORK_STATUS_FAULT_RELAY_CONNECT:
- # self.notify_dealer(
- # 'device_fault',
- # **{
- # 'title': u'注意!注意!您的设备发生继电器粘连',
- # 'device': u'组号::%s, 二维码编号: %s, 端口号: %s' % (self.device['groupNumber'], self.device['logicalCode'], portStr),
- # 'location': u'组名称:%s, 地址:%s' % (group['groupName'], group['address']),
- # 'fault': u"设备端口发生继电器粘连",
- # 'notifyTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
- # }
- # )
- self.notify_dealer(
- templateName = "device_fault",
- title = "注意!设备继电器发生粘连!",
- device = u"{groupNumber}组-{logicalCode}-{port}".format(groupNumber = self.device["groupNumber"],
- logicalCode = self.device["logicalCode"],
- port = portStr),
- location = u"{address}-{groupName}".format(address = group["address"],
- groupName = group["groupName"]),
- fault = u"设备继电器粘连,端口将一直处于供电状态!",
- notifyTime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
- )
- @staticmethod
- def get_need_time(portCache, allLeftTime):
- """
- 获取此次支付的 needTime 协议中上报的NeedTime的理解:第一次支付获得了300分钟,使用了100分钟,第二次支付又获得了300分钟 然后上传过来的needTime是500分钟
- :param portCache: 端口缓存值
- :param allLeftTime: 上报过来的needTime
- :return:
- """
- leftTime = portCache.get("leftTime", 0)
- needTime = portCache.get("needTime")
- # 如果端口缓存中不存在剩余时间 说明是第一次支付
- # 绝大多数是这种情况
- if not leftTime and not needTime:
- return allLeftTime
- # 如果存在剩余时间 则可以说明此次获得的消费时间 = 上传的总剩余时间-之前的剩余时间(可能会有误差 已经和主板沟通)
- # 充电了一段时间之后的续充
- if leftTime and needTime:
- return allLeftTime - leftTime
- # 如果不存在剩余时间 但是端口缓存中已经存在了总的消费时间 说明是在1-5分钟之内续充 也就是D8指令还没有上来 连续续充 D9, D9
- # 连续续充 连续两次D9
- if not leftTime and needTime:
- return allLeftTime - needTime
- # 这种情况应该是不可能发生的,但是还是以防万一,出现的情况可能是 设备启动后,定时上传指令 比 上传充电指令更早到
- # D8 先 D9后 通过标记应该已经解决
- if leftTime and not needTime:
- return allLeftTime - leftTime
- def refund_user(self, payInfo, usedTime, openId):
- """
- 退钱给用户 每一笔退还 如果不是虚拟卡支付 则说明是金币或者现金启动 后分账方式都需要对经销商进行分账
- :param payInfo: 单笔支付的信息
- :param usedTime: 使用的时间
- :param openId:
- :return: usedTime 退还此笔消费之后还剩余的使用时间 refundMoney 此笔退款退还的钱(币种可能不同 后续需要统一设计)
- """
- needTime = payInfo.get("needTime", 0)
- rechargeRcdId = payInfo.get("rechargeRcdId")
- vCardId = payInfo.get("vCardId")
- coins = payInfo.get("coins")
- # 进入退款后,还是优先分账 分完帐再说
- rechargeRecord = self.do_ledger(rechargeRcdId)
- # 接下来 计算退款率
- leftTime = needTime - usedTime
- # 情况一: 计算出来的剩余时间小于等于0 说明本单使用完毕 无需退款 但有可能下一单还需要退
- if leftTime <= 0:
- return usedTime - needTime, VirtualCoin(0)
- # 情况二:虚拟卡的直接本单不退 由于虚拟卡算1次 所以这一单直接不处理
- if vCardId:
- return 0, VirtualCoin(0)
- # 计算退款率 所需的退款即退款全额 * 退款率
- refundRate = Ratio(float(leftTime) / float(needTime))
- if rechargeRecord and rechargeRecord.isQuickPay:
- # 情况三: 现金退费
- refundMoney = refundRate * RMB(rechargeRecord.money)
- refund_cash(
- rechargeRecord, RMB(refundMoney), VirtualCoin(0),
- minus_total_consume = VirtualCoin(rechargeRecord.coins)) # type: RefundMoneyRecord
- return 0, refundMoney
- else:
- # 情况四:金币退费
- refundMoney = refundRate * VirtualCoin(coins)
- refund_money(self.device, refundMoney, openId)
- return 0, refundMoney
|