| @@ -4,7 +4,7 @@ from curtail_uuid import CurtailUUID | ||
| 4 | 4 | from django.contrib import admin | 
| 5 | 5 | from django.contrib.auth.hashers import make_password | 
| 6 | 6 |  | 
| 7 | -from account.models import LensmanInfo, LensmanLoginLogInfo, UserInfo, UserLoginLogInfo | |
| 7 | +from account.models import LensmanIncomeExpensesInfo, LensmanInfo, LensmanLoginLogInfo, UserIncomeExpensesInfo, UserInfo, UserLoginLogInfo | |
| 8 | 8 |  | 
| 9 | 9 |  | 
| 10 | 10 | class LensmanInfoAdmin(admin.ModelAdmin): | 
| @@ -26,18 +26,30 @@ class LensmanLoginLogInfoAdmin(admin.ModelAdmin): | ||
| 26 | 26 |      list_display = ('lensman_id', 'login_ip', 'login_result', 'status', 'created_at', 'updated_at') | 
| 27 | 27 |  | 
| 28 | 28 |  | 
| 29 | +class LensmanIncomeExpensesInfoAdmin(admin.ModelAdmin): | |
| 30 | +    list_display = ('lensman_id', 'photo_id', 'type', 'amount', 'balance', 'remark', 'status', 'created_at', 'updated_at') | |
| 31 | +    list_filter = ('type', 'status') | |
| 32 | + | |
| 33 | + | |
| 29 | 34 | class UserInfoAdmin(admin.ModelAdmin): | 
| 30 | 35 |      readonly_fields = ('user_id', ) | 
| 31 | -    list_display = ('user_id', 'username', 'wx_uid', 'name', 'sex', 'phone', 'location', 'user_status', 'status', 'created_at', 'updated_at') | |
| 36 | +    list_display = ('user_id', 'user_from', 'username', 'wx_uid', 'name', 'sex', 'phone', 'location', 'user_status', 'status', 'created_at', 'updated_at') | |
| 32 | 37 |      search_fields = ('name', 'phone', 'location') | 
| 33 | -    list_filter = ('sex', 'status', 'user_status') | |
| 38 | +    list_filter = ('user_from', 'sex', 'user_status', 'status') | |
| 34 | 39 |  | 
| 35 | 40 |  | 
| 36 | 41 | class UserLoginLogInfoAdmin(admin.ModelAdmin): | 
| 37 | 42 |      list_display = ('user_id', 'login_ip', 'login_result', 'status', 'created_at', 'updated_at') | 
| 38 | 43 |  | 
| 39 | 44 |  | 
| 45 | +class UserIncomeExpensesInfoAdmin(admin.ModelAdmin): | |
| 46 | +    list_display = ('user_id', 'photo_id', 'type', 'amount', 'balance', 'remark', 'status', 'created_at', 'updated_at') | |
| 47 | +    list_filter = ('type', 'status') | |
| 48 | + | |
| 49 | + | |
| 40 | 50 | admin.site.register(LensmanInfo, LensmanInfoAdmin) | 
| 41 | -admin.site.register(UserInfo, UserInfoAdmin) | |
| 42 | 51 | admin.site.register(LensmanLoginLogInfo, LensmanLoginLogInfoAdmin) | 
| 52 | +admin.site.register(LensmanIncomeExpensesInfo, LensmanIncomeExpensesInfoAdmin) | |
| 53 | +admin.site.register(UserInfo, UserInfoAdmin) | |
| 43 | 54 | admin.site.register(UserLoginLogInfo, UserLoginLogInfoAdmin) | 
| 55 | +admin.site.register(UserIncomeExpensesInfo, UserIncomeExpensesInfoAdmin) | 
| @@ -0,0 +1,72 @@ | ||
| 1 | +# -*- coding: utf-8 -*- | |
| 2 | +from __future__ import unicode_literals | |
| 3 | + | |
| 4 | +from django.db import models, migrations | |
| 5 | + | |
| 6 | + | |
| 7 | +class Migration(migrations.Migration): | |
| 8 | + | |
| 9 | + dependencies = [ | |
| 10 | +        ('account', '0008_userinfo_unionid'), | |
| 11 | + ] | |
| 12 | + | |
| 13 | + operations = [ | |
| 14 | + migrations.CreateModel( | |
| 15 | + name='LensmanIncomeExpensesInfo', | |
| 16 | + fields=[ | |
| 17 | +                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | |
| 18 | +                ('status', models.BooleanField(default=True, help_text='\u72b6\u6001', db_index=True, verbose_name='status')), | |
| 19 | +                ('created_at', models.DateTimeField(help_text='\u521b\u5efa\u65f6\u95f4', verbose_name='created_at', auto_now_add=True)), | |
| 20 | +                ('updated_at', models.DateTimeField(help_text='\u66f4\u65b0\u65f6\u95f4', verbose_name='updated_at', auto_now=True)), | |
| 21 | +                ('lensman_id', models.CharField(max_length=255, blank=True, help_text='\u6444\u5f71\u5e08\u552f\u4e00\u6807\u8bc6', null=True, verbose_name='lensman_id', db_index=True)), | |
| 22 | +                ('photo_id', models.CharField(max_length=255, blank=True, help_text='\u7167\u7247\u552f\u4e00\u6807\u8bc6', null=True, verbose_name='photo_id', db_index=True)), | |
| 23 | +                ('type', models.IntegerField(default=0, help_text='\u6536\u652f\u7c7b\u522b', verbose_name='type', choices=[(0, '\u6536\u5165'), (1, '\u652f\u51fa')])), | |
| 24 | +                ('amount', models.IntegerField(default=0, help_text='\u4f59\u989d\u589e\u51cf\u6570\u91cf(\u5206)', verbose_name='amount')), | |
| 25 | +                ('balance', models.IntegerField(default=0, help_text='\u4f59\u989d\u589e\u51cf\u540e\u6570\u91cf(\u5206)', verbose_name='balance')), | |
| 26 | +                ('remark', models.CharField(help_text='\u5907\u6ce8', max_length=255, null=True, verbose_name='remark', blank=True)), | |
| 27 | + ], | |
| 28 | +            options={ | |
| 29 | + 'verbose_name': 'lensmanincomeexpensesinfo', | |
| 30 | + 'verbose_name_plural': 'lensmanincomeexpensesinfo', | |
| 31 | + }, | |
| 32 | + ), | |
| 33 | + migrations.CreateModel( | |
| 34 | + name='UserIncomeExpensesInfo', | |
| 35 | + fields=[ | |
| 36 | +                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | |
| 37 | +                ('status', models.BooleanField(default=True, help_text='\u72b6\u6001', db_index=True, verbose_name='status')), | |
| 38 | +                ('created_at', models.DateTimeField(help_text='\u521b\u5efa\u65f6\u95f4', verbose_name='created_at', auto_now_add=True)), | |
| 39 | +                ('updated_at', models.DateTimeField(help_text='\u66f4\u65b0\u65f6\u95f4', verbose_name='updated_at', auto_now=True)), | |
| 40 | +                ('user_id', models.CharField(max_length=255, blank=True, help_text='\u7528\u6237\u552f\u4e00\u6807\u8bc6', null=True, verbose_name='user_id', db_index=True)), | |
| 41 | +                ('photo_id', models.CharField(max_length=255, blank=True, help_text='\u7167\u7247\u552f\u4e00\u6807\u8bc6', null=True, verbose_name='photo_id', db_index=True)), | |
| 42 | +                ('type', models.IntegerField(default=0, help_text='\u6536\u652f\u7c7b\u522b', verbose_name='type', choices=[(0, '\u6536\u5165'), (1, '\u652f\u51fa')])), | |
| 43 | +                ('amount', models.IntegerField(default=0, help_text='\u4f59\u989d\u589e\u51cf\u6570\u91cf(\u5206)', verbose_name='amount')), | |
| 44 | +                ('balance', models.IntegerField(default=0, help_text='\u4f59\u989d\u589e\u51cf\u540e\u6570\u91cf(\u5206)', verbose_name='balance')), | |
| 45 | +                ('remark', models.CharField(help_text='\u5907\u6ce8', max_length=255, null=True, verbose_name='remark', blank=True)), | |
| 46 | + ], | |
| 47 | +            options={ | |
| 48 | + 'verbose_name': 'userincomeexpensesinfo', | |
| 49 | + 'verbose_name_plural': 'userincomeexpensesinfo', | |
| 50 | + }, | |
| 51 | + ), | |
| 52 | + migrations.AddField( | |
| 53 | + model_name='lensmaninfo', | |
| 54 | + name='balance', | |
| 55 | + field=models.IntegerField(default=0, help_text='\u6444\u5f71\u5e08\u4f59\u989d(\u5206)', verbose_name='balance'), | |
| 56 | + ), | |
| 57 | + migrations.AddField( | |
| 58 | + model_name='userinfo', | |
| 59 | + name='balance', | |
| 60 | + field=models.IntegerField(default=0, help_text='\u7528\u6237\u4f59\u989d(\u5206)', verbose_name='balance'), | |
| 61 | + ), | |
| 62 | + migrations.AlterField( | |
| 63 | + model_name='lensmaninfo', | |
| 64 | + name='proportion', | |
| 65 | + field=models.FloatField(default=1.0, help_text='\u6444\u5f71\u5e08\u5206\u6210\u6bd4\u4f8b(0.0 ~ 1.0)', verbose_name='proportion'), | |
| 66 | + ), | |
| 67 | + migrations.AlterField( | |
| 68 | + model_name='userinfo', | |
| 69 | + name='user_from', | |
| 70 | + field=models.IntegerField(default=0, help_text='\u7528\u6237\u6765\u6e90', verbose_name='user_from', choices=[(0, 'APP \u521b\u5efa\u7528\u6237'), (1, '\u5fae\u4fe1\u6388\u6743\u7528\u6237'), (9, '\u6e38\u5ba2\u7528\u6237')]), | |
| 71 | + ), | |
| 72 | + ] | 
| @@ -26,7 +26,9 @@ class LensmanInfo(CreateUpdateMixin): | ||
| 26 | 26 | phone = models.CharField(_(u'phone'), max_length=255, blank=True, null=True, help_text=u'摄影师电话', db_index=True, unique=True) | 
| 27 | 27 | location = models.CharField(_(u'location'), max_length=255, blank=True, null=True, help_text=u'摄影师地址') | 
| 28 | 28 |  | 
| 29 | - proportion = models.FloatField(_(u'proportion'), default=1.0, help_text=u'摄影师分成比例(0.0 ~ 1.0)') | |
| 29 | + proportion = models.FloatField(_(u'proportion'), default=1.0, help_text=u'摄影师分成比例(0.0 ~ 1.0)') | |
| 30 | + | |
| 31 | + balance = models.IntegerField(_(u'balance'), default=0, help_text=u'摄影师余额(分)') | |
| 30 | 32 |  | 
| 31 | 33 | signup_ip = models.CharField(_(u'signup_ip'), max_length=255, blank=True, null=True, help_text=_(u'注册IP')) | 
| 32 | 34 | login_ip = models.CharField(_(u'login_ip'), max_length=255, blank=True, null=True, help_text=_(u'登录IP')) | 
| @@ -63,13 +65,41 @@ class LensmanLoginLogInfo(CreateUpdateMixin): | ||
| 63 | 65 | return unicode(self.pk) | 
| 64 | 66 |  | 
| 65 | 67 |  | 
| 68 | +class LensmanIncomeExpensesInfo(CreateUpdateMixin): | |
| 69 | + INCOME = 0 | |
| 70 | + EXPENSE = 1 | |
| 71 | + | |
| 72 | + TYPE = ( | |
| 73 | + (INCOME, u'收入'), | |
| 74 | + (EXPENSE, u'支出'), | |
| 75 | + ) | |
| 76 | + | |
| 77 | + lensman_id = models.CharField(_(u'lensman_id'), max_length=255, blank=True, null=True, help_text=u'摄影师唯一标识', db_index=True) | |
| 78 | + photo_id = models.CharField(_(u'photo_id'), max_length=255, blank=True, null=True, help_text=u'照片唯一标识', db_index=True) | |
| 79 | + | |
| 80 | + type = models.IntegerField(_(u'type'), choices=TYPE, default=INCOME, help_text=u'收支类别') | |
| 81 | + amount = models.IntegerField(_(u'amount'), default=0, help_text=u'余额增减数量(分)') | |
| 82 | + balance = models.IntegerField(_(u'balance'), default=0, help_text=u'余额增减后数量(分)') | |
| 83 | + | |
| 84 | + remark = models.CharField(_(u'remark'), max_length=255, blank=True, null=True, help_text=u'备注') | |
| 85 | + | |
| 86 | + class Meta: | |
| 87 | + verbose_name = _(u'lensmanincomeexpensesinfo') | |
| 88 | + verbose_name_plural = _(u'lensmanincomeexpensesinfo') | |
| 89 | + | |
| 90 | + def __unicode__(self): | |
| 91 | + return unicode(self.pk) | |
| 92 | + | |
| 93 | + | |
| 66 | 94 | class UserInfo(CreateUpdateMixin): | 
| 67 | 95 | APP_USER = 0 | 
| 68 | 96 | WX_USER = 1 | 
| 97 | + GUEST_USER = 9 | |
| 69 | 98 |  | 
| 70 | 99 | USER_FROM = ( | 
| 71 | 100 | (APP_USER, u'APP 创建用户'), | 
| 72 | 101 | (WX_USER, u'微信授权用户'), | 
| 102 | + (GUEST_USER, u'游客用户'), | |
| 73 | 103 | ) | 
| 74 | 104 |  | 
| 75 | 105 | UNVERIFIED = 0 | 
| @@ -115,6 +145,8 @@ class UserInfo(CreateUpdateMixin): | ||
| 115 | 145 | city = models.CharField(_(u'city'), max_length=255, blank=True, null=True, help_text=u'用户城市') | 
| 116 | 146 | location = models.CharField(_(u'location'), max_length=255, blank=True, null=True, help_text=u'用户地址') | 
| 117 | 147 |  | 
| 148 | + balance = models.IntegerField(_(u'balance'), default=0, help_text=u'用户余额(分)') | |
| 149 | + | |
| 118 | 150 | user_status = models.IntegerField(_(u'user_status'), choices=USER_STATUS, default=UNVERIFIED) | 
| 119 | 151 |  | 
| 120 | 152 | assign_ip = models.CharField(_(u'assign_ip'), max_length=255, blank=True, null=True, help_text=_(u'分配IP')) | 
| @@ -170,3 +202,29 @@ class UserLoginLogInfo(CreateUpdateMixin): | ||
| 170 | 202 |  | 
| 171 | 203 | def __unicode__(self): | 
| 172 | 204 | return unicode(self.pk) | 
| 205 | + | |
| 206 | + | |
| 207 | +class UserIncomeExpensesInfo(CreateUpdateMixin): | |
| 208 | + INCOME = 0 | |
| 209 | + EXPENSE = 1 | |
| 210 | + | |
| 211 | + TYPE = ( | |
| 212 | + (INCOME, u'收入'), | |
| 213 | + (EXPENSE, u'支出'), | |
| 214 | + ) | |
| 215 | + | |
| 216 | + user_id = models.CharField(_(u'user_id'), max_length=255, blank=True, null=True, help_text=u'用户唯一标识', db_index=True) | |
| 217 | + photo_id = models.CharField(_(u'photo_id'), max_length=255, blank=True, null=True, help_text=u'照片唯一标识', db_index=True) | |
| 218 | + | |
| 219 | + type = models.IntegerField(_(u'type'), choices=TYPE, default=INCOME, help_text=u'收支类别') | |
| 220 | + amount = models.IntegerField(_(u'amount'), default=0, help_text=u'余额增减数量(分)') | |
| 221 | + balance = models.IntegerField(_(u'balance'), default=0, help_text=u'余额增减后数量(分)') | |
| 222 | + | |
| 223 | + remark = models.CharField(_(u'remark'), max_length=255, blank=True, null=True, help_text=u'备注') | |
| 224 | + | |
| 225 | + class Meta: | |
| 226 | + verbose_name = _(u'userincomeexpensesinfo') | |
| 227 | + verbose_name_plural = _(u'userincomeexpensesinfo') | |
| 228 | + | |
| 229 | + def __unicode__(self): | |
| 230 | + return unicode(self.pk) | 
| @@ -1,6 +1,7 @@ | ||
| 1 | 1 | # -*- coding: utf-8 -*- | 
| 2 | 2 |  | 
| 3 | 3 | from curtail_uuid import CurtailUUID | 
| 4 | +from django.conf import settings | |
| 4 | 5 | from django.contrib.auth.hashers import check_password, make_password | 
| 5 | 6 | from django.contrib.auth.models import Group, User | 
| 6 | 7 | from django.http import JsonResponse | 
| @@ -207,6 +208,19 @@ def wx_authorize_api(request): | ||
| 207 | 208 | }) | 
| 208 | 209 |  | 
| 209 | 210 |  | 
| 211 | +def guest_login_api(request): | |
| 212 | + try: | |
| 213 | + user = UserInfo.objects.get(user_id=settings.GUEST_USER_ID) | |
| 214 | + except UserInfo.DoesNotExist: | |
| 215 | + return response(UserStatusCode.GUEST_NOT_FOUND) | |
| 216 | + | |
| 217 | +    return JsonResponse({ | |
| 218 | + 'status': 200, | |
| 219 | + 'message': u'Guest 登录成功', | |
| 220 | + 'data': user.data, | |
| 221 | + }) | |
| 222 | + | |
| 223 | + | |
| 210 | 224 | class UserViewSet(viewsets.ModelViewSet): | 
| 211 | 225 | """ | 
| 212 | 226 | API endpoint that allows users to be viewed or edited. | 
| @@ -18,6 +18,8 @@ urlpatterns = [ | ||
| 18 | 18 | url(r'^u/login$', account_views.user_login_api, name='user_login_api'), # 用户登录 | 
| 19 | 19 |  | 
| 20 | 20 | url(r'^u/wx/authorize$', account_views.wx_authorize_api, name='wx_authorize_api'), # 微信用户授权 | 
| 21 | + | |
| 22 | + url(r'^u/guest$', account_views.guest_login_api, name='guest_login_api'), # 游客登录 | |
| 21 | 23 | ] | 
| 22 | 24 |  | 
| 23 | 25 | # 群组相关 | 
| @@ -225,6 +225,7 @@ WECHAT_GET_OAUTH2_ACCESS_TOKEN = 'https://api.weixin.qq.com/sns/oauth2/access_to | ||
| 225 | 225 |  | 
| 226 | 226 | WECHAT_GET_USERINFO = 'https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s' | 
| 227 | 227 |  | 
| 228 | +# 微信支付设置 | |
| 228 | 229 | WXPAY_NOTIFY_SUCCESS = '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>' | 
| 229 | 230 | WXPAY_NOTIFY_FAIL = '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[XML PARSE FAIL]]></return_msg></xml>' | 
| 230 | 231 |  | 
| @@ -250,6 +251,9 @@ PAI2_HOME_MAX_ROWS = 400 # 首页照片最大数量, PAI2_HOME_PER_PAGE * PAI2_ | ||
| 250 | 251 | # 群组设置 | 
| 251 | 252 | GROUP_PER_PAGE = 20 # 群组每页数量 | 
| 252 | 253 |  | 
| 254 | +# 游客设置 | |
| 255 | +GUEST_USER_ID = 'guest' | |
| 256 | + | |
| 253 | 257 | # 价格设置 | 
| 254 | 258 | LENSMAN_PHOTO_HAGGLE_MAX_TIMES = 3 # 摄影师照片最大砍价次数 | 
| 255 | 259 |  | 
| @@ -26,6 +26,8 @@ class UserStatusCode(BaseStatusCode): | ||
| 26 | 26 | USER_PASSWORD_ERROR = StatusCodeField(400102, u'User Password Error', description=u'用户密码错误') | 
| 27 | 27 | USERNAME_HAS_REGISTERED = StatusCodeField(400103, u'Username Has Registered', description=u'用户名已注册') | 
| 28 | 28 |  | 
| 29 | + GUEST_NOT_FOUND = StatusCodeField(400111, u'Guest Not Found', description=u'游客不存在') | |
| 30 | + | |
| 29 | 31 |  | 
| 30 | 32 | class PhotoStatusCode(BaseStatusCode): | 
| 31 | 33 | """ 照片相关错误码 4010xx """ |