def post(self, match_id): """" TODO: 如果回调时赛事已满员则自动退款处理 Parteam Api request example: { "message": "成功", "code": 200, "attribute": { "userId": 169, "orderNo": "11354642167546", "paymentMethod":1, // 支付类型 1微信,2支付宝 "orderState":2 // // 订单状态 1 待付款 2 已付款 3 超时未付款 4 已发货 } } """ data = self.request.body.decode() data = json.loads(data) if not data.get("attribute"): raise ApiException(400, "未包含 attribute") order = data['attribute'] member = MatchMember.get_or_404(pt_order_no=order['orderNo']) if order['orderState'] in (2, 4): # 将状态为未支付报名状态修改为正常 if member.state == MatchMember.MatchMemberState.wait_pay: payment_method = (TeamOrder.OrderPaymentMethod.WXPAY, TeamOrder.OrderPaymentMethod.ALIPAY )[order['paymentMethod'] - 1] with self.db.transaction(): MatchMember.update( state=MatchMember.MatchMemberState.normal).where( MatchMember.id == member.id).execute() TeamOrder.update( paid=datetime.now(), state=TeamOrder.OrderState.TRADE_BUYER_PAID, gateway_trade_no=order['orderNo'], payment_method=payment_method).where( TeamOrder.id == member.order_id).execute() # 统计赛事人数 Match.update_members_count(member.match_id) if member.group_id > 0: MatchGroup.update_members_count(member.group_id) match_notify.join_match_done\ .apply_async(args=[match_id, member.id], retry=True) self.write_success()
def scan_match_start_time(): """ 定时扫描 Match 表, 提前两小时发送比赛开始通知 :return: """ now = datetime.now() max_dt = now + timedelta(hours=2) min_dt = now - timedelta(minutes=10) matches = Match.select().where(Match.pushed.is_null(), Match.start_time >= min_dt, Match.start_time <= max_dt) for match in matches: match_start.delay(match_id=match.id) Match.update(pushed=now).where(Match.id == match.id).execute()
def get_object(self, match_id: int, preview: bool = False): try: if preview: query = Match.select().where(Match.id == match_id) else: query = Match.select()\ .where(Match.id == match_id, (Match.state == Match.MatchState.closed.value) | (Match.state == Match.MatchState.cancelled.value) | (Match.state == Match.MatchState.opening.value) | (Match.state == Match.MatchState.finished.value)) obj = query.get() except Match.DoesNotExist: raise ApiException(404, "赛事不存在") return obj
def post(self, match_id: int): """ 提交退塞请求, http_form: reason: 退赛原因, insists: 强制退出, 强制退出比赛无退款 :param match_id: int, Match.id """ form = self.validated_arguments insists = form.pop("insists", False) match = Match.get_or_404(id=match_id) if not insists: self.can_leave(match, self.current_user.id) member = self.has_joined_match(match, user_id=self.current_user.id) notify_url = urljoin(self.request.full_url(), self.reverse_url("rest_matches_refund_callback")) try: MatchService.leave(user_id=self.current_user.id, match=match, notify_url=notify_url, insists=insists) except MatchException as e: logging.error(e) msg = "退出赛事失败, 原因: `{0}`".format(str(e)) raise ApiException(422, msg) self.set_status(204, "退出赛事成功") self.finish()
def get(self, match_id): match = Match.get_or_404(id=match_id) query = MatchMember.select(MatchMember, MatchGroup).join( MatchGroup, join_type=JOIN_LEFT_OUTER, on=(MatchMember.group_id == MatchGroup.id).alias("group")).where( MatchMember.match_id == match.id).order_by( MatchMember.id.desc()) group_query = MatchGroup.select() \ .where(MatchGroup.match_id == match_id) self.logger.debug(group_query.sql()) groups = [] for group in group_query: groups.append({"name": group.name}) group_name = self.get_argument("group_name", "") if group_name: query = query.where(MatchGroup.name == group_name) members = self.paginate_query(query) sum_fee = MatchMember.select( fn.SUM(MatchMember.total_fee).alias("sum")).where( MatchMember.match_id == match_id).scalar() self.render("match/match_members_list.html", sum_fee=sum_fee, groups=groups, match=match, members=members, pagination=members.pagination)
def get(self, user_id): """ 获取用户参与的比赛 """ paid = self.get_argument("paid", None) query = Match.select(Match, MatchMember, Team).join( MatchMember, on=(MatchMember.match_id == Match.id ).alias("member")).switch(Match).join( Team, on=(Match.team_id == Team.id).alias("team")).where( MatchMember.user_id == user_id) if paid == "0": query = query.where( MatchMember.state == MatchMember.MatchMemberState.wait_pay) elif paid == "1": query = query.where( MatchMember.state > MatchMember.MatchMemberState.wait_pay) query = self.paginate_query(query) filtered = self.filter_query(query) page = self.paginate_query(filtered) data = self.get_paginated_data(page=page, alias='matches') self.write(data)
def _ban(self, member_id): """ ban 成员,并保存原有状态 """ member = MatchMember.get_or_404(id=member_id) if member.state == MatchMember.MatchMemberState.banned: self.write_success() else: MatchMember.update(state=MatchMember.MatchMemberState.banned, state_before_ban=member.state).where( MatchMember.id == member_id).execute() Match.update_members_count(member.match_id) self.write_success()
def get(self, round_id): match_round = MatchRound.get_or_404(id=round_id) match = Match.get_or_404(id=match_round.match_id) query = MatchMember.select().where( MatchMember.match_id == match.id, MatchMember.state == MatchMember.MatchMemberState.normal) members = [] for member in query: members.append({"id": member.id, "name": member.name}) query = MatchAgainst.select().where( MatchAgainst.round_id == match_round.id).order_by( MatchAgainst.id.asc()) againsts = [] for against in query: againsts.append(against.info) match_options = MatchOption.select().where( MatchOption.match_id == match.id) self.render("match/round_againsts.html", match=match, match_round=match_round, members=members, againsts=againsts, match_options=match_options)
def new_match_status(self, match_id: int): """ 主办方发布新战报时向赛事成员推送信息 :param self: :param match_id: :return: """ match = Match.get(id=match_id) # type: Match team = Team.get(id=match.team_id) # type: Team members = MatchService.members(match, state=MatchMember.MatchMemberState.normal) infos = [] for member in members: infos.append({"mobile": member.mobile, "userId": member.user_id}) message = NewMatchScheduleMessage( user_infos=infos, match_id=match_id, match_name=match.title, sponsor_name=team.name, sponsor_pic_url=team.get_cover_url(size="medium")) pt = Parteam(app.settings["parteam_api_url"]) if not pt.push_message(message): self.retry(ParteamRequestError("调用派队推送接口失败"))
def get(self, match_id): keyword = self.get_argument("kw", "") match = Match.get_or_404(id=match_id) query = MatchMember.query_all_members(match_id) group_query = MatchGroup.select() \ .where(MatchGroup.match_id == match_id) self.logger.debug(group_query.sql()) groups = [] for group in group_query: groups.append({"name": group.name}) group_name = self.get_argument("group_name", "") if group_name: query = query.where(MatchGroup.name == group_name) if keyword: if is_mobile(keyword): query = query.where(MatchMember.mobile == keyword) else: query = query.where(MatchMember.name.contains(keyword)) members = self.paginate_query(query) sum_fee = MatchMember.select( fn.SUM(MatchMember.total_fee).alias("sum")).where( MatchMember.match_id == match_id).scalar() self.render("match/match_members_list.html", sum_fee=sum_fee, match=match, groups=groups, members=members, pagination=members.pagination)
def match_start(self, match_id: int): """ 赛事开始前, 调用派队消息推送接口 :param self: celery task Context :param match_id: :return: """ match = Match.get(id=match_id) # type: Match team = Team.get(id=match.team_id) # type: Team members = MatchService.members(match, state=MatchMember.MatchMemberState.normal) infos = [] for member in members: infos.append({"userId": member.user_id, "mobile": member.mobile}) message = MatchStartMessage( user_infos=infos, match_id=match_id, match_name=match.title, sponsor_name=team.name, sponsor_pic_url=team.get_cover_url(size="medium")) pt = Parteam(app.settings["parteam_api_url"]) if not pt.push_message(message=message): self.retry(exc=ParteamRequestError("调用派队推送接口失败"))
def post(self, match_id): match = Match.get_or_404(id=match_id) form = CreateCoverForm(self.arguments) if form.validate(): cover = MatchCover(match_id=match.id) form.populate_obj(cover) if "coverfile" in self.request.files: to_bucket = self.settings['qiniu_avatar_bucket'] to_key = "match:cover:%s%s" % (match_id, time.time()) to_key = hashlib.md5(to_key.encode()).hexdigest() cover_key = self.upload_file( "coverfile", to_bucket=to_bucket, to_key=to_key, ) cover.cover_key = cover_key cover.save() self.redirect(self.reverse_url("admin_match_detail", match_id)) return self.render("match/add_cover.html", form=form, match=match)
def post(self, match_id, status_id): match = Match.get_or_404(id=match_id) status = MatchStatus.get_or_404(id=status_id) delete_photo_keys = self.get_argument("delete-photo-keys", "") form = MatchStatusForm(self.arguments) if not form.validate(): self.render("match/statuses_edit.html", match=match, status=status, form=form) else: if delete_photo_keys: delete_photo_keys_list = delete_photo_keys.split(",") else: delete_photo_keys_list = [] if "photos" in self.request.files: photo_keys_list = self.upload_photos(match_id) else: photo_keys_list = [] new_photos_set = set(status.photos) ^ set(delete_photo_keys_list) new_photos_set = new_photos_set.union(set(photo_keys_list)) form.populate_obj(status) status.photos = list(new_photos_set) status.save() self.redirect(self.reverse_url("admin_match_statuses", match_id))
def _settlement_application(self, match_id): # 提交结算申请 match = Match.get_or_404(id=match_id) if not match.can_apply_settlement(): self.write_error(403, "此活动不能手动结算") else: MatchService.settlement_application(self.current_user, match) self.write_success()
def get(self, round_id): match_round = MatchRound.get_or_404(id=round_id) match = Match.get_or_404(id=match_round.match_id) form = EditRoundForm(obj=match_round) self.render("match/round_edit.html", form=form, match=match)
def get(self, match_id): match = Match.get_or_404(id=match_id) cover = MatchCover(match_id=match_id) form = CreateCoverForm(obj=cover) self.render("match/add_cover.html", form=form, match=match)
def get(self, match_id): match = Match.get_or_404(id=match_id) match_round = MatchRound(match_id=match_id) form = CreateRoundForm(obj=match_round) self.render("match/round_create.html", form=form, match=match)
def get(self, match_id, state): match = Match.get_or_404(id=match_id) if state == "normal": # 查询正常参赛成员 members = MatchMember\ .query_all_members(match_id)\ .where(MatchMember.state == MatchMember.MatchMemberState.normal) else: members = MatchMember.query_all_members(match_id) # 排除文件和图片项 option_info_list = [ option_info for option_info in match.option_info_list if not (option_info.is_photo() or option_info.is_file() or option_info.is_avatar() or option_info.is_idcard_photo()) ] option_name_list = [ option_info.option_name for option_info in option_info_list ] option_name_list.extend(["报名时间", "状态"]) file_name = "{match_name}成员列表.xlsx"\ .format(match_name=match.title) # temp_file_name = str(uuid.uuid4()) o = io.BytesIO() with Workbook(o) as workbook: worksheet = workbook.add_worksheet() row, col = 0, 0 worksheet.write_row(row, col, option_name_list) for member in members: row, col = row + 1, 0 assert isinstance(member, MatchMember) for option_info in option_info_list: option_value = option_info.get_option_value(member) # 处理布尔值显示 if option_info.is_leader_check() or option_info.is_boolean( ): option_value = "是" if option_value else "否" if option_info.is_gender(): option_value = member.display_gender() worksheet.write(row, col, option_value) col += 1 worksheet.write(row, col, member.created.strftime('%Y.%m.%d')) worksheet.write(row, col + 1, member.state_name) data = o.getvalue() o.close() self.set_header("Content-Type", "application/octet-stream") self.set_header("Content-Disposition", "attachment; filename={0}".format(file_name)) self.add_header("Content-Length", len(data)) self.set_header("Content-Transfer-Encoding", "binary") self.write(data)
def get(self, match_id, status_id): match = Match.get_or_404(id=match_id) status = MatchStatus.get_or_404(id=status_id) form = MatchStatusForm(obj=status) self.render("match/statuses_edit.html", match=match, status=status, form=form)
def get(self, match_id): match = Match.get_or_404(id=match_id) # 获取赛事分组信息 groups = MatchGroup.select().where( MatchGroup.match_id == match.id).order_by( MatchGroup.sort_num.desc()) # 获取报名表自定义选项 custom_options = MatchOption.select().where( MatchOption.match_id == match.id).order_by( MatchOption.sort_num.desc()) rounds = MatchRound.select(MatchRound, ).where( MatchRound.match_id == match.id).order_by(MatchRound.created.asc(), MatchRound.id.asc()) covers = MatchCover.select().where( MatchCover.match_id == match.id).order_by(MatchCover.id.desc()) # 获取对阵图 RightMember = MatchMember.alias() against_query = MatchAgainst.select( MatchAgainst, MatchMember, RightMember).join( MatchMember, on=(MatchAgainst.left_member_id == MatchMember.id ).alias("left_member")).switch(MatchAgainst).join( RightMember, join_type=JOIN_LEFT_OUTER, on=(MatchAgainst.right_member_id == RightMember.id ).alias("right_member")).where( MatchAgainst.match_id == match_id).order_by( MatchAgainst.start_time.asc()) againsts = {} for against in against_query: if against.round_id not in againsts: againsts[against.round_id] = [] againsts[against.round_id].append(against) # 如果已经结束 获取是否已经提交了结算申请 is_application_exist =\ SettlementApplication.is_application_exist(match_id) self.render("match/detail.html", match=match, rounds=rounds, groups=groups, custom_options=custom_options, covers=covers, againsts=againsts, is_application_exist=is_application_exist)
def get(self, match_id): match = Match.get_or_404(id=match_id) query = MatchStatus.select( MatchStatus, ).where(MatchStatus.match_id == match.id).order_by( MatchStatus.created.desc()) statuses = self.paginate_query(query) self.render("match/statuses.html", statuses=statuses, match=match)
def cancel(cls, match: Match, user: User): """ 主办方取消赛事 :param match: :param user: :return: """ from yiyun.tasks.match import batch_refund_worker if not match.can_cancel(): raise MatchStateError("当前赛事状态无法取消: {0}".format(match.state)) with cls.database.transaction(): Match.update(state=Match.MatchState.cancelled.value)\ .where(Match.id == match.id).execute() members = MatchMember.select()\ .where(MatchMember.match_id == match.id, MatchMember.state >= MatchMember.MatchMemberState.wait_pay.value, MatchMember.state <= MatchMember.MatchMemberState.normal.value) for member in members: batch_refund_worker.delay(member_id=member.id)
def _unban(self, member_id): """ unban 成员,并恢复到 ban 时原状态 """ member = MatchMember.get_or_404(id=member_id) match = Match.get_or_404(id=member.match_id) if member.state != MatchMember.MatchMemberState.banned: self.write_success() else: # 判断是不是人数已满等 以确定是否可以恢复 res = match.can_join() if res["can"]: MatchMember.update(state=member.state_before_ban).where( MatchMember.id == member_id).execute() Match.update_members_count(member.match_id) self.write_success() else: self.set_status(403) self.write({"reason": res["reason"]})
def post(self, round_id): match_round = MatchRound.get_or_404(id=round_id) match = Match.get_or_404(id=match_round.match_id) form = EditRoundForm(self.arguments) if form.validate(): form.populate_obj(match_round) match_round.save() self.redirect(self.reverse_url("admin_match_detail", match.id)) return self.render("match/round_edit.html", form=form, match=match)
def post(self, match_id): action = self.get_argument("action") match = Match.get_or_404(id=match_id) if action == "pass": Match.update( state=Match.MatchState.opening, updated=datetime.now()).where( Match.id == match.id, Match.state << [Match.MatchState.wait_review, Match.MatchState.in_review ]).execute() elif action == "reject": Match.update( state=Match.MatchState.rejected, reject_time=datetime.now(), reject_reason=self.get_argument("reject_reason", "")).where( Match.id == match.id, Match.state << [Match.MatchState.wait_review, Match.MatchState.in_review ]).execute() self.write_success()
def get(self, match_id): match = Match.get_or_404(id=match_id) qr = MatchService.get_preview_qrcode(match.id) o = io.BytesIO() qr.save(o, format="JPEG") qr_obj = o.getvalue() o.close() self.set_header('Expires', '0') self.set_header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0') self.set_header('Content-type', 'image/jpg') self.set_header('Content-length', len(qr_obj)) self.write(qr_obj)
def post(self): to_bucket = self.settings['qiniu_file_bucket'] to_key = "match:rules:image:%s%s" % (self.current_user.id, time.time()) to_key = hashlib.md5(to_key.encode()).hexdigest() image_key = self.upload_file('image', to_bucket=to_bucket, to_key=to_key) url_dict = Match.get_cover_urls(image_key) if url_dict: mini_url = url_dict['url'] + url_dict['sizes'][0] else: mini_url = "" self.write(mini_url)
def post(self, match_id): match = Match.get_or_404(id=match_id) form = CreateRoundForm(self.arguments) match_round = MatchRound(match_id=match.id) if form.validate(): form.populate_obj(match_round) match_round.save() service.match.MatchService.add_match_status_notify(match) self.redirect(self.reverse_url("admin_match_detail", match_id)) return self.render("match/round_create.html", form=form, match=match)
def can_leave(self, match: Match, user_id: int) -> bool: """ 赛事是否可以退出 :param match: :param user_id: :return: """ # 先判断是否加入过赛事 self.has_joined_match(match, user_id) # 当前赛事状态是不是允许退出 if match.can_leave(): return True raise ApiException(422, "不满足赛事退塞条件, 无法退出")
def get(self, match_id): """ 获取赛事轮次列表 """ match = Match.get_or_404(id=match_id) query = MatchRound.select().where( MatchRound.match_id == match.id).order_by( MatchRound.start_time.asc()) rounds = [] for match_round in query: rounds.append(match_round.info) RightMember = MatchMember.alias() against_query = MatchAgainst.select( MatchAgainst, MatchMember, RightMember).join( MatchMember, on=(MatchAgainst.left_member_id == MatchMember.id ).alias("left_member")).switch(MatchAgainst).join( RightMember, join_type=JOIN_LEFT_OUTER, on=(MatchAgainst.right_member_id == RightMember.id ).alias("right_member")).where( MatchAgainst.match_id == match_id).order_by( MatchAgainst.start_time.asc()) againsts = {} for against in against_query: if against.round_id not in againsts: againsts[against.round_id] = [] against_info = against.info against_info['left_member'] = against.left_member.mini_info if against.right_member: against_info['right_member'] = against.right_member.mini_info againsts[against.round_id].append(against_info) for i in range(0, len(rounds)): round_id = rounds[i]['id'] rounds[i]['against_mapping'] = againsts.get(round_id, []) self.write({"rounds": rounds})