123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- # coding=utf-8
- import datetime
- import logging
- import typing
- from arrow import Arrow
- from django.conf import settings
- from apilib.monetary import VirtualCoin
- from apps.web.constant import START_DEVICE_STATUS, DEALER_CONSUMPTION_AGG_KIND, DeviceCmdCode
- from apps.web.core.device_define.cmCZ import FINISH_REASON_MAP, CARD_RESPONSE, CARD_CST_MIN, CARD_CST
- from apps.web.core.networking import MessageSender
- from apps.web.device.models import Device
- from apps.web.eventer.base import WorkEvent, ComNetPayAckEvent, IdStartAckEvent, AckEventProcessorIntf
- from apps.web.user.utils import clear_frozen_user_balance
- from apps.web.utils import set_start_key_status
- from apps.web.user.models import ConsumeRecord, Card, CardRechargeOrder
- logger = logging.getLogger(__name__)
- class cmCZSubEventWorker(WorkEvent):
- def do(self, **args):
- funCode = self.event_data["cmdCode"]
- if funCode == "26":
- return self._do_report()
- if funCode == "10":
- return self._do_card()
- def _do_report(self):
- self.device.set_online(31)
- portInfo = self.event_data.pop("portInfo")
- Device.update_dev_control_cache(self.device.devNo, portInfo)
- def _do_card(self):
- cardNo = self.event_data["cardNo"]
- cardCst = self.device.otherConf.get("cardCst", CARD_CST)
- cardCstMin = self.device.otherConf.get("cardCstMin", CARD_CST_MIN)
- card = self.update_card_dealer_and_type(cardNo) # type: Card
- if not card:
- logger.info("[cmCZSubEventWorker _do_card] not find card, devNo = {}, cardNo = {}".format(self.device.devNo, cardNo))
- self.device.deviceAdapter.response_card(CARD_RESPONSE.FAIL, 0, 0)
- return
- if card.frozen:
- logger.info("[cmCZSubEventWorker _do_card] card is frozen, devNo = {}, cardNo = {}".format(self.device.devNo, cardNo))
- self.device.deviceAdapter.response_card(CARD_RESPONSE.FAIL, 0, 0)
- return
- # 如果存在的话 充值一次卡
- order = CardRechargeOrder.get_last_to_do_one(str(card.id))
- self.recharge_id_card(card=card, rechargeType='append', order=order)
- card.reload()
- if VirtualCoin(card.balance) <= VirtualCoin(cardCstMin):
- logger.info("[cmCZSubEventWorker _do_card] card not enough, devNo = {}, cardNo = {}, balance = {}, min = {}".format(self.device.devNo, cardNo, card.balance, cardCstMin))
- self.device.deviceAdapter.response_card(CARD_RESPONSE.NOT_ENOUGH, 0, 0)
- return
- # 剩下的情况回复主板 启动卡的金额为卡上余额和卡启动金额的最小值
- cst = min(VirtualCoin(card.balance), VirtualCoin(cardCst))
- logger.info("[cmCZSubEventWorker _do_card] card success, devNo = {}, cardNo = {}, balance = {}, cst = {}".format(self.device.devNo, cardNo, card.balance, cst))
- self.device.deviceAdapter.response_card(CARD_RESPONSE.SUCCESS, cst, card.balance, cardNo)
- class cmCZSubComNetPayAckEvent(ComNetPayAckEvent):
- def ack_msg(self):
- payload = {
- 'order_id': self.event_data['order_id'],
- 'order_type': self.event_data['order_type'],
- 'status': self.event_data['status']
- }
- MessageSender.send_no_wait(
- device=self.device.deviceAdapter.masterDevice,
- cmd=DeviceCmdCode.EVENT_ACK,
- payload=payload
- )
- def do_impl(self):
- order_id = self.event_data['order_id']
- order = ConsumeRecord.objects(ownerId = self.device.ownerId, orderNo = order_id).first() # type: ConsumeRecord
- if not order:
- logger.debug('order<no={}> is not exist.'.format(self.event_data['order_id']))
- return
- if order.status == 'finished':
- logger.debug('order<{}> has been fished.'.format(repr(order)))
- return
- addr, port = self.event_data['port'].split("-")
- self.event_data["port"] = int(port)
- if order.used_port != self.event_data['port']:
- logger.error('port is not equal. {} != {}'.format(self.event_data['port'], order.used_port))
- return
- if self.pre_processor:
- self.event_data = self.pre_processor.pre_processing(self.device, self.event_data)
- if self.event_data['status'] in ['running', 'finishing']:
- return self.deal_running_event(order)
- if self.event_data['status'] == 'finished':
- return self.deal_finished_event(order)
- def do_running_order(self, order, result): # type: (ConsumeRecord, dict)->None
- """
- 处理正在运行的订单
- """
- # 订单消息已经被回复过
- if order.status in ["running", "finished"]:
- logger.debug('order<{}> no need to deal. this has done.'.format(repr(order)))
- return
- # 启动设备的时候 设备实际启动成功 但是订单串口超时 不知道订单的明确状态 后面启动时间又重新上报
- if order.status == "unknown":
- errorDesc = u"设备信号恢复,订单正常运行"
- logger.info("order <{}> timeout to running")
- # 正常运行的订单
- else:
- errorDesc = u""
- order.errorDesc = errorDesc
- order.isNormal = True
- order.status = 'running'
- order.startTime = datetime.datetime.fromtimestamp(result['sts'])
- order.save()
- set_start_key_status(start_key=order.startKey, state=START_DEVICE_STATUS.FINISHED, order_id=str(order.id))
- def do_finished_order(self, order, result): # type: (ConsumeRecord, dict)->None
- """
- 处理结束的订单
- """
- portCache = Device.get_port_control_cache(self.device.devNo, str(order.used_port))
- self.event_data["portCache"] = portCache
- if order.status == "running":
- order.isNormal = True
- order.status = "finished"
- order.errorDesc = u""
- order.finishedTime = datetime.datetime.fromtimestamp(result['fts'])
- # 非正常状态 相当于订单最开始串口超时 然后直接变为结束
- elif order.status == "unknown":
- order.isNormal = True
- order.status = "finished"
- order.errorDesc = u"设备信号恢复,订单正常结束(0001)"
- order.startTime = datetime.datetime.fromtimestamp(result['sts'])
- order.finishedTime = datetime.datetime.fromtimestamp(result['fts'])
- # 正常状态 相当于订单启动失败或者是中间running单没有上来
- elif order.status == "created":
- order.isNormal = True
- order.status = "finished"
- order.errorDesc = u"设备信号恢复,订单正常结束(0002)"
- order.startTime = datetime.datetime.fromtimestamp(result['sts'])
- order.finishedTime = datetime.datetime.fromtimestamp(result['fts'])
- else:
- logger.warning('order<{}> status = <{}> to finished. no deal with'.format(repr(order), order.status))
- order.save()
- set_start_key_status(start_key=order.startKey, state=START_DEVICE_STATUS.FINISHED, order_id=str(order.id))
- def do_finished_event(self, order, sub_orders, merge_order_info):
- """
- 订单状态已经变更完成 进一步处理 完成返费 结束订单等等
- """
- order.reload()
- totalCst = self.event_data["totalCst"] / 3600.0
- sts = self.event_data["sts"]
- fts = self.event_data["fts"]
- coins = merge_order_info["coins"]
- cst = min(VirtualCoin(totalCst), VirtualCoin(coins))
- backCoins = VirtualCoin(coins) - cst
- duration = (fts - sts) / 60
- extra = [
- {u"本次订购金额": "{}".format(VirtualCoin(coins).amount)},
- {u"本次实际使用时长": "{}分钟".format(duration)},
- {u"消费金额": "{}(金币)".format(cst.amount)},
- ]
- if backCoins > VirtualCoin(0):
- extra.append({u"退费金额": u"{}(金币)".format(VirtualCoin(backCoins).amount)})
- clear_frozen_user_balance(self.device, order, duration, spendElec=0, backCoins=backCoins, user=order.user)
- # 组织消费信息
- consumeDict = {
- "reason": self._get_finish_reason(),
- DEALER_CONSUMPTION_AGG_KIND.DURATION: duration,
- DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: VirtualCoin(cst).mongo_amount,
- }
- order.update_service_info(consumeDict)
- self.notify_user_service_complete(
- service_name='充电',
- openid=order.user.managerialOpenId,
- port=str(order.used_port),
- address=order.address,
- reason=consumeDict["reason"],
- finished_time=order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'),
- extra=extra
- )
- def merge_order(self, master_order, sub_orders):
- """
- 诚马的插座不会有续充
- """
- start_time = Arrow.fromdatetime(master_order.startTime, tzinfo=settings.TIME_ZONE)
- coins = master_order.package["coins"]
- price = master_order.package["price"]
- portCache = {
- "openId": master_order.openId,
- "consumeType": "mobile",
- "coins": coins,
- "price": price,
- "estimatedTs": int(start_time.timestamp + 720 * 60 * 60),
- }
- return portCache
- def _get_finish_reason(self):
- if "reason" in self.event_data:
- return FINISH_REASON_MAP.get(self.event_data["reason"], u"未知原因")
- return u"未知原因"
- class cmCZSubIdStartAckEvent(IdStartAckEvent):
- # def do_impl(self, **args):
- # addr, port = self.event_data['port'].split("-")
- # self.event_data["port"] = int(port)
- #
- # super(cmCZSubIdStartAckEvent, self).do_impl(**args)
- def post_after_start(self, order=None):
- pass
- def post_after_finish(self, order=None):
- pass
- def merge_order(self, master_order, sub_orders): # type:(ConsumeRecord, Iterable[ConsumeRecord])->dict
- start_time = Arrow.fromdatetime(master_order.startTime, tzinfo=settings.TIME_ZONE)
- coins = self.event_data["fee"]
- cardNo = self.event_data["cardNo"]
- portCache = {
- "openId": master_order.openId,
- "consumeType": "card",
- "coins": coins,
- "price": coins,
- "cardNo": cardNo,
- "estimatedTs": int(start_time.timestamp + 720 * 60 * 60),
- }
- return portCache
- def do_finished_event(self, order, merge_order_info): # type:(ConsumeRecord, dict)->None
- order.reload()
- totalCst = self.event_data["totalCst"] / 3600.0
- sts = self.event_data["sts"]
- fts = self.event_data["fts"]
- coins = merge_order_info["coins"]
- cst = min(VirtualCoin(totalCst), VirtualCoin(coins))
- backCoins = VirtualCoin(coins) - cst
- duration = (fts - sts) / 60
- extra = [
- {u"本次订购金额": "{}".format(VirtualCoin(coins).amount)},
- {u"本次实际使用时长": "{}分钟".format(duration)},
- {u"消费金额": "{}(金币)".format(cst.amount)},
- {u'实体卡': '{}--No:{}'.format(self.card.cardName, self.card.cardNo)}
- ]
- if backCoins > VirtualCoin(0):
- extra.append({u"退费金额": u"{}(金币)".format(VirtualCoin(backCoins).amount)})
- self.card.clear_frozen_balance(str(order.id), backCoins)
- self.card.reload()
- # 组织消费信息
- consumeDict = {
- "reason": self._get_finish_reason(),
- DEALER_CONSUMPTION_AGG_KIND.DURATION: duration,
- DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: VirtualCoin(cst).mongo_amount,
- }
- order.update_service_info(consumeDict)
- self.notify_user_service_complete(
- service_name='充电',
- openid=order.user.managerialOpenId,
- port=str(order.used_port),
- address=order.address,
- reason=consumeDict["reason"],
- finished_time=order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'),
- extra=extra
- )
- def checkout_order(self, order):
- fee = VirtualCoin(order.coin)
- self.card.freeze_balance(transaction_id=str(order.id), fee=fee)
- def _get_finish_reason(self):
- if "reason" in self.event_data:
- return FINISH_REASON_MAP.get(self.event_data["reason"], u"未知原因")
- return u"未知原因"
- class cmCZSubAckEventProcessorIntf(AckEventProcessorIntf):
- def pre_processing(self, device, event_data): # type:(DeviceDict, dict)->dict
- addr, port = event_data['port'].split("-")
- event_data["port"] = int(port)
- event_data["fee"] = event_data["balance"]
- return event_data
|