# -*- coding: utf-8 -*- #!/usr/bin/env python import datetime import logging from mongoengine import DoesNotExist from apilib.monetary import RMB, VirtualCoin from apilib.utils_string import make_title_from_dict from apilib.utils_sys import memcache_lock from apps.web.constant import Const from apps.web.dealer.models import Dealer from apps.web.device.models import WeifuleDeviceOrder, Group, Device from apps.web.eventer import EventBuilder from apps.web.eventer.base import WorkEvent from apps.web.user.models import ServiceProgress, UserVirtualCard, VCardConsumeRecord, \ ConsumeRecord, Card, CardConsumeRecord, CardRechargeOrder, MyUser from apps.web.user.transaction_deprecated import refund_money logger = logging.getLogger(__name__) created_order_32 = 32 executing_order_33 = 33 finished_order_34 = 34 id_card_request_35 = 35 card_is_normal = 1 card_not_in_db = 2 card_is_forzen = 3 card_has_not_order = 4 #IC卡适用 card_less_balance = 5 #ID卡适用 card_type_is_ic = 6#IC卡不允许当做ID卡使用 class builder(EventBuilder): def __getEvent__(self, device_event): event_data = self.deviceAdapter.analyze_event_data(device_event['data']) if event_data is None: return None if event_data['fun_code'] in [created_order_32,executing_order_33,finished_order_34,id_card_request_35]: return ChargingWorkEvent(self.deviceAdapter, event_data) class ChargingWorkEvent(WorkEvent): #微付乐的板子:根据设备上报上来的订单,创建正在服务的信息 def create_progress_for_weifule_order(self,dev,consumeRcd,order): try: progress = ServiceProgress.objects.filter(weifuleOrderNo = order['id']).first() if progress: return port = int(order['port_id']) attachParas = consumeRcd.attachParas needKind,needValue,unit = self.analyse_need_from_package(order) consumeOrder = { 'orderNo': consumeRcd.orderNo, 'coin': consumeRcd.coin.mongo_amount, 'money': consumeRcd.money.mongo_amount, 'unit': unit, 'consumeType':'mobile_vcard' if u'虚拟卡' in consumeRcd.remarks else 'mobile' } if needKind: consumeOrder.update({needKind:needValue}) new_service_progress = ServiceProgress( open_id=consumeRcd.openId, device_imei=consumeRcd.devNo, devTypeCode=dev['devType']['code'], port=port, attachParas=attachParas if attachParas else {}, start_time=int(order['create_time']), finished_time=24*60*60, consumeOrder = consumeOrder, expireAt = datetime.datetime.now() + datetime.timedelta(days = 91) ) new_service_progress.save() except Exception as e: logger.exception(e) def consume_money_for_ID_card(self,agentId,cardNo,balance,money): card = Card.objects.get(agentId=agentId,cardNo = cardNo) if card.balance < money: card.balance = RMB(0) else: card.balance = (card.balance - money) # card.showBalance = card.balance #只有微付乐是创建订单成功后才真正扣费 try: card.save() except Exception as e: logger.exception(e) return card def update_balance_for_IC_card(self,card,balance): card.balance = balance try: card.save() except Exception,e: pass def is_order_accepted(self,orderNo,funCode): return True if WeifuleDeviceOrder.objects.filter(orderNo = orderNo,funCode = funCode).count() > 0 else False def record_order_event(self,order,funCode): newObj = WeifuleDeviceOrder(orderNo = order['id'],funCode = funCode,order = order) try: newObj.save() except Exception,e: logger.exception('save order event error=%s' % e) pass def record_consume_for_card(self, card, money, orderNo): group = Group.get_group(self.device['groupId']) address = group['address'] group_number = self.device['groupNumber'] now = datetime.datetime.now() new_record = { 'orderNo': orderNo, 'time': now.strftime("%Y-%m-%d %H:%M:%S"), 'dateTimeAdded': now, 'openId': card.openId, 'ownerId': self.device['ownerId'], 'coin': money.mongo_amount, 'money': money.mongo_amount, 'devNo': self.device['devNo'], 'logicalCode': self.device['logicalCode'], 'groupId': self.device['groupId'], 'address': address, 'groupNumber': group_number, 'groupName': group['groupName'], 'devTypeCode': self.device.devTypeCode, 'devTypeName': self.device.devTypeName, 'isNormal': True, 'status': ConsumeRecord.Status.RUNNING, 'remarks': u'刷卡消费', 'errorDesc': '', 'sequanceNo': '', 'desc': '', 'attachParas': {}, 'servicedInfo': {} } ConsumeRecord.get_collection().insert_one(new_record) # 刷卡消费也记录一条数据 new_card_record = { 'orderNo': orderNo, 'openId': card.openId, 'cardId': str(card.id), 'money': money.mongo_amount, 'balance': card.balance.mongo_amount, 'devNo': self.device['devNo'], 'devType': self.device['devType']['name'], 'logicalCode': self.device['logicalCode'], 'groupId': self.device['groupId'], 'address': address, 'groupNumber': group_number, 'groupName': group['groupName'], 'result': 'success', 'remarks': u'刷卡消费', 'sequanceNo': '', 'dateTimeAdded': datetime.datetime.now(), 'desc': '', 'servicedInfo': {}, 'linkedConsumeRcdOrderNo':str(new_record['orderNo']) } CardConsumeRecord.get_collection().insert(new_card_record) return new_record['orderNo'], new_card_record['orderNo'] def __translate_reason(self,cause,chrmt): if cause == 7: if chrmt == 0: return u'订购时间已经用完。' elif chrmt == 2: return u'订购电量已经用完。' elif cause == 5: return u'支付的金额已经使用完毕' elif cause == 6: return u'用户手工停止了充电' elif cause == 8: return u'故障导致充电停止' elif cause == 9: return u'本端口功率过载,主动关闭' elif cause == 10: return u'刷卡结束' elif cause == 11: return u'没有连接充电器,主动关闭' elif cause == 12: return u'远程关闭' elif cause == 13: return u'检测到烟雾告警,主动关闭' elif cause == 20: return u'端口功率过小。可能是电池已经充满,也可能是所接负载功率太小' return u'充电结束。' def response_id_card(self,msgDict): data = msgDict cardNo = str(data['card_no']) Card.record_dev_card_no(self.device['devNo'],cardNo) card = self.find_card_by_card_no(cardNo) # 如果没有卡,直接返回 # cardNo,portId,balance,result if card is None: return self.deviceAdapter.response_card_event(cardNo=data['card_no'], portId = data['port_id'], balance = 0,result=card_not_in_db) elif card.frozen: return self.deviceAdapter.response_card_balance(cardNo=data['card_no'], portId = data['port_id'],balance=0, result=card_is_forzen) if card.cardType == 'IC':#如果以前是离线卡,只能用于离线卡,需要提醒用户 return self.deviceAdapter.response_card_balance(cardNo=data['card_no'], portId = data['port_id'],balance=0, result=card_is_forzen) card = self.update_card_dealer_and_type(cardNo, 'ID') #: 首先检查订单,并进行充值 #: 用户首先在手机客户端充值,需要这个时刻刷卡上报事件将充值的订单同步上来 card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id)) if card_recharge_order: result = self.recharge_id_card(card = card, rechargeType = 'append', order = card_recharge_order) card.reload() self.deviceAdapter.response_card_event(data['card_no'], data['port_id'], card.balance)#返回的cardNo为16进制 #确定发送成功了,然后再保存到数据库中 card.save() def analyse_need_from_package(self,order): needKind,needValue,unit = 'needElec',0,u'度' if order['chrmt'] == 0: needKind = 'needTime' needValue = order['amount']/60 unit = u'分钟' elif order['chrmt'] == 2: needValue = order['amount']/1000000.0 else: needKind = 'needPower' needValue = order['amount']/60 unit = u'分钟' return needKind,needValue,unit def do(self, **args): devNo = self.device['devNo'] funCode = self.event_data['fun_code'] if funCode == id_card_request_35: self.response_id_card(self.event_data) else:#处理任务事件 order = self.event_data['order'] logger.info('weifule charging event detected, devNo=%s' % (devNo,)) with memcache_lock(key = '%s-%s-%s-finished' % (devNo, order['id'], funCode), value = '1', expire = 120) as acquired: if acquired: self.deal_order(devNo, funCode, order, self.event_data) def deal_order(self,devNo,funCode,order,msgDict): dealer = Dealer.get_dealer(self.device['ownerId']) #开始充电最重要的功能核对订单,以及记录订单。 #扫码情况下,需要检查订单,如果订单成功了,就不需要核查,如果订单失败的,需要核查,然后把没有扣的钱扣掉 #刷卡情况下,正式把刷卡的钱扣掉,并产生消费的订单。 #投币情况下,把投币扣费的记录记录下来即可 #首先,统一回复确认报文 if funCode in [created_order_32,executing_order_33,finished_order_34,id_card_request_35]: try: self.deviceAdapter.ack_event(order['id'], funCode) except Exception,e: logger.info('ack event devNo=%s err=%s' % (devNo,e)) pass if funCode == created_order_32 :#订单创建成功,开始收单,并检查账单 #回收余额也会有订单,但是没有端口,不需要处理,直接pass掉 if order['order_type'] == 'card_refund': return #首先把端口状态给刷新掉(虽然是任务在设备上排队的通知,端口肯定是要忙起来的,先搞成忙) Device.update_dev_control_cache(self.device['devNo'],{ str(order['port_id']):{'status':Const.DEV_WORK_STATUS_WORKING} } ) if order['order_type'] == 'card_start':#刷卡 cardNo = str(order['card_no']) #登记下卡相关信息 onceCard = Device.objects.get(devNo = devNo).otherConf.get('onceCard',100) card = self.update_card_dealer_and_type(cardNo, 'ID') balance = card.balance - RMB(order['coins']/100.0) self.update_card_balance(card, balance) #卡的显示余额同步下 card.showBalance = card.balance card.save() fee = RMB(onceCard/10.0) # 记录卡消费记录以及消费记录 orderNo, cardOrderNo = self.record_consume_for_card(card, fee,order['id']) # 记录当前服务的progress,便于手机界面查询进度 needKind,needValue,unit = self.analyse_need_from_package(order) consumeDict = {'orderNo': orderNo,'money': fee,'coin': onceCard/10.0,'cardOrderNo': cardOrderNo,needKind:needValue,'unit':unit} ServiceProgress.register_card_service_for_weifule(self.device, order['port_id'], card, consumeDict) # 通知用户,已经扣费 self.notify_balance_has_consume_for_card(card, fee) elif order['order_type'] == 'apps_start':#扫码 #检查订单是否失败,如果是失败的,需要纠正为成功,并把用户的余额扣除掉 consumeRcd = ConsumeRecord.objects.filter(orderNo = order['id']).first() if consumeRcd is None or consumeRcd.isNormal: return consumeRcd.isNormal = True consumeRcd.save() if u'虚拟卡' in consumeRcd.remarks: pass#虚拟卡,不管成功与否都已经先扣掉了配额(有些不合理),这里什么都不处理 else: user = MyUser.objects.get(openId = consumeRcd.openId,groupId = consumeRcd.groupId) user.pay(VirtualCoin(order['coins'])) self.create_progress_for_weifule_order(self.device,consumeRcd,order) #如果是启动了订单,需要更新端口状态,需要刷新服务状态 elif funCode == executing_order_33: progress = ServiceProgress.objects.filter(weifuleOrderNo = order['id']).first() if (not progress) or (not order.has_key('port_id')):#只有卡充值的时候,order才没有port return Device.update_dev_control_cache(self.device['devNo'],{ str(order['port_id']):{ 'status':Const.DEV_WORK_STATUS_WORKING, 'openId':progress.open_id, 'startTime':datetime.datetime.now().strftime(Const.DATETIME_FMT) } } ) progress.status = 'running' progress.runningTime = datetime.datetime.now() progress.save() #订单结束,需要把进行账单核对、记录更新、通知、退费等; elif funCode == finished_order_34: #如果是离线卡的余额回收,修改下余额,然后直接返回即可 if order['order_type'] == 'card_refund': card = Card.objects.filter(agentId = dealer['agentId'],cardNo = str(order['card_no'])).first() if card is None: return else: card.balance = RMB(order['balance']/100.0) card.save() return order['reason'] = self.__translate_reason(order['cause'],order['chrmt']) progress = ServiceProgress.objects.filter(weifuleOrderNo = order['id']).first() if progress is None: return progress.status = 'finished' progress.isFinished = True progress.save() consumeRcd = ConsumeRecord.objects.filter(orderNo = order['id']).first() if not consumeRcd:#投币的不会生成订单 return try: group = Group.get_group(self.device['groupId']) consumeDict = { 'reason': order['reason'], 'chargeIndex': order['port_id'], 'duration': round(order['time']/60.0,1), 'elec':round(order['elec']/1000000.0,2) } notifyDict = { 'service': u'充电服务完成', 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'remark': u'谢谢您的支持' } refundDict = { 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') } coins = consumeRcd.coin if order['chrmt'] in [0,1]:#按照时间计费 needTime = order['amount']/60 usedTime = order['time']/60 leftTime = order['left_time']/60 if order['left_time'] > 0 else 0 leftTimeStr = leftTime actualNeedTime = usedTime + leftTime consumeDict.update({ 'needTime':needTime, 'leftTime': leftTimeStr, 'actualNeedTime': u'动态功率计算为%s分钟' % actualNeedTime, }) titleDictList = [ {u'设备编号':self.device['logicalCode']}, {u'端口':order['port_id']}, {u'地址':group['address']}, {u'结束原因':order['reason']}, {u'订购时间':u'%s分钟' % needTime}, {u'应充时间':u'%s分钟' % actualNeedTime}, {u'充电时间':u'%s分钟' % usedTime}, {u'剩余时间':u'%s分钟' % leftTimeStr} ] notifyDict.update( { 'title': make_title_from_dict(titleDictList) } ) backCoins = coins * (float(leftTime) / float(actualNeedTime)) titleDictList = [ {u'设备编号':self.device['logicalCode']}, {u'端口':order['port_id']}, {u'付款':u'%s元' % coins}, {u'预定时间':u'%s分钟' % needTime}, {u'应充时间':u'%s分钟' % actualNeedTime}, {u'充电时间':u'%s分钟' % usedTime}, {u'剩余时间':u'%s分钟' % leftTimeStr}, ] refundDict.update( { 'title':make_title_from_dict(titleDictList), 'backCount': u'金币:%s' % backCoins } ) else: needElec = order['amount']/1000000.0 usedElec = order['elec']/1000000.0 leftElec = needElec-usedElec if needElec > usedElec else 0.0 titleDictList = [ {u'设备编号':self.device['logicalCode']}, {u'端口':order['port_id']}, {u'结束原因':order['reason']}, {u'订购电量':u'%s度' % needElec}, {u'消耗电量':u'%s度' % usedElec}, {u'剩余电量':u'%s分钟' % leftElec}, ] notifyDict.update( { 'title': make_title_from_dict(titleDictList) } ) backCoins = coins * (float(leftElec) / float(needElec)) titleDictList = [ {u'设备编号':self.device['logicalCode']}, {u'端口':order['port_id']}, {u'结束原因':order['reason']}, {u'订购电量':u'%s度' % needElec}, {u'消耗电量':u'%s度' % usedElec}, {u'剩余电量':u'%s度' % leftElec}, ] refundDict.update( { 'title':make_title_from_dict(titleDictList), 'backCount': u'金币:%s' % backCoins } ) if u'虚拟卡' in consumeRcd.remarks: #退额度 try: vRcd = VCardConsumeRecord.objects.get(orderNo = order['id']) vCard = UserVirtualCard.objects.get(id = vRcd.cardId) except DoesNotExist,e: logger.info('can not find the vCard id = %s' % vRcd.cardId) return #通知服务结束 notifyOpenId = self.get_managerialOpenId_by_openId(vRcd.openId) if vCard else '' self.notify_user(notifyOpenId, 'service_complete',**notifyDict) #不需要退款,直接返回,不通知 if self.device.is_auto_refund: if order['chrmt'] in [0,1]: vCard.refund_quota(vRcd,usedTime,0.0,backCoins.mongo_amount) else: vCard.refund_quota(vRcd,0.0,usedElec,backCoins.mongo_amount) #刷卡的方式 elif order['order_type'] == 'card_start' and order['card_no']: dealer = Dealer.get_dealer(ownerId = self.device['ownerId']) card = Card.objects.filter(agentId = dealer['agentId'],cardNo = str(order['card_no'])).first() if card is None:#离线卡没有绑定或者在线卡被解绑了 return #先通知 notifyOpenId = card.managerialOpenId if card else '' self.notify_user(notifyOpenId, 'service_complete',**notifyDict) #不需要退款,直接返回.在线卡需要退费,离线卡只登记使用记录就OK if self.device.is_auto_refund and card.cardType == 'ID': card = self.refund_money_for_card(RMB(backCoins), card.id) card.showBalance = card.balance card.save() consumeDict.update({'refundedMoney': str(backCoins)}) self.notify_user(notifyOpenId, 'refund_coins', **refundDict) else:#扫码的 user = MyUser.objects(openId = consumeRcd.openId, groupId = self.device['groupId']).first() if not user: return #通知服务结束 notifyOpenId = user.managerialOpenId if user else '' self.notify_user(notifyOpenId, 'service_complete',**notifyDict) #如果需要退款,计算退款数据. if self.device.is_auto_refund: consumeDict.update({'refundedMoney': str(backCoins)}) refund_money(self.device, backCoins, consumeRcd.openId) self.notify_user(notifyOpenId, 'refund_coins', **refundDict) ServiceProgress.update_progress_and_consume_rcd(self.device['ownerId'],{'weifuleOrderNo':order['id']}, consumeDict) except Exception,e: logger.exception('some exception happed,devNo=%s,e=%s' % (devNo,e)) finally: Device.clear_port_control_cache(devNo,order['port_id']) self.deviceAdapter.get_port_status_from_dev() else: pass