views2.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. # coding=utf-8
  2. import logging
  3. from django.http import JsonResponse
  4. from django.views.generic import View
  5. from typing import TYPE_CHECKING
  6. import simplejson as json
  7. from voluptuous import MultipleInvalid
  8. from apilib.utils_sys import MemcachedLock
  9. from apps.web.constant import Const, START_DEVICE_STATUS
  10. from apps.web.core import ROLE
  11. from apps.web.core.exceptions import ServiceException
  12. from apps.web.core.services import wrapper_start_device, StartDeviceEngine
  13. from apps.web.core.utils import JsonOkResponse, JsonErrorResponse, async_operation_no_catch, DefaultJsonErrorResponse
  14. from apps.web.device.models import DeviceType
  15. from apps.web.exceptions import UnifiedConsumeOrderError
  16. from apps.web.user.models import ConsumeRecord, UserBalanceLog, Card, CardBalanceLog
  17. from apps.web.user.utils import get_consume_order
  18. from apps.web.user.utils2 import UnifiedConsumeOrderManager, ConsumeOrderStateEngine, get_paginate
  19. from apps.web.user.validator2 import unifiedConsumeOrderSchema, startConsumeOrderSchema
  20. from apps.web.utils import error_tolerate, permission_required, get_start_key_status
  21. if TYPE_CHECKING:
  22. from django.core.handlers.wsgi import WSGIRequest
  23. logger = logging.getLogger(__name__)
  24. @error_tolerate(logger=logger, nil=JsonErrorResponse(description=u"预下单错误,请刷新页面重试"))
  25. @permission_required(ROLE.myuser)
  26. def unifiedOrder(request): # type:(WSGIRequest) -> JsonResponse
  27. """
  28. 创建消费订单
  29. """
  30. payload = json.loads(request.body)
  31. try:
  32. data = unifiedConsumeOrderSchema(payload)
  33. except MultipleInvalid as me:
  34. return JsonErrorResponse(description=u"启动参数校验异常【】".format(me.path[0]))
  35. # 解析支付参数 创建支付环境
  36. try:
  37. with UnifiedConsumeOrderManager(**data) as manager:
  38. order = manager.buildOrder()
  39. except UnifiedConsumeOrderError as ue:
  40. return JsonErrorResponse(description=ue.message)
  41. ConsumeOrderStateEngine(order).to_wait_confirm()
  42. return JsonOkResponse(payload={
  43. "orderNo": order.orderNo,
  44. "price": order.price,
  45. "isFree": order.isFree,
  46. "isPostPaid": order.isPostPaid,
  47. "logicalCode": order.logicalCode,
  48. "package": order.package.showDict
  49. })
  50. @error_tolerate(logger=logger, nil=JsonErrorResponse(description=u"获取预下单错误,请刷新页面重试"))
  51. @permission_required(ROLE.myuser)
  52. def getUnifiedOrder(request):
  53. orderNo = request.GET.get("orderNo")
  54. order = get_consume_order(orderNo) # type: ConsumeRecord
  55. if not order:
  56. return JsonErrorResponse(u"获取订单失败,请刷新页面重试")
  57. if order.status != order.Status.WAIT_CONF:
  58. return JsonErrorResponse(u"订单状态异常,请重新扫码下单")
  59. return JsonOkResponse(payload={
  60. "orderNo": order.orderNo,
  61. "price": order.price,
  62. "isFree": order.isFree,
  63. "isPostPaid": order.isPostPaid,
  64. "logicalCode": order.logicalCode,
  65. "package": order.package.showDict
  66. })
  67. @error_tolerate(logger=logger, nil=JsonErrorResponse(description=u"启动设备异常,请刷新页面重试"))
  68. @permission_required(ROLE.myuser)
  69. def startAction(request):
  70. """
  71. 启动设备
  72. """
  73. payload = json.loads(request.body)
  74. try:
  75. data = startConsumeOrderSchema(payload)
  76. except MultipleInvalid as me:
  77. return JsonErrorResponse(description=u"启动参数校验异常【】".format(me.path[0]))
  78. order = get_consume_order(data["orderNo"]) # type: ConsumeRecord
  79. if not order:
  80. logger.warning('[_start_device ERROR] cannot get order, order = {}'.format(data))
  81. return JsonErrorResponse(description=u"启动参数校验异常【订单查询失败】")
  82. # 启动锁,防止用户对同一笔订单重复下单并且支付
  83. lockKey = order.startLockKey
  84. logger.info(
  85. '[_start_device] user({}) on device(devNo={}, port={}) lockKey={}, order={}'.format(
  86. order.openId, order.devNo, order.port, lockKey, order
  87. )
  88. )
  89. locker = MemcachedLock(key=lockKey, value='1', expire=180)
  90. if not locker.acquire():
  91. logger.error(
  92. '[_start_device ERROR] cannot get device lock<{}>, openId={}, devNo={}, port={}'.format(lockKey, order.openId, order.devNo, order.port)
  93. )
  94. return JsonErrorResponse(description=u'订单正在运行中,请刷新界面查看订单详情')
  95. # 订单的状态锁住 防止用户对同一笔订单重复支付
  96. try:
  97. proxy = StartDeviceEngine(order) # type: StartDeviceEngine
  98. # 检查订单的支付情况 更多的是余额检验 检验完成之后 根据订单的支付选择 决定是否生成支付信息并添加
  99. paymentInfo = proxy.get_payment_info()
  100. paymentInfo and order.update_payment(paymentInfo)
  101. except ServiceException as se:
  102. ConsumeOrderStateEngine(order).to_failure(se.result["description"])
  103. return JsonResponse(se.result)
  104. # 这个地方只负责将设备的启动传递到新的线程 对于设备启动的结果实际上是未知的
  105. release_locker = True
  106. try:
  107. async_operation_no_catch(wrapper_start_device, locker, proxy)
  108. release_locker = False
  109. except ServiceException as e:
  110. logger.info("[[_start_device ERROR] service exception = {}]".format(e.result))
  111. return JsonResponse(e.result)
  112. except Exception as e:
  113. logger.exception('[_start_device ERROR](order={}) error={}'.format(order, e))
  114. return JsonResponse({'result': 0, 'description': u'系统开小差了,请您重新试试吧', 'payload': {}})
  115. else:
  116. return JsonResponse({'result': 1, 'description': u'等待设备启动', 'payload': {
  117. 'outTradeNo': order.orderNo, 'orderType': 'consume', 'adShow': order.owner.ad_show
  118. }})
  119. finally:
  120. logger.debug('release_locker = {}'.format(release_locker))
  121. release_locker and locker.release()
  122. @error_tolerate(logger=logger, nil=JsonErrorResponse(description=u"获取服务异常"))
  123. @permission_required(ROLE.myuser)
  124. def getCurrentUse(request): # type: (WSGIRequest) -> JsonResponse
  125. openId = request.user.openId
  126. orderId = request.GET.get("orderId")
  127. pageIndex = int(request.GET.get("pageIndex", 1))
  128. pageSize = int(request.GET.get("pageSize", 10))
  129. query = ConsumeRecord.objects.filter(
  130. openId=openId,
  131. status__in=[
  132. ConsumeRecord.Status.RUNNING, ConsumeRecord.Status.END
  133. ]
  134. )
  135. if orderId:
  136. query = query.filter(id=orderId)
  137. dataList = list()
  138. for order in query.skip((pageIndex-1)*pageSize).limit(pageSize): # type: ConsumeRecord
  139. device = order.device
  140. data = {
  141. 'startTime': order.service.deviceStartTime,
  142. 'address': order.address,
  143. 'groupName': order.groupName,
  144. 'devType': order.devTypeName,
  145. 'devTypeCode': order.devTypeCode,
  146. 'logicalCode': order.logicalCode,
  147. 'status': Const.DEV_WORK_STATUS_WORKING,
  148. 'devNo': order.devNo,
  149. 'majorDeviceType': device.majorDeviceType,
  150. 'port': order.port
  151. }
  152. # 添加功率曲线
  153. if 'show_PG_to_user' in device.owner.features and device.support_power_graph and order.port:
  154. data.update({'showPG': True, 'port': order.port})
  155. # 添加各种按钮
  156. data.update(DeviceType.get_services_button(device['devType']['id']))
  157. # 添加端口的使用详情
  158. curInfo = device.deviceAdapter.get_port_using_detail(order.port, {})
  159. data.update(curInfo)
  160. dataList.append(data)
  161. return JsonOkResponse(payload={
  162. 'total': pageSize * pageIndex,
  163. "dataList": dataList
  164. }
  165. )
  166. class GetCurrentOrder(View):
  167. @staticmethod
  168. def _get_response(orderProcessing, succeeded, desc='', record = None):
  169. record = record or {}
  170. return JsonOkResponse(
  171. payload = {
  172. 'orderProcessing': orderProcessing,
  173. 'succeeded': succeeded,
  174. 'desc': desc,
  175. 'record': record
  176. }
  177. )
  178. def _get_not_yet_response(self):
  179. return self._get_response(orderProcessing=True, succeeded=None)
  180. def _get_stop_polling_unknown(self, desc=None):
  181. desc = desc or u"系统异常"
  182. return self._get_response(orderProcessing=False, succeeded=False, desc=desc)
  183. def _get_polling_finished_failed(self, desc, record=None):
  184. return self._get_response(orderProcessing=False, succeeded=False, desc=desc, record=record)
  185. def _get_polling_finished_success(self, desc, record):
  186. return self._get_response(orderProcessing=False, succeeded=True, desc=desc, record=record)
  187. def get(self, request):
  188. orderNo = request.GET.get("startKey")
  189. exp = request.GET.get("exp", 0)
  190. start_key_status = get_start_key_status(orderNo)
  191. # 先查找失败的 如果订单状态为失败 直接返回不用访问数据库
  192. if start_key_status:
  193. state = start_key_status['state']
  194. if state in [START_DEVICE_STATUS.FAILURE]:
  195. return self._get_polling_finished_failed(
  196. start_key_status['reason']
  197. )
  198. # 其余的状态
  199. if state not in [START_DEVICE_STATUS.FINISHED]:
  200. if exp < 2 * 60 * 1000:
  201. return self._get_not_yet_response()
  202. order = get_consume_order(orderNo)
  203. if not order:
  204. return self._get_polling_finished_failed(u'订单查询失败')
  205. if order.status == order.Status.WAIT_CONF:
  206. return self._get_not_yet_response()
  207. # 不需要根据状态进行判断了 直接判断设备有无启动即可
  208. if order.service.deviceStartTime:
  209. return self._get_polling_finished_success(
  210. desc=u'您已成功启动设备。如果有疑问,请点击右下角"设备无反应"按钮',
  211. record={'coins': order.price, 'detailLink': order.detail_link}
  212. )
  213. else:
  214. return self._get_polling_finished_failed(
  215. desc=order.description,
  216. record={'coins': order.price, 'detailLink': order.detail_link}
  217. )
  218. @error_tolerate(logger=logger, nil=JsonErrorResponse(description=u"获取服务异常"))
  219. @permission_required(ROLE.myuser)
  220. def getUserBalanceCharge(request):
  221. user = request.user
  222. pageIndex = int(request.GET.get("pageIndex", 1))
  223. pageSize = int(request.GET.get("pageSize", 10))
  224. logs = UserBalanceLog.get_logs(user, pageIndex=pageIndex, pageSize=pageSize)
  225. dataList = [_.to_dict() for _ in logs.order_by("-id")]
  226. return JsonOkResponse(payload={
  227. "total": get_paginate(dataList, pageSize=pageSize, pageIndex=pageIndex),
  228. "dataList": dataList
  229. })
  230. @error_tolerate(logger=logger, nil=JsonErrorResponse(description=u"获取服务异常"))
  231. @permission_required(ROLE.myuser, ROLE.dealer)
  232. def getCardBalanceCharge(request):
  233. cardId = request.GET.get("cardId")
  234. pageIndex = int(request.GET.get("pageIndex", 1))
  235. pageSize = int(request.GET.get("pageSize", 10))
  236. card = Card.objects.filter(id=cardId).first()
  237. if not card:
  238. return JsonErrorResponse(description=u"获取服务异常 查询卡失败")
  239. logs = CardBalanceLog.get_logs(card, pageIndex=pageIndex, pageSize=pageSize)
  240. dataList = [_.to_dict() for _ in logs.order_by("-id")]
  241. return JsonOkResponse(payload={
  242. "total": get_paginate(dataList, pageSize=pageSize, pageIndex=pageIndex),
  243. "dataList": dataList
  244. })