Browse Source

Merge remote-tracking branch 'origin/test'

locky 3 years ago
parent
commit
a5cb1c8bfc

+ 83 - 5
AdminController/TestServeController.py

@@ -19,6 +19,9 @@ from Object.UidTokenObject import UidTokenObject
 from Service.CommonService import CommonService
 from django.db.models import Q, F
 from time import strftime
+from Ansjer.config import BASE_DIR
+from Object import MergePic
+import os
 
 
 class TestServeView(View):
@@ -56,10 +59,41 @@ class TestServeView(View):
 
     def AItest(self, userID, request, request_dict, response):
         file = request.FILES.get('file', None)
-        if not file:
-            return response.json(444)
-        maxLabels = int(request_dict.get('maxLabels', 5))
-        minConfidence = float(request_dict.get('minConfidence', 55))
+        file2 = request.FILES.get('file_one', None)
+        file3 = request.FILES.get('file_two', None)
+        file4 = request.FILES.get('file_three', None)
+
+        post_file_list = [file, file2, file3, file4]
+        file_list = []
+        for index in range(len(post_file_list)):
+            if post_file_list[index]:
+                file_list.append(post_file_list[index])
+
+        del post_file_list
+        if len(file_list) > 1:
+            merge = []
+            now_time = int(time.time())
+            dir_path = os.path.join(BASE_DIR, 'static/', str(now_time))
+            if not os.path.exists(dir_path):
+                os.makedirs(dir_path)
+            for item in file_list:
+                if hasattr(item, 'name'):
+                    file_path = os.path.join(dir_path, item.name)
+                    with open(file_path, 'wb') as f:
+                        for c in item.chunks():
+                            f.write(c)
+                            merge.append(file_path)
+
+            image_size = 500  # 每张小图片的大小
+            image_colnum = 2  # 合并成一张图后,一行有几个小图
+            MergePic.merge_images(dir_path, image_size, image_colnum)
+            # return HttpResponse(dir_path + '.jpg')
+            photo = open(dir_path + '.jpg', 'rb')
+        else:
+            photo = file_list[0]
+
+        maxLabels = int(request_dict.get('maxLabels', 10))
+        minConfidence = float(request_dict.get('minConfidence', 30))
 
         try:
             client = boto3.client(
@@ -71,7 +105,7 @@ class TestServeView(View):
             # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rekognition.html#Rekognition.Client.detect_labels
             rekognition_res = client.detect_labels(
                 Image={
-                    'Bytes': file.read()},
+                    'Bytes': photo.read()},
                 MaxLabels=maxLabels,
                 MinConfidence=minConfidence)
             print('rekognition_res: ', rekognition_res)
@@ -79,3 +113,47 @@ class TestServeView(View):
         except Exception as e:
             print(e)
             return response.json(500, repr(e))
+
+
+
+
+
+
+        files1 = request.FILES.get('image1')
+        files2 = request.FILES.get('image2')
+        files3 = request.FILES.get('image3')
+        files4 = request.FILES.get('image4')
+        file_list = [files1,files2,files3,files4]
+        merge = []
+        now_time = int(time.time())
+        dir_path = os.path.join(BASE_DIR,'static/', str(now_time))
+        if not os.path.exists(dir_path):
+            os.makedirs(dir_path)
+        for item in file_list:
+            if hasattr(item,'name'):
+                file_path = os.path.join(dir_path,item.name)
+                with open(file_path, 'wb') as f:
+                    for c in item.chunks():
+                        f.write(c)
+                        merge.append(file_path)
+
+        image_size = 500  # 每张小图片的大小
+        image_colnum = 2  # 合并成一张图后,一行有几个小图
+        MergePic.merge_images(dir_path, image_size, image_colnum)
+        files = open(dir_path+'.jpg','rb')
+        # return HttpResponse(files.read())
+
+
+        labels = int(request_dict.get('labels',5))
+        minConfidence = int(request_dict.get('minConfidence',99))
+        if not files:
+            return HttpResponse('请上传图片!!!!')
+        client = boto3.client('rekognition', aws_access_key_id='AKIA2E67UIMD6JD6TN3J',aws_secret_access_key='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',region_name='us-east-1')
+        # image = open('E:/photo/a615fa40b8c476bab0f6eeb332e62a5a-1000.jpg', "rb")
+        response = client.detect_labels(Image={'Bytes':files.read()},MaxLabels=labels,MinConfidence=minConfidence)
+        # for obj in response['Labels']:
+        #     exit(obj)
+        #     if obj['Name'] == 'Person':
+        #         jsonstr = json.dumps(obj)
+        return HttpResponse(json.dumps(response, ensure_ascii=False),
+                            content_type="application/json,charset=utf-8")

