12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- import datetime
- import json
- import logging
- import re
- from mongoengine import Q
- from typing import TYPE_CHECKING
- from apilib.monetary import RMB
- from apilib.utils_datetime import to_datetime
- from apps.web.common.proxy import ClientConsumeModelProxy
- from apps.web.constant import Const, DeviceCmdCode, MQTT_TIMEOUT, CONSUMETYPE
- from apps.web.core.adapter.base import SmartBox
- from apps.web.core.exceptions import ServiceException, InvalidParameter
- from apps.web.core.networking import MessageSender
- from apps.web.dealer.models import Dealer
- from apps.web.device.models import Device
- from apps.web.user.constant2 import StartDeviceType
- from apps.web.user.models import ConsumeRecord, MyUser
- logger = logging.getLogger(__name__)
- if TYPE_CHECKING:
- pass
- class PolicyPackageInit(SmartBox):
- INIT_POLICY = {
- 'forIdcard': {
- 'policyType': 'time',
- 'billingMethod': 'prepaid',
- 'autoRefund': True,
- 'money': 1,
- "minAfterStartCoins": 0,
- "rule": {
- 'prices': [
- {
- "price": 0.25,
- "power": 150
- },
- {
- "price": 0.36,
- "power": 250
- },
- {
- "price": 0.54,
- "power": 350
- },
- {
- "price": 0.72,
- "power": 500
- },
- {
- "price": 0.90,
- "power": 700
- },
- {
- "price": 1.08,
- "power": 990
- },
- ],
- 'price': 1
- }
- },
- 'forApps': {
- "policyType": "time",
- "rule": {
- "prices": [
- {
- "price": 0.25,
- "power": 150
- },
- {
- "price": 0.36,
- "power": 250
- },
- {
- "price": 0.54,
- "power": 350
- },
- {
- "price": 0.72,
- "power": 500
- },
- {
- "price": 0.90,
- "power": 700
- },
- {
- "price": 1.08,
- "power": 990
- },
- ],
- "price": 1
- }
- }
- }
- def _generate_id(self, ids):
- i = 1
- while True:
- if str(i) in ids:
- i += 1
- else:
- return str(i)
- def _sort_by_sn(self):
- # 调整sn顺序
- for item in self.ruleList:
- item["sn"] = self.ruleList.index(item)
- def _formart_ruleList(self, isTemp=False):
- if not isTemp and not filter(lambda _: _.get('switch') == True, self.ruleList):
- raise ServiceException(
- {'result': 0, 'description': '没有可供用户选择的套餐, 请新增或启用 至少一个用户套餐',
- 'payload': {}})
- ids = [str(rule['id']) for rule in self.ruleList if 'id' in rule]
- for i, rule in enumerate(self.ruleList):
- ruleId = str(rule.get('id', ''))
- if not ruleId:
- ruleId = self._generate_id(ids)
- ids.append(ruleId)
- # 充满自停套餐
- if ruleId == 'autoPackage':
- rule['time'] = float(rule.get('time') or 0)
- # 自定义输入套餐
- if ruleId == 'customPackage':
- rule['time'] = float(rule.get('time') or 0)
- self.washConfig[ruleId] = {
- 'name': rule.get('name') or '套餐{}'.format(i + 1),
- 'price': round(RMB(rule.get('price') or 0), 2),
- 'coins': round(RMB(rule.get('price') or 0), 2)
- }
- if 'switch' in rule:
- self.washConfig[ruleId].update({'switch': rule.get('switch', True)})
- if 'time' in rule:
- self.washConfig[ruleId].update({'time': round(float(rule.get('time') or 0), 2)})
- if 'unit' in rule:
- self.washConfig[ruleId].update({'unit': rule.get('unit')})
- if 'sn' in rule:
- self.washConfig[ruleId].update({'sn': rule['sn']})
- if 'autoStop' in rule:
- self.washConfig[ruleId]['autoStop'] = rule['autoStop']
- if 'autoRefund' in rule:
- self.washConfig[ruleId]['autoRefund'] = rule.get('autoRefund', False)
- # 此处有可能传入空字符串 加一个or判断为0
- if 'minAfterStartCoins' in rule:
- minAfterStartCoins = round(RMB(rule['minAfterStartCoins'] or 0), 2)
- if minAfterStartCoins > 0 and minAfterStartCoins < self.washConfig[ruleId]['coins']:
- ServiceException(
- {'result': 0, 'description': '套餐({})最小启动金额需要大于套餐金额{}'.format(self.washConfig[ruleId]['name'], minAfterStartCoins), 'payload': {}})
- self.washConfig[ruleId]['minAfterStartCoins'] = minAfterStartCoins
- # 此处有可能传入空字符串 加一个or判断为0
- if 'minFee' in rule:
- self.washConfig[ruleId]['minFee'] = round(RMB(rule['minFee'] or 0), 2)
- if isTemp:
- self.washConfig[ruleId]['billingMethod'] = 'prepaid'
- else:
- self.washConfig[ruleId]['billingMethod'] = 'postpaid'
- # 检验部分
- if RMB(rule.get('price') or 0) > self.dealer.maxPackagePrice:
- raise ServiceException(
- {'result': 0, 'description': '套餐支付金额( {}元 )超过最大限制'.format(rule['name']), 'payload': {}})
- def _formart_policy_rule(self, policy):
- # 时间计费参数
- prices = policy.get('rule', {}).get('prices', [])
- # 电量计费参数
- price = policy.get('rule', {}).get('price')
- # 检验传过来的参数是否完整
- policyType = policy.get('policyType')
- if policyType == 'time':
- if prices:
- pass
- else:
- raise ServiceException(
- {'result': 0, 'description': '按时间计费规则不可为空, 请填写计费规则',
- 'payload': {}})
- if price:
- price = round(float(price), 2)
- else:
- price = round(self.INIT_POLICY['forApps']['rule']['price'], 2)
- elif policyType == 'elec':
- if prices:
- pass
- else:
- prices = self.INIT_POLICY['forApps']['rule']['prices']
- if price:
- price = round(float(price), 2)
- else:
- raise ServiceException(
- {'result': 0, 'description': '按电量计费参数不可为空, 请填写计费参数',
- 'payload': {}})
- else:
- raise ServiceException(
- {'result': 0, 'description': '计费方式不存在',
- 'payload': {}})
- # 时间计费部分
- if not prices:
- raise ServiceException(
- {'result': 0, 'description': '按时间计费规则不可为空, 请填写计费规则',
- 'payload': {}})
- for item in prices:
- item['power'] = int(float(item.get('power', 0)))
- item['price'] = round(float(item.get('price', 0)), 2)
- if RMB(item['price']) > self.dealer.maxPackagePrice:
- raise ServiceException(
- {'result': 0, 'description': '计费规则(功率: {}, 价格: {})金额超限'.format(item['power'], item['price']),
- 'payload': {}})
- prices = sorted(prices, key=lambda _: _['power'])
- # 电量计费部分
- if price > 10:
- raise ServiceException(
- {'result': 0, 'description': '电量模式: 电费单价金额超限(电费单价不得大于10元/度)'.format(price),
- 'payload': {}})
- return {'prices': prices, 'price': price}
- def _formart_apps(self):
- forApps = self.policyTemp.get('forApps', {})
- forApps['rule'] = self._formart_policy_rule(forApps)
- forApps['policyType'] = forApps.get('policyType')
- return forApps
- def _formart_Idcard(self):
- forIdcard = self.policyTemp.get('forIdcard', {})
- forIdcard['policyType'] = forIdcard.get('policyType', 'time')
- forIdcard['rule'] = self._formart_policy_rule(forIdcard)
- forIdcard['billingMethod'] = forIdcard.get('billingMethod', 'prepaid')
- if forIdcard['billingMethod'] == 'prepaid':
- forIdcard['money'] = round(float(forIdcard.get('money', 1)), 2)
- if RMB(forIdcard['money']) > self.dealer.maxPackagePrice:
- raise ServiceException(
- {'result': 0, 'description': '刷一次卡扣费金额( {} )超限'.format(forIdcard['money']), 'payload': {}})
- forIdcard['autoRefund'] = forIdcard.get('autoRefund', True)
- elif forIdcard['billingMethod'] == 'postpaid':
- forIdcard['minAfterStartCoins'] = round(float(forIdcard.get('minAfterStartCoins', 1)), 2)
- return forIdcard
- def _formart_policy(self):
- self.policyTemp = {'forApps': self._formart_apps(), 'forIdcard': self._formart_Idcard()}
- def _get_ruleList(self, isTemp=False):
- if isTemp:
- config = self.device.get('tempWashConfig', {})
- else:
- config = self.device['washConfig']
- ruleList = []
- for packageId, rule in config.items():
- item = {
- 'id': packageId,
- 'name': rule['name'],
- 'coins': rule.get('price'),
- 'price': rule.get('price'),
- 'time': rule.get('time', 20),
- 'description': rule.get('description', ''),
- 'imgList': rule.get('imgList', []),
- 'unit': rule.get('unit', u'分钟'),
- 'switch': rule.get('switch', True),
- }
- if 'sn' in rule:
- item['sn'] = rule['sn']
- if 'autoStop' in rule:
- item['autoStop'] = rule['autoStop']
- if 'autoRefund' in rule:
- item['autoRefund'] = rule['autoRefund']
- if 'minAfterStartCoins' in rule:
- item['minAfterStartCoins'] = rule['minAfterStartCoins']
- if 'minFee' in rule:
- item['minFee'] = rule['minFee']
- if 'billingMethod' in rule:
- item['billingMethod'] = rule['billingMethod']
- ruleList.append(item)
- return sorted(ruleList, key=lambda x: (x.get('sn'), x.get('id')))
- def _get_displaySwitch(self, isTemp=False):
- displaySwitchs = {
- "displayCoinsSwitch": False,
- "displayPriceSwitch": isTemp,
- "displayTimeSwitch": True,
- "setBasePriceAble": False,
- "setPulseAble": False
- }
- return displaySwitchs
- def _get_policy(self):
- return self.device.policyTemp or self.INIT_POLICY
- def get_reg_model(self, dealer, devTypeId, isTemp=False, **kw):
- if isTemp:
- ruleList = []
- else:
- ruleList = [
- {
- 'id': 'autoPackage',
- 'autoRefund': False,
- 'autoStop': True,
- 'billingMethod': 'postpaid',
- 'coins': 0.0,
- 'minAfterStartCoins': 3.0,
- 'minFee': 0.1,
- "name": "充满自停",
- 'policyType': 'time',
- 'price': 0.0,
- 'sn': 0,
- 'switch': True,
- 'time': 720.0,
- "unit": "分钟",
- },
- {
- 'id': 'customPackage',
- 'autoRefund': False,
- 'autoStop': True,
- 'billingMethod': 'postpaid',
- 'coins': 0.0,
- 'minAfterStartCoins': 3.0,
- 'minFee': 0.1,
- "name": "自定义",
- 'policyType': 'time',
- 'price': 0.0,
- 'sn': 1,
- 'switch': True,
- 'time': 720.0,
- "unit": "分钟",
- },
- {
- 'autoRefund': False,
- 'autoStop': True,
- 'billingMethod': 'postpaid',
- 'coins': 0.0,
- 'minAfterStartCoins': 1.0,
- 'minFee': 0.0,
- "name": "60分钟",
- 'policyType': 'time',
- 'price': 0.0,
- 'sn': 2,
- 'switch': True,
- 'time': 60.0,
- "unit": "分钟",
- },
- ]
- payload = {'ruleList': ruleList, 'displaySwitchs': self._get_displaySwitch(isTemp=isTemp),
- 'policyTemp': self.INIT_POLICY}
- if devTypeId in dealer.defaultWashConfig:
- dealerDefaultWashConfig = dealer.defaultWashConfig[devTypeId]
- if dealerDefaultWashConfig.get('policyTemp'):
- payload.update({'policyTemp': dealerDefaultWashConfig['policyTemp']})
- if dealerDefaultWashConfig.get('displaySwitchs'):
- payload.update({'displaySwitchs': dealerDefaultWashConfig['displaySwitchs']})
- if dealerDefaultWashConfig.get('ruleList') and not isTemp:
- payload.update({'ruleList': dealerDefaultWashConfig['ruleList']})
- return payload
- def reg_model(self, owner, ruleList, policyTemp, devType):
- self.dealer = owner # type: Dealer
- self.ruleList = ruleList
- self.devType = devType
- # self.displaySwitchs = self._get_displaySwitch
- self.policyTemp = policyTemp
- self.washConfig = {}
- # 1.根据sn排序
- self._sort_by_sn()
- # 2 计费规则 policy 格式化
- self._formart_policy()
- # 3.套餐 ruleList 格式话
- self._formart_ruleList(False)
- payload = {'ruleList': ruleList, 'policyTemp': policyTemp}
- owner.defaultWashConfig[str(devType['id'])] = payload
- owner.save()
- return self.washConfig, self.policyTemp
- def get_show_package(self, dev, isTemp=False):
- self.device = dev # type: DeviceDict
- group = self.device.group # type: GroupDict
- # 探测是否地址为免费活动组,默认为否
- is_free_service = group.is_free
- if isTemp:
- config = self.device.get('tempWashConfig', {})
- else:
- config = self.device['washConfig']
- packages = []
- for packageId, rule in config.items():
- # 没有启用的套餐 直接掠过
- if not rule.get("switch", True):
- continue
- price = rule['price']
- item = {
- 'id': packageId,
- 'coins': rule['coins'],
- 'name': rule['name'],
- 'time': rule['time'],
- 'price': price,
- 'unit': rule.get('unit', u'分钟'),
- 'sn': rule.get('sn')
- }
- if 'minFee' in rule and rule['minFee'] and float(rule['minFee']) > 0:
- item.update({'minFee': rule.get('minFee')})
- if 'minAfterStartCoins' in rule and rule['minAfterStartCoins'] and float(rule['minAfterStartCoins']) > 0:
- item.update({'minAfterStartCoins': rule.get('minAfterStartCoins')})
- if is_free_service:
- item.update({'description': '当前处于免费时段'})
- else:
- item.update({'description': self._get_policy_text()})
- if 'policyType' in rule:
- item.update({'policyType': rule.get('policyType')})
- packages.append(item)
- return sorted(packages, key=lambda x: (x.get('sn'), x.get('id')))
- def format_device_package(self, isTemp=False, **kw):
- self.dealer = self.device.owner # type: Dealer
- self.ruleList = kw['serviceData']
- self.displaySwitchs = kw['displaySwitchs']
- self.policyTemp = kw['policyTemp']
- self.washConfig = {}
- if kw['logicalCode'].__class__.__name__ == 'list':
- devNoList = [Device.get_devNo_by_logicalCode(lc) for lc in kw['logicalCode']]
- else:
- devNoList = [Device.get_devNo_by_logicalCode(kw['logicalCode'])]
- devDict = Device.get_dev_by_nos(devNoList)
- if not devDict:
- raise ServiceException({'result': 2, 'description': u'找不到设备'})
- devList = devDict.keys()
- if isTemp:
- # 1.根据sn排序
- self._sort_by_sn()
- # 2.套餐 ruleList 格式话
- self._formart_ruleList(isTemp)
- # 3. 更新数据
- Device.objects.filter(devNo__in=devList, ownerId=str(self.dealer.id)).update(tempWashConfig=self.washConfig)
- else:
- # 1.根据sn排序
- self._sort_by_sn()
- # 2 计费规则 policy 格式化
- self._formart_policy()
- # 3.套餐 ruleList 格式话
- self._formart_ruleList(isTemp)
- # 4 更新数据
- Device.objects.filter(devNo__in=devList, ownerId=str(self.dealer.id)).update(washConfig=self.washConfig,
- policyTemp=self.policyTemp)
- # 清理缓存
- Device.invalid_many_device_cache(devList)
- Device.get_many_dev_control_cache(devList)
- def get(self, dev, isTemp=False):
- self.device = dev # type: DeviceDict
- # 1. 获取ruleList 套餐
- ruleList = self._get_ruleList(isTemp)
- # 2. 获取displacySwitch 显示开关
- # displaySwitchs = dev.otherConf.get('displaySwitchs') or self._get_displaySwitch(isTemp)
- # 3. 获取policy 计费规则
- policyTemp = self._get_policy()
- return {'ruleList': ruleList, 'policyTemp': policyTemp}
- def dealer_show_package(self, isTemp=False, **kw):
- displaySwitchs = self._get_displaySwitch(isTemp)
- try:
- # 1. 获取ruleList 套餐
- ruleList = self._get_ruleList(isTemp)
- # 2. 获取policy 计费规则
- policyTemp = self._get_policy()
- except:
- return {'displaySwitchs': displaySwitchs, 'policyTemp': self.INIT_POLICY,
- 'ruleList': []}
- return { 'displaySwitchs': displaySwitchs, 'policyTemp': policyTemp, 'ruleList': ruleList}
- def user_show_package(self, isTemp=False):
- group = self.device.group # type: GroupDict
- # 探测是否地址为免费活动组,默认为否
- is_free_service = group.is_free
- if isTemp:
- config = self.device.get('tempWashConfig', {})
- else:
- config = self.device['washConfig']
- if "displaySwitchs" in self.device.otherConf:
- displaySwitchs = self.device.otherConf.get('displaySwitchs')
- displaySwitchs = dict(filter(lambda x: "display" in x[0], displaySwitchs.items()))
- else:
- displaySwitchs = {'displayCoinsSwitch': True,
- 'displayTimeSwitch': True,
- 'displayPriceSwitch': True,
- }
- packages = []
- for packageId, rule in config.items():
- # 没有启用的套餐 直接掠过
- if not rule.get("switch", True):
- continue
- item = {
- 'id': packageId
- }
- if 'name' in rule:
- item['name'] = rule['name']
- if 'coins' in rule:
- item['coins'] = rule['coins']
- if 'price' in rule:
- item['price'] = rule['price']
- if 'time' in rule:
- item['time'] = rule['time']
- if 'description' in rule:
- item['description'] = rule['description']
- if 'unit' in rule:
- item['unit'] = rule['unit']
- if 'sn' in rule:
- item['sn'] = rule['sn']
- if 'minFee' in rule and rule['minFee'] and float(rule['minFee']) > 0:
- item.update({'minFee': rule.get('minFee')})
- if 'minAfterStartCoins' in rule and rule['minAfterStartCoins'] and float(rule['minAfterStartCoins']) > 0:
- item.update({'minAfterStartCoins': rule.get('minAfterStartCoins')})
- if 'autoRefund' in rule:
- item.update({'autoRefund': rule.get('autoRefund')})
- if is_free_service:
- item.update({'description': '当前处于免费时段'})
- item.update({'policyType': self._get_policy_type()})
- item.update(displaySwitchs)
- packages.append(item)
- return sorted(packages, key=lambda x: (x.get('sn'), x.get('id')))
- def _get_policy_type(self):
- policy = self._get_policy()
- forApps = policy.get('forApps')
- return forApps.get('policyType', 'time')
- def _get_policy_text(self):
- policy = self._get_policy()
- forApps = policy.get('forApps')
- policyType = forApps.get('policyType')
- text = ''
- if policyType == 'time':
- text += '根据功率挡位的时长计费'
- elif policyType == 'elec':
- price = forApps.get('rule', {}).get('price')
- text += '根据电量({}元/度)计费'.format(price)
- else:
- pass
- return text
- @property
- def support_device_package(self):
- return True
- class PolicyCommon(PolicyPackageInit, SmartBox):
- """
- 子类型区别
- 1. 默认类型(put_people_first):
- 常规套餐: 1.后付费,启动前需要充值 2.账户余额不足支付, 挂起一单, 直到下次支付, 才能再次启动, 3.只能启动一单.
- 临时套餐: 1.预付费. 2.下发金额,费用到了会停止. 3.可以启动多单.
- 2. 特殊类型(look_at_money):
- 常规套餐: 1.后付费,启动前需要充值. 2.启动下发账户全部余额, 余额用完会停止充电. 3.只能启动一单.
- 临时套餐: 1.预付费. 2.下发金额,费用到了会停止. 3.可以启动多单.
- """
- def reverse_hex(self, data):
- # type:(str) -> str
- if not isinstance(data, (str, unicode)):
- raise TypeError
- return ''.join(list(reversed(re.findall(r'.{2}', data))))
- def send_mqtt(self, payload, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, timeout=10):
- """
- 发送mqtt 指令默认210 返回data
- """
- result = MessageSender.send(self.device, cmd,
- payload=payload, timeout=timeout)
- if cmd in [DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE]:
- return
- if 'rst' in result and result['rst'] != 0:
- if result['rst'] == -1:
- raise ServiceException(
- {'result': 2, 'description': u'该设备正在玩命找网络,请您稍候再试', 'rst': -1})
- elif result['rst'] == 1:
- raise ServiceException(
- {'result': 2, 'description': u'该设备忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能', 'rst': 1})
- elif result['rst'] == 2:
- raise ServiceException(
- {'result': 2, 'description': u'设备启动失败, 检测未在工作状态', 'rst': 2})
- elif result['rst'] == 3:
- raise ServiceException(
- {'result': 2, 'description': u'无启动端口信息', 'rst': 3})
- elif result['rst'] == 4:
- raise ServiceException(
- {'result': 2, 'description': u'设备未启动', 'rst': 4})
- elif result['rst'] == 5:
- raise ServiceException(
- {'result': 2, 'description': u'续充失败, 计费规则不一致', 'rst': 5})
- elif result['rst'] == 6:
- raise ServiceException(
- {'result': 2, 'description': u'端口正在使用中, 请选择其他端口启动充电', 'rst': 6})
- elif result['rst'] == 7:
- raise ServiceException(
- {'result': 2, 'description': u'启动参数有误, 请联系经销商重新配置(rule_type)', 'rst': 7})
- elif result['rst'] == 8:
- raise ServiceException(
- {'result': 2, 'description': u'启动参数有误, 请联系经销商重新配置(billing_method)', 'rst': 8})
- elif result['rst'] == 9:
- raise ServiceException(
- {'result': 2, 'description': u'启动参数有误, 请联系经销商重新配置(order_id)', 'rst': 9})
- elif result['rst'] == 10:
- raise ServiceException(
- {'result': 2, 'description': u'启动参数有误, 请联系经销商重新配置(open_id)', 'rst': 10})
- elif result['rst'] == 11:
- raise ServiceException(
- {'result': 2, 'description': u'启动参数有误, 请联系经销商重新配置(rule)', 'rst': 11})
- elif result['rst'] == 12:
- raise ServiceException(
- {'result': 2, 'description': u'启动参数有误, 启动时间小于1分钟, 请联系经销商调整套餐', 'rst': 12})
- elif result['rst'] == 13:
- raise ServiceException(
- {'result': 2, 'description': u'启动参数有误, 启动电量小于0.01kW·h, 请联系经销商调整套餐', 'rst': 13})
- elif result['rst'] == 14:
- raise ServiceException(
- {'result': 2, 'description': u'没有找到此订单', 'rst': 14})
- if result.get('data'):
- return result['data']
- else:
- return result
- def _check_package(self, package):
- """
- 获取设备启动的发送数据 根据设备的当前模式以及套餐获取
- :param package:
- :return:
- """
- unit = package.get('unit', u'分钟')
- _time = float(package.get('time', 0))
- coins = float(package.get('coins', 0))
- price = float(package.get('price', 0))
- policyType = package.get('policyType')
- if not policyType:
- raise ServiceException({'result': 2, 'description': u'套餐计费方式错误, 请重新选择后配置'})
- billingMethod = package.get('billingMethod', CONSUMETYPE.POSTPAID)
- result = {'policyType': policyType, 'coins': coins, 'price': price, 'billingMethod': billingMethod}
- if billingMethod == CONSUMETYPE.POSTPAID:
- if policyType == 'time':
- # 按时间计费
- if unit == u'小时':
- result.update({'needTime': _time * 60, })
- elif unit == u'天':
- result.update({'needTime': _time * 24 * 60})
- elif unit == u'秒':
- result.update({'needTime': _time / 60, })
- elif unit == u'分钟':
- result.update({'needTime': _time, })
- else:
- raise ServiceException({'result': 2, 'description': u'套餐单位错误,请联系经销商'})
- elif policyType == 'elec':
- if unit == u'度':
- result.update({'needElec': _time, })
- else:
- raise ServiceException({'result': 2, 'description': u'套餐单位错误,请联系经销商'})
- else:
- if coins <= 0:
- raise ServiceException({'result': 2, 'description': u'套餐单位错误,套餐金币不能为0'})
- if price <= 0:
- raise ServiceException({'result': 2, 'description': u'套餐单位错误,套餐金额不能为0'})
- if policyType == 'time':
- pass
- elif policyType == 'elec':
- pass
- else:
- raise ServiceException({'result': 2, 'description': u'套餐单位错误,请联系经销商'})
- return result
- def update_device_configs(self, updateDict):
- dev = Device.objects.get(devNo=self.device.devNo)
- deviceConfigs = dev.otherConf.get('deviceConfigs', {})
- deviceConfigs.update(updateDict)
- dev.otherConf['deviceConfigs'] = deviceConfigs
- dev.save()
- Device.invalid_device_cache(self.device.devNo)
- @property
- def look_at_money(self):
- return self.device.devTypeFeatures.get('look_at_money', False)
- @property
- def accumulate_max_power(self):
- return self.device.devTypeFeatures.get('accumulate_max_power', False)
- @property
- def device_configs(self):
- dev = Device.get_dev(self.device.devNo)
- deviceConfigs = dev.get('otherConf', {}).get('deviceConfigs', {})
- return deviceConfigs
- @property
- def server_configs(self):
- dev = Device.get_dev(self.device.devNo)
- serverConfigs = dev.get('otherConf', {}).get('serverConfigs', {})
- return serverConfigs
- def update_server_configs(self, updateDict):
- dev = Device.objects.get(devNo=self.device.devNo)
- serverConfigs = dev.otherConf.get('serverConfigs', {})
- serverConfigs.update(updateDict)
- dev.otherConf['serverConfigs'] = serverConfigs
- dev.save()
- Device.invalid_device_cache(self.device.devNo)
- def stop(self, port=None):
- return self.send_mqtt({'funCode': 'stop', 'port': int(port), 'role': 'user'})
- def stop_charging_port(self, port):
- return self.send_mqtt({'funCode': 'stop', 'port': int(port)})
- @property
- def isHaveStopEvent(self):
- return True
- def check_dev_status(self, attachParas=None):
- if attachParas is None:
- raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路、电池类型信息'})
- if attachParas.get('isTempPackage') == True:
- washConfig = self.device.get('tempWashConfig', {})
- else:
- washConfig = self.device.get('washConfig', {})
- packageId = attachParas.get('packageId')
- if packageId: # 此时为快捷启动充电之前已经校验过一次
- package = washConfig.get(packageId)
- billingMethod = package.get('billingMethod')
- port = attachParas['chargeIndex']
- result = self.send_mqtt({'funCode': 'port_info', 'port': int(port), })
- if 'order_id' in result:
- if billingMethod == CONSUMETYPE.POSTPAID:
- raise ServiceException({'result': 2, 'description': u'当前端口繁忙'})
- else:
- openId = attachParas.get('openId')
- if result['open_id'] == openId and result['order_type'] == 'com_start' and result[
- 'billing_method'] == billingMethod:
- return
- else:
- raise ServiceException({'result': 2, 'description': u'当前端口繁忙'})
- def prepare_package(self, packageId, attachParas, startType=StartDeviceType.ON_LIEN):
- is_temp_package = 'isTempPackage' in attachParas and attachParas['isTempPackage'] is True
- if is_temp_package:
- washConfig = self._device.get('tempWashConfig', None)
- else:
- washConfig = self._device.get('washConfig', None)
- # 校验业务配置
- if not washConfig:
- raise ServiceException({'result': 2, 'description': u'未配置业务,请联系经销商'})
- # 校验套餐ID的有效性
- package = washConfig.get(packageId)
- if package is None:
- raise ServiceException({'result': 2, 'description': u'该套餐不存在,请联系经销商'})
- package['packageId'] = packageId
- policyType = self.device.policyTemp.get('forApps', {}).get('policyType', 'time')
- self.billingMethod = package.get('billingMethod', 'prepaid')
- if self.billingMethod == CONSUMETYPE.POSTPAID: # 后付费
- package = {
- "packageId": packageId,
- "name": package.get('name'),
- "switch": package.get('switch'),
- "policyType": policyType,
- "billingMethod": package.get('billingMethod'),
- "price": round(float(package.get('price', 0.0)), 2),
- "coins": round(float(package.get('coins', 0.0)), 2),
- "minAfterStartCoins": round(float(package.get('minAfterStartCoins', 0.0)), 2),
- "minFee": round(float(package.get('minFee', 0.0)), 2),
- "time": round(float(package.get('time')), 2),
- "unit": package.get('unit'),
- "sn": package.get('sn'),
- }
- if packageId == 'customPackage':
- customValue = float(attachParas.get('customValue') or 0)
- policyType = package['policyType']
- if policyType == 'time':
- unit = package['unit']
- if unit == '小时':
- customValue = customValue * 60
- if customValue <= 10:
- raise ServiceException({'result': 2, 'description': '自定义套餐充电时长不能小于10分钟'})
- limitTime = float(package['time']) or 720
- package['limitTime'] = limitTime
- if customValue > limitTime:
- raise ServiceException(
- {'result': 2, 'description': '自定义套餐充最大充电时长为{}分钟,请规范操作'.format(limitTime)})
- elif policyType == 'elec':
- if customValue <= 0:
- raise ServiceException({'result': 2, 'description': '自定义套餐充电电量不能小于0度'})
- limitTime = float(package['time']) or 10
- package['limitTime'] = limitTime
- if customValue > limitTime:
- raise ServiceException({'result': 2, 'description': '自定义套餐充最大充电量为{}度,请规范操作'.format(limitTime)})
- else:
- raise ServiceException({'result': 2, 'description': '非法自定义套餐, 请联系经销商重新配置计费规则'})
- package['time'] = customValue
- elif packageId == 'autoPackage':
- policyType = package['policyType']
- fullValue = float(package['time'])
- if policyType == 'time':
- unit = package['unit']
- if unit == '小时':
- fullValue = fullValue * 60
- # 最大720分钟 超过就
- package['fullValue'] = fullValue or 720
- elif policyType == 'elec':
- package['fullValue'] = fullValue or 10
- else:
- raise ServiceException({'result': 2, 'description': '套餐计费规则配置错误, 请联系经销商重新配置计费规则'})
- else:
- # 预付费
- # 1. 检验套餐设置的投币数
- if 'coins' not in package:
- raise ServiceException({'result': 2, 'description': u'套餐未设置投币数,请联系经销商'})
- package = {
- "packageId": packageId,
- "name": package.get('name'),
- "switch": package.get('switch'),
- "policyType": policyType,
- "autoRefund": package.get('autoRefund', False),
- "billingMethod": package.get('billingMethod'),
- "price": round(float(package.get('price', 0.0)), 2),
- "coins": round(float(package.get('coins', 0.0)), 2),
- "minAfterStartCoins": round(float(package.get('minAfterStartCoins', 0.0)), 2),
- "minFee": round(float(package.get('minFee', 0.0)), 2),
- "time": round(float(package.get('time')), 2),
- "unit": package.get('unit'),
- "sn": package.get('sn'),
- }
- return package
- def start_device_realiable(self, order):
- attachParas = order.attachParas
- package = order.package
- port = attachParas.get('chargeIndex')
- if not port:
- raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路'})
- payload = {
- 'funCode': 'start',
- 'port': int(port),
- 'order_id': order.orderNo,
- 'open_id': order.openId,
- 'attach_paras': {},
- }
- if attachParas.get('isFree'):
- payload['attach_paras']['isFree'] = True
- if attachParas.get('onPoints'):
- payload['attach_paras']['onPoints'] = True
- self.generate_rule(package, payload)
- uart_source = []
- uart_source.append(
- {'write_start': json.dumps(payload, indent=4),
- 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')})
- result = self.send_mqtt(payload=payload, timeout=MQTT_TIMEOUT.START_DEVICE)
- uart_source.append(
- {'rece_start': json.dumps(result, indent=4), 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')})
- # 兼容远程上分 远程上分没有订单
- try:
- order.update(uart_source=uart_source)
- except:
- pass
- return result
- def stop_by_order(self, port, orderNo):
- """
- 用户扫码后, 如果订单没有结算, 点击停止按钮, 会去设备上查询, 如果真的没有查询到模块, 此单直接作废掉, 不计费, 让用户重新扫码使用
- """
- order = ClientConsumeModelProxy.get_one(shard_filter = {'ownerId': self.device.ownerId}, devNo=self.device.devNo, orderNo=orderNo, status__ne='finished') # type: ConsumeRecord
- if order:
- order_id = order.orderNo
- if order.remarks == '刷卡消费' or (order.startKey and order.startKey.startswith(self.device.devNo)): # 刷卡
- order_id = order.startKey
- try:
- self.send_mqtt({'funCode': 'stop', 'role': 'user', 'order_id': order_id, 'port': int(port)})
- except ServiceException as e:
- if e.result['rst'] == 14: # 没有找到订单, 服务器有订单 设备上没有订单, 设备上订单丢失
- order.update(isNormal=False, errorDesc='设备上订单已丢失')
- else:
- raise e
- def check_power_price_rules(self, packages):
- save_configs = []
- lastMaxPower = 0
- for package in packages:
- try:
- minPower = float(package.get('min'))
- maxPower = float(package.get('max'))
- price = float(package.get('price'))
- except Exception:
- raise InvalidParameter(u'价格规则填写错误,请正确的输入每个功率段')
- self.check_params_range(str(price), minData=0.0, maxData=10.0, desc='功率段价格')
- if maxPower > 9999:
- raise InvalidParameter(u'最大功率不能超过9999W')
- if minPower != lastMaxPower:
- raise InvalidParameter(u'功率段之间有未覆盖到的区间,请正确设置功率段')
- else:
- lastMaxPower = maxPower
- save_configs.append({'min': minPower, 'max': maxPower, 'price': price})
- return save_configs
- def get_power_price_rules(self):
- serverConfigs = self.get_server_configs()
- packages = serverConfigs.get('power_price_rules', [])
- if not packages:
- packages = Const.POWER_PRICE_RULES
- return packages
- def get_uart_step(self, account_type=None, is_card=False):
- if account_type == 'RATIO_TIME':
- if is_card:
- powerLevel = self.device.policyTemp.get('forIdcard', {}).get('rule', {}).get('ratios', [])
- else:
- powerLevel = self.device.policyTemp.get('forApps', {}).get('rule', {}).get('ratios', [])
- else:
- if is_card:
- powerLevel = self.device.policyTemp.get('forIdcard', {}).get('rule', {}).get('prices', [])
- else:
- powerLevel = self.device.policyTemp.get('forApps', {}).get('rule', {}).get('prices', [])
- account_rule = []
- for pp in powerLevel:
- if 'power' in pp and 'price' in pp:
- account_rule.append(
- '{:0>4d}-{:0>5d}'.format(int(pp['power']), int(pp['price'] * 100)))
- return account_rule
- def generate_rule(self, package, payload):
- data = self._check_package(package)
- billingMethod = data['billingMethod']
- policyType = data['policyType']
- # rule_type: 'MONEY | TIME | ELEC | TIME_MONEY | ELEC_MONEY | TIME_ELEC | TIME_ELEC_MONEY | RATIO_TIME'#8种规则
- account_type = 'real' # account_type(max, real, min) 计费的规则是最大功率计费,还是实时功率计费
- if self.accumulate_max_power:
- account_type = 'max'
- policy = {'billing_method': billingMethod}
- if billingMethod == CONSUMETYPE.POSTPAID: # 后付费
- if policyType == 'time':
- if self.look_at_money:
- policy.update({
- 'rule_type': 'MONEY',
- 'rule': {
- 'account_type': account_type,
- 'need_time': int(data['needTime'] * 60), # 精确到秒
- 'step': self.get_uart_step()
- }})
- open_id = payload.get('open_id')
- user = MyUser.objects.filter(openId=open_id).first()
- if user:
- balance = user.calc_currency_balance(self.device.owner, self.device.group)
- policy['rule']['amount'] = int(balance * 100)
- else:
- policy.update({
- 'rule_type': 'TIME_MONEY',
- 'rule': {
- 'account_type': account_type,
- 'need_time': int(data['needTime'] * 60), # 精确到秒
- 'step': self.get_uart_step()
- }})
- elif policyType == 'elec':
- elecFee = float(self.device.policyTemp.get('forApps', {}).get('rule', {}).get('price', 1)) # 电费单价, 用于显示金额
- policy.update({
- 'rule_type': 'ELEC',
- 'rule': {
- 'account_type': account_type,
- 'need_elec': int(data['needElec'] * 3600 * 1000), # 精确到焦耳
- 'elec_fee': int(elecFee * 100)
- }})
- else:
- raise ServiceException({'result': 2, 'description': u'套餐单位错误,请联系经销商'})
- else: # 预付费
- if policyType == 'time':
- policy.update({
- 'rule_type': 'MONEY',
- 'rule': {
- 'account_type': account_type,
- 'amount': int(data['coins'] * 100),
- 'step': self.get_uart_step()
- },
- 'fee': int(data['coins'] * 100), # 本单实际支付金额
- },
- )
- elif policyType == 'elec':
- elecFee = float(self.device.policyTemp.get('forApps', {}).get('rule', {}).get('price', 1)) # 电费单价, 用于显示金额
- policy.update({
- 'rule_type': 'MONEY_BY_ELEC',
- 'rule': {
- 'account_type': account_type,
- 'amount': int(data['coins'] * 100), # 精确到焦耳
- 'elec_fee': int(elecFee * 100)
- },
- 'fee': int(data['coins'] * 100), # 本单实际支付金额
- })
- else:
- raise ServiceException({'result': 2, 'description': u'套餐单位错误,请联系经销商'})
- payload.update(policy)
- def custom_push_url(self, order, user, **kw):
- """
- # 如果是启动, 就进入个人服务中心, 如果是结束就进入订单详细
- """
- from apps.web.utils import concat_user_center_entry_url
- from apps.web.utils import concat_front_end_url
- if order.status in [ConsumeRecord.Status.END, ConsumeRecord.Status.FINISHED]:
- return super(PolicyCommon, self).custom_push_url(order, user, **kw)
- else:
- return concat_user_center_entry_url(agentId=user.productAgentId, redirect=concat_front_end_url(
- uri='/user/index.html#/user/deviceStatus?orderId={}'.format(str(order.id))))
- def format_port_using_detail(self, detailDict):
- detailDict = super(PolicyCommon, self).format_port_using_detail(detailDict)
- if 'actualNeedTime' in detailDict:
- detailDict.pop('actualNeedTime')
- if 'coins' in detailDict:
- detailDict['coins'] = '{}元'.format(detailDict['coins'])
- if 'leftTime' in detailDict:
- detailDict.pop('leftTime')
- return detailDict
- def get_port_info(self, line):
- result = self.send_mqtt({'funCode': 'port_info', 'port': int(line)})
- port_info = {}
- # 主板串口传回数据
- if 'current_power' in result:
- port_info['power'] = str(result['current_power'])
- if 'status' in result:
- port_info['status'] = result['status']
- if 'duration' in result:
- port_info['usedTime'] = round(result['duration'] / 60.0, 1)
- if 'elec' in result:
- port_info['usedElec'] = round(result['elec'] / 3600000.0, 3)
- if 'rule' in result:
- rule = result['rule']
- if 'rule_type' in result:
- rule_type = result['rule_type']
- if rule_type == 'TIME_MONEY':
- if 'need_time' in rule:
- port_info['needTime'] = round(rule['need_time'] / 60.0, 1)
- port_info['actualNeedTime'] = round(rule['need_time'] / 60.0, 1)
- elif rule_type == 'ELEC':
- if 'need_elec' in rule:
- port_info['needElec'] = round(rule['need_elec'] / 3600000.0, 3)
- elif rule_type == 'MONEY':
- if 'amount' in rule:
- port_info['coins'] = round(rule['amount'] / 100.0, 2)
- elif rule_type == 'MONEY_BY_ELEC':
- if 'amount' in rule:
- port_info['coins'] = round(rule['amount'] / 100.0, 2)
- if 'need_pay' in result:
- if result['need_pay'] == -1:
- pass
- else:
- port_info['consumeMoney'] = format(result['need_pay'] / 3600.0 / 100.0, '.2f') + '元'
- if 'billing_method' in result:
- if result['billing_method'] == CONSUMETYPE.POSTPAID:
- port_info['consumeType'] = CONSUMETYPE.POSTPAID
- else:
- if result['order_type'] == 'com_start':
- port_info['consumeType'] = CONSUMETYPE.MOBILE
- elif result['order_type'] == 'id_start':
- port_info['consumeType'] = CONSUMETYPE.CARD
- if 'card_id' in result:
- port_info['cardNo'] = format(int(result['card_id'], 16), 'd')
- if 'sts' in result:
- port_info['startTime'] = to_datetime(result['sts']).strftime('%m-%d %H:%M:%S')
- attach_paras = result.get('attach_paras', {})
- if 'onPoints' in attach_paras:
- port_info['nickName'] = '经销商上分'
- elif 'isFree' in attach_paras:
- port_info.pop('coins', None)
- port_info.pop('consumeMoney', None)
- port_info['consumeType'] = 'isFree'
- else:
- if 'order_id' in result:
- consumeRcd = ConsumeRecord.objects.filter(
- Q(orderNo=result['order_id']) | Q(startKey=result['order_id'])).first()
- user = consumeRcd.user
- port_info['nickName'] = user.nickname
- port_info['openId'] = user.openId
- if consumeRcd.package.get('packageId', '') == 'autoPackage':
- if consumeRcd.package.get('policyType') == 'time':
- port_info['needTime'] = 999
- port_info.pop('needElec', None)
- port_info.pop('usedElec', None)
- elif consumeRcd.package.get('policyType') == 'elec':
- port_info['needTime'] = 999
- port_info.pop('needTime', None)
- else:
- if consumeRcd.package.get('policyType') == 'time':
- port_info.pop('needElec', None)
- port_info.pop('usedElec', None)
- elif consumeRcd.package.get('policyType') == 'elec':
- port_info.pop('needTime', None)
- port_info.pop('leftTime', None)
- return port_info
- def get_ports_info(self, **kw):
- result = self.get_port_status_from_dev()
- port_list = []
- ctrInfo = Device.get_dev_control_cache(self.device.devNo)
- statsMap = {Const.DEV_WORK_STATUS_IDLE: 'idle',
- Const.DEV_WORK_STATUS_WORKING: 'busy',
- Const.DEV_WORK_STATUS_FAULT: 'fault',
- Const.DEV_WORK_STATUS_FORBIDDEN: 'ban',
- Const.DEV_WORK_STATUS_CONNECTED: 'connected',
- Const.DEV_WORK_STATUS_FINISHED: 'finished'}
- for port, _info in result.items():
- _info['index'] = port
- if _info.get('status') == Const.DEV_WORK_STATUS_WORKING:
- _info.update(self.get_port_using_detail(port, ctrInfo, isLazy=True))
- for k, v in _info.items():
- if k.lower().endswith('money'):
- _info.pop(k)
- _info['status'] = statsMap.get(_info['status'], 'busy')
- port_list.append(_info)
- return port_list
- def check_order_state(self, openId):
- """
- 通过 openId 以及设备来鉴别 订单
- :param openId:
- :return:
- """
- dealerId = self.device.ownerId
- return ClientConsumeModelProxy.get_not_finished_record(ownerId=dealerId, openId=openId,
- devTypeCode=self._device["devType"]["code"],
- package__billingMethod=CONSUMETYPE.POSTPAID)
- def force_stop_order(self, order, **kwargs):
- order.update(isNormal=False, errorDesc='经销商强制结束订单')
|