# -*- coding: utf-8 -*- # !/usr/bin/env python import datetime import logging import time from mongoengine import DoesNotExist from apilib.monetary import VirtualCoin, RMB from apps.web.constant import Const, DEALER_CONSUMPTION_AGG_KIND from apps.web.common.transaction import UserConsumeSubType from apps.web.core.accounting import Accounting from apps.web.core.exceptions import ServiceException from apps.web.dealer.models import Dealer from apps.web.device.models import Group, Device, DeviceDict from apps.web.report.utils import record_consumption_stats from .device import BtPulseDevice, BtChargingSocket, BtSoftSwitch, BtTimeSwitchIR, BtClothesTree from apps.web.user.models import ConsumeRecord, Redpack from apps.web.user.utils import RedpackBuilder logger = logging.getLogger(__name__) class ActionBtDeviceBuilder(object): @staticmethod def create(code, dev): if code == Const.DEVICE_TYPE_CODE_B_PULSE or code == Const.DEVICE_TYPE_CODE_B_QUICK_CHARGE_TWO: return BtPulseDevice(dev) elif code == Const.DEVICE_TYPE_CODE_B_CHARGING: return BtChargingSocket(dev) elif code == Const.DEVICE_TYPE_CODE_B_TIME_SWITCH_GPIO: return BtSoftSwitch(dev) elif code == Const.DEVICE_TYPE_CODE_B_TIME_SWITCH_IR: return BtTimeSwitchIR(dev) elif code == Const.DEVICE_TYPE_CODE_B_CLOTHES_TREE: return BtClothesTree(dev) else: return None class StartAction(object): def __init__(self, smartBox, user, packageId, attachParas): self._smartBox = smartBox self._user = user self._packageId = packageId self._result = {} self._attachParas = attachParas self._order = None super(StartAction, self).__init__() @property def device(self): # type:()->DeviceDict return self._smartBox def handle_verify_service(self): group = Group.get_group(self._smartBox['groupId']) # 校验业务配置 if 'washConfig' not in self._smartBox: raise ServiceException({'result': 2, 'description': u'未配置该业务,请联系经销商(1002)'}) # 校验套餐ID的有效性 package = self._smartBox['washConfig'].get(self._packageId) if package is None: raise ServiceException({'result': 2, 'description': u'该套餐不存在,请联系经销商'}) # 检验套餐设置的投币数 if 'coins' not in package: raise ServiceException({'result': 2, 'description': u'套餐未设置投币数,请联系经销商'}) # 校验经销商有效性 dealer = Dealer.get_dealer(group['ownerId']) if dealer is None: raise ServiceException({'result': 2, 'description': u'该地址所属经销商无效,请联系服务商'}) self._attachParas['package'] = self._smartBox['washConfig'].get(self._packageId) def handle_deducting_fee(self): self._order.update(status = 'Deducting') def create_consume_record(self, **kwargs): try: group = Group.get_group(self._smartBox['groupId']) isFree = group.get('isFree', False) if isFree: pay_money = RMB(0) else: pay_money = RMB(self._attachParas['package']['price']) pulse = int(self._attachParas['package']['coins']) group = Group.get_group(self._smartBox['groupId']) address = group['address'] group_number = self._smartBox['groupNumber'] new_record = { 'orderNo': ConsumeRecord.make_no(self.device.logicalCode, UserConsumeSubType.NETPAY), 'openId': self._user.openId, 'coin': VirtualCoin(pulse), 'money': pay_money, 'devNo': self._smartBox['devNo'], 'ownerId': self._smartBox['ownerId'], 'logicalCode': self._smartBox['logicalCode'], 'groupId': self._smartBox['groupId'], 'address': address, 'groupNumber': group_number, 'groupName': group['groupName'], 'devTypeCode': self.device.devTypeCode, 'devTypeName': self.device.devTypeName, 'status': ConsumeRecord.Status.CREATED, 'isNormal': False, 'attachParas': self._attachParas, 'package': self._attachParas['package'] } self._order = ConsumeRecord(**new_record) self._order.save() logger.debug('%s created.' % repr(self._order)) return self._order except Exception, e: logger.exception(e) raise ServiceException({'result': 0, 'description': u'记录消费失败'}) def handle_encode_cmd(self): try: actionBox = ActionBtDeviceBuilder.create(self._smartBox['code'], self._smartBox) self._result.update( {'devNo': self._smartBox['devNo'], 'cmdId': 1, 'order': { 'id': str(self._order.id), 'orderNo': self._order.orderNo, 'money': self._order.money, 'coin': self._order.coin }, 'command': actionBox.encode_cmd(cmdId = 1, **{'seqNo': int(time.time()), 'attachParas': self._attachParas})}) except ServiceException, e: logger.exception('failed to start the device(devNo=%s)' % (self._smartBox['devNo'],)) raise e def get_result(self): return self._result class FreeStartAction(StartAction): def __init__(self, smartBox, openId, packageId, attachParas): super(FreeStartAction, self).__init__(smartBox, openId, packageId, attachParas) class PaymentStartAction(StartAction): def __init__(self, smartBox, user, packageId, attachParas): super(PaymentStartAction, self).__init__(smartBox, user, packageId, attachParas) def handle_verify_service(self): try: StartAction.handle_verify_service(self) except ServiceException as e: raise e if Redpack.can_use(dealer=self.device.owner, devTypeCode=self.device.devTypeCode): package = self._attachParas['package'] balance = self._user.calc_currency_balance(self.device.owner, self.device.group) # 正常套餐 红包 + 余额 如果他们能支撑套餐 直接启动 redpack = Redpack.auto_suit_with_coins({'openId': self._user.openId}, balance, package) if redpack: self._attachParas.update({'redpackId': redpack['id']}) return pay_count = VirtualCoin(self._attachParas['package']['coins']) errCode, errMsg = self._user.verify_payment_for_bt(self.device, pay_count) if errCode != 1: raise ServiceException({'result': errCode, 'description': errMsg}) def handle_deducting_fee(self): if self._order.redpackId: RedpackBuilder.bt_user_pay_coin_with_redpack(self._order) else: pay_count = VirtualCoin(self._attachParas['package']['coins']) errCode, errMsg = self._user.deduct_fee_for_bt(self.device, pay_count) if errCode != 1: raise ServiceException({'result': errCode, 'description': errMsg}) self._order.update(status = 'Deducting') class FinishAction(object): def __init__(self, smartBox, user, packageId): self._smartBox = smartBox self._user = user self._packageId = packageId self._order = None self._result = None self._service_info = {} super(FinishAction, self).__init__() def is_free(self): return False def handle_refund(self): pass def get_consume_order(self, orderId): try: self._order = ConsumeRecord.objects.get(ownerId = self._smartBox['ownerId'], id = str(orderId)) # type: ConsumeRecord start_time = long(time.time()) duration = 0 try: package = self._order.attachParas['package'] if 'unit' in package: if package['unit'] == u'分钟': duration = long(package['time']) elif package['unit'] == u'小时': duration = long(package['time']) * 60.0 elif package['unit'] == u'天': duration = long(package['time']) * 24.0 * 60.0 elif package['unit'] == u'包': duration = long(package['time']) else: duration = long(package['time']) except Exception: pass finished_time = long(start_time + duration * 60.0) self._service_info = { 'startTime': start_time, 'finishedTime': finished_time, 'totalTime': duration } except DoesNotExist: raise ServiceException({'result': 0, 'description': u'消费订单不存在'}) if (datetime.datetime.now() - self._order.dateTimeAdded).total_seconds() > 300: raise ServiceException({'result': 0, 'description': u'消费订单已失效'}) def update_account_record(self): pass def update_consume_status(self, status): try: self._order.status = status if status == 'Charged': self._order.isNormal = True status = self._order.update_agg_info({ DEALER_CONSUMPTION_AGG_KIND.DURATION: self._service_info['totalTime'], DEALER_CONSUMPTION_AGG_KIND.COIN: self._order.coin }) if status: record_consumption_stats(self._order) else: logger.error('[handle_consume_record]failed to update_agg_info record=%r' % (self._order,)) self._order.save() except Exception: raise ServiceException({'result': 0, 'description': u'更新消费订单失败'}) def handle_service_progress(self): try: if self._service_info['totalTime'] > 0: Device.update_dev_control_cache( devNo = self._smartBox['devNo'], newValue = { 'openId': self._user.openId, 'status': Const.DEV_WORK_STATUS_WORKING, 'totalTime': self._service_info['totalTime'] * 60, 'startTime': self._service_info['startTime'], 'finishedTime': self._service_info['finishedTime'] }) except Exception, e: logger.exception(e) class FreeFinishAction(FinishAction): def __init__(self, smartBox, user, packageId): super(FreeFinishAction, self).__init__(smartBox, user, packageId) def is_free(self): return True class PaymentFinishAction(FinishAction): def __init__(self, smartBox, user, packageId): super(PaymentFinishAction, self).__init__(smartBox, user, packageId) def handle_refund(self): if self._order.status != 'Deducting': return pay_count = VirtualCoin(self._order.coin) errCode, errMsg = self._user.refund(pay_count) if errCode != 1: raise ServiceException({'result': errCode, 'description': errMsg}) def update_account_record(self): try: updated = self._user.update(inc__total_consumed = self._order.coin) if not updated: logger.error(u'记录累计消费失败. openId = %s' % self._user.openId) Accounting.recordNetPayCoinCount(self._smartBox['devNo']) except Exception, e: logger.exception(e) raise ServiceException({'result': 0, 'description': u'记录消费失败'}) class StartService(object): def __init__(self, user, dev, packageId, attachParas): self._smartBox = dev self._user = user self._packageId = packageId self._attachParas = attachParas self._order = None super(StartService, self).__init__() def __get_service(self): group = Group.get_group(self._smartBox['groupId']) isFree = group.get('isFree', False) return FreeStartAction(self._smartBox, self._user, self._packageId, self._attachParas) if isFree \ else PaymentStartAction(self._smartBox, self._user, self._packageId, self._attachParas) def start(self): result = {'result': 1, 'description': None, 'payload': {}} try: service = self.__get_service() service.handle_verify_service() service.create_consume_record() service.handle_encode_cmd() service.handle_deducting_fee() result['payload'] = service.get_result() except ServiceException, e: logger.exception(e) result = e.result return result class FinishService(object): def __init__(self, user, dev, cmdId, orderId, packageId, errCode): self._smartBox = dev self._user = user self._cmdId = cmdId self._orderId = orderId self._errCode = errCode self._packageId = packageId super(FinishService, self).__init__() def __get_service(self): group = Group.get_group(self._smartBox['groupId']) isFree = group.get('isFree', False) return FreeFinishAction(self._smartBox, self._user, self._packageId) if isFree \ else PaymentFinishAction(self._smartBox, self._user, self._packageId) def start(self): result = {'result': 1, 'description': None, 'payload': {}} try: service = self.__get_service() service.get_consume_order(self._orderId) if self._errCode == 1: service.handle_service_progress() service.update_consume_status('Charged') service.update_account_record() else: service.handle_refund() service.update_consume_status('Refund') except ServiceException, e: result = e.result return result