+ 1 - 1
Ansjer/urls.py

@@ -18,7 +18,7 @@ from Controller import FeedBack, EquipmentOTA, EquipmentInfo, AdminManage, AppIn
     VerifyCodeController, FileController, UIDController, LogController, SalesController, \
     OrderTaskController, HistoryUIDController, UIDManageUserController, SerialNumberController, CompanyController, \
     RegionController, VPGController, LanguageController, TestController, DeviceConfirmRegion, S3GetStsController, \
-    DetectControllerV2, ShadowController, TestDetectController, PcInfo, PctestController, DeviceDebug, \
+    DetectControllerV2, ShadowController, TestDetectController, PcInfo, PctestController, DeviceDebug, PaymentCycle, \
     DeviceLogController
 from AdminController import UserManageController, RoleController, MenuController, TestServeController, \
     ServeManagementController, LogManagementController, DeviceManagementController, VersionManagementController

+ 19 - 3
Controller/CloudStorage.py

@@ -52,6 +52,7 @@ from Service.CommonService import CommonService
 from Object.m3u8generate import PlaylistGenerator
 from Object.WechatPayObject import WechatPayObject
 from django.db.models import Q, F, Count
+from Controller.PaymentCycle import Paypal
 from Ansjer.config import SERVER_TYPE
 from Service.ModelService import ModelService
 
@@ -216,7 +217,7 @@ class CloudStorageView(View):
         qs = qs.values("id", "title", "content", "price", "day", "currency", "bucket__storeDay",
                        "bucket__bucket", "bucket__area", "commodity_code",
                        "commodity_type", "is_discounts", "virtual_price", "expire",
-                       "discount_price", "discount_content", "symbol")
+                       "discount_price", "discount_content", "symbol","cycle_config_id")
 
         if qs.exists():
             ql = list(qs)
@@ -229,7 +230,8 @@ class CloudStorageView(View):
                 for key, val in enumerate(items_list):
                     pay_types = Pay_Type.objects.filter(store_meal=items_list[key]['id']).values("id", "payment")
                     items_list[key]['pay_type'] = list(pay_types)
-
+                    items_list[key]['is_pay_cycle'] = 1 if items_list[key]['cycle_config_id'] else 0
+                    del items_list[key]['cycle_config_id']
                 res_c = {'area': area, 'items': items_list}
                 res.append(res_c)
             #是否促销
@@ -1130,7 +1132,7 @@ class CloudStorageView(View):
         smqs = Store_Meal.objects.filter(id=rank, pay_type=pay_type, lang__lang=lang, is_show=0). \
             values('currency', 'price', 'lang__content', 'day',
                    'commodity_type', 'lang__title',
-                   'expire', 'commodity_code', 'discount_price', 'bucket__mold')
+                   'expire', 'commodity_code', 'discount_price', 'bucket__mold', 'cycle_config_id')
         if not smqs.exists():
             return response.json(173)
         currency = smqs[0]['currency']
@@ -1157,6 +1159,20 @@ class CloudStorageView(View):
 
         orderID = CommonService.createOrderID()
         if pay_type == 1:
+            # 订阅周期扣款
+            if(smqs[0]['cycle_config_id']):
+                subInfo = Paypal.subscriptions(store_info=smqs[0],lang=lang,orderID=orderID)
+                if not subInfo:
+                    return response.json(10048)
+                Order_Model.objects.create(orderID=orderID, UID=uid, channel=channel, userID_id=userID,
+                                           desc=content, payType=pay_type, payTime=nowTime,
+                                           price=price, currency=currency, addTime=nowTime, updTime=nowTime,
+                                           pay_url=subInfo['url'], isSelectDiscounts=is_select_discount,
+                                           commodity_code=commodity_code, commodity_type=commodity_type,
+                                           rank_id=rank, plan_id=subInfo['plan_id'])
+                return response.json(0, {"redirectUrl": subInfo['url'], "orderID": orderID})
+
+            #正常扣款
             cal_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
             if lang != 'cn':
                 cal_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)

