weifuleCommon.py 66 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import json
  5. import logging
  6. import time
  7. import traceback
  8. from typing import TYPE_CHECKING, Optional, Tuple
  9. from apilib.monetary import VirtualCoin, RMB, Ratio
  10. from apilib.utils_string import make_title_from_dict
  11. from apps.web.constant import Const, START_DEVICE_STATUS, DEALER_CONSUMPTION_AGG_KIND
  12. from apps.web.core.accounting import Accounting
  13. from apps.web.device.models import Device, GroupDict, Group, DeviceDict
  14. from apps.web.device.timescale import FluentedEngine
  15. from apps.web.eventer.base import WorkEvent
  16. from apps.web.exceptions import UnifiedConsumeOrderError
  17. from apps.web.user.constant2 import StartDeviceType
  18. from apps.web.user.models import ConsumeRecord, UserVirtualCard, MonthlyPackage, ServiceProgress, CardRechargeOrder, \
  19. Card, CardConsumeRecord, RechargeRecord, RefundMoneyRecord, VCardConsumeRecord
  20. from apps.web.user.utils2 import generate_card_payment, ConsumeOrderStateEngine, UnifiedCardConsumeOrderManager
  21. from apps.web.utils import set_start_key_status
  22. logger = logging.getLogger(__name__)
  23. card_is_normal = 1
  24. card_not_in_db = 2
  25. card_is_forzen = 3
  26. card_has_not_order = 4 # IC卡适用
  27. card_less_balance = 5 # ID卡适用
  28. card_type_is_ic = 6 # IC卡不允许当做ID卡使用
  29. no_load_code = 7
  30. if TYPE_CHECKING:
  31. pass
  32. class WeiFuLeStatusEvent(WorkEvent):
  33. def do(self, **args):
  34. return self._do_update_charge_status()
  35. def __translate_status_from_str(self, status):
  36. dictConf = {
  37. 'idle': Const.DEV_WORK_STATUS_IDLE,
  38. 'busy': Const.DEV_WORK_STATUS_WORKING,
  39. 'forbid': Const.DEV_WORK_STATUS_FORBIDDEN,
  40. 'fault': Const.DEV_WORK_STATUS_FAULT,
  41. 'running': Const.DEV_WORK_STATUS_WORKING,
  42. 'link': Const.DEV_WORK_STATUS_CONNECTED,
  43. 'estop': Const.DEV_WORK_STATUS_ESTOP,
  44. 'ready': Const.DEV_WORK_STATUS_READY,
  45. }
  46. return dictConf.get(status, Const.DEV_WORK_STATUS_FAULT)
  47. def _do_update_charge_status(self):
  48. def do_flush():
  49. ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  50. for port, status in port_status.items():
  51. if str(port) in ctrInfo:
  52. ctrInfo[str(port)]['status'] = self.__translate_status_from_str(status)
  53. else:
  54. ctrInfo[str(port)] = {'status': self.__translate_status_from_str(status)}
  55. Device.update_dev_control_cache(self.device.devNo, ctrInfo)
  56. source = self.event_data
  57. data = source.get('data', {})
  58. upTime = source.get('time')
  59. port_status = self.parse_status(data)
  60. if not upTime or not port_status:
  61. return
  62. import time
  63. now = time.time()
  64. need_flush = bool((now - upTime) < 10) # 半分钟以内的状态上报为有效上报
  65. if not need_flush:
  66. logger.info('Timeout status report!')
  67. return
  68. do_flush()
  69. def parse_status(self, data):
  70. return data.get('port_status', {})
  71. class WeiFuLeCommonEvent(object):
  72. def pay_apps_start_by_32(self, order, callbackSP=None):
  73. """
  74. 此函数会对订单进行扣款(补扣或者抢锁扣款,依据上报的事件)
  75. 一般callbackSP为创建服务进程函数
  76. """
  77. consumeRcd = ConsumeRecord.objects.filter(orderNo=order['id']).first()
  78. # 检查订单是否完成
  79. if consumeRcd is None or consumeRcd.isNormal:
  80. logger.info('weifule({}) pay by startAcion..'.format(order['id']))
  81. return
  82. # 创建锁
  83. result = ConsumeRecord.objects.filter(orderNo=order['id'], isNormal=False, attachParas__payBy=None).update(
  84. attachParas__payBy='event')
  85. # TODO 抢扣 或者 补扣
  86. if result:
  87. package = consumeRcd.package
  88. attachParas = consumeRcd.attachParas
  89. group = self.device.group # type: GroupDict
  90. if group.is_free:
  91. pass
  92. # 先寻找是否可用 包月套餐
  93. monthlyPackage = MonthlyPackage.get_enable_one(
  94. openId=consumeRcd.openId,
  95. groupId=consumeRcd.groupId,
  96. )
  97. if monthlyPackage:
  98. monthlyPackage.deduct(consumeRcd)
  99. elif package.get('name') == '充满自停' and package.get('coins') == 0 and package.get('price') == 0: # 后付费
  100. consumeRcd.update(isNormal=True, status='')
  101. else:
  102. # 虚拟卡查找
  103. vCardId = attachParas.get('vCardId')
  104. if vCardId:
  105. vCard = UserVirtualCard.objects.filter(id=vCardId).first()
  106. if not vCard:
  107. consumeRcd.update(isNormal=False, status=START_DEVICE_STATUS.FAILURE,
  108. errorDesc='没有找到虚拟卡.或虚拟卡已经过期(event)')
  109. self.deviceAdapter.stop(order['port'])
  110. vCardConsumeOrder = vCard.consume(openId=consumeRcd.openId, group=group, dev=self.device,
  111. package=package,
  112. attachParas=attachParas, nickname=consumeRcd.nickname,
  113. orderNo=consumeRcd.orderNo)
  114. if not vCardConsumeOrder:
  115. # 扣除虚拟卡失败. 设备又启动了, 直接发明了停止掉
  116. consumeRcd.update(isNormal=False, status=START_DEVICE_STATUS.FAILURE,
  117. errorDesc='扣除虚拟卡额度的时候,出现异常,请重试(event)')
  118. logger.info('vCardConsumeOrder error , devNo=<{}> stop port({}) now.'.format(self.device.devNo, order['port'], ))
  119. return self.deviceAdapter.stop(order['port'])
  120. else:
  121. user = consumeRcd.user
  122. user.update(inc__total_consumed=VirtualCoin(package['coins']))
  123. user.pay(coins=VirtualCoin(package['coins'])) # 允许有负值
  124. # 纠正订单信息
  125. if consumeRcd.status == START_DEVICE_STATUS.FAILURE:
  126. errorDesc = consumeRcd.errorDesc + "(<{}补充扣款{}币>)".format(
  127. datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  128. VirtualCoin(package['coins']).mongo_amount)
  129. if vCardId:
  130. errorDesc += '(虚拟卡抵扣)'
  131. # TODO 补充扣款只扣除金币 所以如果有充值订单 修正订单为金币支付
  132. consumeRcd.update(isNormal=True,
  133. status='',
  134. errorDesc=errorDesc,
  135. rechargeRcdId='',
  136. attachParas__lastRechargeRcdId=consumeRcd.rechargeRcdId)
  137. else:
  138. consumeRcd.update(isNormal=True,
  139. status='')
  140. logger.info('weifule({}) pay by event..'.format(order['id']))
  141. if callable(callbackSP):
  142. callbackSP(consumeRcd, order)
  143. else:
  144. logger.info('weifule({}) pay by startAcion..'.format(order['id']))
  145. class WeiFuLePolicyProcess(WorkEvent):
  146. def translate_reason(self, order):
  147. return '充电结束'
  148. @staticmethod
  149. def _auto_parser_card_no(card_no):
  150. return card_no if card_no.isdigit() and len(card_no) > 8 else '{}'.format(int(card_no, 16))
  151. @staticmethod
  152. def _auto_parser_card_type(card_type):
  153. if card_type in ['ID', 'online_card']:
  154. return 'ID'
  155. elif card_type in ['IC', 'offline_card']:
  156. return 'IC'
  157. else:
  158. return ''
  159. @staticmethod
  160. def _notify_card_title(card, order): # type: (Card, dict) -> dict
  161. if card:
  162. if card.is_id_card:
  163. card_title = u'在线卡片'
  164. elif card.is_ic_card:
  165. card_title = u'离线卡片'
  166. else:
  167. card_title = u'实体卡片'
  168. return {card_title: u'{}--No.({})'.format(card.cardName or card.nickName, card.cardNo)}
  169. else:
  170. return {}
  171. def notify_to_user(self, openId, extra):
  172. group = Group.get_group(self.device['groupId'])
  173. self.notify_user_service_complete(
  174. service_name='充电',
  175. openid=openId,
  176. port='',
  177. address=group['address'],
  178. reason=self.consumeDict.get('reason'),
  179. finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  180. url=self.device.deviceAdapter.custom_push_url(self.consumeRcd, self.consumeRcd.user),
  181. extra=extra)
  182. def create_progress_for_socket_order(self, consumeRcd, devInfo):
  183. try:
  184. port = int(devInfo['port'])
  185. progress = ServiceProgress.objects.filter(device_imei=self.device.devNo, port=port,
  186. devTypeCode=self.device.devTypeCode).first()
  187. if not progress:
  188. progress = ServiceProgress(device_imei=self.device.devNo, port=port,
  189. devTypeCode=self.device.devTypeCode)
  190. progress.start_time = devInfo['create_time']
  191. progress.finished_time = devInfo['create_time'] + 60 * 60 * 12
  192. progress.isFinished = False
  193. else:
  194. if devInfo['id'] in progress.consumes:
  195. return
  196. if progress.isFinished == False:
  197. progress.finished_time = progress.finished_time + 60 * 60 * 12
  198. else:
  199. progress.consumes = []
  200. progress.start_time = devInfo['create_time']
  201. progress.finished_time = devInfo['create_time'] + devInfo.get('amount_time', 60 * 60 * 12)
  202. progress.isFinished = False
  203. progress.open_id = consumeRcd.openId
  204. progress.consumes.append(devInfo['id'])
  205. progress.status = 'running'
  206. progress.save()
  207. except Exception as e:
  208. logger.exception(e)
  209. def record_consume_for_card(self, card, money, desc=u'', servicedInfo=None, sid=None, attachParas=None):
  210. # type: (Card, RMB, unicode, Optional[dict], Optional[str], Optional[dict])->Tuple[ConsumeRecord, CardConsumeRecord]
  211. group = Group.get_group(self.device['groupId'])
  212. address = group['address']
  213. group_number = self.device['groupNumber']
  214. now = datetime.datetime.now()
  215. if not servicedInfo:
  216. servicedInfo = {}
  217. if not attachParas:
  218. attachParas = {}
  219. new_record = ConsumeRecord.objects.create(**{
  220. 'orderNo': sid,
  221. 'time': now.strftime('%Y-%m-%d %H:%M:%S'),
  222. 'dateTimeAdded': now,
  223. 'openId': card.openId,
  224. 'nickname': card.nickName,
  225. 'ownerId': self.device['ownerId'],
  226. 'coin': money.mongo_amount,
  227. 'money': money.mongo_amount,
  228. 'devNo': self.device['devNo'],
  229. 'logicalCode': self.device['logicalCode'],
  230. 'groupId': self.device['groupId'],
  231. 'address': address,
  232. 'groupNumber': group_number,
  233. 'groupName': group['groupName'],
  234. 'devTypeCode': self.device.devTypeCode,
  235. 'devTypeName': self.device.devTypeName,
  236. 'isNormal': True,
  237. 'remarks': u'刷卡消费',
  238. 'errorDesc': '',
  239. 'sequanceNo': '',
  240. 'desc': desc,
  241. 'attachParas': attachParas,
  242. 'servicedInfo': servicedInfo
  243. })
  244. # 刷卡消费也记录一条数据
  245. new_card_record = CardConsumeRecord.objects.create(**{
  246. 'orderNo': sid,
  247. 'openId': card.openId,
  248. 'cardId': str(card.id),
  249. 'money': money.mongo_amount,
  250. 'balance': card.balance.mongo_amount,
  251. 'devNo': self.device['devNo'],
  252. 'devType': self.device['devType']['name'],
  253. 'logicalCode': self.device['logicalCode'],
  254. 'groupId': self.device['groupId'],
  255. 'address': address,
  256. 'groupNumber': group_number,
  257. 'groupName': group['groupName'],
  258. 'result': 'success',
  259. 'remarks': u'刷卡消费',
  260. 'sequanceNo': '',
  261. 'dateTimeAdded': datetime.datetime.now(),
  262. 'desc': desc,
  263. 'servicedInfo': servicedInfo,
  264. 'linkedConsumeRcdOrderNo': str(new_record['orderNo'])
  265. })
  266. return new_record, new_card_record
  267. def response_id_card(self):
  268. """
  269. 回复ID卡余额查询
  270. """
  271. card_no = self.event_data['card_no']
  272. cardNo = self._auto_parser_card_no(card_no)
  273. Card.record_dev_card_no(self.device.devNo, cardNo)
  274. card = self.update_card_dealer_and_type(cardNo)
  275. resp_message = {'fun_code': self.event_data['fun_code'], 'card_no': card_no, 'balance': 0, 'policy': {}}
  276. # 如果没有卡,直接返回
  277. if not card or not card.isBinded:
  278. resp_message.update({'result': card_not_in_db})
  279. else:
  280. if card.frozen:
  281. resp_message.update({'result': card_is_forzen})
  282. else:
  283. # 遇到没有充值的订单 重新充值一次
  284. card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id))
  285. if card_recharge_order:
  286. self.recharge_id_card(card=card, rechargeType='append', order=card_recharge_order)
  287. card.reload()
  288. oper = self.event_data.get('reduce')
  289. if oper:
  290. result, policy, showBalance = self.generate_Idcard_rules(card)
  291. resp_message.update({'result': result, 'balance': RMB.yuan_to_fen(showBalance), 'policy': policy})
  292. else:
  293. resp_message.update({'result': card_is_normal, 'balance': RMB.yuan_to_fen(card.balance)})
  294. return self.deviceAdapter.send_mqtt(resp_message)
  295. def generate_Idcard_rules(self, card):
  296. _policyTemp = self.device.policyTemp.get('forIdcard', self.deviceAdapter.INIT_POLICY['forIdcard'])
  297. policyType = _policyTemp.get('policyType')
  298. billingMethod = _policyTemp.get('billingMethod')
  299. # 策略计费
  300. policy = {
  301. 'type': policyType,
  302. 'mode': 'price',
  303. 'auto_stop': True,
  304. 'over_money_stop': True,
  305. 'open_id': card.openId,
  306. 'billingMethod': _policyTemp['billingMethod'],
  307. 'rule': {}
  308. }
  309. if policyType == 'time':
  310. # 格式化规则
  311. prices = sorted(_policyTemp.get('rule', {}).get('prices', []), key=lambda _: _['power'])
  312. for item in prices:
  313. item['value'] = int(float(item.pop('price')) * 100)
  314. item['power'] = int(item['power'])
  315. policy['rule'] = {'time': 'full_stop', 'prices': prices}
  316. elif policyType == 'elec':
  317. price = RMB.yuan_to_fen(_policyTemp.get('price', 1))
  318. policy['rule'] = {'time': 'full_stop', 'price': price}
  319. else:
  320. pass
  321. # 付费时间的不同 导致下发的不同的参数
  322. if billingMethod == 'prepaid':
  323. money = _policyTemp['money']
  324. # 预支付的启动金额不足
  325. if RMB(money) > RMB(card.balance):
  326. result = card_less_balance
  327. showBalance = RMB(card.balance)
  328. # 预支付的启动金额足够
  329. else:
  330. result = card_is_normal
  331. showBalance = RMB(card.balance) - RMB(money)
  332. # 获取预支付模式下的自动退款
  333. auto_refund = _policyTemp.get('autoRefund')
  334. policy.update({
  335. 'auto_refund': auto_refund,
  336. 'money': int(float(money) * 100),
  337. })
  338. # 后支付的模式
  339. elif billingMethod == 'postpaid':
  340. showBalance = card.balance
  341. # 对比后支付的最小启动金额
  342. minAfterStartCoins = _policyTemp.get('minAfterStartCoins')
  343. if RMB(minAfterStartCoins) > RMB(card.balance):
  344. result = card_less_balance # 余额不足
  345. else:
  346. result = card_is_normal
  347. policy.update({
  348. 'auto_refund': False,
  349. 'money': int(float(card.balance) * 100)
  350. })
  351. else:
  352. result = card_not_in_db
  353. showBalance = RMB(0)
  354. return result, policy, showBalance
  355. def _do_ack_order(self, order):
  356. funCode = self.event_data['fun_code']
  357. if funCode in [32, 34]:
  358. try:
  359. self.deviceAdapter.ack_event(order['id'], funCode)
  360. except Exception as e:
  361. logger.info('ack event devNo=%s err=%s' % (self.device.devNo, e))
  362. def _do_created_order_32(self, order):
  363. if order['order_type'] == 'apps_start': # 扫码
  364. self.pay_apps_start_by_32(order)
  365. elif order['order_type'] == 'card_start':
  366. self.pay_card_start_by_32(order)
  367. elif order['order_type'] == 'coin_start':
  368. self.pay_coin_start_by_32(order)
  369. else:
  370. pass
  371. logger.info('finished 32 !!order=<{}>'.format(order))
  372. def _do_exec_order_33(self, order):
  373. if 'exec_time' in order:
  374. now = datetime.datetime.fromtimestamp(order['exec_time'])
  375. elif 'execute_time' in order:
  376. now = datetime.datetime.fromtimestamp(order['execute_time'])
  377. else:
  378. now = datetime.datetime.now()
  379. ConsumeRecord.objects.filter(orderNo=order['id']).update(startTime=now)
  380. logger.info('now to exec next order <{}>'.format(order['id']))
  381. def _do_finished_order_34(self, order):
  382. # 如果是离线卡的余额回收,修改下余额,然后直接返回即可
  383. if order['order_type'] == 'card_refund':
  384. return self.end_for_card_refund(order)
  385. self.common_verify_order(order)
  386. if not self.consumeRcd:
  387. return
  388. try:
  389. # 初始化订单的类型(预付费, 后付费)
  390. self._init_common_info(order)
  391. self.billingMethod = self.consumeRcd.servicedInfo.get('billingMethod')
  392. if self.billingMethod == 'postpaid':
  393. self.finishe_postpaid_process(order)
  394. elif self.billingMethod == 'prepaid':
  395. self.finish_prepaid_process(order)
  396. self.consumeRcd.update_service_info(self.consumeDict)
  397. else:
  398. logger.info('No such process.. orderNo={}'.format(self.consumeRcd.orderNo))
  399. self.notify_to_user(self.consumeRcd.user.managerialOpenId, self.extra)
  400. logger.info('order is finished.. orderNo={}'.format(self.consumeRcd.orderNo))
  401. except Exception as e:
  402. logger.exception('some exception happed,devNo=%s,e=%s' % (self.device.devNo, e))
  403. finally:
  404. ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  405. lineInfo = ctrInfo.get(str(order['port']), {})
  406. # 如果缓存中要结束的端口是本单的 就清空缓存 否则不处
  407. if order['id'] == lineInfo.get('orderNo'):
  408. Device.clear_port_control_cache(self.device.devNo, order['port'])
  409. def common_verify_order(self, order):
  410. self.consumeRcd = ConsumeRecord.objects.filter(orderNo=order['id'],
  411. status__ne='finished').first() # type:ConsumeRecord
  412. if not self.consumeRcd:
  413. logger.info('Repeat processing!! orderNo<{}>'.format(order['id']))
  414. return
  415. else:
  416. self.consumeRcd.status = 'finished'
  417. self.consumeRcd.finishedTime = datetime.datetime.now()
  418. self.consumeRcd.servicedInfo['source'] = json.dumps(order)
  419. self.consumeRcd.save()
  420. if 'card_no' in order:
  421. cardNo = self._auto_parser_card_no(order['card_no'])
  422. self.card = self.update_card_dealer_and_type(cardNo=cardNo)
  423. progress = ServiceProgress.objects.filter(device_imei=self.device.devNo, port=int(order['port']),
  424. devTypeCode=self.device.devTypeCode,
  425. consumes__contains=order['id']).first()
  426. if progress is None:
  427. pass
  428. else:
  429. progress.consumes.remove(order['id'])
  430. if len(progress.consumes) > 0: # 还有其他的单未处理完成
  431. progress.save()
  432. else:
  433. progress.status = 'finished' # 此时为本次服务的最后一单
  434. progress.isFinished = True
  435. progress.save()
  436. def end_for_card_refund(self, order):
  437. return self.update_card_dealer_and_type(cardNo=order['card_no'], cardType='IC',
  438. balance=RMB(order['balance'] / 100.0))
  439. def _init_common_info(self, order):
  440. """
  441. 初始化结束要用到的数据
  442. """
  443. self.refundProtectionTime = int(self.device['otherConf'].get('refundProtectionTime', 5))
  444. self.consumeDict = self.consumeRcd.servicedInfo
  445. self.duration = round(order['time'] / 60.0, 1)
  446. self.elec = round(order['elec'] / 1000000.0, 3)
  447. self.power = round(order.get('power', 0), 1)
  448. order['reason'] = self.translate_reason(order)
  449. self.consumeDict.update({'reason': order['reason'], 'duration': self.duration, 'elec': self.elec})
  450. # 添加一个分档功率
  451. if self.power:
  452. self.consumeDict.update({'maxPower': self.power})
  453. self.extra = [
  454. {u'使用详情': u'{}分钟(端口: {})'.format(self.duration, self.consumeDict['chargeIndex'])}
  455. ]
  456. # 32启动补扣或者刷卡建单 *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
  457. def pay_apps_start_by_32(self, order, callbackSP=None):
  458. """
  459. 此函数会对订单进行扣款(补扣或者抢锁扣款,依据上报的事件)
  460. 一般callbackSP为创建服务进程函数
  461. """
  462. consumeRcd = ConsumeRecord.objects.filter(orderNo=order['id']).first() # type: ConsumeRecord
  463. # 检查订单是否完成
  464. if consumeRcd is None or consumeRcd.isNormal:
  465. logger.info('weifule({}) pay by startAction..'.format(order['id']))
  466. return
  467. # 创建锁
  468. result = ConsumeRecord.objects.filter(
  469. orderNo=order['id'], isNormal=False, attachParas__payBy=None).update(
  470. attachParas__payBy='event'
  471. )
  472. if not result:
  473. return
  474. # TODO 在线支付的抢扣或者补扣
  475. # 订单为免费的情况 更换状态即可
  476. if consumeRcd.isFree:
  477. logger.info("[pay_apps_start_by_32] free order = {}, event = {}".format(consumeRcd, self.event_data))
  478. consumeRcd.update(isNormal=True, status=START_DEVICE_STATUS.RUNNING)
  479. return
  480. # 后支付的情况 更换订单状态即可
  481. if consumeRcd.package.isPostpaid:
  482. logger.info("[pay_apps_start_by_32] post paid order = {}, event = {}".format(consumeRcd, self.event_data))
  483. consumeRcd.update(isNormal=True, status=START_DEVICE_STATUS.RUNNING)
  484. # 预支付的情况 考虑要补扣金额
  485. else:
  486. consumeRcd.frozen_payer_balance()
  487. # 纠正订单信息
  488. if consumeRcd.status == START_DEVICE_STATUS.FAILURE:
  489. errorDesc = consumeRcd.description + "(<{}补充扣款>)".format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
  490. consumeRcd.update(isNormal=True, status=START_DEVICE_STATUS.RUNNING, errorDesc=errorDesc)
  491. else:
  492. consumeRcd.update(isNormal=True, status=START_DEVICE_STATUS.RUNNING)
  493. logger.info('weifule({}) pay by event..'.format(order['id']))
  494. set_start_key_status(start_key=consumeRcd.startKey, state=START_DEVICE_STATUS.FINISHED, order_id=str(consumeRcd.id))
  495. if callable(callbackSP):
  496. callbackSP(consumeRcd, order)
  497. self.create_progress_for_socket_order(consumeRcd, order)
  498. def pay_card_start_by_32(self, order):
  499. # 查找卡
  500. logger.debug("[pay_card_start_by_32] order = {}".format(order))
  501. cardNo, cardType = self._auto_parser_card_no(order['card_no']), self._auto_parser_card_type(order['card_type'])
  502. card = self.update_card_dealer_and_type(cardNo, cardType) # type: Card
  503. # 非法卡
  504. if not card or not card.isBinded or card.frozen:
  505. logger.info("[pay_card_start_by_32], not find normal card! order = {}".format(order))
  506. if card and card.is_id_card:
  507. self.device.deviceAdapter.stop_charging_port(order["port"])
  508. return
  509. # extra = [
  510. # {u'设备地址': u'{}'.format(self.device.group.address)},
  511. # {u'设备编号': u'{}'.format(self.device.logicalCode)},
  512. # self._notify_card_title(card, order)
  513. # ]
  514. #
  515. # 创建订单参数
  516. data = {
  517. 'devInfo': {'logicalCode': self.device.logicalCode},
  518. 'userInfo': {'cardId': str(card.id)},
  519. }
  520. # 从policy中构建出套餐参数
  521. startInfo = {
  522. 'sequenceNo': order['id'],
  523. 'startType': StartDeviceType.CARD,
  524. 'port': order['port'],
  525. 'packageId': '',
  526. "name": "刷卡套餐",
  527. "policyType": order['policy']['type'],
  528. "autoRefund": order['policy']['auto_refund'],
  529. "autoStop": order['policy']['auto_stop'],
  530. "billingMethod": order['policy']['billingMethod'],
  531. "rules": order['policy']["rule"]
  532. }
  533. # 预支付的情况 套餐的金额等于付款的金额
  534. if startInfo["billingMethod"] == "prepaid":
  535. startInfo["price"] = RMB.fen_to_yuan(order['policy']['money']) # type:RMB
  536. # 后支付的情况 套餐金额暂时不变
  537. else:
  538. startInfo["price"] = RMB(0) # type:RMB
  539. data["startInfo"] = startInfo
  540. # 根据参数组装订单
  541. try:
  542. with UnifiedCardConsumeOrderManager(**data) as manager:
  543. consumeRcd = manager.buildOrder() # type: ConsumeRecord
  544. except UnifiedConsumeOrderError as ue:
  545. logger.error("[pay_card_start_by_32] order create order error = {}, order = {}".format(ue, order))
  546. self.device.deviceAdapter.stop_charging_port(order["port"])
  547. return
  548. # 预支付的情况下支付
  549. if card.is_id_card:
  550. # 预支付的情况下 进行扣款操作
  551. if not consumeRcd.isPostPaid:
  552. try:
  553. payment = generate_card_payment(consumeRcd)
  554. if payment:
  555. consumeRcd.update_payment(payment)
  556. consumeRcd.frozen_payer_balance()
  557. if not consumeRcd.isPaid:
  558. raise ValueError('')
  559. except Exception as e:
  560. self.device.deviceAdapter.stop_charging_port(order["port"])
  561. logger.exception(e)
  562. result = {
  563. "deviceStartTime": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(order["create_time"]))
  564. }
  565. ConsumeOrderStateEngine(consumeRcd).to_running(result)
  566. start_time_stamp = order.get('create_time')
  567. start_time = datetime.datetime.fromtimestamp(start_time_stamp)
  568. ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  569. lineInfo = ctrInfo.get(str(order['port']), {})
  570. if not lineInfo or lineInfo.get('status') == Const.DEV_WORK_STATUS_IDLE:
  571. lineInfo = {
  572. 'port': str(order['port']),
  573. 'status': Const.DEV_WORK_STATUS_WORKING,
  574. 'order_type': 'card_start',
  575. 'startTime': start_time.strftime('%Y-%m-%d %H:%M:%S'),
  576. 'orderNo': consumeRcd.orderNo,
  577. }
  578. Device.update_port_control_cache(self.device.devNo, lineInfo)
  579. # 预付费流程 *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
  580. def finish_prepaid_process(self, order):
  581. # 中间留着写业务
  582. self._prepaid_analyze_order_info(order)
  583. self._prepaid_calc_refund_coins(order)
  584. # 扫码的退费处理
  585. if order['order_type'] == 'apps_start':
  586. self.prepaid_end_for_app_start(order)
  587. # 刷卡的退费处理
  588. elif order['order_type'] == 'card_start' and order['card_no']:
  589. self.prepaid_end_for_card_start(order)
  590. self._prepaid_analyze_notify_info(order)
  591. def _prepaid_analyze_order_info(self, order):
  592. try:
  593. if order['order_type'] == 'apps_start':
  594. pass
  595. elif order['order_type'] == 'card_start':
  596. pass
  597. except:
  598. logger.info(traceback.format_exc())
  599. def _prepaid_calc_refund_coins(self, order):
  600. auto_refund = order['policy'].get('auto_refund', False)
  601. self.backCoins = RMB(0)
  602. money = VirtualCoin(self.consumeRcd.money)
  603. minFee = VirtualCoin(self.consumeRcd.package.get('minFee') or 0)
  604. # 免费地址:
  605. if self.consumeRcd.is_free:
  606. pass
  607. else:
  608. if self._auto_parser_card_type(order.get('card_type')) == 'IC':
  609. logger.info(
  610. 'IC card is not refundable orderNo=<{}> duration=<{}>'.format(self.consumeRcd.orderNo, self.duration))
  611. return
  612. if self.duration < self.refundProtectionTime:
  613. self.backCoins = money
  614. else:
  615. usedFee = min(VirtualCoin(order['money'] * 0.01), money) # 做一个使用费用的保护 防止使用金额溢出
  616. usedFee = max(usedFee, minFee) # 和最小消费做比较 取大的
  617. if auto_refund:
  618. self.backCoins = money - usedFee
  619. logger.info(
  620. 'REFUND STATUS orderNo=<{}>, auto_refund={}, refundProtectionTime=<{}>, duration=<{}>, backCoins=<{}> minFee=<{}>'.format(
  621. self.consumeRcd.orderNo, auto_refund, self.refundProtectionTime, self.duration, self.backCoins, minFee
  622. ))
  623. def _prepaid_analyze_notify_info(self, order):
  624. # 消费部分
  625. if order['order_type'] == 'apps_start':
  626. if self.consumeDict['policyType'] == 'time':
  627. self.extra.extend([
  628. {u'订购套餐': self.consumeDict['needTime']},
  629. ])
  630. elif self.consumeDict['policyType'] == 'elec':
  631. self.extra.extend([
  632. {u'订购套餐': self.consumeDict['needElec']},
  633. {u'消耗电量': self.consumeDict['elec']},
  634. ])
  635. else:
  636. pass
  637. elif order['order_type'] == 'card_start':
  638. self.extra.append(self._notify_card_title(order))
  639. else:
  640. pass
  641. # 金额部分
  642. if self.consumeRcd.is_free:
  643. self.extra.append({u'付费金额': u'免费使用!'})
  644. else:
  645. if self.backCoins > RMB(0):
  646. self.extra.append({u'使用明细': u'支付{}元,退费{}元'.format(self.consumeRcd.money, self.backCoins)})
  647. else:
  648. self.extra.append({u'使用明细': u'支付{}元'.format(self.consumeRcd.money, self.backCoins)})
  649. if order['order_type'] == 'card_start':
  650. self.extra.append({u'卡片余额': u'{}元'.format(self.card.balance)})
  651. def prepaid_end_for_app_start(self, order):
  652. # 如果需要退款,计算退款数据.
  653. if self.backCoins > RMB(0):
  654. openId = self.consumeRcd.user.openId
  655. lineInfo = {'openId': openId}
  656. refundRMB = RMB(0)
  657. is_temp_package = self.consumeRcd.is_temp_package
  658. if is_temp_package and self.consumeRcd.recharge_record_id:
  659. rechargeRcd = RechargeRecord.objects.filter(id=self.consumeRcd.recharge_record_id,
  660. openId=openId,
  661. devNo=self.device.devNo,
  662. isQuickPay=True,
  663. groupId=self.device.groupId).first()
  664. refundRMB = rechargeRcd.money * (self.backCoins.amount / self.consumeRcd.coin.amount)
  665. lineInfo.update({'rechargeRcdId': self.consumeRcd.recharge_record_id})
  666. self.refund_net_pay(self.consumeRcd.user, lineInfo,
  667. refundRMB, self.backCoins, self.consumeDict, is_temp_package)
  668. def prepaid_end_for_card_start(self, order):
  669. if not self.card: # 离线卡没有绑定或者在线卡被解绑了
  670. return
  671. # 不需要退款,直接返回.在线卡需要退费,离线卡只登记使用记录就OK
  672. if self.backCoins > RMB(0) and self.card.cardType == 'ID':
  673. self.refund_money_for_card(self.backCoins, self.card.id)
  674. self.consumeDict.update({
  675. DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: self.backCoins.mongo_amount
  676. })
  677. # 最后维护一次 订单上显示的卡余额
  678. self.card.reload()
  679. self.consumeDict.update({
  680. 'cardBalance': self.card.balance.mongo_amount
  681. })
  682. # 后付费流程*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
  683. def finishe_postpaid_process(self, order):
  684. self._postpaid_analyze_order_info(order)
  685. self._postpaid_calc_used_fee(order)
  686. # 扫码扣除余额
  687. if order['order_type'] == 'apps_start':
  688. self.postpay_end_for_app_start(order)
  689. # 刷卡的扣除余额
  690. elif order['order_type'] == 'card_start' and order['card_no']:
  691. self.postpay_end_for_card_start(order)
  692. self._postpaid_analyze_notify_info(order)
  693. def _postpaid_analyze_order_info(self, order):
  694. pass
  695. def _postpaid_calc_used_fee(self, order):
  696. self.usedFee = VirtualCoin(order['money'] * 0.01)
  697. # 最小金额处理
  698. minFee = VirtualCoin(self.consumeRcd.package.get('minFee') or 0)
  699. # 免费地址:
  700. if self.consumeRcd.is_free:
  701. self.usedFee = VirtualCoin(0)
  702. else:
  703. # 使用金额不能超过本单的卡余额
  704. if self.duration < self.refundProtectionTime:
  705. self.usedFee = VirtualCoin(0)
  706. else:
  707. if order['order_type'] == 'apps_start':
  708. self.usedFee = max(self.usedFee, minFee)
  709. elif order['order_type'] == 'card_start':
  710. pass
  711. logger.info(
  712. 'REFUND STATUS orderNo=<{}>, isFree={} usedFee={}, refundProtectionTime=<{}>, duration=<{}> minFee=<{}>'.format(
  713. self.consumeRcd.orderNo, self.consumeRcd.is_free, self.usedFee, self.refundProtectionTime,
  714. self.duration, minFee
  715. ))
  716. def _postpaid_analyze_notify_info(self, order):
  717. # 消费部分
  718. if order['order_type'] == 'apps_start':
  719. if self.consumeDict['policyType'] == 'time':
  720. self.extra.extend([
  721. {u'订购套餐': u'{}'.format(self.consumeDict['needTime'])},
  722. ])
  723. elif self.consumeDict['policyType'] == 'elec':
  724. self.extra.extend([
  725. {u'订购套餐': u'{}度'.format(self.consumeDict['needElec'])},
  726. {u'消耗电量': u'{}度'.format(self.consumeDict['elec'])},
  727. ])
  728. # 金额部分
  729. if self.consumeRcd.is_free:
  730. self.extra.append({u'扣费金额': u'免费使用!'})
  731. else:
  732. if self.consumeRcd.status == ConsumeRecord.Status.FINISHED:
  733. if self.consumeRcd.paymentInfo.get('via') == 'virtualCard':
  734. desc = u'(已使用优惠卡券抵扣本次消费)'
  735. else:
  736. desc = u'(已使用账户余额自动结算本次消费)' if self.usedFee > VirtualCoin(0) else ''
  737. else:
  738. desc = u'(您的账户余额已不足以抵扣本次消费,请前往账单中心进行支付)'
  739. self.consumeDict['reason'] += desc
  740. self.extra.append({u'消费金额': u'{}元'.format(self.usedFee)})
  741. self.extra.append(
  742. {u'用户余额': u'{}元'.format(
  743. self.consumeRcd.user.calc_currency_balance(self.device.owner, self.device.group))})
  744. if order['order_type'] == 'card_start':
  745. self.extra.append(self._notify_card_title(order))
  746. if self.consumeDict['policyType'] == 'time':
  747. pass
  748. elif self.consumeDict['policyType'] == 'elec':
  749. self.extra.extend([
  750. {u'消耗电量': '{}度'.format(self.consumeDict['elec'])}
  751. ])
  752. else:
  753. pass
  754. self.extra.append({u'扣费金额': u'{}元'.format(self.usedFee)})
  755. self.extra.append({u'卡片余额': u'{}元'.format(self.card.balance)})
  756. def postpay_end_for_app_start(self, order):
  757. def pay_order():
  758. try:
  759. self.consumeRcd.status = 'running'
  760. self.consumeRcd.attachParas['packageId'] = self.consumeRcd.package.get('packageId')
  761. self.consumeRcd.save()
  762. self.consumeRcd.s_to_e()
  763. except:
  764. pass
  765. # 1 更新订单金额
  766. self.consumeRcd.coin = self.usedFee
  767. self.consumeRcd.money = self.usedFee
  768. self.consumeRcd.servicedInfo = self.consumeDict
  769. self.consumeRcd.save()
  770. # 2 去结算
  771. if self.consumeRcd.is_free:
  772. pass
  773. else:
  774. pay_order()
  775. self.consumeRcd.reload()
  776. # 如果结算成功
  777. if self.consumeRcd.status == ConsumeRecord.Status.FINISHED:
  778. # 如果结算使用的是虚拟卡
  779. if self.consumeRcd.paymentInfo.get('via') == 'virtualCard':
  780. # 虚拟卡抵扣 更新订单金额为0
  781. self.consumeRcd.update(coin=VirtualCoin(0), money=RMB(0))
  782. try:
  783. if "rcdId" in self.consumeRcd.paymentInfo:
  784. consumeRcdId = self.consumeRcd.paymentInfo['rcdId']
  785. else:
  786. consumeRcdId = self.consumeRcd.paymentInfo['itemId']
  787. vCardConsumeRcd = VCardConsumeRecord.objects.get(id=consumeRcdId)
  788. vCard = UserVirtualCard.objects.get(id=vCardConsumeRcd.cardId)
  789. vCard.refund_quota(vCardConsumeRcd, self.duration, 0.0, VirtualCoin(0).mongo_amount)
  790. except:
  791. pass
  792. else:
  793. pass
  794. def postpay_end_for_card_start(self, order):
  795. if not self.card: # 离线卡没有绑定或者在线卡被解绑了
  796. return
  797. if self.card.cardType == 'ID':
  798. self.card.update(dec__balance=self.usedFee)
  799. self.card.reload()
  800. # 维护 订单上显示的卡余额
  801. self.consumeDict.update({
  802. 'cardBalance': self.card.balance.mongo_amount,
  803. DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: self.usedFee.mongo_amount,
  804. })
  805. # 维护一次订单
  806. self.consumeRcd.update(coin=self.usedFee.mongo_amount, money=self.usedFee.mongo_amount)
  807. CardConsumeRecord.objects.filter(orderNo=self.consumeRcd.orderNo).update(
  808. money=self.usedFee.mongo_amount,
  809. balance=self.card.balance.mongo_amount,
  810. finishedTime=self.consumeRcd.finishedTime,
  811. servicedInfo=self.consumeDict
  812. )
  813. else:
  814. # 异常情况 不处理
  815. pass
  816. def pay_coin_start_by_32(self, order):
  817. policy = order.get('policy', {})
  818. coin = int(policy['money'] * 0.01)
  819. if coin > 0:
  820. Accounting.syncOfflineCoin(
  821. self.device,
  822. datetime.datetime.fromtimestamp(order['create_time']).strftime('%Y-%m-%d'), coin)
  823. FluentedEngine().in_put_coins_udp(devNo=self.device.devNo,
  824. ts=int(time.time()),
  825. coins=coin,
  826. mode='uart')
  827. logger.info('devNo=<{}> This coin=<{}> consumption has been recorded!'.format(self.device.devNo, coin))
  828. class WeiFuLeCarProcess(object):
  829. desc_map = {
  830. 0: '订购已用完',
  831. 1: '管理员停止',
  832. 2: '设备故障',
  833. 3: '充电枪拔除',
  834. 4: '紧急停止按钮',
  835. 5: '计量故障',
  836. 6: '功率过载',
  837. 7: '电压过载',
  838. 8: '电流过载',
  839. 9: '超过单次充电最大时长',
  840. 10: '设备启动失败',
  841. 11: '继电器故障',
  842. 12: '刷卡停止',
  843. 13: '用户远程停止',
  844. 14: '经销商远程停止',
  845. 15: '温度超限'
  846. }
  847. CREATED = 'created'
  848. RUNNING = 'running'
  849. WAITING = 'wait'
  850. FINISHED = 'finished'
  851. END = 'end'
  852. TIMEOUT = 'timeout'
  853. FAILURE = 'failure'
  854. def __init__(self, smartbox):
  855. self.devNo = smartbox.device.devNo
  856. self.fun_code = smartbox.event_data.get('fun_code')
  857. self.order_info = smartbox.event_data.get('order')
  858. self.dev = smartbox.device # type: DeviceDict
  859. self.adapter = smartbox.deviceAdapter
  860. self.smartbox = smartbox
  861. def _exec_order_33(self):
  862. start_time = self.order_info.get('exec_time')
  863. start_time = datetime.datetime.fromtimestamp(start_time)
  864. order_id = self.order_info.get('id')
  865. consumeOrder = ConsumeRecord.objects.filter(orderNo=order_id, status__ne=ConsumeRecord.Status.FINISHED).first()
  866. if not consumeOrder:
  867. logger.info('no this order')
  868. return
  869. consumeOrder.update(startTime=start_time)
  870. def apps_start(self):
  871. """
  872. 'order_type' : 'apps_start',
  873. 'money' : 1.2388653318201,
  874. 'id' : '86743505437857520201225084915001',
  875. 'left_money' : 98.76113466818,
  876. 'last_clock' : 607.31,
  877. 'create_time' : 1608857355,
  878. 'time' : 475.525,
  879. 'amount' : 100,
  880. 'elec' : 12388.653318201,
  881. 'exec_time' : 1608857355,
  882. 'status' : 'running',
  883. 'last_ecnt' : 16777128
  884. """
  885. dev_stat = self.order_info.get('status')
  886. order = ConsumeRecord.objects.filter(orderNo=self.order_info['id']).first()
  887. if not order:
  888. logger.info('no this order')
  889. return
  890. if dev_stat == 'running':
  891. self._apps_start_do_record_running(dev_stat, order)
  892. elif dev_stat == 'waiting':
  893. self._apps_start_do_record_waiting(dev_stat, order)
  894. elif dev_stat == 'finished':
  895. self._apps_start_do_record_finished(dev_stat, order)
  896. def _apps_start_do_record_running(self, dev_stat, order):
  897. rechargeRcdId = order.rechargeRcdId or ''
  898. if order.status == self.CREATED: # 让adapter处理
  899. if 'adapter' in self.order_info: # 服务器过来直接处理
  900. order.update(status=self.RUNNING)
  901. else:
  902. if (time.time() - self.order_info.get('create_time', 0)) > 300:
  903. ConsumeRecord.objects.filter(id=order.id, status=self.CREATED).update(status=self.RUNNING, isNormal=True)
  904. elif order.status == self.RUNNING: # 设备上报
  905. start_time = self.order_info.get('exec_time')
  906. start_time = datetime.datetime.fromtimestamp(start_time)
  907. order.update(
  908. status=self.RUNNING,
  909. startTime=start_time
  910. )
  911. elif order.status == self.WAITING: # 33 上来的wait单变running
  912. start_time = self.order_info.get('exec_time')
  913. start_time = datetime.datetime.fromtimestamp(start_time)
  914. order.update(
  915. status=self.RUNNING,
  916. startTime=start_time,
  917. )
  918. elif order.status in [self.TIMEOUT, self.FAILURE]: # 此时已经退费,标记为后支付,不做处理
  919. start_time = self.order_info.get('exec_time') or self.order_info.get('create_time')
  920. port = self.order_info.get('port')
  921. start_time = datetime.datetime.fromtimestamp(start_time)
  922. order.update(
  923. status=self.RUNNING,
  924. coin=VirtualCoin(0),
  925. money=RMB(0),
  926. startTime=start_time,
  927. attachParas__payAfterUse=True,
  928. rechargeRcdId='',
  929. attachParas__last_refund_rechargeRcdId=rechargeRcdId,
  930. isNormal=True,
  931. errorDesc='设备启动已退费, 此单标记为后支付<dev_stat:{}>'.format(dev_stat),
  932. )
  933. consumeOrder = {
  934. 'orderNo': order.orderNo,
  935. 'coin': 0,
  936. 'consumeType': 'postpaid',
  937. }
  938. self.adapter.register_service_progress(order.openId, order.orderNo, port, consumeOrder, order.attachParas)
  939. title = make_title_from_dict([
  940. {u'设备地址': u'{}'.format(self.dev.group.address)},
  941. {u'设备编号': u'{}'.format(self.dev['logicalCode'])},
  942. ])
  943. self.smartbox.notify_user(
  944. order.user.managerialOpenId,
  945. 'dev_start',
  946. **{
  947. 'title': title,
  948. 'things': u'您的汽车充电服务已启动',
  949. 'remark': u'感谢您的支持!',
  950. 'time': start_time.strftime(Const.DATETIME_FMT)
  951. }
  952. )
  953. elif order.status in [self.FINISHED, self.END]:
  954. pass
  955. else:
  956. logger.info('error order status devStat=<{}>, order=<{}>'.format(dev_stat, order.orderNo))
  957. def _apps_start_do_record_waiting(self, dev_stat, order):
  958. # 兼容系统 全改为running单
  959. rechargeRcdId = order.rechargeRcdId or ''
  960. if order.status == self.CREATED: # 启动过程服务器中断停留在
  961. if (time.time() - self.order_info.get('create_time', 0)) > 300:
  962. ConsumeRecord.objects.filter(id=order.id, status=self.CREATED).update(status=self.RUNNING, isNormal=True)
  963. elif order.status == self.RUNNING: # 服务器成功启动置为running
  964. order.update(
  965. status=self.RUNNING
  966. )
  967. elif order.status == self.WAITING: # 重复
  968. pass
  969. elif order.status in [self.TIMEOUT, self.FAILURE]: # 此时已经退费,标记为后支付,不做处理
  970. order.update(
  971. status=self.RUNNING,
  972. coin=VirtualCoin(0),
  973. money=RMB(0),
  974. attachParas__payAfterUse=True,
  975. rechargeRcdId='',
  976. attachParas__last_refund_rechargeRcdId=rechargeRcdId,
  977. isNormal=True,
  978. errorDesc='设备启动已退费, 此单标记为后支付<dev_stat:{}>'.format(dev_stat),
  979. )
  980. consumeOrder = {
  981. 'orderNo': order.orderNo,
  982. 'coin': 0,
  983. 'consumeType': 'postpaid',
  984. }
  985. self.adapter.register_service_progress(order.openId, order.orderNo, consumeOrder, order.attachParas)
  986. elif order.status in [self.FINISHED, self.END]:
  987. pass
  988. def _apps_start_do_record_finished(self, dev_stat, order):
  989. if order.status == self.CREATED: # 启动过程中服务器出错
  990. ConsumeRecord.objects.filter(id=order.id, status=self.CREATED).update(status=self.RUNNING, isNormal=True)
  991. order.reload()
  992. self.pre_pay(order)
  993. elif order.status in [self.RUNNING, self.WAITING]: # 正常状态的 或者修正状态的单
  994. payAfterUse = order.attachParas.get('payAfterUse')
  995. if payAfterUse:
  996. self.after_pay(order)
  997. else:
  998. self.pre_pay(order)
  999. elif order.status == self.TIMEOUT: # 设备启动后,100事件先上(该状态为running),210回复改为timeout,后付费处理
  1000. order.update(
  1001. status=self.RUNNING,
  1002. coin=VirtualCoin(0),
  1003. money=RMB(0),
  1004. attachParas__payAfterUse=True,
  1005. rechargeRcdId='',
  1006. attachParas__last_refund_rechargeRcdId=order.rechargeRcdId,
  1007. isNormal=True,
  1008. errorDesc='设备启动已退费, 此单标记为后支付<dev_stat:{}>'.format(dev_stat),
  1009. )
  1010. order.reload()
  1011. self.after_pay(order)
  1012. elif order.status == self.FAILURE: # 状态位置不处理
  1013. pass
  1014. elif order.status in [self.FINISHED, self.END]:
  1015. logger.info('do apps finished --> no order found or repeated submit orderNo:{}'.format(order.orderNo))
  1016. return
  1017. logger.info('apps start event orderNo:{} is over'.format(order))
  1018. def pre_pay(self, order):
  1019. openId = order.openId
  1020. # leftMoney = RMB.fen_to_yuan(self.order_info.get('left_money', 0)) # 传过来的这个值有问题
  1021. amount = RMB.fen_to_yuan(self.order_info.get('amount', 0))
  1022. usedMoney = RMB.fen_to_yuan(self.order_info.get('money'))
  1023. leftMoney = amount - usedMoney
  1024. used_time = round(self.order_info.get('time') / 60.0, 2)
  1025. used_elec = self.order_info.get('elec') / 10.0 ** 6
  1026. reason = self.desc_map.get(self.order_info.get('cause'))
  1027. execTimeStamp = self.order_info.get('exec_time')
  1028. finishedTimeStamp = self.order_info.get('over_time')
  1029. port = self.order_info.get('port')
  1030. finishedTime = datetime.datetime.fromtimestamp(finishedTimeStamp)
  1031. if not execTimeStamp:
  1032. startTime = finishedTime
  1033. else:
  1034. startTime = datetime.datetime.fromtimestamp(execTimeStamp)
  1035. status = self.order_info.get('status')
  1036. consumeDict = {
  1037. DEALER_CONSUMPTION_AGG_KIND.DURATION: '%d' % used_time,
  1038. DEALER_CONSUMPTION_AGG_KIND.ELEC: '%.2f' % used_elec,
  1039. DEALER_CONSUMPTION_AGG_KIND.COIN: amount.mongo_amount,
  1040. 'reason': reason
  1041. }
  1042. order.status = status
  1043. order.startTime = startTime
  1044. order.finishedTime = finishedTime
  1045. order.save()
  1046. user = order.user
  1047. group = Group.get_group(self.dev['groupId'])
  1048. extra = [
  1049. # {u'订单号': '{}'.format(order.orderNo)},
  1050. {u'本次使用时长': '{}(分钟)'.format(used_time)},
  1051. ]
  1052. self.smartbox.notify_user_service_complete(
  1053. service_name='充电',
  1054. openid=user.managerialOpenId if user else '',
  1055. port=port,
  1056. address=group['address'],
  1057. reason=reason,
  1058. finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  1059. extra=extra
  1060. )
  1061. if order.attachParas.get('is_auto_refund', False):
  1062. recharge_record = None
  1063. if order.rechargeRcdId:
  1064. recharge_record = RechargeRecord.objects(
  1065. id=order.rechargeRcdId, isQuickPay=True).first() # type: RechargeRecord
  1066. if recharge_record:
  1067. refundMoneyRecord = RefundMoneyRecord.objects.filter(openId=openId,
  1068. rechargeObjId=recharge_record.id).first()
  1069. if not refundMoneyRecord:
  1070. self._do_refund_money(recharge_record, order, leftMoney, consumeDict)
  1071. else:
  1072. self._do_refund_coin(order, leftMoney, consumeDict)
  1073. order.update_service_info(consumeDict)
  1074. ServiceProgress.objects.filter(device_imei=self.devNo, weifuleOrderNo=order.orderNo).update(
  1075. isFinished=True, finished_time=int(time.time())
  1076. )
  1077. self.adapter.async_update_portinfo_from_dev()
  1078. def after_pay(self, order):
  1079. """
  1080. 只针对设备下发之后,没有回复的故障单为后付费
  1081. :return:
  1082. """
  1083. used_money = RMB.fen_to_yuan(self.order_info.get('money', 0))
  1084. amount = RMB.fen_to_yuan(self.order_info.get('amount', 0))
  1085. used_time = round(self.order_info.get('time') / 60.0, 2)
  1086. used_elec = self.order_info.get('elec') / 10.0 ** 6
  1087. reason = self.desc_map.get(self.order_info.get('cause'))
  1088. execTimeStamp = self.order_info.get('exec_time')
  1089. finishedTimeStamp = self.order_info.get('over_time')
  1090. port = self.order_info.get('port')
  1091. finishedTime = datetime.datetime.fromtimestamp(finishedTimeStamp)
  1092. if not execTimeStamp:
  1093. startTime = finishedTime
  1094. else:
  1095. startTime = datetime.datetime.fromtimestamp(execTimeStamp)
  1096. consumeDict = {
  1097. DEALER_CONSUMPTION_AGG_KIND.DURATION: '%d' % used_time,
  1098. DEALER_CONSUMPTION_AGG_KIND.ELEC: '%.2f' % used_elec,
  1099. 'reason': reason,
  1100. }
  1101. order.startTime = startTime
  1102. order.finishedTime = finishedTime
  1103. coins = VirtualCoin(order.package.get('coins', 0))
  1104. price = RMB(order.package.get('price'))
  1105. ratio = Ratio(float(used_money) / float(amount))
  1106. order.coin = coins * ratio
  1107. order.money = price * ratio
  1108. if order.money == RMB(0):
  1109. order.coin = VirtualCoin(0)
  1110. order.save()
  1111. extra = [
  1112. # {u'订单号': '{}'.format(order.orderNo)},
  1113. {u'本次使用时长': '{}(分钟)'.format(used_time)},
  1114. ]
  1115. if order.money > RMB(0):
  1116. self._pay_by_coins(order)
  1117. else:
  1118. order.update(status=self.FINISHED)
  1119. order.reload()
  1120. if order.status == self.FINISHED:
  1121. extra.append({u'本单消费金额': '{}金币(已使用账户余额自动结算本次消费)'.format(order.coin.mongo_amount)})
  1122. consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.COIN: order.coin.mongo_amount})
  1123. else:
  1124. extra.append({u'本单消费金额': '{}元((您的账户余额不足以抵扣本次消费,请前往账单中心进行支付))'.format(order.money.mongo_amount)})
  1125. consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: order.money.mongo_amount})
  1126. self.smartbox.notify_user_service_complete(
  1127. service_name='充电结束',
  1128. openid=order.user.managerialOpenId,
  1129. port=str(port),
  1130. address=self.dev.group['address'],
  1131. reason=reason,
  1132. finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  1133. extra=extra
  1134. )
  1135. order.update_service_info(consumeDict)
  1136. ServiceProgress.objects.filter(device_imei=self.devNo, weifuleOrderNo=order.orderNo).update(
  1137. isFinished=True, finished_time=int(time.time())
  1138. )
  1139. def _pay_by_coins(self, order):
  1140. try:
  1141. order.s_to_e()
  1142. except Exception:
  1143. return False
  1144. def _do_refund_money(self, recharge_record, consumeOrder, leftMoney, consumeDict):
  1145. logger.info('now do refund money!')
  1146. coins = consumeOrder.coin
  1147. openId = consumeOrder.openId
  1148. price = recharge_record.money
  1149. user = consumeOrder.user
  1150. refundMoney = RMB(price) * Ratio(float(leftMoney) / float(coins))
  1151. if refundMoney > RMB(price):
  1152. refundMoney = RMB(price)
  1153. if refundMoney > RMB(0):
  1154. self.smartbox.refund_net_pay(user, {'rechargeRcdId': str(recharge_record.id), 'openId': user.openId},
  1155. refundMoney,
  1156. VirtualCoin(0), consumeDict, True)
  1157. title = make_title_from_dict([
  1158. {u'设备地址': u'{}'.format(self.dev.group.address)},
  1159. {u'设备编号': u'{}'.format(self.dev['logicalCode'])},
  1160. ])
  1161. self.smartbox.notify_user(self.smartbox.get_managerialOpenId_by_openId(openId), 'refund_coins', **{
  1162. 'title': title,
  1163. 'backCount': u'金额:%s' % refundMoney,
  1164. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')})
  1165. logger.info('refund cash is successful refundMoney:{}'.format(refundMoney))
  1166. def _do_refund_coin(self, consumeOrder, leftMoney, consumeDict):
  1167. logger.info('now do refund coin !')
  1168. coins = consumeOrder.coin
  1169. openId = consumeOrder.openId
  1170. user = consumeOrder.user
  1171. backCoins = VirtualCoin(coins) * Ratio(float(leftMoney) / float(coins))
  1172. if backCoins > VirtualCoin(coins):
  1173. backCoins = VirtualCoin(coins)
  1174. if backCoins > RMB(0):
  1175. self.smartbox.refund_net_pay(user, {'openId': openId}, RMB(0), backCoins, consumeDict, False)
  1176. title = make_title_from_dict([
  1177. {u'设备地址': u'{}'.format(self.dev.group.address)},
  1178. {u'设备编号': u'{}'.format(self.dev['logicalCode'])},
  1179. ])
  1180. self.smartbox.notify_user(self.smartbox.get_managerialOpenId_by_openId(openId), 'refund_coins', **{
  1181. 'title': title,
  1182. 'backCount': u'金币:%s' % backCoins,
  1183. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')})
  1184. # ---------------------刷卡处理-------------------------
  1185. def card_start(self):
  1186. status = self.order_info.get('status')
  1187. if self.fun_code == 32:
  1188. self._card_start_do_record_running_32()
  1189. elif self.fun_code == 34:
  1190. self._card_start_do_record_finished_34()
  1191. elif self.fun_code == 33:
  1192. self._exec_order_33()
  1193. def card_refund(self):
  1194. """
  1195. 离线卡:上报退费 做一次卡余额同步
  1196. """
  1197. status = self.order_info.get('status')
  1198. if self.fun_code == 32:
  1199. if status == 'finished':
  1200. self._card_refund_do_record_finished()
  1201. else:
  1202. pass
  1203. elif self.fun_code == 34:
  1204. if status == 'finished':
  1205. pass
  1206. # self._card_refund_do_record_finished()
  1207. else:
  1208. pass
  1209. def _card_start_do_record_running_32(self):
  1210. # used_money = RMB.fen_to_yuan(self.order_info.get('money', 0))
  1211. # left_money = RMB.fen_to_yuan(self.order_info.get('left_money'))
  1212. amount = RMB.fen_to_yuan(self.order_info.get('amount', 0))
  1213. card_balance = RMB.fen_to_yuan(self.order_info.get('balance'))
  1214. start_time_stamp = self.order_info.get('create_time')
  1215. start_time = datetime.datetime.fromtimestamp(start_time_stamp)
  1216. # used_time = self.order_info.get('time') / 60
  1217. # used_elec = self.order_info.get('elec') / 10 ** 6
  1218. # status = self.order_info.get('status')
  1219. order_id = self.order_info.get('id')
  1220. card_no = self.order_info.get('card_no')
  1221. port = self.order_info.get('port')
  1222. card = Card.objects.filter(cardNo=card_no, cardType='IC', dealerId=self.dev.ownerId).first()
  1223. if not card:
  1224. logger.info('no this card cardNo:{}'.format(card_no))
  1225. return
  1226. # 做一次订单号去重
  1227. order = ConsumeRecord.objects.filter(orderNo=order_id).first()
  1228. if order:
  1229. logger.info('order already exists cardNo:{}, order_id'.format(card_no, order_id))
  1230. return
  1231. servicedInfo = {
  1232. 'cardNo': card_no,
  1233. 'port': port,
  1234. 'cardBalance': card_balance.mongo_amount
  1235. }
  1236. attachParas = {
  1237. 'orderNo': order_id,
  1238. 'chargeIndex': str(port)
  1239. }
  1240. # 记录卡消费记录以及消费记录
  1241. orderNo, cardOrderNo = self.smartbox.record_consume_for_card(card, amount, servicedInfo=servicedInfo,
  1242. attachParas=attachParas)
  1243. consumeOrder = {
  1244. 'orderNo': orderNo,
  1245. 'cardOrderNo': cardOrderNo,
  1246. 'coin': str(amount),
  1247. 'consumeType': 'card',
  1248. }
  1249. self._register_card_service_progress(card, port, consumeOrder, order_id)
  1250. title = make_title_from_dict([
  1251. {u'设备地址': u'{}'.format(self.dev.group.address)},
  1252. {u'设备编号': u'{}'.format(self.dev['logicalCode'])},
  1253. {u'实体卡': u'{}--No:{}'.format(card.cardName or card.nickName, card.cardNo)},
  1254. {u'本次消费': u'{} 元'.format(amount)},
  1255. {u'卡余额': u'{} 元'.format(card_balance)},
  1256. ])
  1257. self.smartbox.notify_user(
  1258. card.managerialOpenId,
  1259. 'dev_start',
  1260. **{
  1261. 'title': title,
  1262. 'things': u'刷卡消费',
  1263. 'remark': u'感谢您的支持!',
  1264. 'time': start_time.strftime(Const.DATETIME_FMT)
  1265. }
  1266. )
  1267. def _card_start_do_record_finished_34(self):
  1268. used_time = round(self.order_info.get('time') / 60.0, 2)
  1269. finishedTimeStamp = self.order_info.get('over_time')
  1270. finishedTime = datetime.datetime.fromtimestamp(finishedTimeStamp)
  1271. execTimeStamp = self.order_info.get('exec_time')
  1272. if not execTimeStamp:
  1273. startTime = finishedTime
  1274. else:
  1275. startTime = datetime.datetime.fromtimestamp(execTimeStamp)
  1276. reason = self.desc_map.get(self.order_info.get('cause'))
  1277. card_no = self.order_info.get('card_no')
  1278. used_elec = self.order_info.get('elec') / 10 ** 6
  1279. card_balance = RMB.fen_to_yuan(self.order_info.get('balance'))
  1280. amount = RMB.fen_to_yuan(self.order_info.get('amount'))
  1281. port = self.order_info.get('port')
  1282. order_id = self.order_info.get('id')
  1283. # left_money = RMB.fen_to_yuan(self.order_info.get('left_money')) #这个值有问题
  1284. used_money = RMB.fen_to_yuan(self.order_info.get('money'))
  1285. left_money = amount - used_money
  1286. consumeOrder = ConsumeRecord.objects.filter(devNo=self.devNo, orderNo=order_id,
  1287. status__ne=ConsumeRecord.Status.FINISHED).first()
  1288. if not consumeOrder:
  1289. logger.info('do card finished --> no order found or repeated submit orderNo:{}'.format(order_id))
  1290. return
  1291. openId = consumeOrder.openId
  1292. consumeOrder.servicedInfo.update({
  1293. DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: left_money.mongo_amount,
  1294. DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: amount.mongo_amount,
  1295. DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: (amount - left_money).mongo_amount,
  1296. DEALER_CONSUMPTION_AGG_KIND.DURATION: '%d' % used_time,
  1297. DEALER_CONSUMPTION_AGG_KIND.ELEC: '%.2f' % used_elec,
  1298. 'cardBalance': card_balance.mongo_amount,
  1299. 'reason': reason,
  1300. 'cardNo': card_no
  1301. })
  1302. consumeOrder.status = self.FINISHED
  1303. consumeOrder.startTime = startTime
  1304. consumeOrder.finishedTime = finishedTime
  1305. consumeOrder.save()
  1306. ServiceProgress.update_progress_and_consume_rcd(
  1307. self.dev['ownerId'],
  1308. {
  1309. 'open_id': openId,
  1310. 'device_imei': self.devNo,
  1311. 'isFinished': False,
  1312. 'weifuleOrderNo': order_id,
  1313. },
  1314. consumeOrder.servicedInfo,
  1315. progressDict={'isFinished': True, 'finished_time': finishedTimeStamp}
  1316. )
  1317. user = consumeOrder.user
  1318. group = Group.get_group(self.dev['groupId'])
  1319. extra = [
  1320. # {u'订单号': '{}'.format(consumeOrder.orderNo)},
  1321. {u'本次使用时长': '{}(分钟)'.format(used_time)},
  1322. {u'卡片余额': '{}(元)'.format(card_balance)},
  1323. ]
  1324. self.smartbox.notify_user_service_complete(
  1325. service_name='刷卡充电',
  1326. openid=user.managerialOpenId if user else '',
  1327. port=str(port),
  1328. address=group['address'],
  1329. reason=reason,
  1330. finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  1331. extra=extra
  1332. )
  1333. logger.info('card start event orderNo:{} is over'.format(order_id))
  1334. self.adapter.async_update_portinfo_from_dev()
  1335. def _register_card_service_progress(self, card, port, consumeOrder, weifuleOrderNo=None, attachParas=None):
  1336. return ServiceProgress.objects.create(
  1337. open_id=card.openId,
  1338. device_imei=self.devNo,
  1339. devTypeCode=self.dev.devTypeCode,
  1340. port=port,
  1341. cardId=str(card.id),
  1342. attachParas=attachParas if attachParas else {},
  1343. start_time=int(time.time()),
  1344. finished_time=int(time.time() + 24 * 60 * 60),
  1345. consumeOrder=consumeOrder if consumeOrder else {},
  1346. weifuleOrderNo=weifuleOrderNo,
  1347. ).id
  1348. def _card_refund_do_record_finished(self):
  1349. card_no = self.order_info.get('card_no')
  1350. card_balance = RMB.fen_to_yuan(self.order_info.get('balance'))
  1351. refundMoney = RMB.fen_to_yuan(self.order_info.get('refund'))
  1352. finishedTime = self.order_info.get('over_time')
  1353. finishedTime = datetime.datetime.fromtimestamp(finishedTime).strftime('%Y-%m-%d %H:%M:%S')
  1354. card = self.smartbox.update_card_dealer_and_type(card_no, cardType='IC', balance=card_balance)
  1355. if card:
  1356. logger.info('Card balance sync completed, cardNo:{} , refundMoney:{} ,finishedTime:{}'.format(
  1357. card_no, refundMoney.mongo_amount, finishedTime))
  1358. title = make_title_from_dict([
  1359. {u'设备地址': u'{}'.format(self.dev.group.address)},
  1360. {u'设备编号': u'{}'.format(self.dev['logicalCode'])},
  1361. {u'实体卡': u'{}--No:{}'.format(card.cardName, card.cardNo)},
  1362. {u'卡余额': u'{} 元'.format(card_balance)},
  1363. ])
  1364. self.smartbox.notify_user(
  1365. card.managerialOpenId,
  1366. 'refund_coins',
  1367. **{
  1368. 'title': title,
  1369. 'backCount': u'%s 元' % refundMoney,
  1370. 'finishTime': finishedTime
  1371. }
  1372. )
  1373. else:
  1374. logger.info(
  1375. 'Card balance sync failed, cardNo:{} , refundMoney:{} ,finishedTime:{}'.format(
  1376. card_no, refundMoney.mongo_amount, finishedTime))