| @@ -398,7 +398,12 @@ def consumer_info_api(request): | ||
| 398 | 398 | log.has_scan = True | 
| 399 | 399 | log.save() | 
| 400 | 400 |  | 
| 401 | - # TODO: 发放会员权益 | |
| 401 | + if not dupload: | |
| 402 | + user.shots_num += 1 | |
| 403 | + if user.level < UserInfo.MEMBER_BLACK_GOLD: | |
| 404 | + user.level += 1 | |
| 405 | + user.save() | |
| 406 | + # TODO: 发放会员权益 | |
| 402 | 407 |  | 
| 403 | 408 | return response(200, 'Submit Consumer Info Success', u'提交消费者信息成功') | 
| 404 | 409 |  | 
| @@ -6,9 +6,10 @@ from django.conf import settings | ||
| 6 | 6 | from django.db import transaction | 
| 7 | 7 | from django_logit import logit | 
| 8 | 8 | from django_response import response | 
| 9 | +from paginator import pagination | |
| 9 | 10 |  | 
| 10 | 11 | from account.models import UserInfo | 
| 11 | -from member.models import (GoodsInfo, GoodsOrderInfo, MemberActivityInfo, MemberActivitySigninInfo, | |
| 12 | +from member.models import (CouponInfo, GoodsInfo, GoodsOrderInfo, MemberActivityInfo, MemberActivitySigninInfo, | |
| 12 | 13 | MemberActivitySignupInfo, RightInfo) | 
| 13 | 14 | from utils.error.errno_utils import (MemberActivityStatusCode, MemberGoodStatusCode, MemberRightStatusCode, | 
| 14 | 15 | UserStatusCode) | 
| @@ -208,6 +209,23 @@ def good_exchange(request): | ||
| 208 | 209 |  | 
| 209 | 210 |  | 
| 210 | 211 | @logit | 
| 212 | +def coupons(request): | |
| 213 | +    brand_id = request.POST.get('brand_id', settings.KODO_DEFAULT_BRAND_ID) | |
| 214 | +    user_id = request.POST.get('user_id', '') | |
| 215 | +    page = request.POST.get('page', 1) | |
| 216 | +    num = request.POST.get('num', 20) | |
| 217 | + | |
| 218 | +    coupons = CouponInfo.objects.filter(user_id=user_id, status=True).order_by('-pk') | |
| 219 | + coupons, left = pagination(coupons, page, num) | |
| 220 | + coupons = [coupon.data for coupon in coupons] | |
| 221 | + | |
| 222 | +    return response(200, data={ | |
| 223 | + 'coupons': coupons, | |
| 224 | + 'left': left, | |
| 225 | + }) | |
| 226 | + | |
| 227 | + | |
| 228 | +@logit | |
| 211 | 229 | def integrals(request): | 
| 212 | 230 |      brand_id = request.POST.get('brand_id', settings.KODO_DEFAULT_BRAND_ID) | 
| 213 | 231 |  | 
| @@ -230,15 +248,16 @@ def integrals(request): | ||
| 230 | 248 | @logit | 
| 231 | 249 | def activity_list(request): | 
| 232 | 250 |      brand_id = request.POST.get('brand_id', settings.KODO_DEFAULT_BRAND_ID) | 
| 251 | +    user_id = request.POST.get('user_id', '') | |
| 233 | 252 |  | 
| 234 | 253 |      raw_activitys = MemberActivityInfo.objects.filter(status=True).order_by('position') | 
| 235 | 254 | banners = [] | 
| 236 | 255 | activitys = [] | 
| 237 | 256 | for act in raw_activitys: | 
| 238 | 257 | if act.is_slider: | 
| 239 | - banners.append(act.data) | |
| 258 | + banners.append(act.data(user_id)) | |
| 240 | 259 | else: | 
| 241 | - activitys.append(act.data) | |
| 260 | + activitys.append(act.data(user_id)) | |
| 242 | 261 |  | 
| 243 | 262 |      return response(200, data={ | 
| 244 | 263 | 'banners': banners, | 
| @@ -249,6 +268,7 @@ def activity_list(request): | ||
| 249 | 268 | @logit | 
| 250 | 269 | def activity_detail(request): | 
| 251 | 270 |      brand_id = request.POST.get('brand_id', settings.KODO_DEFAULT_BRAND_ID) | 
| 271 | +    user_id = request.POST.get('user_id', '') | |
| 252 | 272 |      activity_id = request.POST.get('activity_id') | 
| 253 | 273 |  | 
| 254 | 274 | try: | 
| @@ -257,7 +277,7 @@ def activity_detail(request): | ||
| 257 | 277 | return response(MemberActivityStatusCode.ACTIVITY_NOT_FOUND) | 
| 258 | 278 |  | 
| 259 | 279 |      return response(200, data={ | 
| 260 | - 'activity': act.data, | |
| 280 | + 'activity': act.data(user_id), | |
| 261 | 281 | }) | 
| 262 | 282 |  | 
| 263 | 283 |  | 
| @@ -284,7 +304,7 @@ def activity_signup(request): | ||
| 284 | 304 | # TODO: 延迟(活动当天)推送模版消息(时间,地点) | 
| 285 | 305 |  | 
| 286 | 306 |      return response(200, data={ | 
| 287 | - 'activity': act.data, | |
| 307 | + 'activity': act.data(user_id), | |
| 288 | 308 | }) | 
| 289 | 309 |  | 
| 290 | 310 |  | 
| @@ -316,5 +336,5 @@ def activity_signin(request): | ||
| 316 | 336 | # TODO: 立即推送模版消息(感谢您参加活动,获得的积分) | 
| 317 | 337 |  | 
| 318 | 338 |      return response(200, data={ | 
| 319 | - 'activity': act.data, | |
| 339 | + 'activity': act.data(user_id), | |
| 320 | 340 | }) | 
| @@ -309,6 +309,8 @@ urlpatterns += [ | ||
| 309 | 309 | url(r'^member/good/detail$', member_views.good_detail, name='member_good_detail'), | 
| 310 | 310 | url(r'^member/good/exchange$', member_views.good_exchange, name='member_good_exchange'), | 
| 311 | 311 |  | 
| 312 | + url(r'^member/coupons$', member_views.coupons, name='member_coupons'), | |
| 313 | + | |
| 312 | 314 | url(r'^member/integrals$', member_views.integrals, name='member_integrals'), | 
| 313 | 315 |  | 
| 314 | 316 | url(r'^member/activity/list$', member_views.activity_list, name='member_activity_list'), | 
| @@ -0,0 +1,52 @@ | ||
| 1 | +# -*- coding: utf-8 -*- | |
| 2 | +# Generated by Django 1.11.26 on 2019-12-12 01:52 | |
| 3 | +from __future__ import unicode_literals | |
| 4 | + | |
| 5 | +from django.db import migrations, models | |
| 6 | +import django_models_ext.fileext | |
| 7 | +import shortuuidfield.fields | |
| 8 | +import simditor.fields | |
| 9 | + | |
| 10 | + | |
| 11 | +class Migration(migrations.Migration): | |
| 12 | + | |
| 13 | + dependencies = [ | |
| 14 | +        ('member', '0006_auto_20191201_2110'), | |
| 15 | + ] | |
| 16 | + | |
| 17 | + operations = [ | |
| 18 | + migrations.CreateModel( | |
| 19 | + name='CouponInfo', | |
| 20 | + fields=[ | |
| 21 | +                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | |
| 22 | +                ('status', models.BooleanField(db_index=True, default=True, help_text='Status', verbose_name='status')), | |
| 23 | +                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Create Time', verbose_name='created_at')), | |
| 24 | +                ('updated_at', models.DateTimeField(auto_now=True, help_text='Update Time', verbose_name='updated_at')), | |
| 25 | +                ('coupon_id', shortuuidfield.fields.ShortUUIDField(blank=True, db_index=True, editable=False, help_text='\u5238\u552f\u4e00\u6807\u8bc6', max_length=22, null=True, unique=True)), | |
| 26 | +                ('user_id', models.CharField(blank=True, db_index=True, help_text='\u7528\u6237\u552f\u4e00\u6807\u8bc6', max_length=32, null=True, verbose_name='user_id')), | |
| 27 | +                ('active_at', models.DateTimeField(blank=True, help_text='\u751f\u6548\u65f6\u95f4', null=True, verbose_name='active_at')), | |
| 28 | +                ('expire_at', models.DateTimeField(blank=True, help_text='\u8fc7\u671f\u65f6\u95f4', null=True, verbose_name='expire_at')), | |
| 29 | +                ('right_id', models.CharField(blank=True, db_index=True, help_text='\u6743\u76ca\u552f\u4e00\u6807\u8bc6', max_length=32, null=True, verbose_name='right_id')), | |
| 30 | +                ('right_type', models.IntegerField(choices=[(0, '\u5b9e\u7269'), (1, '\u865a\u62df'), (2, '\u4f18\u60e0\u5238')], db_index=True, default=1, help_text='\u6743\u76ca\u7c7b\u578b', verbose_name='right_type')), | |
| 31 | +                ('icon', models.ImageField(blank=True, help_text='\u6743\u76ca\u56fe\u6807', null=True, upload_to=django_models_ext.fileext.upload_path, verbose_name='icon')), | |
| 32 | +                ('title', models.CharField(blank=True, help_text='\u6743\u76ca\u540d\u79f0', max_length=255, null=True, verbose_name='title')), | |
| 33 | +                ('subtitle', models.CharField(blank=True, help_text='\u6743\u76ca\u4e8c\u7ea7\u540d\u79f0', max_length=255, null=True, verbose_name='subtitle')), | |
| 34 | +                ('detail', simditor.fields.RichTextField(blank=True, help_text='\u6743\u76ca\u8be6\u60c5', null=True, verbose_name='detail')), | |
| 35 | +                ('level1', models.CharField(blank=True, help_text='level1', max_length=255, null=True, verbose_name='level1')), | |
| 36 | +                ('level2', models.CharField(blank=True, help_text='level2', max_length=255, null=True, verbose_name='level2')), | |
| 37 | +                ('level3', models.CharField(blank=True, help_text='level3', max_length=255, null=True, verbose_name='level3')), | |
| 38 | +                ('level4', models.CharField(blank=True, help_text='level4', max_length=255, null=True, verbose_name='level4')), | |
| 39 | +                ('level5', models.CharField(blank=True, help_text='level5', max_length=255, null=True, verbose_name='level5')), | |
| 40 | +                ('minlevel', models.IntegerField(default=0, help_text='\u6743\u76ca\u6700\u4f4e\u4f1a\u5458\u7ea7\u522b', verbose_name='minlevel')), | |
| 41 | +                ('position', models.IntegerField(db_index=True, default=1, help_text='\u6392\u5e8f', verbose_name='position')), | |
| 42 | + ], | |
| 43 | +            options={ | |
| 44 | + 'verbose_name': '\u4f1a\u5458\u5238\u4fe1\u606f', | |
| 45 | + 'verbose_name_plural': '\u4f1a\u5458\u5238\u4fe1\u606f', | |
| 46 | + }, | |
| 47 | + ), | |
| 48 | + migrations.AlterModelOptions( | |
| 49 | + name='membercouponinfo', | |
| 50 | +            options={'verbose_name': '\u4f1a\u5458\u5238\u4fe1\u606f', 'verbose_name_plural': '\u4f1a\u5458\u5238\u4fe1\u606f'}, | |
| 51 | + ), | |
| 52 | + ] | 
| @@ -169,9 +169,83 @@ class RightInfo(BaseModelMixin): | ||
| 169 | 169 | 'level4': self.level4, | 
| 170 | 170 | 'level5': self.level5, | 
| 171 | 171 | 'minlevel': self.minlevel, | 
| 172 | - "able": True, | |
| 173 | - "left_num": 3, | |
| 174 | - "left_tip": 3, | |
| 172 | + 'able': True, | |
| 173 | + 'left_num': 3, | |
| 174 | + 'left_tip': 3, | |
| 175 | + } | |
| 176 | + | |
| 177 | + | |
| 178 | +class CouponInfo(BaseModelMixin): | |
| 179 | + PHYSICAL = 0 | |
| 180 | + VIRTUAL = 1 | |
| 181 | + COUPON = 2 | |
| 182 | + | |
| 183 | + RIGHT_TYPE_TUPLE = ( | |
| 184 | + (PHYSICAL, u'实物'), | |
| 185 | + (VIRTUAL, u'虚拟'), | |
| 186 | + (COUPON, u'优惠券'), | |
| 187 | + ) | |
| 188 | + | |
| 189 | + coupon_id = ShortUUIDField(_(u'coupon_id'), max_length=32, blank=True, null=True, help_text=u'券唯一标识', db_index=True, unique=True) | |
| 190 | + user_id = models.CharField(_(u'user_id'), max_length=32, blank=True, null=True, help_text=u'用户唯一标识', db_index=True) | |
| 191 | + | |
| 192 | + active_at = models.DateTimeField(_(u'active_at'), blank=True, null=True, help_text=_(u'生效时间')) | |
| 193 | + expire_at = models.DateTimeField(_(u'expire_at'), blank=True, null=True, help_text=_(u'过期时间')) | |
| 194 | + | |
| 195 | + right_id = models.CharField(_(u'right_id'), max_length=32, blank=True, null=True, help_text=u'权益唯一标识', db_index=True) | |
| 196 | + right_type = models.IntegerField(_(u'right_type'), choices=RIGHT_TYPE_TUPLE, default=VIRTUAL, help_text=u'权益类型', db_index=True) | |
| 197 | + | |
| 198 | + icon = models.ImageField(_(u'icon'), upload_to=upload_path, blank=True, null=True, help_text=u'权益图标') | |
| 199 | + title = models.CharField(_(u'title'), max_length=255, blank=True, null=True, help_text=u'权益名称') | |
| 200 | + subtitle = models.CharField(_(u'subtitle'), max_length=255, blank=True, null=True, help_text=u'权益二级名称') | |
| 201 | + detail = RichTextField(_(u'detail'), blank=True, null=True, help_text=u'权益详情') | |
| 202 | + | |
| 203 | + level1 = models.CharField(_(u'level1'), max_length=255, blank=True, null=True, help_text=u'level1') | |
| 204 | + level2 = models.CharField(_(u'level2'), max_length=255, blank=True, null=True, help_text=u'level2') | |
| 205 | + level3 = models.CharField(_(u'level3'), max_length=255, blank=True, null=True, help_text=u'level3') | |
| 206 | + level4 = models.CharField(_(u'level4'), max_length=255, blank=True, null=True, help_text=u'level4') | |
| 207 | + level5 = models.CharField(_(u'level5'), max_length=255, blank=True, null=True, help_text=u'level5') | |
| 208 | + | |
| 209 | + minlevel = models.IntegerField(_(u'minlevel'), default=0, help_text=u'权益最低会员级别') | |
| 210 | + | |
| 211 | + position = models.IntegerField(_(u'position'), default=1, help_text=u'排序', db_index=True) | |
| 212 | + | |
| 213 | + class Meta: | |
| 214 | + verbose_name = _(u'会员券信息') | |
| 215 | + verbose_name_plural = _(u'会员券信息') | |
| 216 | + | |
| 217 | + def __unicode__(self): | |
| 218 | + return unicode(self.pk) | |
| 219 | + | |
| 220 | + @property | |
| 221 | + def icon_path(self): | |
| 222 | + return upload_file_path(self.icon) | |
| 223 | + | |
| 224 | + @property | |
| 225 | + def icon_url(self): | |
| 226 | + return upload_file_url(self.icon) | |
| 227 | + | |
| 228 | + @property | |
| 229 | + def data(self): | |
| 230 | +        return { | |
| 231 | + 'coupon_id': self.coupon_id, | |
| 232 | + 'active_at': tc.local_string(self.active_at, format='%Y%m%d'), | |
| 233 | + 'expire_at': tc.local_string(self.expire_at, format='%Y%m%d'), | |
| 234 | + 'right_id': self.right_id, | |
| 235 | + 'right_type': self.right_type, | |
| 236 | + 'icon': self.icon_url, | |
| 237 | + 'title': self.title, | |
| 238 | + 'subtitle': self.subtitle, | |
| 239 | + 'detail': self.detail, | |
| 240 | + 'level1': self.level1, | |
| 241 | + 'level2': self.level2, | |
| 242 | + 'level3': self.level3, | |
| 243 | + 'level4': self.level4, | |
| 244 | + 'level5': self.level5, | |
| 245 | + 'minlevel': self.minlevel, | |
| 246 | + 'able': True, | |
| 247 | + 'left_num': 3, | |
| 248 | + 'left_tip': 3, | |
| 175 | 249 | } | 
| 176 | 250 |  | 
| 177 | 251 |  | 
| @@ -262,13 +336,25 @@ class MemberActivityInfo(BaseModelMixin): | ||
| 262 | 336 | return upload_file_url(self.slider_image) | 
| 263 | 337 |  | 
| 264 | 338 | @property | 
| 265 | - def data(self): | |
| 339 | + def final_state(self): | |
| 340 | + tdate = tc.local_date() | |
| 341 | + if tdate < self.date: | |
| 342 | + return u'报名中' | |
| 343 | + if tdate == self.date: | |
| 344 | + return u'活动中' | |
| 345 | + return u'已结束' | |
| 346 | + | |
| 347 | + def is_signed(self, user_id): | |
| 348 | + # 是否已报名 | |
| 349 | + return MemberActivitySignupInfo.objects.filter(user_id=user_id, activity_id=self.activity_id, status=True).exists() | |
| 350 | + | |
| 351 | + def data(self, user_id): | |
| 266 | 352 |          return { | 
| 267 | 353 | 'id': self.activity_id, | 
| 268 | 354 | 'activity_id': self.activity_id, | 
| 269 | 355 | 'title': self.title, | 
| 270 | 356 | 'subtitle': self.subtitle, | 
| 271 | - 'date': tc.local_string(self.date, '%Y-%m-%d'), | |
| 357 | + 'date': tc.local_string(self.date, format='%Y-%m-%d'), | |
| 272 | 358 | 'city': self.city, | 
| 273 | 359 | 'location': self.location, | 
| 274 | 360 | 'lat': self.lat, | 
| @@ -279,8 +365,8 @@ class MemberActivityInfo(BaseModelMixin): | ||
| 279 | 365 | 'share_img_link': self.share_img_link, | 
| 280 | 366 | 'share_h5_link': self.share_h5_link, | 
| 281 | 367 | 'slider_image': self.slider_image_url, | 
| 282 | - 'state': 0, | |
| 283 | - 'is_signed': 0, | |
| 368 | + 'state': self.final_state, | |
| 369 | + 'is_signed': self.is_signed(user_id), | |
| 284 | 370 | } | 
| 285 | 371 |  | 
| 286 | 372 |  | 
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | CodeConvert==2.0.5 | 
| 2 | 2 | Pillow==5.0.0 | 
| 3 | 3 | StatusCode==1.0.0 | 
| 4 | -TimeConvert==1.5.0 | |
| 4 | +TimeConvert==1.5.1 | |
| 5 | 5 | furl==2.1.0 | 
| 6 | 6 | isoweek==1.3.3 | 
| 7 | 7 | jsonfield==2.0.2 |