+ 249 - 0
Controller/PaymentCycle.py

@@ -0,0 +1,249 @@
+from Ansjer.config import PAYPAL_CRD,SERVER_DOMAIN,SERVER_DOMAIN_SSL
+from Model.models import PayCycleConfigModel,Order_Model, Store_Meal, UID_Bucket, PromotionRuleModel, Unused_Uid_Meal,Device_Info
+from Service.CommonService import CommonService
+from django.http import JsonResponse, HttpResponseRedirect, HttpResponse
+import requests
+import time
+from Object.ResponseObject import ResponseObject
+import paypalrestsdk
+from paypalrestsdk import BillingAgreement
+from django.views.generic.base import View
+from django.db import transaction
+from Controller import CloudStorage
+from django.db.models import Q, F, Count
+
+#周期扣款相关
+class Paypal:
+    def subscriptions(store_info,lang,orderID):
+        cycle_config = PayCycleConfigModel.objects.filter(id=store_info['cycle_config_id']).values()
+        if not cycle_config:
+            return False
+        cal_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+        if lang != 'cn':
+            cal_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+        return_url = "{SERVER_DOMAIN_SSL}payCycle/paypalCycleReturn?lang={lang}". \
+            format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL, lang=lang)
+        # call_sub_url = "http://binbin.uicp.vip/cloudstorage/dopaypalcallback?orderID={orderID}".format(
+        # SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL, orderID=orderID)
+        BillingPlan = {
+            "description": orderID,
+            "merchant_preferences": {
+                "auto_bill_amount": "YES",
+                "cancel_url": cal_url,  # 取消协议url
+                "initial_fail_amount_action": "CANCEL",
+                "max_fail_attempts": "1",  # 允许的最大失败付款尝试次数
+                "return_url": return_url,  # 客户批准协议的url
+                # "notify_url": "http://www.notify.com",  #通知客户协议已创建的 URL。只读并保留供将来使用。
+                "setup_fee": {
+                    "currency": store_info['currency'],
+                    "value": store_info['price'],
+                }
+            },
+            "name": store_info['lang__content'],
+            "payment_definitions": [
+                {
+                    "amount": {
+                        "currency": store_info['currency'],
+                        "value": store_info['price']
+                    },
+                    # "charge_models": [
+                    #     {
+                    #         "amount": {
+                    #             "currency": "USD",
+                    #             "value": "20"
+                    #         },
+                    #         "type": "TAX"   #税金
+                    #     }
+                    # ],
+                    "cycles": cycle_config[0]['cycles'],
+                    "frequency": cycle_config[0]['frequency'],
+                    "frequency_interval": cycle_config[0]['frequencyInterval'],
+                    "name": store_info['lang__title'],
+                    "type": "REGULAR"
+                },
+            ],
+            "type": "INFINITE",
+        }
+        paypalrestsdk.configure(PAYPAL_CRD)
+        billing_plan = paypalrestsdk.BillingPlan(BillingPlan)
+        if billing_plan.create():
+            billing_plan.activate()  # 激活
+            plan_id = billing_plan.id
+        else:
+            print(billing_plan.error)
+            return False
+
+        now_time = int(time.time())
+        start_date_timestamp = CommonService.calcMonthLater(1, now_time) - (5 * 86400) #下次扣款为下个月提前5天扣款
+        start_date_str = CommonService.timestamp_to_str(start_date_timestamp, "%Y-%m-%dT%H:%M:%SZ")
+        #订阅
+        billingAgreement = {
+            "name": store_info['lang__content'],
+            "description": orderID,
+            "start_date": start_date_str,
+            "plan": {
+                "id": plan_id
+            },
+            "payer": {
+                "payment_method": "paypal"
+            },
+        }
+        billing_agreement = paypalrestsdk.BillingAgreement(billingAgreement)
+        # print(billing_agreement.create())
+        if billing_agreement.create():
+            for link in billing_agreement.links:
+                if link.rel == "approval_url":
+                    return {"plan_id": plan_id, "url": link.href}
+        else:
+            print(billing_agreement.error)
+            return False
+
+class PaypalCycleNotify(View):
+    def get(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.GET, request, operation)
+
+    def post(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.POST, request, operation)
+
+    def validation(self, request_dict, request, operation):
+        response = ResponseObject()
+        if operation is None:
+            return response.json(444, 'error path')
+        elif operation == 'paypalCycleReturn':  # paypal成功订阅回调
+            return self.do_paypal_cycle_return(request_dict, response)
+        elif operation == 'paypalCycleNotify':  # paypal 周期付款回调
+            return self.do_paypal_webhook_notify(request_dict, response)
+    def do_paypal_cycle_return(self, request_dict, response):
+        lang = request_dict.get('lang', 'en')
+        token = request_dict.get('token',None)
+        paypalrestsdk.configure(PAYPAL_CRD)
+        billing_agreement = paypalrestsdk.BillingAgreement()
+        billing_agreement_response = billing_agreement.execute(token)
+        if billing_agreement_response.error:
+            print(billing_agreement_response.error)
+            red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+            if lang != 'cn':
+                red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+            return HttpResponseRedirect(red_url)
+
+        orderID = billing_agreement_response.description
+        agreement_id = billing_agreement_response.id
+        promotion_rule_id = ''
+        try:
+            order_qs = Order_Model.objects.filter(orderID=orderID, status=0)
+
+            if not orderID:
+                print("not orderID")
+                red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+                if lang != 'cn':
+                    red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+                return HttpResponseRedirect(red_url)
+
+            order_list = order_qs.values("UID", "channel", "commodity_code", "rank", "isSelectDiscounts",
+                                         "userID__userID",
+                                         "userID__username")
+            userid = order_list[0]['userID__userID']
+            username = order_list[0]['userID__username']
+            UID = order_list[0]['UID']
+            channel = order_list[0]['channel']
+            rank = order_list[0]['rank']
+            smqs = Store_Meal.objects.filter(id=rank). \
+                values("day", "bucket_id", "bucket__storeDay", "expire")
+            bucketId = smqs[0]['bucket_id']
+            if not smqs.exists():
+                print("not smqs")
+                red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+                if lang != 'cn':
+                    red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+                return HttpResponseRedirect(red_url)
+            # ##
+            ubqs = UID_Bucket.objects.filter(uid=UID).values("id", "bucket_id", "bucket__storeDay", "bucket__region",
+                                                             "endTime", "use_status")
+            expire = smqs[0]['expire']
+
+            if order_list[0]['isSelectDiscounts'] == 1:
+                expire = smqs[0]['expire'] * 2
+            # 是否有促销
+            nowTime = int(time.time())
+            promotion = PromotionRuleModel.objects.filter(status=1, startTime__lte=nowTime,
+                                                          endTime__gte=nowTime).values('id','ruleConfig')
+            if promotion.exists():
+                promotion_rule_id = promotion[0]['id']
+                expire = expire * 2
+            with transaction.atomic():
+                if ubqs.exists():
+                    ubq = ubqs[0]
+                    if ubq['use_status'] == 1 and ubq['bucket_id'] == bucketId:  #套餐使用中并且相同套餐叠加过期时间
+                        endTime = CommonService.calcMonthLater(expire, ubq['endTime'])
+                        UID_Bucket.objects.filter(id=ubq['id']).update \
+                            (uid=UID, channel=channel, bucket_id=bucketId,
+                             endTime=endTime, updateTime=nowTime)
+                    else:     #已过期或者不相同的套餐加入未使用的关联套餐表
+                        has_unused = Unused_Uid_Meal.objects.filter(uid=UID, bucket_id=bucketId).values("id")
+                        nums = 2 if order_list[0]['isSelectDiscounts'] == 1 else 1
+                        if promotion.exists():
+                            nums = nums + 1
+                        if has_unused.exists():
+                            Unused_Uid_Meal.objects.filter(id=has_unused[0]['id']).update(num=F('num') + nums)
+                        else:
+                            Unused_Uid_Meal.objects.create(uid=UID,channel=channel,addTime=nowTime,num=nums,
+                                                           expire=smqs[0]['expire'],bucket_id=bucketId)
+                        UID_Bucket.objects.filter(id=ubq['id']).update(has_unused=1)
+                    uid_bucket_id = ubq['id']
+                else:
+                    endTime = CommonService.calcMonthLater(expire)
+                    ub_cqs = UID_Bucket.objects.create \
+                        (uid=UID, channel=channel, bucket_id=bucketId, endTime=endTime, addTime=nowTime,
+                         updateTime=nowTime,use_status=1)
+                    uid_bucket_id = ub_cqs.id
+
+                dvq = Device_Info.objects.filter(UID=UID, vodPrimaryUserID='', vodPrimaryMaster='')
+                if dvq.exists():
+                    dvq_set_update_dict = {
+                        'vodPrimaryUserID': userid,
+                        'vodPrimaryMaster': username
+                    }
+                    dvq.update(**dvq_set_update_dict)
+
+                # uid_main_exist = UIDMainUser.objects.filter(UID=UID)
+                # if not uid_main_exist.exists():
+                #     uid_main_dict = {
+                #         'UID': UID,
+                #         'user_id': userid
+                #     }
+                #     UIDMainUser.objects.create(**uid_main_dict)
+
+                order_qs.update(status=1, updTime=nowTime, uid_bucket_id=uid_bucket_id,
+                                promotion_rule_id=promotion_rule_id,agreement_id=agreement_id)
+                datetime = time.strftime("%Y-%m-%d", time.localtime())
+                sys_msg_text_list = ['温馨提示:尊敬的客户,您的' + UID + '设备在' + datetime + '已成功订阅云存套餐',
+                                     'Dear customer,you already subscribed the cloud storage package successfully for device ' + UID + ' on ' + time.strftime(
+                                         "%b %dth,%Y", time.localtime())]
+
+                CloudStorage.CloudStorageView.do_vod_msg_Notice(self, UID, channel, userid, lang, sys_msg_text_list, 'SMS_219738485')
+
+                # return response.json(0)
+                red_url = "{SERVER_DOMAIN_SSL}web/paid2/success.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+                if lang != 'cn':
+                    red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_success.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+                return HttpResponseRedirect(red_url)
+        except Exception as e:
+            print(repr(e))
+            if order_qs:
+                order_qs.update(status=10, promotion_rule_id=promotion_rule_id)
+            red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+            if lang != 'cn':
+                red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+            return HttpResponseRedirect(red_url)
+
+
+    def do_paypal_webhook_notify(self, request_dict, response):
+        paymentId = request_dict.get('paymentId', None)
+        PayerID = request_dict.get('PayerID', None)
+        lang = request_dict.get('lang', 'en')
+        token = request_dict.get('token',None)
+        paypalrestsdk.configure(PAYPAL_CRD)

