123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390 |
- # -*- 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
|