@@ -8,12 +8,13 @@ from django_logit import logit |
||
8 | 8 |
from django_query import get_query_value |
9 | 9 |
from django_response import response |
10 | 10 |
from paginator import pagination |
11 |
+from TimeConvert import TimeConvert as tc |
|
11 | 12 |
|
12 | 13 |
from account.models import UserInfo |
13 | 14 |
from coupon.models import UserCouponInfo |
14 | 15 |
from member.models import (GoodsInfo, GoodsOrderInfo, MemberActivityInfo, MemberActivitySigninInfo, |
15 | 16 |
MemberActivitySignupInfo, RightInfo) |
16 |
-from utils.error.errno_utils import (MemberActivityStatusCode, MemberGoodStatusCode, MemberRightStatusCode, |
|
17 |
+from utils.error.errno_utils import (MemberActivityStatusCode, MemberCouponStatusCode, MemberGoodStatusCode, MemberRightStatusCode, |
|
17 | 18 |
UserStatusCode) |
18 | 19 |
from utils.redis.connect import r |
19 | 20 |
from utils.redis.rkeys import MEMBER_SEND_COUPON_LIST, MEMBER_UPGRADE_INFO |
@@ -112,7 +113,7 @@ def goods(request): |
||
112 | 113 |
except UserInfo.DoesNotExist: |
113 | 114 |
return response(UserStatusCode.USER_NOT_FOUND) |
114 | 115 |
|
115 |
- raw_goods = GoodsInfo.objects.filter(only_for_member=False, left_num__gt=0, status=True).order_by('position') |
|
116 |
+ raw_goods = GoodsInfo.objects.filter(only_for_member=False, left_num__gt=0, status=True).order_by('position', '-pk') |
|
116 | 117 |
banners = goods = [] |
117 | 118 |
for good in raw_goods: |
118 | 119 |
if good.is_slider: |
@@ -190,6 +191,9 @@ def good_exchange(request): |
||
190 | 191 |
except GoodsInfo.DoesNotExist: |
191 | 192 |
return response(MemberGoodStatusCode.GOOD_NOT_FOUND) |
192 | 193 |
|
194 |
+ if good.left_num <= 0: |
|
195 |
+ return response(MemberGoodStatusCode.GOOD_STOCK_NOT_ENOUGH) |
|
196 |
+ |
|
193 | 197 |
if user.level < good.minlevel: |
194 | 198 |
return response(MemberGoodStatusCode.GOOD_NO_EXCHANGE_PERMISSION) |
195 | 199 |
|
@@ -199,9 +203,6 @@ def good_exchange(request): |
||
199 | 203 |
user.integral -= good.integral |
200 | 204 |
user.save() |
201 | 205 |
|
202 |
- if good.left_num <= 0: |
|
203 |
- return response(MemberGoodStatusCode.GOOD_STOCK_NOT_ENOUGH) |
|
204 |
- |
|
205 | 206 |
good.left_num -= 1 |
206 | 207 |
good.save() |
207 | 208 |
|
@@ -261,6 +262,50 @@ def coupons(request): |
||
261 | 262 |
|
262 | 263 |
|
263 | 264 |
@logit |
265 |
+def user_coupon_detail(request): |
|
266 |
+ brand_id = request.POST.get('brand_id', settings.KODO_DEFAULT_BRAND_ID) |
|
267 |
+ user_id = request.POST.get('user_id', '') |
|
268 |
+ user_coupon_id = request.POST.get('user_coupon_id', '') |
|
269 |
+ |
|
270 |
+ try: |
|
271 |
+ coupon = UserCouponInfo.objects.get(user_coupon_id=user_coupon_id, user_id=user_id, status=True) |
|
272 |
+ except UserCouponInfo.DoesNotExist: |
|
273 |
+ return response(MemberCouponStatusCode.USER_COUPON_NOT_FOUND) |
|
274 |
+ |
|
275 |
+ return response(200, data=coupon.data) |
|
276 |
+ |
|
277 |
+ |
|
278 |
+@logit |
|
279 |
+@transaction.atomic |
|
280 |
+def user_coupon_use(request): |
|
281 |
+ brand_id = request.POST.get('brand_id', settings.KODO_DEFAULT_BRAND_ID) |
|
282 |
+ admin_id = request.POST.get('admin_id', '') |
|
283 |
+ user_id = request.POST.get('user_id', '') |
|
284 |
+ user_coupon_id = request.POST.get('user_coupon_id', '') |
|
285 |
+ |
|
286 |
+ try: |
|
287 |
+ coupon = UserCouponInfo.objects.select_for_update().get(user_coupon_id=user_coupon_id, user_id=user_id, status=True) |
|
288 |
+ except UserCouponInfo.DoesNotExist: |
|
289 |
+ return response(MemberCouponStatusCode.USER_COUPON_NOT_FOUND) |
|
290 |
+ |
|
291 |
+ if not coupon.has_actived: |
|
292 |
+ return response(MemberCouponStatusCode.USER_COUPON_NOT_ACTIVED) |
|
293 |
+ |
|
294 |
+ if coupon.has_expired: |
|
295 |
+ return response(MemberCouponStatusCode.USER_COUPON_HAS_EXPIRED) |
|
296 |
+ |
|
297 |
+ if coupon.has_used: |
|
298 |
+ return response(MemberCouponStatusCode.USER_COUPON_HAS_USED) |
|
299 |
+ |
|
300 |
+ coupon.has_used = True |
|
301 |
+ coupon.admin_id = admin_id |
|
302 |
+ coupon.used_at = tc.utc_datetime() |
|
303 |
+ coupon.save() |
|
304 |
+ |
|
305 |
+ return response(200, data=coupon.data) |
|
306 |
+ |
|
307 |
+ |
|
308 |
+@logit |
|
264 | 309 |
def integrals(request): |
265 | 310 |
brand_id = request.POST.get('brand_id', settings.KODO_DEFAULT_BRAND_ID) |
266 | 311 |
|
@@ -310,6 +310,8 @@ urlpatterns += [ |
||
310 | 310 |
url(r'^member/good/exchange$', member_views.good_exchange, name='member_good_exchange'), |
311 | 311 |
|
312 | 312 |
url(r'^member/coupons$', member_views.coupons, name='member_coupons'), |
313 |
+ url(r'^member/user/coupon/detail$', member_views.user_coupon_detail, name='user_coupon_detail'), |
|
314 |
+ url(r'^member/user/coupon/use$', member_views.user_coupon_use, name='user_coupon_use'), |
|
313 | 315 |
|
314 | 316 |
url(r'^member/integrals$', member_views.integrals, name='member_integrals'), |
315 | 317 |
|
@@ -64,7 +64,7 @@ class Command(CompatibilityBaseCommand): |
||
64 | 64 |
active_at=tc.utc_datetime(), |
65 | 65 |
expire_at=tc.utc_datetime(days=365), |
66 | 66 |
coupon_valid_period=coupon.coupon_valid_period, |
67 |
- coupon_limit_brand_ids=coupon.coupon_limit_brand_ids, |
|
67 |
+ coupon_limit_model_ids=coupon.coupon_limit_model_ids, |
|
68 | 68 |
) |
69 | 69 |
|
70 | 70 |
else: |
@@ -105,7 +105,7 @@ class Command(CompatibilityBaseCommand): |
||
105 | 105 |
active_at=tc.utc_datetime(), |
106 | 106 |
expire_at=tc.utc_datetime(days=365), |
107 | 107 |
coupon_valid_period=coupon.coupon_valid_period, |
108 |
- coupon_limit_brand_ids=coupon.coupon_limit_brand_ids, |
|
108 |
+ coupon_limit_model_ids=coupon.coupon_limit_model_ids, |
|
109 | 109 |
) |
110 | 110 |
|
111 | 111 |
close_old_connections() |
@@ -11,7 +11,7 @@ class CouponInfoAdmin(admin.ModelAdmin): |
||
11 | 11 |
|
12 | 12 |
|
13 | 13 |
class UserCouponInfoAdmin(admin.ModelAdmin): |
14 |
- list_display = ('brand_id', 'brand_name', 'user_coupon_id', 'coupon_id', 'user_id', 'coupon_title', 'coupon_value', 'active_at', 'expire_at', 'coupon_valid_period', 'coupon_limit_brand_ids', 'has_used', 'admin_id', 'used_at', 'status', 'created_at', 'updated_at') |
|
14 |
+ list_display = ('brand_id', 'brand_name', 'user_coupon_id', 'coupon_id', 'user_id', 'coupon_title', 'coupon_value', 'active_at', 'expire_at', 'coupon_valid_period', 'coupon_limit_model_ids', 'has_used', 'admin_id', 'used_at', 'status', 'created_at', 'updated_at') |
|
15 | 15 |
list_filter = ('brand_id', 'has_used', 'status') |
16 | 16 |
|
17 | 17 |
|
@@ -0,0 +1,34 @@ |
||
1 |
+# -*- coding: utf-8 -*- |
|
2 |
+# Generated by Django 1.11.26 on 2019-12-30 08:05 |
|
3 |
+from __future__ import unicode_literals |
|
4 |
+ |
|
5 |
+from django.db import migrations |
|
6 |
+import jsonfield.fields |
|
7 |
+ |
|
8 |
+ |
|
9 |
+class Migration(migrations.Migration): |
|
10 |
+ |
|
11 |
+ dependencies = [ |
|
12 |
+ ('coupon', '0006_auto_20191230_1516'), |
|
13 |
+ ] |
|
14 |
+ |
|
15 |
+ operations = [ |
|
16 |
+ migrations.RemoveField( |
|
17 |
+ model_name='couponinfo', |
|
18 |
+ name='coupon_limit_brand_ids', |
|
19 |
+ ), |
|
20 |
+ migrations.RemoveField( |
|
21 |
+ model_name='usercouponinfo', |
|
22 |
+ name='coupon_limit_brand_ids', |
|
23 |
+ ), |
|
24 |
+ migrations.AddField( |
|
25 |
+ model_name='couponinfo', |
|
26 |
+ name='coupon_limit_model_ids', |
|
27 |
+ field=jsonfield.fields.JSONField(blank=True, help_text='\u5238\u9650\u5236\u4f7f\u7528 model_ids', null=True, verbose_name='coupon_limit_model_ids'), |
|
28 |
+ ), |
|
29 |
+ migrations.AddField( |
|
30 |
+ model_name='usercouponinfo', |
|
31 |
+ name='coupon_limit_model_ids', |
|
32 |
+ field=jsonfield.fields.JSONField(blank=True, help_text='\u5238\u9650\u5236\u4f7f\u7528 model_ids', null=True, verbose_name='coupon_limit_model_ids'), |
|
33 |
+ ), |
|
34 |
+ ] |
@@ -33,7 +33,7 @@ class CouponInfo(BaseModelMixin): |
||
33 | 33 |
coupon_valid_period = models.IntegerField(_(u'coupon_valid_period'), default=0, help_text=_(u'维修券有效时间(单位:天)')) |
34 | 34 |
coupon_expire_at = models.DateTimeField(_(u'coupon_expire_at'), blank=True, null=True, help_text=_(u'维修券过期时间')) |
35 | 35 |
|
36 |
- coupon_limit_brand_ids = JSONField(_(u'coupon_limit_brand_ids'), blank=True, null=True, help_text=u'券限制使用 brand_ids') |
|
36 |
+ coupon_limit_model_ids = JSONField(_(u'coupon_limit_model_ids'), blank=True, null=True, help_text=u'券限制使用 model_ids') |
|
37 | 37 |
|
38 | 38 |
class Meta: |
39 | 39 |
verbose_name = _(u'券信息') |
@@ -60,7 +60,7 @@ class UserCouponInfo(BaseModelMixin): |
||
60 | 60 |
expire_at = models.DateTimeField(_(u'expire_at'), blank=True, null=True, help_text=_(u'过期时间')) |
61 | 61 |
coupon_valid_period = models.IntegerField(_(u'coupon_valid_period'), default=0, help_text=_(u'券有效时间(单位:天)')) |
62 | 62 |
|
63 |
- coupon_limit_brand_ids = JSONField(_(u'coupon_limit_brand_ids'), blank=True, null=True, help_text=u'券限制使用 brand_ids') |
|
63 |
+ coupon_limit_model_ids = JSONField(_(u'coupon_limit_model_ids'), blank=True, null=True, help_text=u'券限制使用 model_ids') |
|
64 | 64 |
|
65 | 65 |
has_used = models.BooleanField(_(u'has_used'), default=False, help_text=u'是否已核销', db_index=True) |
66 | 66 |
admin_id = models.CharField(_(u'admin_id'), max_length=32, blank=True, null=True, help_text=u'核销员唯一标识', db_index=True) |
@@ -82,6 +82,18 @@ class UserCouponInfo(BaseModelMixin): |
||
82 | 82 |
return upload_file_url(self.coupon_image) |
83 | 83 |
|
84 | 84 |
@property |
85 |
+ def has_actived(self): |
|
86 |
+ if not self.active_at: |
|
87 |
+ return True |
|
88 |
+ return tc.utc_datetime() > self.active_at |
|
89 |
+ |
|
90 |
+ @property |
|
91 |
+ def has_expired(self): |
|
92 |
+ if not self.expire_at: |
|
93 |
+ return False |
|
94 |
+ return tc.utc_datetime() > self.expire_at |
|
95 |
+ |
|
96 |
+ @property |
|
85 | 97 |
def data(self): |
86 | 98 |
return { |
87 | 99 |
'user_coupon_id': self.user_coupon_id, |
@@ -94,7 +106,9 @@ class UserCouponInfo(BaseModelMixin): |
||
94 | 106 |
'active_at': tc.local_string(self.active_at, format='%Y%m%d'), |
95 | 107 |
'expire_at': tc.local_string(self.expire_at, format='%Y-%m-%d'), |
96 | 108 |
'coupon_valid_period': self.coupon_valid_period, |
97 |
- 'coupon_limit_brand_ids': self.coupon_limit_brand_ids, |
|
109 |
+ 'coupon_limit_model_ids': self.coupon_limit_model_ids, |
|
110 |
+ 'has_actived': self.has_actived, |
|
111 |
+ 'has_expired': self.has_expired, |
|
98 | 112 |
'has_used': self.has_used, |
99 | 113 |
'admin_id': self.admin_id, |
100 | 114 |
'used_at': self.used_at, |
@@ -81,6 +81,15 @@ class MemberActivityStatusCode(BaseStatusCode): |
||
81 | 81 |
ACTIVITY_NOT_FOUND = StatusCodeField(503701, 'Activity Not Found', description=u'活动不存在') |
82 | 82 |
|
83 | 83 |
|
84 |
+class MemberCouponStatusCode(BaseStatusCode): |
|
85 |
+ """ 会员优惠券相关错误码 5040xx """ |
|
86 |
+ USER_COUPON_NOT_FOUND = StatusCodeField(504001, 'User Coupon Not Found', description=u'用户优惠券不存在') |
|
87 |
+ |
|
88 |
+ USER_COUPON_HAS_USED = StatusCodeField(504010, 'User Coupon Has Used', description=u'用户优惠券已使用') |
|
89 |
+ USER_COUPON_NOT_ACTIVED = StatusCodeField(504011, 'User Coupon Not Actived', description=u'用户优惠券未生效') |
|
90 |
+ USER_COUPON_HAS_EXPIRED = StatusCodeField(504012, 'User Coupon Has Expired', description=u'用户优惠券已过期') |
|
91 |
+ |
|
92 |
+ |
|
84 | 93 |
class LensmanStatusCode(BaseStatusCode): |
85 | 94 |
""" 摄影师相关错误码 4000xx """ |
86 | 95 |
LENSMAN_NOT_FOUND = StatusCodeField(400001, 'Lensman Not Found', description=u'摄影师不存在') |
@@ -6,7 +6,7 @@ from utils.redis.rkeys import MEMBER_SHOT_DATA |
||
6 | 6 |
|
7 | 7 |
def update_member_shot_data(): |
8 | 8 |
from member.models import ShotTypeInfo |
9 |
- shots_types = ShotTypeInfo.objects.filter(status=True).order_by('position') |
|
9 |
+ shots_types = ShotTypeInfo.objects.filter(status=True).order_by('position', '-pk') |
|
10 | 10 |
shots_types = [st.data for st in shots_types] |
11 | 11 |
r.setjson(MEMBER_SHOT_DATA, shots_types) |
12 | 12 |
return shots_types |