+ 8 - 24
Controller/UidSetController.py

@@ -160,30 +160,14 @@ class UidSetView(View):
                     else:
                         val = val + 1
                         print('UID_Preview表没有数据')
-                if 'UID_Bucket' in id_list:
-                    del_uid = UID_Bucket.objects.filter(uid__in=uid)
-                    if del_uid.exists():
-                        del_uid.delete()
-                        # print ('删除UID_Bucket')
-                    else:
-                        val = val + 1
-                        print('UID_Bucket表没有数据')
-                if 'VodHlsModel' in id_list:
-                    del_uid = VodHlsModel.objects.filter(uid__in=uid)
-                    if del_uid.exists():
-                        # del_uid.delete()
-                        print ('删除VodHlsModel')
-                    else:
-                        val = val + 1
-                        print('VodHlsModel表没有数据')
-                if 'Order_Model' in id_list:
-                    del_uid = Order_Model.objects.filter(UID__in=uid)
-                    if del_uid.exists():
-                        del_uid.delete()
-                        # print ('删除Order_Model')
-                    else:
-                        val = val + 1
-                        print('Order_Model表没有数据')
+                if 'Reset_Vod' in id_list:  # 重置设备云存
+                    UID_Bucket.objects.filter(uid__in=uid).delete()
+                    Unused_Uid_Meal.objects.filter(uid__in=uid).delete()
+                    Order_Model.objects.filter(UID__in=uid).delete()
+                    StsCrdModel.objects.filter(uid__in=uid).delete()
+                    VodHlsModel.objects.filter(uid__in=uid).delete()
+                    ExperienceContextModel.objects.filter(uid__in=uid).delete()
+                    Device_Info.objects.filter(UID__in=uid).update(vodPrimaryUserID='', vodPrimaryMaster='')
                 if 'Device_Info' in id_list:
                     del_uid = Device_Info.objects.filter(UID__in=uid)
                     if del_uid.exists():

