| @@ -4,10 +4,12 @@ from django.conf.urls import url | ||
| 4 | 4 |  | 
| 5 | 5 | from account import views as account_views | 
| 6 | 6 | from group import views as group_views | 
| 7 | +from message import views as message_views | |
| 7 | 8 | from photo import views as photo_views | 
| 8 | 9 | from operation import views as op_views | 
| 9 | 10 |  | 
| 10 | 11 |  | 
| 12 | +# 帐户相关 | |
| 11 | 13 | urlpatterns = [ | 
| 12 | 14 | url(r'^login$', account_views.lesman_login_api, name='lesman_login_api'), # 摄影师登录 | 
| 13 | 15 | url(r'^u/is_registered$', account_views.user_is_registered_api, name='user_is_registered_api'), # 用户是否已经注册 | 
| @@ -17,6 +19,7 @@ urlpatterns = [ | ||
| 17 | 19 | url(r'^u/wx/authorize$', account_views.wx_authorize_api, name='wx_authorize_api'), # 微信用户授权 | 
| 18 | 20 | ] | 
| 19 | 21 |  | 
| 22 | +# 群组相关 | |
| 20 | 23 | urlpatterns += [ | 
| 21 | 24 | url(r'^g/create$', group_views.group_create_api, name='group_create_api'), # 群组创建 | 
| 22 | 25 | url(r'^g/detail$', group_views.group_detail_api, name='group_detail_api'), # 群组详情 | 
| @@ -30,6 +33,7 @@ urlpatterns += [ | ||
| 30 | 33 | url(r'^g/refuse$', group_views.group_refuse_api, name='group_refuse_api'), # 申请拒绝 | 
| 31 | 34 | ] | 
| 32 | 35 |  | 
| 36 | +# 飞图相关 | |
| 33 | 37 | urlpatterns += [ | 
| 34 | 38 | url(r'^f/upload$', group_views.flyimg_upload_api, name='flyimg_upload_api'), # 飞图上传 | 
| 35 | 39 | url(r'^f/list$', group_views.flyimg_upload_api, name='flyimg_list_api'), # 飞图列表 | 
| @@ -40,17 +44,27 @@ urlpatterns += [ | ||
| 40 | 44 | url(r'^f/thumbup/cancel$', group_views.thumbup_cancel_api, name='thumbup_cancel_api'), # 飞图点赞取消 | 
| 41 | 45 | ] | 
| 42 | 46 |  | 
| 47 | +# 消息相关 | |
| 48 | +urlpatterns += [ | |
| 49 | + url(r'^msg/list$', message_views.message_list_api, name='message_list_api'), # 消息列表 | |
| 50 | + url(r'^msg/list/(?P<msg_type>\w+)$', message_views.message_type_list_api, name='message_type_list_api'), # 分类消息列表 | |
| 51 | + url(r'^msg/read$', message_views.message_read_api, name='message_read_api'), # 消息读取 | |
| 52 | +] | |
| 53 | + | |
| 54 | +# 控制器相关 | |
| 43 | 55 | urlpatterns += [ | 
| 44 | 56 | url(r'^uuid_init$', photo_views.uuid_init, name='uuid_init'), # 生成唯一标识 | 
| 45 | 57 | url(r'^uuid$', photo_views.uuid, name='uuid'), # 获取唯一标识 | 
| 46 | 58 | url(r'^photos/upload$', photo_views.upload_photo, name='upload_photo'), # 上传图片 | 
| 47 | 59 | ] | 
| 48 | 60 |  | 
| 61 | +# 二维码相关 | |
| 49 | 62 | urlpatterns += [ | 
| 50 | 63 | url(r'^s/(?P<session>\w+)$', photo_views.session_detail_api, name='session_detail_api'), # Session 详情 | 
| 51 | 64 | url(r'^p/(?P<photo>\w+)$', photo_views.photo_standard_api, name='photo_standard_api'), # standard thumbnail, available for free | 
| 52 | 65 | ] | 
| 53 | 66 |  | 
| 67 | +# 系统相关 | |
| 54 | 68 | urlpatterns += [ | 
| 55 | 69 | url(r'^op/upgrade$', op_views.upgrade_api, name='upgrade_api'), # APP 升级 | 
| 56 | 70 | url(r'^op/splash$', op_views.splash_api, name='splash_api'), # 启动页面 | 
| @@ -27,4 +27,7 @@ | ||
| 27 | 27 | 4029 —— 该用户不在群组 | 
| 28 | 28 |  | 
| 29 | 29 | 4、飞图信息 —— 403 | 
| 30 | - 4030 —— 飞图不存在 | |
| 30 | + 4030 —— 飞图不存在 | |
| 31 | + | |
| 32 | +5、消息相关 —— 409 | |
| 33 | + 4091 —— 消息不存在 | 
| @@ -9,6 +9,8 @@ from rest_framework import viewsets | ||
| 9 | 9 |  | 
| 10 | 10 | from account.models import UserInfo | 
| 11 | 11 | from group.models import GroupInfo, GroupUserInfo, GroupPhotoInfo, PhotoCommentInfo, PhotoThumbUpInfo | 
| 12 | +from message.models import UserMessageInfo | |
| 13 | + | |
| 12 | 14 | from group.serializers import GroupInfoSerializer, GroupUserInfoSerializer, GroupPhotoInfoSerializer | 
| 13 | 15 |  | 
| 14 | 16 | from utils.thumbnail_utils import make_thumb | 
| @@ -556,6 +558,18 @@ def comment_submit_api(request): | ||
| 556 | 558 | comment=comment, | 
| 557 | 559 | ) | 
| 558 | 560 |  | 
| 561 | + UserMessageInfo.objects.create( | |
| 562 | + from_uid=user_id, | |
| 563 | + from_nickname=group_user.nickname, | |
| 564 | + from_avatar=group_user.avatar, | |
| 565 | + to_uid=group_photo.user_id, | |
| 566 | + group_id=group_photo.group_id, | |
| 567 | + photo_id=group_photo.pk, | |
| 568 | + msg_type=UserMessageInfo.COMMENT, | |
| 569 | + msg_title=u'评论', | |
| 570 | + msg_content=comment, | |
| 571 | + ) | |
| 572 | + | |
| 559 | 573 | photo_comments = PhotoCommentInfo.objects.filter( | 
| 560 | 574 | photo_id=photo_id, | 
| 561 | 575 | ) | 
| @@ -611,6 +625,18 @@ def thumbup_submit_api(request): | ||
| 611 | 625 | photo_thumbup.thumbup = True | 
| 612 | 626 | photo_thumbup.save() | 
| 613 | 627 |  | 
| 628 | + UserMessageInfo.objects.create( | |
| 629 | + from_uid=user_id, | |
| 630 | + from_nickname=group_user.nickname, | |
| 631 | + from_avatar=group_user.avatar, | |
| 632 | + to_uid=group_photo.user_id, | |
| 633 | + group_id=group_photo.group_id, | |
| 634 | + photo_id=group_photo.pk, | |
| 635 | + msg_type=UserMessageInfo.THUMBUP, | |
| 636 | + msg_title=u'点赞', | |
| 637 | + msg_content=u'点赞', | |
| 638 | + ) | |
| 639 | + | |
| 614 | 640 | photo_thumbups = PhotoThumbUpInfo.objects.filter( | 
| 615 | 641 | photo_id=photo_id, | 
| 616 | 642 | thumbup=True, | 
| @@ -714,6 +740,18 @@ def thumbup_cancel_api(request): | ||
| 714 | 740 | photo_thumbup.thumbup = False | 
| 715 | 741 | photo_thumbup.save() | 
| 716 | 742 |  | 
| 743 | + UserMessageInfo.objects.create( | |
| 744 | + from_uid=user_id, | |
| 745 | + from_nickname=group_user.nickname, | |
| 746 | + from_avatar=group_user.avatar, | |
| 747 | + to_uid=group_photo.user_id, | |
| 748 | + group_id=group_photo.group_id, | |
| 749 | + photo_id=group_photo.pk, | |
| 750 | + msg_type=UserMessageInfo.THUMBUP, | |
| 751 | + msg_title=u'取消点赞', | |
| 752 | + msg_content=u'取消点赞', | |
| 753 | + ) | |
| 754 | + | |
| 717 | 755 | photo_thumbups = PhotoThumbUpInfo.objects.filter( | 
| 718 | 756 | photo_id=photo_id, | 
| 719 | 757 | thumbup=True, | 
| @@ -0,0 +1,3 @@ | ||
| 1 | +from django.contrib import admin | |
| 2 | + | |
| 3 | +# Register your models here. | 
| @@ -0,0 +1,36 @@ | ||
| 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 | + ] | |
| 11 | + | |
| 12 | + operations = [ | |
| 13 | + migrations.CreateModel( | |
| 14 | + name='UserMessageInfo', | |
| 15 | + fields=[ | |
| 16 | +                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | |
| 17 | +                ('status', models.BooleanField(default=True, help_text='\u72b6\u6001', verbose_name='status')), | |
| 18 | +                ('created_at', models.DateTimeField(help_text='\u521b\u5efa\u65f6\u95f4', verbose_name='created_at', auto_now_add=True)), | |
| 19 | +                ('updated_at', models.DateTimeField(help_text='\u66f4\u65b0\u65f6\u95f4', verbose_name='updated_at', auto_now=True)), | |
| 20 | +                ('from_uid', models.CharField(max_length=255, blank=True, help_text='\u53d1\u9001\u6d88\u606f\u7528\u6237\u552f\u4e00\u6807\u8bc6', null=True, verbose_name='from_uid', db_index=True)), | |
| 21 | +                ('from_nickname', models.CharField(help_text='\u53d1\u9001\u6d88\u606f\u7528\u6237\u6635\u79f0', max_length=255, null=True, verbose_name='from_nickname', blank=True)), | |
| 22 | +                ('from_avatar', models.CharField(help_text='\u53d1\u9001\u6d88\u606f\u7528\u6237\u5934\u50cf', max_length=255, null=True, verbose_name='from_avatar', blank=True)), | |
| 23 | +                ('to_uid', models.CharField(max_length=255, blank=True, help_text='\u63a5\u6536\u6d88\u606f\u7528\u6237\u552f\u4e00\u6807\u8bc6', null=True, verbose_name='to_uid', db_index=True)), | |
| 24 | +                ('group_id', models.CharField(help_text='\u7fa4\u7ec4\u552f\u4e00\u6807\u8bc6', max_length=255, null=True, verbose_name='group_id', blank=True)), | |
| 25 | +                ('photo_id', models.CharField(help_text='\u98de\u56fe\u552f\u4e00\u6807\u8bc6', max_length=255, null=True, verbose_name='photo_id', blank=True)), | |
| 26 | +                ('msg_type', models.CharField(default=b'system', help_text='\u6d88\u606f\u7c7b\u578b', max_length=255, verbose_name='msg_type', db_index=True)), | |
| 27 | +                ('msg_title', models.CharField(help_text='\u6d88\u606f\u6807\u9898', max_length=255, null=True, verbose_name='msg_title', blank=True)), | |
| 28 | +                ('msg_content', models.TextField(help_text='\u6d88\u606f\u5185\u5bb9', null=True, verbose_name='msg_content', blank=True)), | |
| 29 | +                ('read', models.BooleanField(default=False, help_text='\u6d88\u606f\u662f\u5426\u5df2\u8bfb', verbose_name='read')), | |
| 30 | + ], | |
| 31 | +            options={ | |
| 32 | + 'verbose_name': 'usermessageinfo', | |
| 33 | + 'verbose_name_plural': 'usermessageinfo', | |
| 34 | + }, | |
| 35 | + ), | |
| 36 | + ] | 
| @@ -0,0 +1,68 @@ | ||
| 1 | +# -*- coding: utf-8 -*- | |
| 2 | + | |
| 3 | +from django.conf import settings | |
| 4 | +from django.db import models | |
| 5 | +from django.utils.translation import ugettext_lazy as _ | |
| 6 | + | |
| 7 | +from pai2.basemodels import CreateUpdateMixin | |
| 8 | + | |
| 9 | + | |
| 10 | +class UserMessageInfo(CreateUpdateMixin): | |
| 11 | + SYSTEM = 'system' | |
| 12 | + COMMENT = 'comment' | |
| 13 | + THUMBUP = 'thumbup' | |
| 14 | + | |
| 15 | + MESSAGE_TYPE = ( | |
| 16 | + (SYSTEM, u'系统'), | |
| 17 | + (COMMENT, u'评论'), | |
| 18 | + (THUMBUP, u'点赞'), | |
| 19 | + ) | |
| 20 | + | |
| 21 | + MESSAGE_TYPE_INFO = [ | |
| 22 | +        { | |
| 23 | + 'msg_type': SYSTEM, | |
| 24 | + 'msg_avatar': settings.SYSTEM_MESSAGE_AVATAR | |
| 25 | +        }, { | |
| 26 | + 'msg_type': COMMENT, | |
| 27 | + 'msg_avatar': settings.COMMENT_MESSAGE_AVATAR | |
| 28 | +        }, { | |
| 29 | + 'msg_type': THUMBUP, | |
| 30 | + 'msg_avatar': settings.THUMBUP_MESSAGE_AVATAR | |
| 31 | + } | |
| 32 | + ] | |
| 33 | + | |
| 34 | + from_uid = models.CharField(_(u'from_uid'), max_length=255, blank=True, null=True, help_text=u'发送消息用户唯一标识', db_index=True) | |
| 35 | + from_nickname = models.CharField(_(u'from_nickname'), max_length=255, blank=True, null=True, help_text=u'发送消息用户昵称') | |
| 36 | + from_avatar = models.CharField(_(u'from_avatar'), max_length=255, blank=True, null=True, help_text=u'发送消息用户头像') | |
| 37 | + | |
| 38 | + to_uid = models.CharField(_(u'to_uid'), max_length=255, blank=True, null=True, help_text=u'接收消息用户唯一标识', db_index=True) | |
| 39 | + | |
| 40 | + group_id = models.CharField(_(u'group_id'), max_length=255, blank=True, null=True, help_text=u'群组唯一标识') | |
| 41 | + photo_id = models.CharField(_(u'photo_id'), max_length=255, blank=True, null=True, help_text=u'飞图唯一标识') | |
| 42 | + | |
| 43 | + msg_type = models.CharField(_(u'msg_type'), max_length=255, default='system', help_text=u'消息类型', db_index=True) | |
| 44 | + msg_title = models.CharField(_(u'msg_title'), max_length=255, blank=True, null=True, help_text=u'消息标题') | |
| 45 | + msg_content = models.TextField(_(u'msg_content'), blank=True, null=True, help_text=u'消息内容') | |
| 46 | + read = models.BooleanField(_(u'read'), default=False, help_text=u'消息是否已读') | |
| 47 | + | |
| 48 | + class Meta: | |
| 49 | +        verbose_name = _('usermessageinfo') | |
| 50 | +        verbose_name_plural = _('usermessageinfo') | |
| 51 | + | |
| 52 | + def __unicode__(self): | |
| 53 | +        return u'{0.title}'.format(self) | |
| 54 | + | |
| 55 | + @property | |
| 56 | + def msg_info(self): | |
| 57 | +        return { | |
| 58 | + 'pk': self.pk, | |
| 59 | + 'from_uid': self.from_uid, | |
| 60 | + 'from_nickname': self.from_nickname, | |
| 61 | + 'from_avatar': self.from_avatar, | |
| 62 | + 'group_id': self.group_id, | |
| 63 | + 'photo_id': self.photo_id, | |
| 64 | + 'msg_title': self.msg_title, | |
| 65 | + 'msg_content': self.msg_content, | |
| 66 | + 'read': self.read, | |
| 67 | + 'created_at': self.created_at, | |
| 68 | + } | 
| @@ -0,0 +1,3 @@ | ||
| 1 | +from django.test import TestCase | |
| 2 | + | |
| 3 | +# Create your tests here. | 
| @@ -0,0 +1,73 @@ | ||
| 1 | +# -*- coding: utf-8 -*- | |
| 2 | + | |
| 3 | +from django.conf import settings | |
| 4 | +from django.http import JsonResponse | |
| 5 | + | |
| 6 | +from message.models import UserMessageInfo | |
| 7 | + | |
| 8 | +from utils.page_utils import pagination | |
| 9 | + | |
| 10 | + | |
| 11 | +def message_list_api(request): | |
| 12 | + messages = UserMessageInfo.MESSAGE_TYPE_INFO | |
| 13 | + | |
| 14 | + final_messages = [] | |
| 15 | + for message in messages: | |
| 16 | + type_messages = UserMessageInfo.objects.filter( | |
| 17 | + msg_type=message['msg_type'] | |
| 18 | + ).order_by( | |
| 19 | + '-updated_at' | |
| 20 | + )[:settings.MESSAGE_NUM_PER_PAGE] | |
| 21 | + type_messages = [msg.msg_info for msg in type_messages] | |
| 22 | + message['msg_list'] = type_messages | |
| 23 | + final_messages.append(message) | |
| 24 | + | |
| 25 | +    return JsonResponse({ | |
| 26 | + 'status': 200, | |
| 27 | + 'message': u'获取消息列表成功', | |
| 28 | +        'data': { | |
| 29 | + 'messages': final_messages, | |
| 30 | + }, | |
| 31 | + }) | |
| 32 | + | |
| 33 | + | |
| 34 | +def message_type_list_api(request, msg_type): | |
| 35 | +    page = int(request.GET.get('page', 1)) | |
| 36 | +    num = int(request.GET.get('num', settings.MESSAGE_NUM_PER_PAGE)) | |
| 37 | + | |
| 38 | + type_messages = UserMessageInfo.objects.filter( | |
| 39 | + msg_type=msg_type | |
| 40 | + ).order_by( | |
| 41 | + '-updated_at' | |
| 42 | + ) | |
| 43 | + type_messages, left = pagination(type_messages, page, num) | |
| 44 | + type_messages = [msg.msg_info for msg in type_messages] | |
| 45 | + | |
| 46 | +    return JsonResponse({ | |
| 47 | + 'status': 200, | |
| 48 | + 'message': u'获取消息列表成功', | |
| 49 | +        'data': { | |
| 50 | + 'messages': type_messages, | |
| 51 | + 'left': left, | |
| 52 | + }, | |
| 53 | + }) | |
| 54 | + | |
| 55 | + | |
| 56 | +def message_read_api(request): | |
| 57 | +    pk = int(request.GET.get('pk', -1)) | |
| 58 | + | |
| 59 | + try: | |
| 60 | + message = UserMessageInfo.objects.get(pk=pk) | |
| 61 | + except UserMessageInfo.DoesNotExist: | |
| 62 | +        return JsonResponse({ | |
| 63 | + 'status': 4091, | |
| 64 | + 'message': u'该消息不存在' | |
| 65 | + }) | |
| 66 | + | |
| 67 | + message.read = True | |
| 68 | + message.save() | |
| 69 | + | |
| 70 | +    return JsonResponse({ | |
| 71 | + 'status': 200, | |
| 72 | + 'message': u'已读消息成功', | |
| 73 | + }) | 
| @@ -44,8 +44,9 @@ INSTALLED_APPS = ( | ||
| 44 | 44 | 'api', | 
| 45 | 45 | 'account', | 
| 46 | 46 | 'group', | 
| 47 | - 'photo', | |
| 47 | + 'message', | |
| 48 | 48 | 'operation', | 
| 49 | + 'photo', | |
| 49 | 50 | ) | 
| 50 | 51 |  | 
| 51 | 52 |  INSTALLED_APPS += ('multidomain', ) | 
| @@ -172,6 +173,15 @@ THUMBNAIL_MAX_WIDTH = 360 | ||
| 172 | 173 | DOMAIN = 'http://pai.ai' | 
| 173 | 174 | IMG_DOMAIN = 'http://img.pai.ai' | 
| 174 | 175 |  | 
| 176 | +# 消息图片设置 | |
| 177 | +PAI2_LOGO_URL = DOMAIN + '/static/pai2/img/paiai_96_96.png' | |
| 178 | + | |
| 179 | +SYSTEM_MESSAGE_AVATAR = PAI2_LOGO_URL | |
| 180 | +COMMENT_MESSAGE_AVATAR = PAI2_LOGO_URL | |
| 181 | +THUMBUP_MESSAGE_AVATAR = PAI2_LOGO_URL | |
| 182 | + | |
| 183 | +MESSAGE_NUM_PER_PAGE = 10 | |
| 184 | + | |
| 175 | 185 | try: | 
| 176 | 186 | from local_settings import * | 
| 177 | 187 | except ImportError: | 
| @@ -0,0 +1,15 @@ | ||
| 1 | +# -*- coding: utf-8 -*- | |
| 2 | + | |
| 3 | +from django.db.models.query import QuerySet | |
| 4 | + | |
| 5 | + | |
| 6 | +def pagination(queryset, page, num=10): | |
| 7 | + """ | |
| 8 | + DIY Pagination Funciton | |
| 9 | + :param queryset: | |
| 10 | + :param page: | |
| 11 | + :param num: the number of query for one page | |
| 12 | + :return: the query of the page, the number of query left after the page | |
| 13 | + """ | |
| 14 | + start, end, total = num * (page - 1), num * page, queryset.count() if isinstance(queryset, QuerySet) else len(queryset) | |
| 15 | + return queryset[start: end], max(total - end, 0) |