# -*- coding: utf-8 -*- # !/usr/bin/env python import datetime import requests from celery.utils.log import get_task_logger from django.conf import settings from mongoengine import DoesNotExist from typing import Optional, TYPE_CHECKING from apps.web.agent.models import Agent from apps.web.common.transaction.pay import PayManager from apps.web.common.transaction.refund import RefundManager from apps.web.constant import Const from apps.web.core.bridge import WechatClientProxy from apps.web.core.helpers import ActionDeviceBuilder from apps.web.core.models import WechatUserManagerApp, WechatUserSubscribeManagerApp from apps.web.core.payment import PaymentGateway from apps.web.dealer.models import Dealer, VirtualCard from apps.web.device.models import Group, Device from apps.web.helpers import get_wechat_user_manager_mp_proxy, get_wechat_user_sub_manager_mp_proxy, \ get_wechat_user_messager_app from apps.web.promotion.models import InsuranceOrder, Insurance from apps.web.user.models import RechargeRecord, UserVirtualCard, RefundMoneyRecord from apps.web.user.transaction import post_pay from apps.web.user.transaction_deprecated import refund_post_pay from apps.web.utils import concat_front_end_url if TYPE_CHECKING: from apps.web.common.transaction.pay import PayRecordPoller from apps.web.user.models import MyUser from apps.web.core.payment.type_checking import PaymentGatewayT logger = get_task_logger('user.tasks') def report_to_user_via_wechat(openId, dealerId, templateName, url = None, **kwargs): if not settings.WECHAT_PUSH_ENABLED: logger.info('WECHAT_PUSH_ENABLED is false, you are probably in a dev/testing env') else: dealer = Dealer.objects(id = str(dealerId)).first() # type: Optional[Dealer] if not dealer: return {'info': 'dealer does not exist, id=%s' % (dealerId,)} agent = Agent.objects(id = str(dealer.agentId)).first() # type: Optional[Agent] if not agent: return {'info': 'agent does not exist, id=%s' % (dealer.agentId,)} try: wechat_mp_proxy = get_wechat_user_sub_manager_mp_proxy(agent) wechat_mp_proxy.publish(openId = openId, templateName = templateName, url = url, **kwargs) except Exception as e: logger.info('wechat_mp_proxy.publish is error=<{}>'.format(e)) try: wechat_mp_proxy = get_wechat_user_manager_mp_proxy(agent) wechat_mp_proxy.notify(openId = openId, templateName = templateName, url = url, **kwargs) except Exception as e: logger.info('wechat_mp_proxy.notify is error=<{}>'.format(e)) def send_msg_to_user_via_wechat(productId, openId, templateName, url = None, **kwargs): if not settings.WECHAT_PUSH_ENABLED: logger.info('WECHAT_PUSH_ENABLED is false, you are probably in a dev/testing env') else: try: product_agent = Agent.objects(id = productId).first() if not product_agent: logger.warning('user has no product agent id.') return app = get_wechat_user_messager_app(product_agent) if templateName not in app.templateIdMap: logger.warning('is not support {}'.format(templateName)) return template = app.templateIdMap[templateName] payload = kwargs.get(app.__class__.__name__, None) if not payload: logger.warning('not support subscribe message.') return if isinstance(app, WechatUserManagerApp): WechatClientProxy(app).notify_msg(openId, template, url, **payload) elif isinstance(app, WechatUserSubscribeManagerApp): WechatClientProxy(app).publish_msg(openId, template, url, **payload) else: logger.warning('no support app type.') except Exception as e: logger.exception(e) def poll_user_recharge_record(pay_app_type, record_id, interval, total_count): # type: (str, str, int, int)->None poller_cls = PayManager().get_poller(pay_app_type = pay_app_type) poller = poller_cls( record_id = record_id, interval = interval, total_count = total_count, record_cls = RechargeRecord, post_pay = post_pay) # type: PayRecordPoller poller.start() def test_sync(): headers = {'Content-Type': 'application/json'} with requests.sessions.Session() as session: res = session.request( url = 'http://develop.5tao5ai.com/user/test', method = 'get', headers = headers, timeout = 15) return 'ok' def report_to_user_low_power(devNo, line, power, managerialOpenId, dealerId): """ 低电量用户告警 :param devNo: :param line: :param power: :param managerialOpenId: :param dealerId: :return: """ dev = Device.get_dev(devNo) box = ActionDeviceBuilder.create_action_device(dev) result = box.get_port_info(line) group = Group.get_group(dev.get("groupId")) if result.get("power", 999) < int(power): report_to_user_via_wechat( openId = managerialOpenId or "", dealerId = dealerId, templateName = "device_fault", device = u"{}-{}".format(dev.logicalCode, line), title = u"充电设备低功率告警", level = u"一般", address = u"{}".format(group.get("groupName")), fault = u"您使用 <{}> 设备 <{}> 号充电端口进行充电,当前充电功率 <{}> 过低,可能会影响正常充电。".format(dev.logicalCode, line, power), notifyTime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") ) def notify_virtual_card_expired(): """ 虚拟卡卷到期提醒 提醒方式是 过期前三天 过期后三天 每天18:00提醒一次 霍珀的 虚拟卡 名称叫 电子月票 使用特性 is_huopo 来标识 :return: """ agentIdList = list() huoPoAgentIdList = list() # 找到需要通知的经销商 agents = Agent.objects.only("id", "features") for agent in agents: if agent.features and "notify_virtual_card_expired" in agent.features: if "is_huopo" not in agent.features: agentIdList.append(str(agent.id)) else: huoPoAgentIdList.append(str(agent.id)) dealerIdList = [str(dealer.id) for dealer in Dealer.objects.filter(agentId__in = agentIdList).only("id", "agentId")] huoPoDealerIdList = [str(dealer.id) for dealer in Dealer.objects.filter(agentId__in = huoPoAgentIdList).only("id", "agentId")] # 先找母卡 母卡判断母卡是否停售或者删除 cardTypes = VirtualCard.objects.filter( ownerId__in = dealerIdList + huoPoDealerIdList, status = 1 ) vCards = list() nowTime = datetime.datetime.now() for cardType in cardTypes: leftTime = nowTime - datetime.timedelta(days = min(cardType.needRenewMax, Const.VCARD_NEED_NOTIFY_TIME)) rightTime = nowTime + datetime.timedelta(days = min(cardType.needRenewMin, Const.VCARD_NEED_NOTIFY_TIME)) vCards.extend( UserVirtualCard.objects.filter( cardTypeId = str(cardType.id), expiredTime__gt = leftTime, expiredTime__lt = rightTime ) ) vCardInfoList = [] for vCard in vCards: if vCard.dealerId in dealerIdList: cardName = u"优惠卡卷" else: cardName = u"电子月票" # 即将过期的 if vCard.expiredTime > nowTime: title = u"您的{}<{}>即将过期,为避免影响您的正常使用,请及时续费充值".format(cardName, vCard.cardName) # 已经过期的 else: title = u"您的{}<{}>已经过期,如需继续使用,请及时续费充值".format(cardName, vCard.cardName) expiredTime = vCard.expiredTime.strftime("%Y-%m-%d %H:%M:%S") vCardInfo = { "openId" : vCard.managerialOpenId, "dealerId" : vCard.dealerId, "cardNo" : vCard.cardNo, "cardName":cardName, "title" : title, "expiredTime" : expiredTime } vCardInfoList.append(vCardInfo) for vCardInfo in vCardInfoList: report_to_user_via_wechat( openId=vCardInfo.get("openId"), dealerId=vCardInfo.get("dealerId"), templateName="service_expired", title=vCardInfo.get("title"), cardNo=vCardInfo.get("cardNo"), expiredTime=vCardInfo.get("expiredTime"), remark=u"您可以打开公众号的个人中心,选择{},然后选择您的卡票进行续费".format(vCardInfo.get("cardName")) ) def notify_insurance_order_subscribe(orderId): # type:(InsuranceOrder) -> None """ 通知用户投保成功的通知 {{first.DATA}} 保单名称:{{keyword1.DATA}} 保单种类:{{keyword2.DATA}} 保单单号:{{keyword3.DATA}} 支付保费:{{keyword4.DATA}} 保单生效时间:{{keyword5.DATA}} {{remark.DATA}} :param orderId: 用户保险单的 ID :return: """ try: order = InsuranceOrder.objects.get(id = orderId) except DoesNotExist: logger.error("can not find insurance order to report user!".format(orderId)) return # 防止任务重入以及任务过于延时 做拦截 if not order.effective: logger.warning( "insurance order is not effective , need not to report user!".format(orderId, order.effective)) return if order.endTime < datetime.datetime.now(): logger.warning( "insurance order is expired , need not to report user!".format(orderId, order.endTime)) return insurance = order.insurance # type: Insurance user = order.user # type: MyUser title = u"已加入保险保障,本服务由【中国平安】承保" url = concat_front_end_url(uri = '/user/index.html#/insurance/compensate') report_to_user_via_wechat( openId = user.managerialOpenId, dealerId = order.ownerId, templateName = "insurance_sub", title = title, name = insurance.name, category = insurance.categoryName, orderNo = order.orderNo, money = order.money, validTime = u"{}至{}".format( order.startTime.strftime("%Y-%m-%d %H:%M:%S"), order.endTime.strftime("%Y-%m-%d %H:%M:%S") ), remark = u"点击查看理赔条款>>>", url = url ) def notify_insurance_order_cancel(orderId): """ 通知用户保单取消的通知 {{first.DATA}} 保单号:{{keyword1.DATA}} 产品名称:{{keyword2.DATA}} 被保险人:{{keyword3.DATA}} 保障期间:{{keyword4.DATA}} 退款金额:{{keyword5.DATA}} {{remark.DATA}} :param orderId: 用户保险单的ID :return: """ try: order = InsuranceOrder.objects.get(id = orderId) except DoesNotExist: logger.error("can not find insurance order to report user!".format(orderId)) return # 防止被错误调用 判断一次状态 if order.effective: logger.warning( "insurance order is effective , need not to report user cancel!".format(orderId, order.effective)) return insurance = order.insurance # type: Insurance user = order.user # type: MyUser title = u"退保成功通知" report_to_user_via_wechat( openId = user.managerialOpenId, dealerId = order.ownerId, templateName = "insurance_cancel", title = title, orderNo = order.orderNo, name = insurance.name, user = u"微信昵称-{}".format(user.nickname), cancelTime = u"于 {} 退保".format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")), money = order.money, remark = u"感谢您的使用" ) def pull_refund_order(): """ 拉取 状态不定的 退款订单 """ refundOrders = RefundMoneyRecord.objects.filter( status__in = [RefundMoneyRecord.Status.PROCESSING], datetimeAdded__gt = datetime.datetime.now() - datetime.timedelta(days = 2) ) for refundOrder in refundOrders: rechargeOrder = RechargeRecord.objects.get(id = refundOrder.rechargeObjId) # type: RechargeRecord payOrder = rechargeOrder.payOrder payGateway = PaymentGateway.clone_from_order(payOrder) # type: PaymentGatewayT try: puller = RefundManager().get_poller(payGateway.pay_app_type) puller(refundOrder).pull(payGateway, payOrder, refund_post_pay) except Exception as e: logger.exception(e)