+ 21 - 5
Model/models.py

@@ -472,9 +472,8 @@ class Store_Meal(models.Model):
     is_show = models.SmallIntegerField(default=0, verbose_name=u'该套餐是否隐藏 [0=否,1是]')
     # lang = models.CharField(default='', max_length=20, verbose_name='语言/国家')
     lang = models.ManyToManyField(to='Lang', verbose_name='套餐语言', db_table='store_meal_lang')
-
+    cycle_config_id = models.IntegerField(null=True, verbose_name='周期付款配置表id')
     # 备用字段
-    spare_1 = models.CharField(default='', blank=True, max_length=64, verbose_name=u'备用字段1')
     spare_2 = models.CharField(default='', blank=True, max_length=64, verbose_name=u'备用字段2')
     spare_3 = models.CharField(default='', blank=True, max_length=64, db_index=True, verbose_name=u'备用字段3')
     spare_4 = models.CharField(default='', blank=True, max_length=64, db_index=True, verbose_name=u'备用字段4')
@@ -623,12 +622,12 @@ class Order_Model(models.Model):
     pay_url = models.CharField(max_length=2000, default='', verbose_name='支付url')
     paypal = models.CharField(max_length=500, null=True, blank=True, verbose_name='支付批准url')
     promotion_rule_id = models.CharField(blank=True, max_length=64, default='', verbose_name='促销id')
