From 59e73fa83e47debc8d825410aa4e456614a3d339 Mon Sep 17 00:00:00 2001 From: nineven Date: Sat, 26 Mar 2022 21:13:33 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B6=88=E6=81=AF=E6=8F=90?= =?UTF-8?q?=E9=86=92=E5=8A=9F=E8=83=BD=EF=BC=8C=E4=B8=8B=E8=BD=BD=E4=BD=99?= =?UTF-8?q?=E9=A2=9D=EF=BC=8C=E7=AD=BE=E5=90=8D=E4=BD=99=E9=A2=9D=E4=B8=8D?= =?UTF-8?q?=E8=B6=B3=E8=87=AA=E5=8A=A8=E6=A3=80=E6=B5=8B=EF=BC=8C=E5=8F=91?= =?UTF-8?q?=E9=80=81=E9=82=AE=E4=BB=B6=E6=88=96=E5=BE=AE=E4=BF=A1=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fir_client/src/components/FirHeader.vue | 3 + .../src/components/user/FirUserNotify.vue | 649 ++++++++++++++++++ .../components/user/FirUserProfileInfo.vue | 4 +- fir_client/src/main.js | 4 + fir_client/src/restful/index.js | 30 + fir_client/src/router/index.js | 11 + .../api/migrations/0004_auto_20220326_1847.py | 75 ++ fir_ser/api/models.py | 42 ++ fir_ser/api/tasks.py | 18 +- fir_ser/api/urls.py | 3 + fir_ser/api/utils/ctasks.py | 16 + fir_ser/api/utils/modelutils.py | 19 +- fir_ser/api/utils/serializer.py | 22 + fir_ser/api/views/login_wx.py | 18 +- fir_ser/api/views/notify.py | 175 +++++ fir_ser/api/views/thirdlogin.py | 36 +- fir_ser/common/core/exception.py | 4 +- fir_ser/common/libs/mp/wechat.py | 30 + fir_ser/common/notify/notify.py | 78 +++ fir_ser/common/notify/ntasks.py | 131 ++++ fir_ser/common/notify/utils.py | 19 + fir_ser/common/utils/caches.py | 2 + fir_ser/config.py | 4 +- fir_ser/fir_ser/settings.py | 18 + fir_ser/xsign/utils/supersignutils.py | 13 +- 25 files changed, 1387 insertions(+), 37 deletions(-) create mode 100644 fir_client/src/components/user/FirUserNotify.vue create mode 100644 fir_ser/api/migrations/0004_auto_20220326_1847.py create mode 100644 fir_ser/api/views/notify.py create mode 100644 fir_ser/common/notify/notify.py create mode 100644 fir_ser/common/notify/ntasks.py create mode 100644 fir_ser/common/notify/utils.py diff --git a/fir_client/src/components/FirHeader.vue b/fir_client/src/components/FirHeader.vue index 801a408..9bba2f4 100644 --- a/fir_client/src/components/FirHeader.vue +++ b/fir_client/src/components/FirHeader.vue @@ -51,6 +51,7 @@ 个人资料 API token 设置域名 + 消息中心 宣传广告 存储管理 @@ -138,6 +139,8 @@ export default { this.$router.push({"name": 'FirUserAdvert'}) } else if (command === 'myorder') { this.$router.push({"name": 'FirUserOrders'}) + } else if (command === 'setnotify') { + this.$router.push({"name": 'FirUserNotify'}) } else if (command === 'qrcode') { this.$router.push({"name": 'FirUserQrcode'}) } else if (command === 'contact') { diff --git a/fir_client/src/components/user/FirUserNotify.vue b/fir_client/src/components/user/FirUserNotify.vue new file mode 100644 index 0000000..15a32db --- /dev/null +++ b/fir_client/src/components/user/FirUserNotify.vue @@ -0,0 +1,649 @@ + + + + + diff --git a/fir_client/src/components/user/FirUserProfileInfo.vue b/fir_client/src/components/user/FirUserProfileInfo.vue index 243ab45..9f82a6e 100644 --- a/fir_client/src/components/user/FirUserProfileInfo.vue +++ b/fir_client/src/components/user/FirUserProfileInfo.vue @@ -413,7 +413,7 @@ export default { return; } if (data.code === 1000) { - if (this.userinfo.uid === data.userinfo.uid) { + if (this.userinfo.uid === data.data.uid) { this.$message.success("绑定成功"); this.wx_visible = false; } @@ -446,7 +446,7 @@ export default { this.wx_visible = false; } }, { - "methods": "POST", "data": {"unique_key": this.unique_key} + "methods": "POST", "data": {"unique_key": this.unique_key, "w_type": 'login'} }) } }, diff --git a/fir_client/src/main.js b/fir_client/src/main.js index 9747d7c..7b97ffb 100644 --- a/fir_client/src/main.js +++ b/fir_client/src/main.js @@ -25,6 +25,8 @@ import { Checkbox, CheckboxGroup, Col, + Collapse, + CollapseItem, Container, DatePicker, Dialog, @@ -158,6 +160,8 @@ Vue.use(InputNumber); Vue.use(Radio); Vue.use(Backtop); Vue.use(Transfer); +Vue.use(Collapse); +Vue.use(CollapseItem); Vue.prototype.$message = Message; Vue.prototype.$notify = Notification; Vue.prototype.$loading = Loading.service; diff --git a/fir_client/src/restful/index.js b/fir_client/src/restful/index.js index 696e4ba..e2395cb 100644 --- a/fir_client/src/restful/index.js +++ b/fir_client/src/restful/index.js @@ -717,6 +717,36 @@ export function appReport(callBack, params, load = true) { ); } +/**消息接收人配置 */ +export function notifyReceiverInfo(callBack, params, load = true) { + getData( + params.methods, + USERSEVER + '/notify/receiver', + params.data, + data => { + callBack(data); + }, + load, + true, + true + ); +} + +/**消息配置 */ +export function notifyConfigInfo(callBack, params, load = true) { + getData( + params.methods, + USERSEVER + '/notify/config', + params.data, + data => { + callBack(data); + }, + load, + true, + true + ); +} + /** 超级签名************************************************相关api */ let SIGNSEVER = DOMAIN + '/api/v1/fir/xsign'; diff --git a/fir_client/src/router/index.js b/fir_client/src/router/index.js index a2979c4..dbf1515 100644 --- a/fir_client/src/router/index.js +++ b/fir_client/src/router/index.js @@ -146,6 +146,17 @@ const router = new VueRouter({ } ] }, + { + path: 'notify', + component: () => import("@/components/user/FirUserNotify"), + children: [ + { + path: ':act', + name: 'FirUserNotify', + meta: {label: '消息中心'}, + } + ] + }, { path: 'orders', name: 'FirUserOrders', diff --git a/fir_ser/api/migrations/0004_auto_20220326_1847.py b/fir_ser/api/migrations/0004_auto_20220326_1847.py new file mode 100644 index 0000000..18ffe62 --- /dev/null +++ b/fir_ser/api/migrations/0004_auto_20220326_1847.py @@ -0,0 +1,75 @@ +# Generated by Django 3.2.3 on 2022-03-26 18:47 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('api', '0003_appbundleidblacklist'), + ] + + operations = [ + migrations.AddField( + model_name='thirdwechatuserinfo', + name='enable_login', + field=models.BooleanField(default=0, verbose_name='是否允许登录'), + ), + migrations.AddField( + model_name='thirdwechatuserinfo', + name='enable_notify', + field=models.BooleanField(default=0, verbose_name='是否允许推送消息'), + ), + migrations.AddField( + model_name='userinfo', + name='notify_available_downloads', + field=models.IntegerField(blank=True, default=0, null=True, verbose_name='下载余额不足通知'), + ), + migrations.AddField( + model_name='userinfo', + name='notify_available_signs', + field=models.IntegerField(blank=True, default=0, null=True, verbose_name='签名余额不足通知'), + ), + migrations.CreateModel( + name='NotifyReceiver', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('receiver_name', models.CharField(max_length=128, unique=True, verbose_name='姓名')), + ('email', models.EmailField(blank=True, max_length=255, null=True, verbose_name='邮箱')), + ('description', models.CharField(blank=True, default='', max_length=256, verbose_name='备注')), + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='添加时间')), + ('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, + verbose_name='用户ID')), + ('weixin', + models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='api.thirdwechatuserinfo', + verbose_name='微信ID')), + ], + options={ + 'verbose_name': '信息接收配置', + 'verbose_name_plural': '信息接收配置', + 'unique_together': {('user_id', 'email'), ('user_id', 'weixin')}, + }, + ), + migrations.CreateModel( + name='NotifyConfig', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('config_name', models.CharField(max_length=128, unique=True, verbose_name='通知名称')), + ('message_type', models.SmallIntegerField( + choices=[(0, '签名余额不足'), (1, '下载次数不足'), (2, '应用签名限额'), (3, '应用签名失败'), (4, '充值到账提醒'), (5, '优惠活动通知'), + (6, '证书到期消息')], default=5, verbose_name='消息类型')), + ('enable_weixin', models.BooleanField(default=True, verbose_name='是否启用该配置项')), + ('enable_email', models.BooleanField(default=True, verbose_name='是否启用该配置项')), + ('description', models.CharField(blank=True, default='', max_length=256, verbose_name='备注')), + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='添加时间')), + ('sender', models.ManyToManyField(to='api.NotifyReceiver', verbose_name='通知接受者方式')), + ('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, + verbose_name='用户ID')), + ], + options={ + 'verbose_name': '信息接收配置', + 'verbose_name_plural': '信息接收配置', + }, + ), + ] diff --git a/fir_ser/api/models.py b/fir_ser/api/models.py index 52d978b..5cc63c3 100644 --- a/fir_ser/api/models.py +++ b/fir_ser/api/models.py @@ -46,6 +46,8 @@ class UserInfo(AbstractUser): storage = models.OneToOneField(to='AppStorage', related_name='app_storage', on_delete=models.SET_NULL, verbose_name="存储", null=True, blank=True) api_token = models.CharField(max_length=256, verbose_name='api访问密钥', default='') + notify_available_downloads = models.IntegerField(default=0, verbose_name="下载余额不足通知", blank=True, null=True) + notify_available_signs = models.IntegerField(default=0, verbose_name="签名余额不足通知", blank=True, null=True) class Meta: verbose_name = '账户信息' @@ -77,6 +79,8 @@ class ThirdWeChatUserInfo(models.Model): head_img_url = models.CharField(max_length=256, verbose_name="用户头像", blank=True, null=True) address = models.CharField(max_length=128, verbose_name="地址", blank=True, null=True) subscribe = models.BooleanField(verbose_name="是否订阅公众号", default=0) + enable_login = models.BooleanField(verbose_name="是否允许登录", default=0) + enable_notify = models.BooleanField(verbose_name="是否允许推送消息", default=0) created_time = models.DateTimeField(auto_now_add=True, verbose_name="授权时间") def __str__(self): @@ -469,3 +473,41 @@ class SystemConfig(models.Model): def __str__(self): return "%s-%s" % (self.key, self.description) + + +class NotifyReceiver(models.Model): + receiver_name = models.CharField(max_length=128, unique=True, verbose_name="姓名") + user_id = models.ForeignKey(to=UserInfo, verbose_name="用户ID", on_delete=models.CASCADE) + weixin = models.ForeignKey(to=ThirdWeChatUserInfo, verbose_name="微信ID", on_delete=models.CASCADE, null=True) + email = models.EmailField(verbose_name='邮箱', max_length=255, blank=True, null=True) + description = models.CharField(verbose_name="备注", max_length=256, default='', blank=True) + create_time = models.DateTimeField(auto_now_add=True, verbose_name="添加时间") + + class Meta: + verbose_name = '信息接收配置' + verbose_name_plural = "信息接收配置" + unique_together = (('user_id', 'email',), ('user_id', 'weixin')) + + def __str__(self): + return "%s-%s-%s" % (self.user_id, self.receiver_name, self.description) + + +class NotifyConfig(models.Model): + user_id = models.ForeignKey(to=UserInfo, verbose_name="用户ID", on_delete=models.CASCADE) + config_name = models.CharField(max_length=128, unique=True, verbose_name="通知名称") + message_type_choices = ( + (0, '签名余额不足'), (1, '下载次数不足'), (2, '应用签名限额'), (3, '应用签名失败'), + (4, '充值到账提醒'), (5, '优惠活动通知'), (6, '证书到期消息')) + message_type = models.SmallIntegerField(choices=message_type_choices, default=5, verbose_name="消息类型") + sender = models.ManyToManyField(to=NotifyReceiver, verbose_name="通知接受者方式") + enable_weixin = models.BooleanField(default=True, verbose_name="是否启用该配置项") + enable_email = models.BooleanField(default=True, verbose_name="是否启用该配置项") + description = models.CharField(verbose_name="备注", max_length=256, default='', blank=True) + create_time = models.DateTimeField(auto_now_add=True, verbose_name="添加时间") + + class Meta: + verbose_name = '信息接收配置' + verbose_name_plural = "信息接收配置" + + def __str__(self): + return "%s-%s-%s" % (self.user_id, self.get_message_type_display(), self.sender) diff --git a/fir_ser/api/tasks.py b/fir_ser/api/tasks.py index 5fe5610..5239afa 100644 --- a/fir_ser/api/tasks.py +++ b/fir_ser/api/tasks.py @@ -8,7 +8,8 @@ import logging from captcha.models import CaptchaStore -from api.utils.ctasks import sync_download_times, auto_clean_upload_tmp_file, auto_clean_remote_client_log +from api.utils.ctasks import sync_download_times, auto_clean_upload_tmp_file, auto_clean_remote_client_log, \ + notify_check_user_download_times, notify_check_apple_developer_devices, notify_check_apple_developer_cert from api.views.login import get_login_type from common.core.sysconfig import Config, invalid_config_cache from common.libs.geetest.geetest_utils import check_bypass_status @@ -69,3 +70,18 @@ def sync_wx_access_token_job(): @app.task def auto_clean_remote_client_job(): auto_clean_remote_client_log() + + +@app.task +def download_times_notify_check_job(): + notify_check_user_download_times() + + +@app.task +def apple_developer_devices_check_job(): + notify_check_apple_developer_devices() + + +@app.task +def apple_developer_cert_notify_check_job(): + notify_check_apple_developer_cert() diff --git a/fir_ser/api/urls.py b/fir_ser/api/urls.py index a7c032e..8cf3033 100644 --- a/fir_ser/api/urls.py +++ b/fir_ser/api/urls.py @@ -23,6 +23,7 @@ from api.views.login import LoginView, UserInfoView, RegistView, AuthorizationVi UserApiTokenView, CertificationView, ChangeInfoView from api.views.login_wx import WeChatLoginView, WeChatLoginCheckView, WeChatBindView, WeChatWebLoginView from api.views.logout import LogoutView +from api.views.notify import NotifyReceiverView, NotifyConfigView from api.views.order import PriceView, OrderView, PaySuccess, OrderSyncView from api.views.report import ReportView from api.views.storage import StorageView, CleanStorageView @@ -55,6 +56,8 @@ urlpatterns = [ re_path("^qrcode$", AppsQrcodeShowView.as_view()), re_path("^package_prices$", PriceView.as_view()), re_path("^orders$", OrderView.as_view()), + re_path("^notify/receiver$", NotifyReceiverView.as_view()), + re_path("^notify/config$", NotifyConfigView.as_view()), re_path("^orders.sync$", OrderSyncView.as_view()), re_path("^certification$", CertificationView.as_view()), re_path(r"^pay_success/(?P\w+)$", PaySuccess.as_view()), diff --git a/fir_ser/api/utils/ctasks.py b/fir_ser/api/utils/ctasks.py index 0023567..0ebcd6b 100644 --- a/fir_ser/api/utils/ctasks.py +++ b/fir_ser/api/utils/ctasks.py @@ -10,6 +10,7 @@ import time from django.core.cache import cache from api.models import Apps, UserInfo, RemoteClientInfo +from common.notify.ntasks import check_user_download_times, check_apple_developer_devices, check_apple_developer_cert from common.utils.storage import Storage from fir_ser.settings import CACHE_KEY_TEMPLATE @@ -50,3 +51,18 @@ def auto_clean_upload_tmp_file(): def auto_clean_remote_client_log(clean_day=30): clean_time = datetime.datetime.now() - datetime.timedelta(days=clean_day) return RemoteClientInfo.objects.filter(created_time__lt=clean_time).delete() + + +def notify_check_user_download_times(): + for user_obj in UserInfo.objects.filter(is_active=True).all(): + check_user_download_times(user_obj, days=[0, 3, 7]) + + +def notify_check_apple_developer_devices(): + for user_obj in UserInfo.objects.filter(is_active=True, supersign_active=True).all(): + check_apple_developer_devices(user_obj, days=[0, 3, 7]) + + +def notify_check_apple_developer_cert(): + for user_obj in UserInfo.objects.filter(is_active=True, supersign_active=True).all(): + check_apple_developer_cert(user_obj, expire_day=7) diff --git a/fir_ser/api/utils/modelutils.py b/fir_ser/api/utils/modelutils.py index 9208950..41a9f2c 100644 --- a/fir_ser/api/utils/modelutils.py +++ b/fir_ser/api/utils/modelutils.py @@ -13,7 +13,7 @@ from django.db.models import Count from rest_framework.pagination import PageNumberPagination from api.models import AppReleaseInfo, UserDomainInfo, DomainCnameInfo, UserAdDisplayInfo, RemoteClientInfo, \ - AppBundleIdBlackList + AppBundleIdBlackList, NotifyReceiver, ThirdWeChatUserInfo from common.base.baseutils import get_server_domain_from_request, get_user_default_domain_name, get_real_ip_address, \ get_origin_domain_name @@ -202,3 +202,20 @@ def check_bundle_id_legal(user_uid, bundle_id): if app_black_obj: return app_black_obj.status == 0 return False + + +def get_notify_wx_queryset(user_obj, message_type): + notify_weixin_ids = NotifyReceiver.objects.filter(notifyconfig__user_id=user_obj, + notifyconfig__message_type=message_type, + notifyconfig__enable_weixin=True, weixin__isnull=False, + user_id=user_obj).values('weixin').distinct() + + return ThirdWeChatUserInfo.objects.filter(subscribe=True, user_id=user_obj, enable_notify=True, + pk__in=notify_weixin_ids).all() + + +def get_notify_email_queryset(user_obj, message_type): + return NotifyReceiver.objects.filter(notifyconfig__user_id=user_obj, + notifyconfig__message_type=message_type, + notifyconfig__enable_email=True, email__isnull=False, + user_id=user_obj).values('email').distinct() diff --git a/fir_ser/api/utils/serializer.py b/fir_ser/api/utils/serializer.py index f9ca69c..9e70ec4 100644 --- a/fir_ser/api/utils/serializer.py +++ b/fir_ser/api/utils/serializer.py @@ -456,3 +456,25 @@ class AppReportSerializer(serializers.ModelSerializer): class Meta: model = models.AppReportInfo exclude = ["id"] + + +class NotifyReceiverSerializer(serializers.ModelSerializer): + class Meta: + model = models.NotifyReceiver + exclude = ["user_id"] + + weixin = serializers.SerializerMethodField() + + def get_weixin(self, obj): + return ThirdWxSerializer(obj.weixin).data + + +class NotifyConfigSerializer(serializers.ModelSerializer): + class Meta: + model = models.NotifyConfig + exclude = ["user_id", "sender"] + + senders = serializers.SerializerMethodField() + + def get_senders(self, obj): + return NotifyReceiverSerializer(obj.sender, many=True).data diff --git a/fir_ser/api/views/login_wx.py b/fir_ser/api/views/login_wx.py index a68aab8..1023026 100644 --- a/fir_ser/api/views/login_wx.py +++ b/fir_ser/api/views/login_wx.py @@ -65,13 +65,14 @@ class WeChatBindView(APIView): ret = BaseResponse() uid = request.user.uid unique_key = request.data.get('unique_key') + w_type = request.data.get('w_type', 'xxxx') if unique_key: cache_obj = WxLoginBindCache(unique_key) cache_data = cache_obj.get_storage_cache() if cache_data: code, qr_info = cache_data else: - code, qr_info = cache_data = make_wx_login_qrcode(f"web.bind.{uid}") + code, qr_info = cache_data = make_wx_login_qrcode(f"web.bind.{uid}.{w_type}") cache_obj.set_storage_cache(cache_data, qr_info.get('expire_seconds', 600)) return wx_qr_code_response(ret, code, qr_info, get_real_ip_address(request)) return Response(ret.dict) @@ -101,14 +102,19 @@ class WeChatLoginCheckView(APIView): ret.msg = "还未绑定用户,请通过手机或者邮箱登录账户之后进行绑定" ret.code = 1005 else: + w_type = wx_ticket_data.get('w_type', '') + to_user = wx_ticket_data.get('to_user', '') user = UserInfo.objects.filter(pk=wx_ticket_data['pk']).first() if user.is_active: - key, user_info = set_user_token(user, request) - serializer = UserInfoSerializer(user_info) - data = serializer.data + if to_user and w_type: + ret.data = {'uid': user.uid, 'to_user': to_user, 'w_type': w_type} + else: + key, user_info = set_user_token(user, request) + serializer = UserInfoSerializer(user_info) + data = serializer.data + ret.userinfo = data + ret.token = key ret.msg = "验证成功!" - ret.userinfo = data - ret.token = key else: ret.msg = "用户被禁用" ret.code = 1005 diff --git a/fir_ser/api/views/notify.py b/fir_ser/api/views/notify.py new file mode 100644 index 0000000..f9e1e2e --- /dev/null +++ b/fir_ser/api/views/notify.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- +# project: fir_ser +# filename: notify +# author: liuyu +# data: 2022/3/25 + + +import logging + +from rest_framework.response import Response +from rest_framework.views import APIView + +from api.models import NotifyReceiver, ThirdWeChatUserInfo, NotifyConfig, UserInfo +from api.utils.modelutils import PageNumber +from api.utils.response import BaseResponse +from api.utils.serializer import NotifyReceiverSerializer, NotifyConfigSerializer +from common.base.baseutils import get_choices_dict, get_choices_name_from_key +from common.core.auth import ExpiringTokenAuthentication + +logger = logging.getLogger(__name__) + + +class NotifyConfigView(APIView): + authentication_classes = [ExpiringTokenAuthentication, ] + + def get(self, request): + res = BaseResponse() + config_pk = request.query_params.get('pk') + + obj_lists = NotifyConfig.objects.filter(user_id=request.user).order_by('message_type').order_by("-create_time") + if config_pk: + obj_lists = obj_lists.filter(pk=config_pk).first() + info = NotifyConfigSerializer(obj_lists) + res.data = info.data + return Response(res.dict) + + info = NotifyConfigSerializer(obj_lists.all(), many=True).data + res.count = obj_lists.count() + message_type_choices = get_choices_dict(NotifyConfig.message_type_choices) + for message_info in message_type_choices: + message_info['data'] = [] + for notify_config_info in info: + if notify_config_info['message_type'] == message_info['id']: + message_info['data'].append(notify_config_info) + if len(message_info['data']) == 0: + data_info = NotifyConfigSerializer(NotifyConfig.objects.filter(pk=-1).first()).data + data_info['config_name'] = message_info['name'] + data_info['message_type'] = message_info['id'] + data_info['m_type'] = message_info['id'] + data_info['id'] = -1 + message_info['data'].append(data_info) + res.message_type_choices = message_type_choices + return Response(res.dict) + + def post(self, request): + res = BaseResponse() + data = request.data + act = data.get('act', '') + if act and act != 'get': + threshold = data.get('threshold', {}) + if threshold: + notify_available_downloads = threshold.get('notify_available_downloads') + notify_available_signs = threshold.get('notify_available_signs') + + if notify_available_downloads is not None: + request.user.notify_available_downloads = notify_available_downloads + if notify_available_signs is not None: + request.user.notify_available_signs = notify_available_signs + if notify_available_downloads is not None or notify_available_signs is not None: + request.user.save(update_fields=['notify_available_downloads', 'notify_available_signs']) + + res.data = UserInfo.objects.filter(pk=request.user.pk).values('notify_available_downloads', + 'notify_available_signs').first() + return Response(res.dict) + + def put(self, request): + res = BaseResponse() + data = request.data + config_pk = data.get('config') + receiver_ids = data.get('receiver') + enable_email = data.get('enable_email') + enable_weixin = data.get('enable_weixin') + m_type = data.get('m_type') + if config_pk and config_pk == -1 and m_type is not None: + notify_config_obj = NotifyConfig.objects.filter(user_id=request.user, message_type=int(m_type)).filter() + if not notify_config_obj: + notify_config_obj = NotifyConfig.objects.create(user_id=request.user, message_type=int(m_type), + config_name=get_choices_name_from_key( + NotifyConfig.message_type_choices, int(m_type))) + else: + notify_config_obj = NotifyConfig.objects.filter(user_id=request.user, pk=config_pk).first() + if notify_config_obj: + if isinstance(receiver_ids, list): + notify_config_obj.sender.set( + NotifyReceiver.objects.filter(user_id=request.user, pk__in=receiver_ids).all()) + if enable_weixin is not None: + notify_config_obj.enable_weixin = enable_weixin + if enable_email is not None: + notify_config_obj.enable_email = enable_email + if enable_weixin is not None or enable_email is not None: + notify_config_obj.save(update_fields=['enable_email', 'enable_weixin']) + + return Response(res.dict) + + +class NotifyReceiverView(APIView): + authentication_classes = [ExpiringTokenAuthentication, ] + + def get(self, request): + res = BaseResponse() + config_pk = request.query_params.get('config') + message_type = request.query_params.get('message_type') + + obj_lists = NotifyReceiver.objects.filter(user_id=request.user) + + config_obj_lists = NotifyConfig.objects.filter(user_id=request.user) + if config_pk and config_pk != '-1': + res.senders = config_obj_lists.values('sender').filter(pk=config_pk).all() + config_obj = config_obj_lists.filter(pk=config_pk).first() + if config_obj: + res.config = {'config_name': config_obj.config_name, + 'id': config_obj.pk, 'm_type': config_obj.message_type, + 'message_type': config_obj.get_message_type_display()} + if config_pk and config_pk == '-1' and message_type is not None: + res.senders = config_obj_lists.values('sender').filter(pk=config_pk).all() + config_name = get_choices_name_from_key(NotifyConfig.message_type_choices, int(message_type)) + res.config = {'config_name': config_name, 'id': -1, 'message_type': config_name, + 'm_type': message_type} + + page_obj = PageNumber() + obj_info_serializer = page_obj.paginate_queryset(queryset=obj_lists.order_by("-create_time"), + request=request, + view=self) + info = NotifyReceiverSerializer(obj_info_serializer, many=True, ) + res.data = info.data + res.count = obj_lists.count() + return Response(res.dict) + + def post(self, request): + res = BaseResponse() + data = request.data + receiver_name = data.get('receiver_name') + description = data.get('description') + wxopenid = data.get('wxopenid') + email = data.get('email') + if receiver_name: + data_info = { + 'receiver_name': receiver_name, + 'description': description, + } + wx_obj = None + if wxopenid: + wx_obj = ThirdWeChatUserInfo.objects.filter(user_id=request.user, openid=wxopenid).first() + if wx_obj: + data_info['weixin'] = wx_obj + if email: + data_info['email'] = email + try: + NotifyReceiver.objects.create(user_id=request.user, **data_info) + if wx_obj: + wx_obj.enable_notify = True + wx_obj.save(update_fields=['enable_notify']) + except Exception as e: + logger.error(f"{request.user} notify receiver add failed . data:{data} Exception:{e}") + res.code = 1001 + res.msg = '数据有误或者已经存在该接收人信息' + return Response(res.dict) + + def delete(self, request): + res = BaseResponse() + pk = request.query_params.get('id') + if pk: + NotifyReceiver.objects.filter(user_id=request.user, pk=pk).delete() + return Response(res.dict) diff --git a/fir_ser/api/views/thirdlogin.py b/fir_ser/api/views/thirdlogin.py index 5da29a4..55ca434 100644 --- a/fir_ser/api/views/thirdlogin.py +++ b/fir_ser/api/views/thirdlogin.py @@ -54,56 +54,62 @@ def reply_login_msg(rec_msg, to_user, from_user, ): content = f'用户 {wx_user_obj.user_id.first_name} 登录成功' WxTemplateMsg(to_user, wx_user_obj.nickname).login_success_msg(wx_user_obj.user_id.first_name) else: - wx_user_info = update_or_create_wx_userinfo(to_user, None, False) + wx_user_info = update_or_create_wx_userinfo(to_user) WxTemplateMsg(to_user, wx_user_info.get('nickname', '')).login_failed_msg() if wx_ticket_info and wx_ticket_info.get('ip_addr'): ip_addr = wx_ticket_info.get('ip_addr') logger.info(f"{content} ip:{ip_addr}") - set_wx_ticket_login_info_cache(rec_msg.Ticket, {'pk': u_data_id}) + set_wx_ticket_login_info_cache(rec_msg.Ticket, {'pk': u_data_id, 'to_user': to_user}) reply_msg = reply.TextMsg(to_user, from_user, content) return reply_msg.send() -def update_or_create_wx_userinfo(to_user, user_obj, create=True): +def update_or_create_wx_userinfo(to_user, user_obj=None, w_type=''): code, wx_user_info = get_userinfo_from_openid(to_user) logger.info(f"get openid:{to_user} info:{to_user} code:{code}") if code: wx_user_info = { 'openid': wx_user_info.get('openid'), - 'nickname': wx_user_info.get('nickname'), # 最新微信接口已经取消该字段 - 'sex': wx_user_info.get('sex'), # 最新微信接口已经取消该字段 + # 'nickname': wx_user_info.get('nickname'), # 最新微信接口已经取消该字段 + # 'sex': wx_user_info.get('sex'), # 最新微信接口已经取消该字段 'subscribe_time': wx_user_info.get('subscribe_time'), - 'head_img_url': wx_user_info.get('headimgurl'), # 最新微信接口已经取消该字段 + # 'head_img_url': wx_user_info.get('headimgurl'), # 最新微信接口已经取消该字段 'address': f"{wx_user_info.get('country')}-{wx_user_info.get('province')}-{wx_user_info.get('city')}", 'subscribe': wx_user_info.get('subscribe'), } - if create: + if user_obj: + if w_type == 'login': + wx_user_info['enable_login'] = True + if w_type == 'notify': + wx_user_info['enable_notify'] = True + ThirdWeChatUserInfo.objects.update_or_create(user_id=user_obj, openid=to_user, defaults=wx_user_info) return wx_user_info def wx_bind_utils(rec_msg, to_user, from_user, content): - uid = rec_msg.Eventkey.split('.')[-1] + w_type = rec_msg.Eventkey.split('.')[-1] + uid = rec_msg.Eventkey.split('.')[-2] wx_user_obj = ThirdWeChatUserInfo.objects.filter(openid=to_user).first() user_obj = UserInfo.objects.filter(uid=uid).first() if wx_user_obj: wx_template_msg_obj = WxTemplateMsg(to_user, wx_user_obj.nickname) if user_obj and user_obj.uid == wx_user_obj.user_id.uid: content = f'账户 {wx_user_obj.user_id.first_name} 已经绑定成功,感谢您的使用' - update_or_create_wx_userinfo(to_user, user_obj) + update_or_create_wx_userinfo(to_user, user_obj, w_type) wx_template_msg_obj.bind_success_msg(user_obj.first_name) else: content = f'账户已经被 {wx_user_obj.user_id.first_name} 绑定' wx_template_msg_obj.bind_failed_msg(content) else: if user_obj: - wx_user_info = update_or_create_wx_userinfo(to_user, user_obj) + wx_user_info = update_or_create_wx_userinfo(to_user, user_obj, w_type) content = f'账户绑定 {user_obj.first_name} 成功' WxTemplateMsg(to_user, wx_user_info.get('nickname', '')).bind_success_msg(user_obj.first_name) if user_obj: - set_wx_ticket_login_info_cache(rec_msg.Ticket, {'pk': user_obj.pk}) + set_wx_ticket_login_info_cache(rec_msg.Ticket, {'pk': user_obj.pk, 'w_type': w_type, 'to_user': to_user}) reply_msg = reply.TextMsg(to_user, from_user, content) return reply_msg.send() @@ -180,7 +186,7 @@ class ValidWxChatToken(APIView): user_obj.email) else: content = '暂无登录绑定信息' - wx_user_info = update_or_create_wx_userinfo(to_user, None, False) + wx_user_info = update_or_create_wx_userinfo(to_user) WxTemplateMsg(to_user, wx_user_info.get('nickname')).query_bind_info_failed_msg( "查询登录绑定", content) @@ -192,7 +198,7 @@ class ValidWxChatToken(APIView): ThirdWeChatUserInfo.objects.filter(openid=to_user).delete() else: content = f'暂无登录绑定信息' - wx_user_info = update_or_create_wx_userinfo(to_user, None, False) + wx_user_info = update_or_create_wx_userinfo(to_user) WxTemplateMsg(to_user, wx_user_info.get('nickname')).query_bind_info_failed_msg( "解除登录绑定", content) @@ -228,7 +234,7 @@ class ThirdWxAccount(APIView): def get(self, request): res = BaseResponse() if get_login_type().get('third', '').get('wxp'): - wx_obj_lists = ThirdWeChatUserInfo.objects.filter(user_id=request.user) + wx_obj_lists = ThirdWeChatUserInfo.objects.filter(user_id=request.user, enable_login=True) page_obj = PageNumber() info_serializer = page_obj.paginate_queryset(queryset=wx_obj_lists.order_by("-subscribe_time"), request=request, @@ -243,7 +249,7 @@ class ThirdWxAccount(APIView): openid = data.get("openid") if get_login_type().get('third', '').get('wxp') and openid: if request.user.check_password(data.get('confirm_pwd', '')): - ThirdWeChatUserInfo.objects.filter(user_id=request.user, openid=openid).delete() + ThirdWeChatUserInfo.objects.filter(user_id=request.user, openid=openid).update(enable_login=False) else: res = BaseResponse() res.code = 1001 diff --git a/fir_ser/common/core/exception.py b/fir_ser/common/core/exception.py index 4d4c965..59b4c87 100644 --- a/fir_ser/common/core/exception.py +++ b/fir_ser/common/core/exception.py @@ -13,11 +13,11 @@ logger = logging.getLogger(__file__) def common_exception_handler(exc, context): - logger.error(f'{context["view"].__class__.__name__} ERROR: {exc}') # context['view'] 是TextView的对象,想拿出这个对象对应的类名 ret = exception_handler(exc, context) # 是Response对象,它内部有个data + logger.error(f'{context["view"].__class__.__name__} ERROR: {exc} ret:{ret}') if not ret: # drf内置处理不了,丢给django 的,我们自己来处理 - return ApiResponse(msg='error', result=str(exc)) + return ApiResponse(msg='error', result=str(exc), code=500) else: return ApiResponse(msg='error', status=ret.status_code, **ret.data, ) diff --git a/fir_ser/common/libs/mp/wechat.py b/fir_ser/common/libs/mp/wechat.py index bb9504b..57ce1da 100644 --- a/fir_ser/common/libs/mp/wechat.py +++ b/fir_ser/common/libs/mp/wechat.py @@ -502,6 +502,36 @@ class WxTemplateMsg(object): } return self.send_msg(msg_id, content_data) + def operate_failed_msg(self, first_name, operate_context, failed_msg, operate_time, description): + msg_id = 'Hnrk5iXRjbaCTVpSIyC5KC8cwFNDgplNUzPsnyDXRLo' + content_data = { + "first": { + "value": f'你好,“{self.wx_nick_name}“,操作失败了', + "color": "#173177" + }, + "keyword1": { + "value": first_name, + "color": "#173177" + }, + "keyword2": { + "value": operate_context, + "color": "#173177" + }, + "keyword3": { + "value": failed_msg, + "color": "#173177" + }, + "keyword4": { + "value": operate_time, + "color": "#173177" + }, + "remark": { + "value": f"{description},感谢您的关注", + "color": "#173177" + }, + } + return self.send_msg(msg_id, content_data) + class WxWebLogin(object): """ diff --git a/fir_ser/common/notify/notify.py b/fir_ser/common/notify/notify.py new file mode 100644 index 0000000..6bde445 --- /dev/null +++ b/fir_ser/common/notify/notify.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- +# project: fir_ser +# filename: notify +# author: liuyu +# data: 2022/3/26 +import logging + +from api.utils.modelutils import get_notify_wx_queryset +from common.base.baseutils import get_format_time +from common.core.sysconfig import Config +from common.libs.mp.wechat import WxTemplateMsg +from common.notify.utils import notify_by_email + +logger = logging.getLogger(__name__) + + +def pay_success_notify(user_obj, order_obj): + """ + 4, '充值到账提醒' + :param user_obj: + :param order_obj: + :return: + """ + message_type = 4 + + title = f'{order_obj.actual_download_times} 下载次数' + if order_obj.actual_download_gift_times > 0: + title = f'{title} 【赠送 {order_obj.actual_download_gift_times}】' + msg = f"用户 {user_obj.first_name} 您好,{order_obj.description}。您购买了 {title}。感谢有你!" + for wx_user_obj in get_notify_wx_queryset(user_obj, message_type): + res = WxTemplateMsg(wx_user_obj.openid, wx_user_obj.nickname).pay_success_msg( + title, + f'{str(order_obj.actual_amount / 100)} 元', + order_obj.get_payment_type_display(), + order_obj.pay_time.strftime("%Y/%m/%d %H:%M:%S"), + order_obj.order_number, order_obj.description) + logger.info(f'user_obj {user_obj} weixin notify pay success result: {res}') + + notify_by_email(user_obj, message_type, msg) + + +def sign_failed_notify(user_obj, developer_obj, app_obj): + """ + 3, '应用签名失败' + :return: + """ + message_type = 3 + now_time = get_format_time().replace('_', ' ') + + msg = Config.MSG_ERROR_DEVELOPER % ( + developer_obj.user_id.first_name, app_obj.name, + now_time, developer_obj.issuer_id) + + for wx_user_obj in get_notify_wx_queryset(user_obj, message_type): + res = WxTemplateMsg(wx_user_obj.openid, wx_user_obj.nickname).operate_failed_msg( + user_obj.first_name, f'应用 {app_obj.name} 签名失败了', + f'开发者{developer_obj.issuer_id} 状态 {developer_obj.get_status_dispaly()}', now_time, '请登录后台查看具体信息') + logger.info(f'user_obj {user_obj} weixin notify pay success result: {res}') + + notify_by_email(user_obj, message_type, msg) + + +def sign_unavailable_developer(user_obj, app_obj): + """ + 3, '应用签名失败' + :return: + """ + message_type = 3 + now_time = get_format_time().replace('_', ' ') + msg = Config.MSG_NOT_EXIST_DEVELOPER % (user_obj.first_name, now_time, app_obj.name) + for wx_user_obj in get_notify_wx_queryset(user_obj, message_type): + res = WxTemplateMsg(wx_user_obj.openid, wx_user_obj.nickname).operate_failed_msg( + user_obj.first_name, f'应用 {app_obj.name} 签名失败了', + f'苹果开发者总设备量已经超限', now_time, '添加新的苹果开发者或者修改开发者设备数量') + logger.info(f'user_obj {user_obj} weixin notify pay success result: {res}') + + notify_by_email(user_obj, message_type, msg) diff --git a/fir_ser/common/notify/ntasks.py b/fir_ser/common/notify/ntasks.py new file mode 100644 index 0000000..30d82a3 --- /dev/null +++ b/fir_ser/common/notify/ntasks.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- +# project: fir_ser +# filename: ntasks +# author: liuyu +# data: 2022/3/26 +import datetime +import logging + +from api.utils.modelutils import get_notify_wx_queryset +from common.base.magic import magic_wrapper, magic_notify +from common.cache.storage import NotifyLoopCache +from common.core.sysconfig import Config +from common.libs.mp.wechat import WxTemplateMsg +from common.notify.utils import notify_by_email +from xsign.models import AppIOSDeveloperInfo +from xsign.utils.modelutils import get_developer_devices + +logger = logging.getLogger(__name__) + + +def download_times_not_enough(user_obj): + """ + 1, '下载次数不足' + :param user_obj: + :return: + """ + message_type = 1 + msg = f"您当前账户下载次数仅剩 {user_obj.download_times},已超过您设置的阈值 {user_obj.notify_available_downloads},为了避免业务使用,望您尽快充值!" + for wx_user_obj in get_notify_wx_queryset(user_obj, message_type): + res = WxTemplateMsg(wx_user_obj.openid, wx_user_obj.nickname).download_times_not_enough_msg( + user_obj.first_name, user_obj.download_times, msg) + logger.info(f'user_obj {user_obj} download times not enough result: {res}') + notify_by_email(user_obj, message_type, msg) + + +def apple_developer_devices_not_enough(user_obj, device_count): + """ + 0, '签名余额不足' + :param user_obj: + :return: + """ + message_type = 0 + msg = f"您当前账户超级签名可用设备仅剩 {device_count},已超过您设置的阈值 {user_obj.notify_available_signs},为了避免业务使用,望您尽快添加苹果开发者!" + for wx_user_obj in get_notify_wx_queryset(user_obj, message_type): + res = WxTemplateMsg(wx_user_obj.openid, wx_user_obj.nickname).apple_developer_devices_not_enough_msg( + user_obj.first_name, device_count, msg) + logger.info(f'user_obj {user_obj} sign devices not enough result: {res}') + notify_by_email(user_obj, message_type, msg) + + +def apple_developer_cert_expired(user_obj, developer_queryset): + """ + 6, '证书到期消息' + :param developer_queryset: + :param user_obj: + :return: + """ + message_type = 6 + developer_count = developer_queryset.count() + developer_obj = developer_queryset.first() + expired_time = developer_obj.cert_expire_time.strftime("%Y年%m月%d") + if developer_count == 1: + issuer_id = developer_obj.issuer_id + cert_id = developer_obj.certid + msg = f"用户 {user_obj.first_name} 您好,您苹果开发者 {issuer_id} ,证书 {cert_id} 即将到期,到期时间 {expired_time},为了保证您开发者可用,请您尽快更新开发者证书,感谢您的关注" + else: + issuer_id = f'{developer_obj.issuer_id} 等 {developer_count} 个开发者ID' + cert_id = f'{developer_obj.certid} 等 {developer_count} 个证书ID' + msg = f"用户 {user_obj.first_name} 您好,您苹果开发者 {issuer_id} ,证书 {cert_id} 即将到期,到期时间 {expired_time},为了保证您开发者可用,请您尽快更新开发者证书,感谢您的关注 " + + for wx_user_obj in get_notify_wx_queryset(user_obj, message_type): + res = WxTemplateMsg(wx_user_obj.openid, wx_user_obj.nickname).cert_expired_msg(issuer_id, + cert_id, + expired_time) + logger.info(f'user_obj {user_obj} apple developer cert expired result: {res}') + + notify_by_email(user_obj, message_type, msg) + + +def check_user_download_times(user_obj, days=None): + if days is None: + days = [0, 3, 7] + if user_obj.notify_available_downloads == 0 or user_obj.notify_available_downloads < user_obj.download_times: + return + notify_rules = [ + { + 'func': magic_wrapper(lambda obj: obj.download_times < obj.notify_available_downloads, user_obj), + 'notify': days, + 'cache': NotifyLoopCache(user_obj.uid, 'download_times'), + 'notify_func': [magic_wrapper(download_times_not_enough, user_obj)] + } + ] + magic_notify(notify_rules) + + +def check_apple_developer_devices(user_obj, days=None): + if days is None: + days = [0, 3, 7] + developer_used_info = get_developer_devices(AppIOSDeveloperInfo.objects.filter(user_id=user_obj)) + device_count = developer_used_info.get('can_sign_number', 0) + if user_obj.notify_available_signs == 0 or user_obj.notify_available_signs < device_count: + return + notify_rules = [ + { + 'func': magic_wrapper(lambda obj: device_count < obj.notify_available_downloads, user_obj), + 'notify': days, + 'cache': NotifyLoopCache(user_obj.uid, 'sign_device_times'), + 'notify_func': [magic_wrapper(apple_developer_devices_not_enough, user_obj, device_count)] + } + ] + magic_notify(notify_rules) + + +def check_apple_developer_cert(user_obj, expire_day=7): + expire_time = datetime.datetime.now() + datetime.timedelta(days=expire_day) + developer_queryset = AppIOSDeveloperInfo.objects.filter(user_id=user_obj, status__in=Config.DEVELOPER_USE_STATUS, + cert_expire_time__lte=expire_time).order_by( + 'cert_expire_time') + if developer_queryset.count() == 0: + return + + notify_rules = [ + { + 'func': magic_wrapper(lambda obj: obj.download_times < obj.notify_available_downloads, user_obj), + 'notify': [0, 3, 7], + 'cache': NotifyLoopCache(user_obj.uid, 'developer_cert'), + 'notify_func': [magic_wrapper(apple_developer_cert_expired, user_obj, developer_queryset)] + } + ] + magic_notify(notify_rules) diff --git a/fir_ser/common/notify/utils.py b/fir_ser/common/notify/utils.py new file mode 100644 index 0000000..fe7ef2f --- /dev/null +++ b/fir_ser/common/notify/utils.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- +# project: fir_ser +# filename: wx +# author: liuyu +# data: 2022/3/23 +import logging + +from api.utils.modelutils import get_notify_email_queryset +from common.utils.sendmsg import get_sender_email_token + +logger = logging.getLogger(__name__) + + +def notify_by_email(user_obj, message_type, msg): + for notify_email in get_notify_email_queryset(user_obj, message_type): + email = notify_email.get('email') + if email: + get_sender_email_token('email', email, 'msg', f'您好,{user_obj.first_name},{msg}') diff --git a/fir_ser/common/utils/caches.py b/fir_ser/common/utils/caches.py index 6d61491..b63a12d 100644 --- a/fir_ser/common/utils/caches.py +++ b/fir_ser/common/utils/caches.py @@ -20,6 +20,7 @@ from common.cache.storage import AppDownloadTodayTimesCache, AppDownloadTimesCac UploadTmpFileNameCache, RedisCacheBase, UserCanDownloadCache, UserFreeDownloadTimesCache, WxTicketCache, \ SignUdidQueueCache, CloudStorageCache from common.core.sysconfig import Config +from common.notify.notify import pay_success_notify from fir_ser.settings import CACHE_KEY_TEMPLATE, SYNC_CACHE_TO_DATABASE logger = logging.getLogger(__name__) @@ -289,6 +290,7 @@ def update_order_info(user_id, out_trade_no, payment_number, payment_type, descr "description"]) add_user_download_times(user_id, download_times) logger.info(f"{user_obj} 订单 {out_trade_no} msg:{order_obj.description}") + pay_success_notify(user_obj, order_obj) return True except Exception as e: logger.error(f"{user_obj} 订单 {out_trade_no} 更新失败 Exception:{e}") diff --git a/fir_ser/config.py b/fir_ser/config.py index bed1f21..63fcf90 100644 --- a/fir_ser/config.py +++ b/fir_ser/config.py @@ -341,8 +341,8 @@ bIX1aWjPxirQX9mzaL3oEQI= class MSGCONF(object): - MSG_NOT_EXIST_DEVELOPER = '用户 %s 你好,应用 %s 签名失败了,苹果开发者总设备量已经超限,请添加新的苹果开发者或者修改开发者设备数量。感谢有你!' - MSG_ERROR_DEVELOPER = '用户 %s 你好,应用 %s 签名失败了,苹果开发者 %s 信息异常,请重新检查苹果开发者状态是否正常。感谢有你!' + MSG_NOT_EXIST_DEVELOPER = '用户 %s 你好,应用 %s 在 %s 签名失败了,苹果开发者总设备量已经超限,请添加新的苹果开发者或者修改开发者设备数量。感谢有你!' + MSG_ERROR_DEVELOPER = '用户 %s 你好,应用 %s 在 %s 签名失败了,苹果开发者 %s 信息异常,请重新检查苹果开发者状态是否正常。感谢有你!' MSG_AUTO_CHECK_DEVELOPER = '用户 %s 你好,苹果开发者 %s 信息异常,请重新检查苹果开发者状态是否正常。感谢有你!' diff --git a/fir_ser/fir_ser/settings.py b/fir_ser/fir_ser/settings.py index ad4dad7..54efffb 100644 --- a/fir_ser/fir_ser/settings.py +++ b/fir_ser/fir_ser/settings.py @@ -468,6 +468,24 @@ CELERY_BEAT_SCHEDULE = { 'schedule': crontab(hour=1, minute=1), 'args': () }, + 'download_times_notify_check_job': { + 'task': 'api.tasks.download_times_notify_check_job', + # 'schedule': SYNC_CACHE_TO_DATABASE.get("auto_check_ios_developer_active_times"), + 'schedule': crontab(hour=1, minute=10), + 'args': () + }, + 'apple_developer_devices_check_job': { + 'task': 'api.tasks.apple_developer_devices_check_job', + # 'schedule': SYNC_CACHE_TO_DATABASE.get("auto_check_ios_developer_active_times"), + 'schedule': crontab(hour=1, minute=20), + 'args': () + }, + 'apple_developer_cert_notify_check_job': { + 'task': 'api.tasks.apple_developer_cert_notify_check_job', + # 'schedule': SYNC_CACHE_TO_DATABASE.get("auto_check_ios_developer_active_times"), + 'schedule': crontab(hour=2, minute=1), + 'args': () + }, 'sync_wx_access_token_job': { 'task': 'api.tasks.sync_wx_access_token_job', 'schedule': SYNC_CACHE_TO_DATABASE.get("wx_get_access_token_times"), diff --git a/fir_ser/xsign/utils/supersignutils.py b/fir_ser/xsign/utils/supersignutils.py index 02585f8..5e1825d 100644 --- a/fir_ser/xsign/utils/supersignutils.py +++ b/fir_ser/xsign/utils/supersignutils.py @@ -22,6 +22,7 @@ from common.base.baseutils import file_format_path, delete_app_profile_file, get from common.base.magic import run_function_by_locker, call_function_try_attempts, magic_wrapper from common.cache.state import CleanErrorBundleIdSignDataState from common.core.sysconfig import Config +from common.notify.notify import sign_failed_notify, sign_unavailable_developer from common.utils.caches import del_cache_response_by_short, send_msg_over_limit, check_app_permission, \ consume_user_download_times_by_app_obj, add_udid_cache_queue, get_and_clean_udid_cache_queue from common.utils.storage import Storage @@ -33,7 +34,7 @@ from xsign.utils.iossignapi import ResignApp, AppDeveloperApiV2 from xsign.utils.modelutils import get_ios_developer_public_num, check_ipa_is_latest_sign, \ update_or_create_developer_udid_info, check_uid_has_relevant, get_developer_udided from xsign.utils.serializer import BillAppInfoSerializer, BillDeveloperInfoSerializer -from xsign.utils.utils import delete_app_to_dev_and_file, send_ios_developer_active_status +from xsign.utils.utils import delete_app_to_dev_and_file logger = logging.getLogger(__name__) @@ -217,10 +218,7 @@ def disable_developer_and_send_email(app_obj, developer_obj): logger.error(f"app {app_obj} developer {developer_obj} sign failed. so disabled") developer_obj.status = 5 developer_obj.save(update_fields=['status']) - send_ios_developer_active_status(developer_obj.user_id, - Config.MSG_ERROR_DEVELOPER % ( - developer_obj.user_id.first_name, app_obj.name, - developer_obj.issuer_id)) + sign_failed_notify(developer_obj.user_id, developer_obj, app_obj) def get_new_developer_by_app_obj(app_obj, obj_base_filter, apple_to_app=False): @@ -416,9 +414,8 @@ class IosUtils(object): if self.user_obj.email: if send_msg_over_limit("get", self.user_obj.email): send_msg_over_limit("set", self.user_obj.email) - send_ios_developer_active_status(self.user_obj, Config.MSG_NOT_EXIST_DEVELOPER - % ( - self.user_obj.first_name, self.app_obj.name)) + sign_unavailable_developer(self.user_obj, self.app_obj) + else: logger.error(f"user {self.user_obj} send msg failed. over limit")