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