+    plan_id = models.CharField(default='', blank=True, max_length=64, verbose_name=u'paypal计划id')
+    agreement_id = models.CharField(default='', blank=True, max_length=64, verbose_name=u'paypal协议id')
 
     # 备用字段
-    spare_1 = models.CharField(default='', blank=True, max_length=64, verbose_name=u'备用字段1')
-    spare_2 = models.CharField(default='', blank=True, max_length=64, verbose_name=u'备用字段2')
-    spare_3 = models.CharField(default='', blank=True, max_length=64, db_index=True, verbose_name=u'备用字段3')
     spare_4 = models.CharField(default='', blank=True, max_length=64, db_index=True, verbose_name=u'备用字段4')
+    spare_2 = models.CharField(default='', blank=True, max_length=64, verbose_name=u'备用字段2')
 
     def __str__(self):
         return self.orderID
@@ -639,6 +638,23 @@ class Order_Model(models.Model):
         verbose_name_plural = verbose_name
         ordering = ('-orderID',)
 
+class PayCycleConfigModel(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name='主键')
+    # name = models.CharField(default='',max_length=200, verbose_name='计划名字')
+    # tax = models.CharField(max_length=2000, default='', verbose_name='税金')
+    cycles = models.IntegerField(verbose_name='周期:0代表无限期', default=0)
+    frequency = models.CharField(max_length=50,verbose_name='频率,MONTH,WEEK,YEAR', default=0)
+    frequencyInterval = models.IntegerField(default=0, verbose_name='频率周期')
+    remark = models.CharField(max_length=1000, default='', verbose_name='备注')
+
+    def __str__(self):
+        return self.id
+
+    class Meta:
+        db_table = 'pay_cycle_config'
+        verbose_name = u'周期付款计划'
+        verbose_name_plural = verbose_name
+
 class PromotionRuleModel(models.Model):
     id = models.AutoField(primary_key=True, verbose_name='主键')
     ruleName = models.TextField(default='', verbose_name='规则名字')                     #json格式, 例: {"cn":"黑色星期五","en":"Black Friday"}

+ 94 - 0
Object/MergePic.py

