@@ -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 |