tasks.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import requests
  5. from celery.utils.log import get_task_logger
  6. from django.conf import settings
  7. from mongoengine import DoesNotExist
  8. from typing import Optional, TYPE_CHECKING
  9. from apps.web.agent.models import Agent
  10. from apps.web.common.transaction.pay import PayManager
  11. from apps.web.common.transaction.refund import RefundManager
  12. from apps.web.constant import Const
  13. from apps.web.core.bridge.wechat import WechatClientProxy
  14. from apps.web.core.helpers import ActionDeviceBuilder
  15. from apps.web.core.models import WechatUserManagerApp, WechatUserSubscribeManagerApp
  16. from apps.web.core.payment import PaymentGateway
  17. from apps.web.dealer.models import Dealer, VirtualCard
  18. from apps.web.device.models import Group, Device
  19. from apps.web.helpers import get_wechat_user_manager_mp_proxy, get_wechat_user_sub_manager_mp_proxy, \
  20. get_wechat_user_messager_app
  21. from apps.web.promotion.models import InsuranceOrder, Insurance
  22. from apps.web.user.models import RechargeRecord, UserVirtualCard, RefundMoneyRecord
  23. from apps.web.user.transaction import post_pay
  24. from apps.web.user.transaction_deprecated import refund_post_pay
  25. from apps.web.utils import concat_front_end_url
  26. if TYPE_CHECKING:
  27. from apps.web.common.transaction.pay import PayRecordPoller
  28. from apps.web.user.models import MyUser
  29. from apps.web.core.payment.type_checking import PaymentGatewayT
  30. logger = get_task_logger('user.tasks')
  31. def report_to_user_via_wechat(openId, dealerId, templateName, url = None, **kwargs):
  32. if not settings.WECHAT_PUSH_ENABLED:
  33. logger.info('WECHAT_PUSH_ENABLED is false, you are probably in a dev/testing env')
  34. else:
  35. dealer = Dealer.objects(id = str(dealerId)).first() # type: Optional[Dealer]
  36. if not dealer:
  37. return {'info': 'dealer does not exist, id=%s' % (dealerId,)}
  38. agent = Agent.objects(id = str(dealer.agentId)).first() # type: Optional[Agent]
  39. if not agent:
  40. return {'info': 'agent does not exist, id=%s' % (dealer.agentId,)}
  41. try:
  42. wechat_mp_proxy = get_wechat_user_sub_manager_mp_proxy(agent)
  43. wechat_mp_proxy.publish(openId = openId, templateName = templateName, url = url, **kwargs)
  44. except Exception as e:
  45. logger.info('wechat_mp_proxy.publish is error=<{}>'.format(e))
  46. try:
  47. wechat_mp_proxy = get_wechat_user_manager_mp_proxy(agent)
  48. wechat_mp_proxy.notify(openId = openId, templateName = templateName, url = url, **kwargs)
  49. except Exception as e:
  50. logger.info('wechat_mp_proxy.notify is error=<{}>'.format(e))
  51. def send_msg_to_user_via_wechat(productId, openId, templateName, url = None, **kwargs):
  52. if not settings.WECHAT_PUSH_ENABLED:
  53. logger.info('WECHAT_PUSH_ENABLED is false, you are probably in a dev/testing env')
  54. else:
  55. try:
  56. product_agent = Agent.objects(id = productId).first()
  57. if not product_agent:
  58. logger.warning('user<id={}> has no product agent id.')
  59. return
  60. app = get_wechat_user_messager_app(product_agent)
  61. if templateName not in app.templateIdMap:
  62. logger.warning('is not support {}'.format(templateName))
  63. return
  64. template = app.templateIdMap[templateName]
  65. payload = kwargs.get(app.__class__.__name__, None)
  66. if not payload:
  67. logger.warning('not support subscribe message.')
  68. return
  69. if isinstance(app, WechatUserManagerApp):
  70. WechatClientProxy(app).notify_msg(openId, template, url, **payload)
  71. elif isinstance(app, WechatUserSubscribeManagerApp):
  72. WechatClientProxy(app).publish_msg(openId, template, url, **payload)
  73. else:
  74. logger.warning('no support app type.')
  75. except Exception as e:
  76. logger.exception(e)
  77. def poll_user_recharge_record(pay_app_type, record_id, interval, total_count):
  78. # type: (str, str, int, int)->None
  79. poller_cls = PayManager().get_poller(pay_app_type = pay_app_type)
  80. poller = poller_cls(
  81. record_id = record_id,
  82. interval = interval,
  83. total_count = total_count,
  84. record_cls = RechargeRecord,
  85. post_pay = post_pay) # type: PayRecordPoller
  86. poller.start()
  87. def test_sync():
  88. headers = {'Content-Type': 'application/json'}
  89. with requests.sessions.Session() as session:
  90. res = session.request(
  91. url = 'http://develop.5tao5ai.com/user/test',
  92. method = 'get',
  93. headers = headers,
  94. timeout = 15)
  95. return 'ok'
  96. def report_to_user_low_power(devNo, line, power, managerialOpenId, dealerId):
  97. """
  98. 低电量用户告警
  99. :param devNo:
  100. :param line:
  101. :param power:
  102. :param managerialOpenId:
  103. :param dealerId:
  104. :return:
  105. """
  106. dev = Device.get_dev(devNo)
  107. box = ActionDeviceBuilder.create_action_device(dev)
  108. result = box.get_port_info(line)
  109. group = Group.get_group(dev.get("groupId"))
  110. if result.get("power", 999) < int(power):
  111. report_to_user_via_wechat(
  112. openId = managerialOpenId or "",
  113. dealerId = dealerId,
  114. templateName = "device_fault",
  115. device = u"{}-{}".format(dev.logicalCode, line),
  116. title = u"充电设备低功率告警",
  117. level = u"一般",
  118. address = u"{}".format(group.get("groupName")),
  119. fault = u"您使用 <{}> 设备 <{}> 号充电端口进行充电,当前充电功率 <{}> 过低,可能会影响正常充电。".format(dev.logicalCode, line, power),
  120. notifyTime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  121. )
  122. def notify_virtual_card_expired():
  123. """
  124. 虚拟卡卷到期提醒
  125. 提醒方式是 过期前三天 过期后三天 每天18:00提醒一次
  126. 霍珀的 虚拟卡 名称叫 电子月票 使用特性 is_huopo 来标识
  127. :return:
  128. """
  129. agentIdList = list()
  130. huoPoAgentIdList = list()
  131. # 找到需要通知的经销商
  132. agents = Agent.objects.only("id", "features")
  133. for agent in agents:
  134. if agent.features and "notify_virtual_card_expired" in agent.features:
  135. if "is_huopo" not in agent.features:
  136. agentIdList.append(str(agent.id))
  137. else:
  138. huoPoAgentIdList.append(str(agent.id))
  139. dealerIdList = [str(dealer.id) for dealer in Dealer.objects.filter(agentId__in = agentIdList).only("id", "agentId")]
  140. huoPoDealerIdList = [str(dealer.id) for dealer in
  141. Dealer.objects.filter(agentId__in = huoPoAgentIdList).only("id", "agentId")]
  142. # 先找母卡 母卡判断母卡是否停售或者删除
  143. cardTypes = VirtualCard.objects.filter(
  144. ownerId__in = dealerIdList + huoPoDealerIdList,
  145. status = 1
  146. )
  147. vCards = list()
  148. nowTime = datetime.datetime.now()
  149. for cardType in cardTypes:
  150. leftTime = nowTime - datetime.timedelta(days = min(cardType.needRenewMax, Const.VCARD_NEED_NOTIFY_TIME))
  151. rightTime = nowTime + datetime.timedelta(days = min(cardType.needRenewMin, Const.VCARD_NEED_NOTIFY_TIME))
  152. vCards.extend(
  153. UserVirtualCard.objects.filter(
  154. cardTypeId = str(cardType.id),
  155. expiredTime__gt = leftTime,
  156. expiredTime__lt = rightTime
  157. )
  158. )
  159. vCardInfoList = []
  160. for vCard in vCards:
  161. if vCard.dealerId in dealerIdList:
  162. cardName = u"优惠卡卷"
  163. else:
  164. cardName = u"电子月票"
  165. # 即将过期的
  166. if vCard.expiredTime > nowTime:
  167. title = u"您的{}<{}>即将过期,为避免影响您的正常使用,请及时续费充值".format(cardName, vCard.cardName)
  168. # 已经过期的
  169. else:
  170. title = u"您的{}<{}>已经过期,如需继续使用,请及时续费充值".format(cardName, vCard.cardName)
  171. expiredTime = vCard.expiredTime.strftime("%Y-%m-%d %H:%M:%S")
  172. vCardInfo = {
  173. "openId" : vCard.managerialOpenId,
  174. "dealerId" : vCard.dealerId,
  175. "cardNo" : vCard.cardNo,
  176. "cardName":cardName,
  177. "title" : title,
  178. "expiredTime" : expiredTime
  179. }
  180. vCardInfoList.append(vCardInfo)
  181. for vCardInfo in vCardInfoList:
  182. report_to_user_via_wechat(
  183. openId=vCardInfo.get("openId"),
  184. dealerId=vCardInfo.get("dealerId"),
  185. templateName="service_expired",
  186. title=vCardInfo.get("title"),
  187. cardNo=vCardInfo.get("cardNo"),
  188. expiredTime=vCardInfo.get("expiredTime"),
  189. remark=u"您可以打开公众号的个人中心,选择{},然后选择您的卡票进行续费".format(vCardInfo.get("cardName"))
  190. )
  191. def notify_insurance_order_subscribe(orderId): # type:(InsuranceOrder) -> None
  192. """
  193. 通知用户投保成功的通知
  194. {{first.DATA}}
  195. 保单名称:{{keyword1.DATA}}
  196. 保单种类:{{keyword2.DATA}}
  197. 保单单号:{{keyword3.DATA}}
  198. 支付保费:{{keyword4.DATA}}
  199. 保单生效时间:{{keyword5.DATA}}
  200. {{remark.DATA}}
  201. :param orderId: 用户保险单的 ID
  202. :return:
  203. """
  204. try:
  205. order = InsuranceOrder.objects.get(id = orderId)
  206. except DoesNotExist:
  207. logger.error("can not find insurance order <id={}> to report user!".format(orderId))
  208. return
  209. # 防止任务重入以及任务过于延时 做拦截
  210. if not order.effective:
  211. logger.warning(
  212. "insurance order <id={}> is not effective <effective={}>, need not to report user!".format(orderId,
  213. order.effective))
  214. return
  215. if order.endTime < datetime.datetime.now():
  216. logger.warning(
  217. "insurance order <id={}> is expired <endTime={}>, need not to report user!".format(orderId, order.endTime))
  218. return
  219. insurance = order.insurance # type: Insurance
  220. user = order.user # type: MyUser
  221. title = u"已加入保险保障,本服务由【中国平安】承保"
  222. url = concat_front_end_url(uri = '/user/index.html#/insurance/compensate')
  223. report_to_user_via_wechat(
  224. openId = user.managerialOpenId,
  225. dealerId = order.ownerId,
  226. templateName = "insurance_sub",
  227. title = title,
  228. name = insurance.name,
  229. category = insurance.categoryName,
  230. orderNo = order.orderNo,
  231. money = order.money,
  232. validTime = u"{}至{}".format(
  233. order.startTime.strftime("%Y-%m-%d %H:%M:%S"),
  234. order.endTime.strftime("%Y-%m-%d %H:%M:%S")
  235. ),
  236. remark = u"点击查看理赔条款>>>",
  237. url = url
  238. )
  239. def notify_insurance_order_cancel(orderId):
  240. """
  241. 通知用户保单取消的通知
  242. {{first.DATA}}
  243. 保单号:{{keyword1.DATA}}
  244. 产品名称:{{keyword2.DATA}}
  245. 被保险人:{{keyword3.DATA}}
  246. 保障期间:{{keyword4.DATA}}
  247. 退款金额:{{keyword5.DATA}}
  248. {{remark.DATA}}
  249. :param orderId: 用户保险单的ID
  250. :return:
  251. """
  252. try:
  253. order = InsuranceOrder.objects.get(id = orderId)
  254. except DoesNotExist:
  255. logger.error("can not find insurance order <id={}> to report user!".format(orderId))
  256. return
  257. # 防止被错误调用 判断一次状态
  258. if order.effective:
  259. logger.warning(
  260. "insurance order <id={}> is effective <effective={}>, need not to report user cancel!".format(orderId,
  261. order.effective))
  262. return
  263. insurance = order.insurance # type: Insurance
  264. user = order.user # type: MyUser
  265. title = u"退保成功通知"
  266. report_to_user_via_wechat(
  267. openId = user.managerialOpenId,
  268. dealerId = order.ownerId,
  269. templateName = "insurance_cancel",
  270. title = title,
  271. orderNo = order.orderNo,
  272. name = insurance.name,
  273. user = u"微信昵称-{}".format(user.nickname),
  274. cancelTime = u"于 {} 退保".format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")),
  275. money = order.money,
  276. remark = u"感谢您的使用"
  277. )
  278. def pull_refund_order():
  279. """
  280. 拉取 状态不定的 退款订单
  281. """
  282. refundOrders = RefundMoneyRecord.objects.filter(
  283. status__in = [RefundMoneyRecord.Status.PROCESSING],
  284. datetimeAdded__gt = datetime.datetime.now() - datetime.timedelta(days = 2)
  285. )
  286. for refundOrder in refundOrders:
  287. rechargeOrder = RechargeRecord.objects.get(id = refundOrder.rechargeObjId) # type: RechargeRecord
  288. payOrder = rechargeOrder.payOrder
  289. payGateway = PaymentGateway.clone_from_order(payOrder) # type: PaymentGatewayT
  290. try:
  291. puller = RefundManager().get_poller(payGateway.pay_app_type)
  292. puller(refundOrder).pull(payGateway, payOrder, refund_post_pay)
  293. except Exception as e:
  294. logger.exception(e)