@@ -0,0 +1,94 @@
+import os
+
+import PIL.Image as Image
+
+
+def resize_by_width(infile, image_size):
+    """按照宽度进行所需比例缩放"""
+    im = Image.open(infile)
+    (x, y) = im.size
+    lv = round(x / image_size, 2) + 0.01
+    x_s = int(x // lv)
+    y_s = int(y // lv)
+    print("x_s", x_s, y_s)
+    out = im.resize((x_s, y_s), Image.ANTIALIAS)
+    return out
+
+
+def get_new_img_xy(infile, image_size):
+    """返回一个图片的宽、高像素"""
+    im = Image.open(infile)
+    (x, y) = im.size
+    lv = round(x / image_size, 2) + 0.01
+    x_s = x // lv
+    y_s = y // lv
+    # print("x_s", x_s, y_s)
+    # out = im.resize((x_s, y_s), Image.ANTIALIAS)
+    return x_s, y_s
+
+
+# 定义图像拼接函数
+def image_compose(image_colnum, image_size, image_rownum, image_names, image_save_path, x_new, y_new):
+    to_image = Image.new('RGB', (image_colnum * x_new, image_rownum * y_new))  # 创建一个新图
+    # 循环遍历,把每张图片按顺序粘贴到对应位置上
+    total_num = 0
+    for y in range(1, image_rownum + 1):
+        for x in range(1, image_colnum + 1):
+            from_image = resize_by_width(image_names[image_colnum * (y - 1) + x - 1], image_size)
+            # from_image = Image.open(image_names[image_colnum * (y - 1) + x - 1]).resize((image_size,image_size ), Image.ANTIALIAS)
+            to_image.paste(from_image, ((x - 1) * x_new, (y - 1) * y_new))
+            total_num += 1
+            if total_num == len(image_names):
+                break
+    return to_image.save(image_save_path)  # 保存新图
+
+
+def get_image_list_fullpath(dir_path):
+    file_name_list = os.listdir(dir_path)
+    image_fullpath_list = []
+    for file_name_one in file_name_list:
+        file_one_path = os.path.join(dir_path, file_name_one)
+        if os.path.isfile(file_one_path):
+            image_fullpath_list.append(file_one_path)
+        else:
+            img_path_list = get_image_list_fullpath(file_one_path)
+            image_fullpath_list.extend(img_path_list)
+    return image_fullpath_list
+
+
+def merge_images(image_dir_path,image_size,image_colnum):
+    # 获取图片集地址下的所有图片名称
+    image_fullpath_list = get_image_list_fullpath(image_dir_path)
+    print("image_fullpath_list", len(image_fullpath_list), image_fullpath_list)
+
+    image_save_path = r'{}.jpg'.format(image_dir_path)  # 图片转换后的地址
+    print(image_save_path);
+    # image_rownum = 4  # 图片间隔,也就是合并成一张图后,一共有几行
+    image_rownum_yu = len(image_fullpath_list) % image_colnum
+    if image_rownum_yu == 0:
+        image_rownum = len(image_fullpath_list) // image_colnum
+    else:
+        image_rownum = len(image_fullpath_list) // image_colnum + 1
+
+    x_list = []
+    y_list = []
+    for img_file in image_fullpath_list:
+        img_x, img_y = get_new_img_xy(img_file, image_size)
+        x_list.append(img_x)
+        y_list.append(img_y)
+
+    print("x_list", sorted(x_list))
+    print("y_list", sorted(y_list))
+    x_new = int(x_list[len(x_list) // 5 * 4])
+    y_new = int(y_list[len(y_list) // 5 * 4])
+    print(" x_new, y_new", x_new, y_new)
+    image_compose(image_colnum, image_size, image_rownum, image_fullpath_list, image_save_path, x_new, y_new)  # 调用函数
+    # for img_file in image_fullpath_list:
+    #     resize_by_width(img_file,image_size)
+
+if __name__ == '__main__':
+
+    image_dir_path = r'E:\photo\test'  # 图片集地址
+    image_size = 500  # 每张小图片的大小
+    image_colnum = 2  # 合并成一张图后,一行有几个小图
+    merge_images(image_dir_path, image_size, image_colnum)

+ 2 - 0
Object/ResponseObject.py

@@ -104,6 +104,7 @@ class ResponseObject(object):
             10045: 'Already the latest version',
             10046: 'Sorry, users who have activated cloud storage packages do not support logout at the moment, please contact customer service',
             10047: 'Please delete all devices under your account first',
+            10048: 'Subscribe to the failure',
         }
         data_cn = {
             0: '成功',
@@ -201,6 +202,7 @@ class ResponseObject(object):
             10045: '当前为最新版本',
             10046: '已开通云存的用户,暂不支持注销,请联系客服',
             10047: '请先删除您当前帐户下的所有设备',
+            10048: '订阅失败',
         }
         if self.lang == 'cn':
             msg = data_cn