diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..acea320
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,18 @@
+language: python
+sudo: enabled
+services:
+ - mysql
+python:
+ - "3.5"
+env:
+ global:
+ - TRAVIS=true
+before_install:
+ - mysql -e 'CREATE DATABASE IF NOT EXISTS wechat_ticket;'
+ - mv configs.example.json configs.json
+# command to install dependencies
+install:
+ - pip install -r requirements.txt
+
+script:
+ python manage.py test
diff --git a/WeChatTicket/settings.py b/WeChatTicket/settings.py
index 706e469..07aa1d6 100644
--- a/WeChatTicket/settings.py
+++ b/WeChatTicket/settings.py
@@ -38,7 +38,10 @@
WECHAT_APPID = CONFIGS['WECHAT_APPID']
WECHAT_SECRET = CONFIGS['WECHAT_SECRET']
-ALLOWED_HOSTS = []
+ALLOWED_HOSTS = [
+ 'd4a939f0.ngrok.io',
+ 'localhost'
+]
# Application definition
diff --git a/WeChatTicket/urls.py b/WeChatTicket/urls.py
index c3e5a8a..94b86dd 100644
--- a/WeChatTicket/urls.py
+++ b/WeChatTicket/urls.py
@@ -15,6 +15,7 @@
"""
from django.conf.urls import url, include
from django.contrib import admin
+from django.contrib.auth import authenticate, login
from wechat.views import CustomWeChatView
from WeChatTicket.views import StaticFileView
diff --git a/adminpage/admin.py b/adminpage/admin.py
index 8c38f3f..992ecaa 100644
--- a/adminpage/admin.py
+++ b/adminpage/admin.py
@@ -1,3 +1,7 @@
from django.contrib import admin
+from wechat.models import *
# Register your models here.
+
+admin.site.register(Activity)
+admin.site.register(Ticket)
\ No newline at end of file
diff --git a/adminpage/models.py b/adminpage/models.py
index 71a8362..d49766e 100644
--- a/adminpage/models.py
+++ b/adminpage/models.py
@@ -1,3 +1,3 @@
from django.db import models
-# Create your models here.
+# Create your models here.
\ No newline at end of file
diff --git a/adminpage/urls.py b/adminpage/urls.py
index db56849..871a666 100644
--- a/adminpage/urls.py
+++ b/adminpage/urls.py
@@ -1,9 +1,20 @@
# -*- coding: utf-8 -*-
#
-
+from adminpage.views import *
+from django.conf.urls import url
__author__ = "Epsirom"
-urlpatterns = []
+urlpatterns = [
+ url(r'^login/?$',adminLogin.as_view()),
+ url(r'^logout/?$',adminLogout.as_view()),
+ url(r'^activity/list/?$',activityList.as_view()),
+ url(r'^activity/create/?$',activityCreate.as_view()),
+ url(r'^activity/delete/?$',activityDelete.as_view()),
+ url(r'^image/upload/?$',imageUpload.as_view()),
+ url(r'^activity/detail/?$',activityDetail.as_view()),
+ url(r'^activity/menu/?$',activityMenu.as_view()),
+ url(r'^activity/checkin/?$',activityCheckin.as_view()),
+]
diff --git a/adminpage/views.py b/adminpage/views.py
index 91ea44a..3fb7b66 100644
--- a/adminpage/views.py
+++ b/adminpage/views.py
@@ -1,3 +1,325 @@
from django.shortcuts import render
+from django.contrib import auth
+from django.utils import timezone
+from codex.baseerror import *
+from codex.baseview import *
+from wechat.models import Activity, Ticket
+import datetime
+from WeChatTicket.settings import SITE_DOMAIN
+from wechat.views import *
+from wechat.wrapper import *
+
# Create your views here.
+
+# 登录
+class adminLogin(APIView):
+ #获取登录状态
+ def get(self):
+ if not self.request.user.is_authenticated():
+ raise ValidateError(self.input)
+
+ #验证用户名和密码登陆(django——superuser)
+ def post(self):
+ self.check_input('username', 'password')
+ username = self.input['username']
+ password = self.input['password']
+ # try:
+ # user = auth.models.User.objects.filter(username__exact = username)
+ # if not user:
+ # raise ValidateError("User name does not exit!")
+ # if user.first().password != password:
+ # raise ValidateError("User password error!")
+ # user = auth.authenticate(username=username,password=password)
+ # auth.login(self.request, user)
+ # except:
+ # raise ValidateError("System error!")
+ try:
+ user = auth.authenticate(username=username,password=password)
+ auth.login(self.request, user)
+ except:
+ raise ValidateError("Fail to login!")
+
+#登出
+class adminLogout(APIView):
+ #登出
+ def post(self):
+ try:
+ auth.logout(self.request)
+ except:
+ raise ValidateError("Fail to logout!")
+
+#活动列表
+class activityList(APIView):
+ #获取活动列表
+ def get(self):
+ # if self.request.user.is_authenticated():
+ activity_List = []
+ activites = Activity.objects.all()
+ for activity in activites:
+ if activity.status >= 0:
+ info = {}
+ info['id'] = activity.id
+ info['name'] = activity.name
+ info['description'] = activity.description
+ info['startTime'] = activity.start_time.timestamp()
+ info['endTime'] = activity.end_time.timestamp()
+ info['place'] = activity.place
+ info['bookStart'] = activity.book_start.timestamp()
+ info['bookEnd'] = activity.book_end.timestamp()
+ info['currentTime'] = datetime.datetime.now().timestamp()
+ info['status'] = activity.status
+ activity_List.append(info)
+ return activity_List
+ # else:
+ # raise ValidateError('Please login!')
+
+#创建活动
+class activityCreate(APIView):
+ def post(self):
+ # if self.request.user.is_authenticated():
+ self.check_input('name', 'key', 'place', 'description', 'picUrl', 'startTime', 'endTime',
+ 'bookStart', 'bookEnd', 'totalTickets', 'status')
+
+ name = self.input['name']
+ key = self.input['key']
+ place = self.input['place']
+ description = self.input['description']
+ pic_url = self.input['picUrl']
+ start_time = self.input['startTime']
+ end_time = self.input['endTime']
+ book_start = self.input['bookStart']
+ book_end = self.input['bookEnd']
+ total_tickets = self.input['totalTickets']
+ remain_tickets = self.input['totalTickets']
+ status = self.input['status']
+ try:
+ new = Activity(name=name, key=key, place=place,
+ description=description, pic_url=pic_url, start_time=start_time,
+ end_time=end_time, book_start=book_start, book_end=book_end,
+ total_tickets=total_tickets, remain_tickets=remain_tickets, status=status)
+ new.save()
+ return new.id
+ except:
+ raise ValidateError('Create activity error!')
+ # else:
+ # raise ValidateError('Please login!')
+
+#删除活动
+class activityDelete(APIView):
+ def post(self):
+ # if self.request.user.is_authenticated():
+ self.check_input('id')
+ id = self.input['id']
+ try:
+ activity = Activity.objects.get(id__exact = id)
+ except:
+ raise ValidateError("ID does not exit!")
+ if activity:
+ if activity.status != Activity.STATUS_DELETED:
+ activity.status = Activity.STATUS_DELETED
+ activity.save()
+ else:
+ raise ValidateError("Activity is already deleted!")
+
+ # else:
+ # raise ValidateError('Please login!')
+
+#上传图像并保存到服务器
+class imageUpload(APIView):
+ def post(self):
+ # if self.request.user.is_authenticated():
+ self.check_input('image')
+ image = self.input['image'][0]
+ try:
+ f = open("static/img/activityImage/" + image.name, "wb")
+ for index in image.chunks():
+ f.write(index)
+ f.close()
+ return SITE_DOMAIN + '/img/activityImage/' + image.name
+ except:
+ raise ValidateError("Fail to upload image!")
+ # else:
+ # raise ValidateError('Please login!')
+
+#活动详情
+class activityDetail(APIView):
+ #获取活动详情
+ def get(self):
+ # if self.request.user.is_authenticated():
+ self.check_input('id')
+ id = self.input['id']
+ try:
+ activity = Activity.objects.get(id=id)
+ ticket_List = Ticket.objects.filter(activity=activity)
+ bookedTickets = 0
+ usedTickets = 0
+ for t in ticket_List:
+ if t.status == Ticket.STATUS_VALID:
+ bookedTickets += 1
+ elif t.status == Ticket.STATUS_USED:
+ usedTickets += 1
+
+ detail = {}
+ detail['name'] = activity.name
+ detail['key'] = activity.key
+ detail['description'] = activity.description
+ detail['startTime'] = activity.start_time.timestamp()
+ detail['endTime'] = activity.end_time.timestamp()
+ detail['place'] = activity.place
+ detail['bookStart'] = activity.book_start.timestamp()
+ detail['bookEnd'] = activity.book_end.timestamp()
+ detail['totalTickets'] = activity.total_tickets
+ detail['picUrl'] = activity.pic_url
+ detail['bookedTickets'] = bookedTickets
+ detail['usedTickets'] = usedTickets
+ detail['currentTime'] = datetime.datetime.now().timestamp()
+ detail['status'] = activity.status
+ return detail
+ except:
+ raise ValidateError("Fail to get activity details!")
+ # else:
+ # raise ValidateError('Please login!')
+
+ # #修改活动详情
+ def post(self):
+ # if self.request.user.is_authenticated():
+ self.check_input('id', 'name', 'place', 'description', 'picUrl', 'startTime', 'endTime', 'bookStart', 'bookEnd', 'totalTickets', 'status')
+ id = self.input['id']
+ try:
+ activity = Activity.objects.get(id=id)
+ activity.id = self.input['id']
+ activity.name = self.input['name']
+ activity.place = self.input['place']
+ activity.description = self.input['description']
+ activity.pic_url = self.input['picUrl']
+ activity.start_time = self.input['startTime']
+ activity.end_time = self.input['endTime']
+ activity.book_start = self.input['bookStart']
+ activity.book_end = self.input['bookEnd']
+ activity.total_tickets = self.input['totalTickets']
+ activity.status = self.input['status']
+
+ activity.save()
+ except:
+ raise ValidateError("Fail to change activity details!")
+ # else:
+ # raise ValidateError('Please login!')
+
+#微信抢票菜单调整
+class activityMenu(APIView):
+ #获取当前微信抢票菜单
+ def get(self):
+ # if self.request.user.is_authenticated():
+ try:
+ wechat_menu = CustomWeChatView.lib.get_wechat_menu()
+ current_btns = list()
+ for b in wechat_menu:
+ if b['name'] == '抢票':
+ current_btns += b.get('sub_button', list())
+ activity_ids = list()
+ for b in current_btns:
+ if 'key' in b:
+ activity_id = b['key']
+ if activity_id.startswith(CustomWeChatView.event_keys['book_header']):
+ activity_id = activity_id[len(CustomWeChatView.event_keys['book_header']):]
+ if activity_id and activity_id.isdigit():
+ activity_ids.append(int(activity_id))
+ acts = Activity.objects.filter(
+ id__in=activity_ids, status=Activity.STATUS_PUBLISHED, book_end__gt=timezone.now()
+ ).order_by('book_end')[: 5]
+
+ activities = []
+ index = 0
+ for a in acts:
+ index += 1
+ activities.append({
+ 'id':a.id,
+ 'name':a.name,
+ 'menuIndex':index
+ })
+
+ allActs = Activity.objects.all()
+ for a in allActs:
+ if a not in acts:
+ if a.book_end.timestamp() > datetime.datetime.now().timestamp() and a.status == Activity.STATUS_PUBLISHED:
+ activities.append({
+ 'id':a.id,
+ 'name':a.name,
+ 'menuIndex':0
+ })
+ return activities
+ except:
+ raise ValidateError('Fail to get WeChat Menu!')
+ # else:
+ # raise ValidateError('Please login!')
+
+ #修改微信抢票菜单
+ def post(self):
+ # if self.request.user.is_authenticated():
+ try:
+ acts = []
+ for id in self.input:
+ act = Activity.objects.get(id=id)
+ acts.append(act)
+ CustomWeChatView.update_menu(acts)
+ except:
+ raise ValidateError('Fail to change WeChat Menu!')
+ # else:
+ # raise ValidateError('Please login!')
+
+#检票
+class activityCheckin(APIView):
+ #检票
+ def post(self):
+ # if self.request.user.is_authenticated():
+ self.check_input('actId')
+ if 'ticket' in self.input.keys():
+ self.check_input('ticket')
+ actId = self.input['actId']
+ ticket = self.input['ticket']
+ try:
+ activity = Activity.objects.get(id=actId)
+ except:
+ raise ValidateError("Activity for this ID does not exit!")
+
+ try:
+ T = Ticket.objects.get(activity=activity, unique_id=ticket)
+ if T.status == Ticket.STATUS_VALID:
+ info = {}
+ info['ticket'] = T.unique_id
+ info['studentId'] = T.student_id
+ T.status = Ticket.STATUS_USED
+ T.save()
+ return info
+ else:
+ ValidateError('Ticket is invalid')
+ except:
+ ValidateError('Fail to Checkin!')
+
+ elif 'studentId' in self.input.keys():
+ self.check_input('studentId')
+ actId = self.input['actId']
+ studentId = self.input['studentId']
+ try:
+ activity = Activity.objects.get(id=actId)
+ except:
+ raise ValidateError("Activity for this ID does not exit!")
+ try:
+ T = Ticket.objects.filter(activity=activity, student_id=studentId)
+ for t in T:
+ if t.status == Ticket.STATUS_VALID:
+ info = {}
+ info['ticket'] = t.unique_id
+ info['studentId'] = t.student_id
+ t.status = Ticket.STATUS_USED
+ t.save()
+ return info
+ else:
+ ValidateError('Ticket is invalid')
+ except:
+ ValidateError('Fail to Checkin!')
+
+ raise ValidateError('Fail to Checkin!')
+ # else:
+ # raise ValidateError('Please login!')
\ No newline at end of file
diff --git a/codex/baseerror.py b/codex/baseerror.py
index 1247d25..87c56e7 100644
--- a/codex/baseerror.py
+++ b/codex/baseerror.py
@@ -32,4 +32,4 @@ def __init__(self, msg):
class ValidateError(BaseError):
def __init__(self, msg):
- super(ValidateError, self).__init__(3, msg)
+ super(ValidateError, self).__init__(3, msg)
\ No newline at end of file
diff --git a/static/img/activityImage/readme.txt b/static/img/activityImage/readme.txt
new file mode 100644
index 0000000..46325bf
--- /dev/null
+++ b/static/img/activityImage/readme.txt
@@ -0,0 +1 @@
+#保存活动图片
diff --git a/templates/news.xml b/templates/news.xml
index 7732a52..e787d0d 100644
--- a/templates/news.xml
+++ b/templates/news.xml
@@ -8,7 +8,7 @@
-
+
{% endfor %}
diff --git a/userpage/urls.py b/userpage/urls.py
index 2ff76f9..d0e07ca 100644
--- a/userpage/urls.py
+++ b/userpage/urls.py
@@ -10,4 +10,6 @@
urlpatterns = [
url(r'^user/bind/?$', UserBind.as_view()),
+ url(r'^activity/detail?$',UserActivityDetail.as_view()),
+ url(r'^ticket/detail?$',TicketDetail.as_view()),
]
diff --git a/userpage/views.py b/userpage/views.py
index 3e5c5dc..eecdfed 100644
--- a/userpage/views.py
+++ b/userpage/views.py
@@ -1,7 +1,9 @@
from codex.baseerror import *
from codex.baseview import APIView
-from wechat.models import User
+from wechat.models import User,Activity,Ticket
+import re
+import datetime
class UserBind(APIView):
@@ -11,7 +13,18 @@ def validate_user(self):
input: self.input['student_id'] and self.input['password']
raise: ValidateError when validating failed
"""
- raise NotImplementedError('You should implement UserBind.validate_user method')
+ self.check_input('student_id')
+ student_id = self.input['student_id']
+ if User.objects.filter(student_id=student_id):
+ raise ValidateError('该学号已被占用!')
+ #check the validity of student_id:
+ if re.match('^[0-9]{10}$',student_id):
+ #check the year
+ year = int(re.match('^[0-9]{4}',student_id).group(0))
+ if (year < 1911 or year > datetime.datetime.now().year):
+ raise ValidateError('无效学号!')
+ else:
+ raise ValidateError('无效学号!')
def get(self):
self.check_input('openid')
@@ -23,3 +36,56 @@ def post(self):
self.validate_user()
user.student_id = self.input['student_id']
user.save()
+
+class UserActivityDetail(APIView):
+ def get(self):
+ self.check_input('id')
+ try:
+ activity_list = Activity.objects.filter(id = self.input['id'])
+ if not activity_list:
+ raise ValidateError('Not found')
+ activity = activity_list[0]
+ if(activity.status != Activity.STATUS_PUBLISHED):
+ raise ValidateError("活动已截止")
+ detail = {}
+ detail['name'] = activity.name
+ detail['key'] = activity.key
+ detail['description'] = activity.description
+ detail['startTime'] = activity.start_time.timestamp()
+ detail['endTime'] = activity.end_time.timestamp()
+ detail['place'] = activity.place
+ detail['bookStart'] = activity.book_start.timestamp()
+ detail['bookEnd'] = activity.book_end.timestamp()
+ detail['totalTickets'] = activity.total_tickets
+ detail['picUrl'] = activity.pic_url
+ detail['remainTickets'] = activity.remain_tickets
+ detail['currentTime'] = datetime.datetime.now().timestamp()
+ return detail
+ except:
+ raise ValidateError('Activity not found!')
+
+class TicketDetail(APIView):
+
+ def get(self):
+ self.check_input('openid','ticket')
+ user = User.objects.filter(open_id=self.input['openid'])
+ if not user:
+ raise LogicError('User not found')
+ ticketlist = Ticket.objects.filter(student_id=user[0].student_id,unique_id=self.input['ticket'])
+ if not ticketlist:
+ errormsg = user[0].student_id+' '+self.input['ticket']
+ raise LogicError(errormsg)
+ ticket = ticketlist[0]
+ activity = ticket.activity
+ detail = {
+ 'activityName': activity.name,
+ 'place': activity.place,
+ 'activityKey': activity.key,
+ 'uniqueId': ticket.unique_id,
+ 'startTime': activity.start_time.timestamp(),
+ 'endTime': activity.end_time.timestamp(),
+ 'currentTime': datetime.datetime.now().timestamp(),
+ 'status': ticket.status,
+ }
+ return detail
+
\ No newline at end of file
diff --git a/wechat/handlers.py b/wechat/handlers.py
index 4211d91..5b62365 100644
--- a/wechat/handlers.py
+++ b/wechat/handlers.py
@@ -1,7 +1,10 @@
# -*- coding: utf-8 -*-
#
from wechat.wrapper import WeChatHandler
-
+from wechat.models import User,Activity,Ticket
+import datetime
+from django.db import transaction
+import uuid
__author__ = "Epsirom"
@@ -65,3 +68,227 @@ def check(self):
def handle(self):
return self.reply_text(self.get_message('book_empty'))
+
+#抢票
+class BookActivityHandler(WeChatHandler):
+
+ def check(self):
+ if self.is_event('CLICK'):
+ event_key = self.view.event_keys['book_header']
+ event_key += self.input['EventKey'][len(event_key):]
+ return self.is_event_click(event_key)
+ else:
+ return self.is_text('抢票')
+ return False
+
+ def handle(self):
+ if not self.user.student_id:
+ return self.reply_text(self.get_message('bind_account'))
+
+ currentTime = datetime.datetime.now().timestamp()
+ if self.is_event('CLICK'):
+ event_key = self.view.event_keys['book_header']
+ event_key += self.input['EventKey'][len(event_key):]
+ if self.is_event_click(event_key):
+ act_id = self.input['EventKey'][len(self.view.event_keys['book_header']):]
+ activity = self.get_activity(act_id)
+ if not activity:
+ return self.reply_text('对不起,没有该项活动')
+ if currentTime < activity.book_start.timestamp():
+ return self.reply_text('对不起,还未开放抢票')
+ if currentTime > activity.book_end.timestamp():
+ return self.reply_text('对不起,抢票已经截止')
+ #check if the student has book a ticket:
+ if Ticket.objects.filter(student_id = self.user.student_id, activity = activity, status = Ticket.STATUS_VALID):
+ return self.reply_text('一个人只能抢一张票哦 ^口..口^')
+ if Ticket.objects.filter(student_id = self.user.student_id, activity = activity, status = Ticket.STATUS_USED):
+ return self.reply_text('您已经抢过该活动的票且使用过')
+ #lock!lock!lock!
+ ticket = self.book_ticket(act_id)
+ activity = self.get_activity(act_id)
+ if ticket:
+ return self.reply_single_news({
+ 'Title':activity.name,
+ 'Description':'抢票成功',
+ 'Url':ticket,
+ 'PicUrl':activity.pic_url,
+ })
+ else:
+ return self.reply_text('没有多的票了!请自行尝试劝退抢到票的朋友们~')
+ else:
+ return False
+
+ elif self.is_text('抢票'):
+ query = self.input['Content'][3:]
+ activity = Activity.objects.filter(key = query).first()
+ if not activity:
+ activity = Activity.objects.filter(name = query).first()
+ if not activity:
+ return self.reply_text('对不起,没有该项活动')
+ if currentTime < activity.book_start.timestamp():
+ return self.reply_text('对不起,还未开放抢票')
+ if currentTime > activity.book_end.timestamp():
+ return self.reply_text('对不起,抢票已经截止')
+ if Ticket.objects.filter(student_id = self.user.student_id, activity = activity, status = Ticket.STATUS_VALID):
+ return self.reply_text('一个人只能抢一张票哦 ^口..口^')
+ if Ticket.objects.filter(student_id = self.user.student_id, activity = activity, status = Ticket.STATUS_USED):
+ return self.reply_text('您已经抢过该活动的票且使用过')
+ if activity.remain_tickets == 0:
+ return self.reply_text('没有多的票了!请自行尝试劝退抢到票的朋友们~')
+ unique_id = uuid.uuid5(uuid.NAMESPACE_DNS,self.user.student_id + activity.name + str(currentTime))
+ Ticket.objects.create(student_id = self.user.student_id, unique_id = unique_id,
+ activity = activity, status = Ticket.STATUS_VALID)
+ activity.remain_tickets -= 1
+ activity.save()
+ ticket = self.url_ticket(unique_id)
+ return self.reply_single_news({
+ 'Title':activity.name,
+ 'Description':'抢票成功',
+ 'Url':ticket,
+ 'PicUrl':activity.pic_url,
+ })
+
+
+ #抢啥
+class BookWhatHandler(WeChatHandler):
+ def check(self):
+ return self.is_event_click(self.view.event_keys['book_what'])
+
+ def handle(self):
+
+ activities = self.get_activities()
+ if not activities:
+ return self.reply_text('对不起,现在没有正在抢票的活动')
+ articles = []
+ currentTime = datetime.datetime.now().timestamp()
+ for activity in activities:
+ if currentTime < activity.end_time.timestamp():
+ articles.append({
+ 'Title': activity.name,
+ 'Description': activity.description,
+ 'Url': self.url_book(activity.id),
+ 'PicUrl': activity.pic_url,
+ })
+ if len(articles) > 0:
+ return self.reply_news(articles)
+ else:
+ return self.reply_text('对不起,现在没有正在抢票的活动')
+
+#查票
+class GetTicketHandler(WeChatHandler):
+ def check(self):
+ return self.is_text('查票') or self.is_event_click(self.view.event_keys['get_ticket'])
+
+ def handle(self):
+ if self.is_event_click(self.view.event_keys['get_ticket']):
+ tickets = self.get_tickets()
+ if not tickets:
+ return self.reply_text('对不起,当前没有已经购买的票')
+ articles = []
+ currentTime = datetime.datetime.now().timestamp()
+ for ticket in tickets:
+ if ticket.status == Ticket.STATUS_VALID:
+ str1 = '有效票未使用'
+ if currentTime > ticket.activity.end_time.timestamp():
+ str1 += ' 活动已结束'
+ elif ticket.status == Ticket.STATUS_USED:
+ str1 = '有效票已使用'
+ else:
+ str1 = '已退票'
+ self.logger.warn(str1)
+ articles.append({
+ 'Title':ticket.activity.name + ' ' + str1,
+ 'Description':'电子票查看',
+ 'Url':self.url_ticket(ticket.unique_id),
+ 'PicUrl':ticket.activity.pic_url,
+ })
+ return self.reply_news(articles)
+ else:
+ query = self.input['Content'][3:]
+ activity = Activity.objects.filter(key = query).first()
+ if not activity:
+ activity = Activity.objects.filter(name = query).first()
+ if not activity:
+ return self.reply_text('对不起,没有这场活动')
+ tickets = Ticket.objects.filter(student_id = self.user.student_id, activity = activity)
+ if not tickets:
+ return self.reply_text('对不起,当前没有已经购买的有效票')
+ for ticket in tickets:
+ if ticket.status == Ticket.STATUS_VALID:
+ currentTime = datetime.datetime.now().timestamp()
+ str1 = '有效票未使用'
+ if currentTime > ticket.activity.end_time.timestamp():
+ str1 += ' 活动已结束'
+ return self.reply_single_news({
+ 'Title':ticket.activity.name + ' ' + str1,
+ 'Description':'电子票查看',
+ 'Url':self.url_ticket(ticket.unique_id),
+ 'PicUrl':ticket.activity.pic_url,
+ })
+ return self.reply_text('对不起,当前没有已经购买的有效票')
+
+#取票
+class PickTicketHandler(WeChatHandler):
+ def check(self):
+ return self.is_text('取票')
+
+ def handle(self):
+ query = self.input['Content'][3:]
+ activity = Activity.objects.filter(key = query).first()
+ if not activity:
+ activity = Activity.objects.filter(name = query).first()
+ if not activity:
+ return self.reply_text('对不起,没有这场活动')
+ tickets = Ticket.objects.filter(student_id = self.user.student_id, activity = activity)
+ if not tickets:
+ return self.reply_text('对不起,当前没有已经购买的有效票')
+ for ticket in tickets:
+ if ticket.status == Ticket.STATUS_VALID:
+ currentTime = datetime.datetime.now().timestamp()
+ str1 = '有效票未使用'
+ if currentTime > ticket.activity.end_time.timestamp():
+ str1 += ' 活动已结束'
+ return self.reply_single_news({
+ 'Title':ticket.activity.name + ' ' + str1,
+ 'Description':'电子票查看',
+ 'Url':self.url_ticket(ticket.unique_id),
+ 'PicUrl':ticket.activity.pic_url,
+ })
+ return self.reply_text('对不起,当前没有已经购买的有效票')
+
+#退票
+class CancelTicketHandler(WeChatHandler):
+ def check(self):
+ return self.is_text('退票')
+
+ def handle(self):
+ query = self.input['Content'][3:]
+ with transaction.atomic():
+ activity = Activity.objects.select_for_update().filter(key = query).first()
+ if not activity:
+ activity = Activity.objects.select_for_update().filter(name = query).first()
+ if not activity:
+ return self.reply_text('对不起,没有这场活动')
+ ticket = Ticket.objects.filter(student_id = self.user.student_id, activity = activity, status = Ticket.STATUS_VALID).first()
+ if not ticket:
+ return self.reply_text('对不起,您没有本场活动未使用的有效票')
+ else:
+ currentTime = datetime.datetime.now().timestamp()
+ if currentTime > ticket.activity.end_time.timestamp():
+ return self.reply_text('对不起,活动已经结束,不能退票')
+ activity.remain_tickets += 1
+ activity.save()
+ ticket.status = Ticket.STATUS_CANCELLED
+ ticket.save()
+ return self.reply_single_news({
+ 'Title':ticket.activity.name,
+ 'Description':'电子票退票成功',
+ 'Url':self.url_ticket(ticket.unique_id),
+ 'PicUrl':ticket.activity.pic_url,
+ })
+ if Ticket.objects.filter(student_id = self.user.student_id, activity = activity, status = Ticket.STATUS_USED).first():
+ return self.reply_text('对不起,这张票您已经使用过')
+ if Ticket.objects.filter(student_id = self.user.student_id, activity = activity, status = Ticket.STATUS_CANCELLED).first():
+ return self.reply_text('这张票已经退票过')
+ return self.reply_text('对不起,您未购买过本场活动的票')
+
diff --git a/wechat/migrations/0003_auto_20181012_1915.py b/wechat/migrations/0003_auto_20181012_1915.py
new file mode 100644
index 0000000..52bd8ff
--- /dev/null
+++ b/wechat/migrations/0003_auto_20181012_1915.py
@@ -0,0 +1,19 @@
+# Generated by Django 2.1.1 on 2018-10-12 11:15
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('wechat', '0002_auto_20160502_1529'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='ticket',
+ name='activity',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='wechat.Activity'),
+ ),
+ ]
diff --git a/wechat/migrations/0004_auto_20181016_0334.py b/wechat/migrations/0004_auto_20181016_0334.py
new file mode 100644
index 0000000..0058807
--- /dev/null
+++ b/wechat/migrations/0004_auto_20181016_0334.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.13 on 2018-10-16 03:34
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('wechat', '0003_auto_20181012_1915'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='user',
+ name='student_id',
+ field=models.CharField(db_index=True, max_length=32),
+ ),
+ ]
diff --git a/wechat/models.py b/wechat/models.py
index 58dc0ee..805a01b 100644
--- a/wechat/models.py
+++ b/wechat/models.py
@@ -5,7 +5,7 @@
class User(models.Model):
open_id = models.CharField(max_length=64, unique=True, db_index=True)
- student_id = models.CharField(max_length=32, unique=True, db_index=True)
+ student_id = models.CharField(max_length=32, unique=False, db_index=True)
@classmethod
def get_by_openid(cls, openid):
@@ -33,13 +33,20 @@ class Activity(models.Model):
STATUS_SAVED = 0
STATUS_PUBLISHED = 1
+ @classmethod
+ def get_by_id(cls, id):
+ try:
+ return cls.objects.get(id=id)
+ except cls.DoesNotExist:
+ raise LogicError('Activity not found')
+
class Ticket(models.Model):
student_id = models.CharField(max_length=32, db_index=True)
unique_id = models.CharField(max_length=64, db_index=True, unique=True)
- activity = models.ForeignKey(Activity)
+ activity = models.ForeignKey(Activity, on_delete=models.DO_NOTHING)
status = models.IntegerField()
STATUS_CANCELLED = 0
STATUS_VALID = 1
- STATUS_USED = 2
+ STATUS_USED = 2
\ No newline at end of file
diff --git a/wechat/views.py b/wechat/views.py
index c0626fc..630499f 100644
--- a/wechat/views.py
+++ b/wechat/views.py
@@ -11,8 +11,17 @@ class CustomWeChatView(WeChatView):
lib = WeChatLib(WECHAT_TOKEN, WECHAT_APPID, WECHAT_SECRET)
handlers = [
- HelpOrSubscribeHandler, UnbindOrUnsubscribeHandler, BindAccountHandler, BookEmptyHandler,
+ HelpOrSubscribeHandler,
+ UnbindOrUnsubscribeHandler,
+ BindAccountHandler,
+ BookEmptyHandler,
+ BookActivityHandler,
+ BookWhatHandler,
+ GetTicketHandler,
+ PickTicketHandler,
+ CancelTicketHandler,
]
+
error_message_handler = ErrorHandler
default_handler = DefaultHandler
@@ -108,3 +117,4 @@ def update_menu(cls, activities=None):
id__in=activity_ids, status=Activity.STATUS_PUBLISHED, book_end__gt=timezone.now()
).order_by('book_end')[: 5])
cls.lib.set_wechat_menu(cls.menu)
+
diff --git a/wechat/wrapper.py b/wechat/wrapper.py
index 136ff04..2e07ab1 100644
--- a/wechat/wrapper.py
+++ b/wechat/wrapper.py
@@ -5,15 +5,16 @@
import json
import logging
import urllib.request
+import uuid
import xml.etree.ElementTree as ET
from WeChatTicket.settings import WECHAT_TOKEN, WECHAT_APPID, WECHAT_SECRET
from django.http import Http404, HttpResponse
from django.template.loader import get_template
-
+from django.db import transaction
from WeChatTicket import settings
from codex.baseview import BaseView
-from wechat.models import User
+from wechat.models import User,Activity,Ticket
__author__ = "Epsirom"
@@ -33,6 +34,35 @@ def __init__(self, view, msg, user):
self.user = user
self.view = view
+ def book_ticket(self,act_id):
+ acts = Activity.objects.select_for_update().filter(id=int(act_id))
+ with transaction.atomic():
+ act = acts[0]
+ if act.remain_tickets > 0:
+ act.remain_tickets -= 1
+ act.save()
+ else:
+ return ''
+ #not return: there's tickets left!
+ return self.create_ticket(act_id)
+
+ def create_ticket(self,id):
+ currentTime = datetime.datetime.now().timestamp()
+ unique_id = uuid.uuid5(uuid.NAMESPACE_DNS,self.user.student_id + id + str(currentTime))
+ activity = Activity.objects.get(id = int(id))
+ Ticket.objects.create(student_id = self.user.student_id, unique_id = unique_id,
+ activity = activity, status = Ticket.STATUS_VALID)
+ return self.url_ticket(unique_id)
+
+ def get_ticket_by_act(self,id):
+ activity = self.get_activity(id)
+ ticket = Ticket.objects.filter(student_id = self.user.student_id, activity = activity,status = Ticket.STATUS_VALID)
+ if ticket:
+ return True
+ else:
+ return False
+
+
def check(self):
raise NotImplementedError('You should implement check() in sub-class of WeChatHandler')
@@ -52,10 +82,10 @@ def reply_text(self, content):
))
def reply_news(self, articles):
- if len(articles) > 10:
- self.logger.warn('Reply with %d articles, keep only 10', len(articles))
+ if len(articles) > 8:
+ self.logger.warn('Reply with %d articles, keep only 8', len(articles))
return get_template('news.xml').render(self.get_context(
- Articles=articles[:10]
+ Articles=articles[:8]
))
def reply_single_news(self, article):
@@ -67,12 +97,37 @@ def get_message(self, name, **data):
return get_template('messages/' + name + '.html').render(dict(
handler=self, user=self.user, **data
))
+ #self.logger.warn(repr(result))
+ #return result
+
+ def get_activity(self,id):
+ activity = Activity.objects.filter(id=int(id))
+ if not activity:
+ return activity
+ return activity[0]
+
+ def get_activities(self):
+ activities = Activity.objects.filter(status = Activity.STATUS_PUBLISHED)
+ return activities
+
+ def get_tickets(self):
+ ticket_list = []
+ tickets = Ticket.objects.filter(student_id = self.user.student_id,status = Ticket.STATUS_VALID)
+ for i in tickets:
+ ticket_list.append(i)
+ tickets = Ticket.objects.filter(student_id = self.user.student_id,status = Ticket.STATUS_USED)
+ for i in tickets:
+ ticket_list.append(i)
+ tickets = Ticket.objects.filter(student_id = self.user.student_id,status = Ticket.STATUS_CANCELLED)
+ for i in tickets:
+ ticket_list.append(i)
+ return ticket_list
def is_msg_type(self, check_type):
return self.input['MsgType'] == check_type
def is_text(self, *texts):
- return self.is_msg_type('text') and (self.input['Content'].lower() in texts)
+ return self.is_msg_type('text') and (self.input['Content'].split()[0] in texts)
def is_event_click(self, *event_keys):
return self.is_msg_type('event') and (self.input['Event'] == 'CLICK') and (self.input['EventKey'] in event_keys)
@@ -88,6 +143,12 @@ def url_help(self):
def url_bind(self):
return settings.get_url('u/bind', {'openid': self.user.open_id})
+
+ def url_book(self,id):
+ return settings.get_url('u/activity',{'id': id})
+
+ def url_ticket(self,ticket):
+ return settings.get_url('u/ticket',{'openid': self.user.open_id,'ticket':ticket})
class WeChatEmptyHandler(WeChatHandler):
@@ -114,7 +175,7 @@ class WeChatLib(object):
logger = logging.getLogger('wechatlib')
access_token = ''
- access_token_expire = datetime.datetime.fromtimestamp(0)
+ access_token_expire = datetime.datetime.fromtimestamp(123456789)
token = WECHAT_TOKEN
appid = WECHAT_APPID
secret = WECHAT_SECRET