diff --git a/fir_client/src/components/FirApps.vue b/fir_client/src/components/FirApps.vue
index 37f2a42..ca5f315 100644
--- a/fir_client/src/components/FirApps.vue
+++ b/fir_client/src/components/FirApps.vue
@@ -27,7 +27,9 @@
-
+
@@ -334,7 +336,7 @@
+ @click="show_package_prices">
@@ -472,7 +474,7 @@
+
+
diff --git a/fir_client/src/restful/index.js b/fir_client/src/restful/index.js
index 70321db..1c3fe12 100644
--- a/fir_client/src/restful/index.js
+++ b/fir_client/src/restful/index.js
@@ -482,4 +482,34 @@ export function iosdevicesudid(callBack, params, load = true) {
true,
true
);
+}
+
+/**获取充值价格信息 */
+export function get_package_prices(callBack, params, load = true) {
+ getData(
+ 'GET',
+ USERSEVER + '/package_prices',
+ params,
+ data => {
+ callBack(data);
+ },
+ load,
+ true,
+ true
+ );
+}
+
+/**用户订单 */
+export function my_order(callBack, params, load = true) {
+ getData(
+ params.methods,
+ USERSEVER + '/orders',
+ params.data,
+ data => {
+ callBack(data);
+ },
+ load,
+ true,
+ true
+ );
}
\ No newline at end of file
diff --git a/fir_client/src/router/index.js b/fir_client/src/router/index.js
index e462092..69dcb41 100644
--- a/fir_client/src/router/index.js
+++ b/fir_client/src/router/index.js
@@ -11,25 +11,6 @@ VueRouter.prototype.push = function push(location) {
};
-// import FirIndex from "@/components/FirIndex";
-// import FirApps from "@/components/FirApps";
-// import FirLogin from "@/components/FirLogin";
-// import FirRegist from "@/components/FirRegist";
-// import FirDownload from "@/components/FirDownload";
-// import FirAppBase from "@/components/FirAppBase";
-// import FirAppInfosBase from "@/components/FirAppInfosBase";
-// import FirAppInfostimeline from "@/components/FirAppInfostimeline";
-// import FirAppInfosbaseinfo from "@/components/FirAppInfosbaseinfo";
-// import FirAppInfossecurity from "@/components/FirAppInfossecurity";
-// import FirAppInfosdevices from "@/components/FirAppInfosdevices";
-// import FirAppInfoscombo from "@/components/FirAppInfoscombo";
-// import FirUserProfileBase from "@/components/FirUserProfileBase";
-// import FirUserProfileInfo from "@/components/FirUserProfileInfo";
-// import FirUserProfileChangePwd from "@/components/FirUserProfileChangePwd";
-// import FirUserProfileStorage from "@/components/FirUserProfileStorage";
-//
-// import FirSuperSignBase from "@/components/FirSuperSignBase";
-
const router = new VueRouter({
mode: 'history',
routes: [
@@ -175,6 +156,18 @@ const router = new VueRouter({
}
]
},
+ {
+ path: '/orders',
+ component: () => import("@/components/FirAppBase"),
+ children: [
+ {
+ path: '',
+ name: 'FirUserOrders',
+ component: () => import("@/components/FirUserOrders"),
+
+ }
+ ]
+ },
{
path: '/register',
name: 'FirRegist',
diff --git a/fir_ser/api/admin.py b/fir_ser/api/admin.py
index df22350..37cdcd1 100644
--- a/fir_ser/api/admin.py
+++ b/fir_ser/api/admin.py
@@ -14,3 +14,5 @@ admin.site.register(AppIOSDeveloperInfo)
admin.site.register(APPSuperSignUsedInfo)
admin.site.register(APPToDeveloper)
admin.site.register(UDIDsyncDeveloper)
+admin.site.register(Price)
+admin.site.register(Order)
diff --git a/fir_ser/api/models.py b/fir_ser/api/models.py
index c1a3548..e3b10ef 100644
--- a/fir_ser/api/models.py
+++ b/fir_ser/api/models.py
@@ -344,3 +344,53 @@ class DeveloperDevicesID(models.Model):
def __str__(self):
return "%s-%s-%s-%s" % (self.id, self.app_id, self.developerid, self.udid)
+
+
+######################################## 订单表 ########################################
+
+class Order(models.Model):
+ """订单"""
+ payment_type_choices = ((0, '微信'), (1, '支付宝'), (2, '优惠码'), (4, '银联'))
+ payment_type = models.SmallIntegerField(choices=payment_type_choices)
+ payment_number = models.CharField(max_length=128, verbose_name="支付第3方订单号", null=True, blank=True)
+ order_number = models.CharField(max_length=128, verbose_name="订单号", unique=True) # 考虑到订单合并支付的问题
+ account = models.ForeignKey("UserInfo", on_delete=models.CASCADE)
+ actual_amount = models.BigIntegerField(verbose_name="实付金额,单位分")
+ actual_download_times = models.BigIntegerField(verbose_name="实际购买的数量", default=0)
+ actual_download_gift_times = models.BigIntegerField(verbose_name="实际赠送的数量", default=0)
+ status_choices = ((0, '交易成功'), (1, '待支付'), (2, '退费申请中'), (3, '已退费'), (4, '主动取消'), (5, '超时取消'))
+ status = models.SmallIntegerField(choices=status_choices, verbose_name="状态")
+ order_type_choices = ((0, '用户下单'), (1, '后台充值'),)
+ order_type = models.SmallIntegerField(choices=order_type_choices, default=0, verbose_name="订单类型")
+ pay_time = models.DateTimeField(blank=True, null=True, verbose_name="付款时间")
+ cancel_time = models.DateTimeField(blank=True, null=True, verbose_name="订单取消时间")
+ created_time = models.DateTimeField(verbose_name="订单创建时间", auto_now_add=True)
+ description = models.TextField('备注', blank=True, null=True, default='')
+
+
+def __str__(self):
+ return "%s-%s-%s元" % (self.account, self.order_number, self.actual_amount / 100)
+
+
+class Price(models.Model):
+ name = models.CharField(max_length=128, unique=True, verbose_name="下载包唯一名称")
+ title = models.CharField(max_length=128, verbose_name="下载包名称")
+ description = models.CharField(max_length=128, verbose_name="下载包描述")
+ price = models.BigIntegerField(null=False, verbose_name="下载包价格,单位分")
+ package_size = models.BigIntegerField(null=False, verbose_name="下载包次数")
+ download_count_gift = models.IntegerField(default=0, null=False, verbose_name="赠送下载次数")
+ is_enable = models.BooleanField(default=True, verbose_name="是否启用该价格")
+ updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
+
+ class Meta:
+ verbose_name = '价格列表'
+ verbose_name_plural = "价格列表"
+ unique_together = ('price', 'package_size')
+
+ def save(self, *args, **kwargs):
+ if Price.objects.filter(is_enable=True).count() > 3: # 最多3个启用的价格表
+ raise
+ super(Price, self).save(*args, **kwargs)
+
+ def __str__(self):
+ return "%s-%s-%s-%s" % (self.name, self.price, self.package_size, self.download_count_gift)
diff --git a/fir_ser/api/urls.py b/fir_ser/api/urls.py
index 65efae5..b834710 100644
--- a/fir_ser/api/urls.py
+++ b/fir_ser/api/urls.py
@@ -23,7 +23,7 @@ from api.views.download import ShortDownloadView
from api.views.uploads import AppAnalyseView, UploadView
from api.views.storage import StorageView
from api.views.receiveudids import IosUDIDView
-
+from api.views.order import PriceView, OrderView
from api.views.supersign import DeveloperView, SuperSignUsedView, AppUDIDUsedView
# router=DefaultRouter()
@@ -49,5 +49,7 @@ urlpatterns = [
re_path("^supersign/developer$", DeveloperView.as_view()),
re_path("^supersign/devices$", SuperSignUsedView.as_view()),
re_path("^supersign/udid$", AppUDIDUsedView.as_view()),
+ re_path("^package_prices$", PriceView.as_view()),
+ re_path("^orders$", OrderView.as_view()),
]
diff --git a/fir_ser/api/utils/serializer.py b/fir_ser/api/utils/serializer.py
index 64f97b5..7f8da25 100644
--- a/fir_ser/api/utils/serializer.py
+++ b/fir_ser/api/utils/serializer.py
@@ -320,3 +320,15 @@ class DeviceUDIDSerializer(serializers.ModelSerializer):
def get_bundle_name(self, obj):
return obj.app_id.name
+
+
+class PriceSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = models.Price
+ exclude = ["id"]
+
+
+class OrdersSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = models.Order
+ exclude = ["id"]
diff --git a/fir_ser/api/utils/storage/caches.py b/fir_ser/api/utils/storage/caches.py
index 4215ab7..2965927 100644
--- a/fir_ser/api/utils/storage/caches.py
+++ b/fir_ser/api/utils/storage/caches.py
@@ -350,5 +350,18 @@ def consume_user_download_times(user_id, app_id, amount=1):
return True
+def add_user_download_times(user_id, download_times=0):
+ with cache.lock("%s_%s" % ('consume_user_download_times', user_id)):
+ try:
+ UserInfo.objects.filter(pk=user_id).update(download_times=F('download_times') + download_times)
+ enable_user_download_times_flag(user_id)
+ for app_obj in Apps.objects.filter(pk=user_id):
+ del_cache_response_by_short(app_obj.app_id)
+ return True
+ except Exception as e:
+ logger.error("%s download_times less then 0. Exception:%s" % (user_id, e))
+ return False
+
+
def check_user_has_all_download_times(user_id):
return get_user_free_download_times(user_id) > 0 or check_user_can_download(user_id)
diff --git a/fir_ser/api/utils/utils.py b/fir_ser/api/utils/utils.py
index 907cedd..f0d15e4 100644
--- a/fir_ser/api/utils/utils.py
+++ b/fir_ser/api/utils/utils.py
@@ -3,7 +3,7 @@
# project: 5月
# author: liuyu
# date: 2020/5/7
-import os, re, json, requests
+import os, re, json, requests, datetime, random
from fir_ser.settings import SUPER_SIGN_ROOT, SERVER_DOMAIN, CAPTCHA_LENGTH, MEDIA_ROOT
from api.models import APPSuperSignUsedInfo, APPToDeveloper, \
UDIDsyncDeveloper, UserInfo, AppReleaseInfo, AppScreenShot
@@ -335,3 +335,11 @@ def clean_storage_data(user_obj, storage_obj=None):
for apptodev_obj in APPToDeveloper.objects.filter(app_id=app_release_obj.app_id).all():
storage_obj.delete_file(apptodev_obj.binary_file, app_release_obj.release_type)
return True
+
+
+def get_order_num(order_type=1):
+ now = datetime.datetime.now()
+ date_str = "{0}{1}{2}{3}{4}{5}{6}".format(order_type, now.year, now.month, now.day, now.hour, now.minute,
+ now.second)
+ return date_str + str(random.randint(1000, 9999)) + str(random.randint(1000, 9999)) + str(
+ random.randint(1000, 9999))
diff --git a/fir_ser/api/views/order.py b/fir_ser/api/views/order.py
new file mode 100644
index 0000000..f42d782
--- /dev/null
+++ b/fir_ser/api/views/order.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python
+# -*- coding:utf-8 -*-
+# project: 3月
+# author: NinEveN
+# date: 2021/3/29
+
+from rest_framework.views import APIView
+from api.utils.response import BaseResponse
+from api.utils.auth import ExpiringTokenAuthentication
+from rest_framework.response import Response
+from api.models import UserInfo, Price, Order
+from api.utils.serializer import PriceSerializer, OrdersSerializer
+from rest_framework.pagination import PageNumberPagination
+from api.utils.utils import get_order_num
+from api.utils.storage.caches import add_user_download_times
+import logging
+from django.utils import timezone
+
+logger = logging.getLogger(__name__)
+
+
+class PageNumber(PageNumberPagination):
+ page_size = 10 # 每页显示多少条
+ page_size_query_param = 'size' # URL中每页显示条数的参数
+ page_query_param = 'page' # URL中页码的参数
+ max_page_size = None # 最大页码数限制
+
+
+class OrderView(APIView):
+ authentication_classes = [ExpiringTokenAuthentication, ]
+
+ def get(self, request):
+ res = BaseResponse()
+ order_id = request.query_params.get("order_id", None)
+ order_obj_lists = Order.objects.filter(account=request.user)
+ if order_id:
+ order_obj_lists = order_obj_lists.filter(order_number=order_id)
+ page_obj = PageNumber()
+ order_info_serializer = page_obj.paginate_queryset(queryset=order_obj_lists.order_by("-created_time"),
+ request=request,
+ view=self)
+ order_info = OrdersSerializer(order_info_serializer, many=True, )
+ res.data = order_info.data
+ res.count = order_obj_lists.count()
+
+ res.payment_type_choices = []
+ choices_list = list(Order.payment_type_choices)
+ for auth_t in choices_list:
+ res.payment_type_choices.append({'id': auth_t[0], 'name': auth_t[1]})
+
+ res.status_choices = []
+ choices_list = list(Order.status_choices)
+ for auth_t in choices_list:
+ res.status_choices.append({'id': auth_t[0], 'name': auth_t[1]})
+
+ res.order_type_choices = []
+ choices_list = list(Order.order_type_choices)
+ for auth_t in choices_list:
+ res.order_type_choices.append({'id': auth_t[0], 'name': auth_t[1]})
+
+ return Response(res.dict)
+
+ def post(self, request):
+ res = BaseResponse()
+ price_id = request.data.get("price_id", None)
+ if price_id:
+ price_obj = Price.objects.filter(name=price_id).first()
+ if price_obj:
+ try:
+ Order.objects.create(payment_type=0, order_number=get_order_num(),
+ account=request.user, status=1, order_type=0, actual_amount=price_obj.price,
+ actual_download_times=price_obj.package_size,
+ actual_download_gift_times=price_obj.download_count_gift)
+ return Response(res.dict)
+ except Exception as e:
+ logger.error("%s 订单 %s 保存失败 Exception:%s" % (request.user, price_id, e))
+ res.code = 1003
+ res.msg = "订单保存失败"
+ else:
+ logger.error("%s 价格 %s 获取失败" % (request.user, price_id))
+ res.code = 1002
+ res.msg = "价格获取失败"
+ else:
+ res.code = 1001
+ res.msg = "错误的价格"
+ return Response(res.dict)
+
+ def put(self, request):
+ res = BaseResponse()
+ order_number = request.data.get("order_number", None)
+ if order_number:
+ order_obj = Order.objects.filter(account=request.user, order_number=order_number, status=1).first()
+ if order_obj:
+ download_times = order_obj.actual_download_times + order_obj.actual_download_gift_times
+ try:
+ order_obj.status = 0
+ now = timezone.now()
+ if not timezone.is_naive(now):
+ now = timezone.make_naive(now, timezone.utc)
+ order_obj.pay_time = now
+ n_download_times = UserInfo.objects.filter(pk=request.user.id).values('download_times').first()
+ order_obj.description = "充值成功,充值下载次数 %s ,现总共可用次数 %s" % (
+ download_times, n_download_times.get("download_times", 0))
+ order_obj.save()
+ add_user_download_times(request.user.id, download_times)
+ except Exception as e:
+ logger.error("%s 订单 %s 更新失败 Exception:%s" % (request.user, order_number, e))
+ res.code = 1003
+ return Response(res.dict)
+ else:
+ logger.error("%s 订单 %s 获取失败" % (request.user, order_number))
+ res.code = 1003
+ res.msg = "订单获取失败,或订单已经支付"
+ else:
+ res.code = 1001
+ res.msg = "订单有误"
+ return Response(res.dict)
+
+
+class PriceView(APIView):
+ authentication_classes = [ExpiringTokenAuthentication, ]
+
+ def get(self, request):
+ res = BaseResponse()
+ price_obj_lists = Price.objects.filter(is_enable=True).all().order_by("updated_time").order_by("price")
+ res.data = PriceSerializer(price_obj_lists, many=True).data
+ return Response(res.dict)
+
+ def delete(self, request, price_id):
+ res = BaseResponse()
+ return Response(res.dict)
+
+ def put(self, request, price_id):
+ res = BaseResponse()
+ return Response(res.dict)
diff --git a/fir_ser/api/views/supersign.py b/fir_ser/api/views/supersign.py
index 44e2c60..6c301c6 100644
--- a/fir_ser/api/views/supersign.py
+++ b/fir_ser/api/views/supersign.py
@@ -19,7 +19,7 @@ import logging
logger = logging.getLogger(__file__)
-class AppsPageNumber(PageNumberPagination):
+class PageNumber(PageNumberPagination):
page_size = 10 # 每页显示多少条
page_size_query_param = 'size' # URL中每页显示条数的参数
page_query_param = 'page' # URL中页码的参数
@@ -42,7 +42,7 @@ class DeveloperView(APIView):
developer_obj2 = developer_obj.filter(issuer_id=appid)
developer_obj = developer_obj1 | developer_obj2
- page_obj = AppsPageNumber()
+ page_obj = PageNumber()
app_page_serializer = page_obj.paginate_queryset(queryset=developer_obj.order_by("-updated_time"),
request=request,
view=self)
@@ -238,7 +238,7 @@ class SuperSignUsedView(APIView):
if bundleid:
SuperSignUsed_obj = SuperSignUsed_obj.filter(app_id__bundle_id=bundleid)
- page_obj = AppsPageNumber()
+ page_obj = PageNumber()
app_page_serializer = page_obj.paginate_queryset(queryset=SuperSignUsed_obj.order_by("-created_time"),
request=request,
view=self)
@@ -263,7 +263,7 @@ class AppUDIDUsedView(APIView):
if bundleid:
AppUDID_obj = AppUDID_obj.filter(app_id__bundle_id=bundleid)
- page_obj = AppsPageNumber()
+ page_obj = PageNumber()
app_page_serializer = page_obj.paginate_queryset(queryset=AppUDID_obj.order_by("-created_time"),
request=request,
view=self)