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, 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 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 _unban(self, member_id): """ unban 成员,并恢复到 ban 时原状态 """ member = MatchMember.get_or_404(id=member_id) if member.state != MatchMember.MatchMemberState.banned: self.write_success() else: MatchMember.update(state=member.state_before_ban).where( MatchMember.id == member_id).execute() self.write_success()
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() 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 get(self, match_id): """ 我的状态 """ member = MatchMember.get_or_none(match_id=match_id, user_id=self.current_user.id) self.write(member.info)
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): 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 has_joined_match(self, match, user_id) -> MatchMember: """ 用户是否加入赛事 :param match: :param user_id: :return: """ try: return MatchMember.get(user_id=user_id, match_id=match.id) except MatchMember.DoesNotExist: raise ApiException(422, "未加入赛事, 无须退出")
def members(cls, match: Match, state: Union[None, MatchMemberState]=None)\ -> SelectQuery: """ 获取赛事参赛成员 :param match: Match, 赛事 :param state: MatchMember.MatchMemberState, 过滤参赛成员状态 :return: SelectQuery """ query = MatchMember.select().where(MatchMember.match_id == match.id) if state: query = query.where(MatchMember.state == state.value) return query
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 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})
def get(self, match_id): """获取赛事信息 :match_id: 赛事ID :returns: 赛事信息 """ preview = self.get_query_argument("preview", False) match = self.get_object(match_id, preview) match.team = Team.get_or_404(id=match.team_id) serializer = MatchSerializer(instance=match) info = serializer.data # 获取赛事分组信息 query = MatchGroup.select().where( MatchGroup.match_id == match.id, ).order_by( MatchGroup.sort_num.desc()) groups = [] for group in query: groups.append(group.info) info['groups'] = groups # 获取赛事海报列表 query = MatchCover.select().where( MatchCover.match_id == match.id).order_by(MatchCover.id.desc()) covers = [] for cover in query: covers.append(cover.info) info['covers'] = covers if self.current_user: member = MatchMember.get_or_none(match_id=match_id, user_id=self.current_user.id) if member: info['my_state'] = member.mini_info self.write(info)
def get(self, match_id): members = 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()) # display_fields = ("name") # # with Workbook("temp.xls") as workbook: # worksheet = workbook.add_worksheet() # worksheet.write_row(0, 0, ) # for member in members: # # # self.write() self.write_success()
def has_joined(self, match: Match, user_id: int): """ 用户是否已经加入过赛事 :param match: :param user_id: :return: """ try: member = MatchMember.select()\ .where(MatchMember.user_id == user_id, MatchMember.match_id == match.id)\ .get() except MatchMember.DoesNotExist: return True else: if member.state == MatchMember.MatchMemberState.leave.value: msg = "你的退赛申请正在处理中, 退赛完成后再尝试" raise ApiException(422, msg, log_message=msg) raise ApiException(422, "您已参赛, 无须重复参加")
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 join_match_done(self, match_id: int, member_id: int): """ 参加赛事完成, 调用派队消息推送接口 :param: self: celery.task.Context :param match_id: int, Match.id :param member_id: int, MatchMember.id """ pt = Parteam(app.settings["parteam_api_url"]) match = Match.get(id=match_id) # type: Team team = Team.get(id=match.team_id) member = MatchMember.get(match_id=match_id, id=member_id) # type: MatchMember user_info = {"mobile": member.mobile, "userId": member.user_id} message = JoinMatchDone(user_infos=[user_info], match_fee=int(member.total_fee * 100), match_id=match_id, match_name=match.title, sponsor_name=team.name, sponsor_pic_url=team.get_cover_url(size="medium")) if not pt.push_message(message=message): raise self.retry(exc=ParteamRequestError("调用派队推送接口失败"))
def leave(cls, user_id, match: Match, notify_url: str = None, insists=False, role: int = 1): """ 退出赛事 :param user_id: :param match: :param notify_url: 派队回调地址 :param insists: 强制退出 :param role: 退赛发起人, 1 用户, 2 赛事方 :return: """ logging.debug("notify_url: {0}, insists: {1}".format( notify_url, insists)) if not insists and notify_url is None: raise AssertionError("非强制退出 `insists=False` 操作需要提供退款回调" "地址 `notify_url`") if insists is False and not match.can_leave(): raise MatchException("赛事无法退出") # 退出赛事 member = MatchMember.get(user_id=user_id, match_id=match.id) # type: MatchMember with Match._meta.database.transaction(): if insists: match.leave(member) # Warning: 数据库事物中尝试远程 HTTP 调用, 需要修复 else: # match.leave_request(member) # 调用退款接口 pt = Parteam(app.settings["parteam_api_url"]) if member.order_id: # 有支付信息, 调用退款接口 order = TeamOrder.get( id=member.order_id) # type: TeamOrder refund_fee = int(order.payment_fee * 100) try: pt.order_refund(user_id=user_id, order_no=member.pt_order_no, refund_fee=refund_fee, notify_url=notify_url, role=role) except NotPaidError as e: logging.warning("调用派队退款接口发现订单未支付: {0}".format(str(e))) TeamOrder.update(state=TeamOrder.OrderState .TRADE_CLOSED_BY_USER.value) \ .where(TeamOrder.id == member.order_id) \ .execute() match.leave(member) except RefundError as e: raise MatchException(e) else: # 更新订单状态为 `退款`, 订单退款状态为 `全部退款` TeamOrder.update( state=TeamOrder.OrderState.TRADE_CLOSED.value, refund_state=TeamOrder.OrderRefundState.FULL_REFUNDED.value, refunded_time=datetime.now())\ .where(TeamOrder.id == member.order_id)\ .execute() match.leave(member) else: # 无支付信息直接退赛 match.leave(member)
def post(self, match_id): """ 报名赛事 :match_id: 赛事ID :returns: """ match = Match.get_or_404(id=match_id) # 检查赛事是否可以报名 result = match.can_join() self.has_joined(match, user_id=self.current_user.id) if not result['can']: raise ApiException(403, result['reason'], log_message="报名失败:{0}, {1}".format( match_id, result['reason'])) team = Team.get_or_404(id=match.team_id) # 分组比赛模式 group = None if match.group_type == 1: group_id = self.get_argument("group_id") group = MatchGroup.get_or_none(id=group_id, match_id=match_id) if group is None: raise ApiException(404, "赛事分组不存在") # 分组是否已报满 if group.max_members <= group.members_count: raise ApiException(403, "赛事分组已报满") total_fee = group.price else: total_fee = match.price name = self.get_argument("name") mobile = self.get_argument("mobile") if not name: raise ApiException(400, "请填写名称") if not mobile: raise ApiException(400, "请填写手机号码") elif not is_mobile(mobile): raise ApiException(400, "手机号码格式有误") # TODO: 上线前移除 # member = MatchMember.get_or_none(match_id=match.id, # user_id=self.current_user.id) # if member is not None: # raise ApiException(403, "你已报名此赛事,无需重复报名") extra_attrs = self.parse_options(match_id) with self.db.transaction() as txn: # 零元赛事无需支付不生成订单 order = None if total_fee > 0: order = TeamOrder.create( order_no=TeamOrder.get_new_order_no(), team=team, user=team.owner_id, title=match.title, order_type=TeamOrder.OrderType.MATCH, payment_fee=total_fee, total_fee=total_fee, activity_id=match.id, state=TeamOrder.OrderState.WAIT_BUYER_PAY) other_attrs = {} if "avatar" in match.fields: other_attrs['avatar_key'] = self.upload_photo("avatar", "头像") if "idcard_photo" in match.fields: other_attrs['idcard_front'] = self.upload_photo( "idcard_front", "证件照片正面") other_attrs['idcard_back'] = self.upload_photo( "idcard_back", "证件照片背面") gender = self.get_argument("gender", "") if gender in ("0", "1"): gender = ("f", "m")[int(gender)] else: gender = "n" if order and order.state == TeamOrder.OrderState.WAIT_BUYER_PAY: member_state = MatchMember.MatchMemberState.wait_pay else: member_state = MatchMember.MatchMemberState.normal team.add_member(self.current_user.id, nick=name, state=TeamMember.TeamMemberState.normal) member = MatchMember.create( match_id=match.id, group_id=group.id if group else 0, member_type=match.join_type, user_id=self.current_user.id, name=name, mobile=mobile, gender=gender, age=self.get_argument("age", "0"), is_leader=self.get_argument("is_leader", False), realname=self.get_argument("realname", ""), idcard_number=self.get_argument("idcard_number", ""), extra_attrs=extra_attrs, order_id=order.id if order else 0, total_fee=total_fee, state=member_state, **other_attrs) # 如果需要付费则生成支付订单 if total_fee > 0: if match.refund_expire: refund_expire = match.refund_expire.strftime( "%Y%m%d%H%M%S") else: refund_expire = datetime.now().strftime("%Y%m%d%H%M%S") resp = self.parteam_request( "/match/openapi/createOrderInfo.do", post_args={ "orderValue": match.id, "eachFee": int(total_fee * 100), "num": 1, "totalFee": int(total_fee * 100), "subject": match.title, "userId": self.current_user.id, "notifyUrl": urljoin( self.request.full_url(), self.reverse_url('rest_match_join_notify', match.id)), "version": 1, "expDatetime": refund_expire, "tradeType": "APP" if self.device_type.lower() in ("ios", "android") else "WEB", }) if "orderNo" not in resp: txn.rollback() raise ApiException( 400, "创建订单失败", log_message="match order fail:{0}".format(resp)) MatchMember.update(pt_order_no=resp['orderNo']).where( MatchMember.id == member.id).execute() # 统计赛事人数 Match.update_members_count(match.id) if group: MatchGroup.update_members_count(group.id) member = MatchMember.get_or_404(id=member.id) member_info = member.info member_info['order'] = { "orderNo": member.pt_order_no, "orderValue": match.id, "eachFee": int(total_fee * 100), "num": 1, "totalFee": int(total_fee * 100), "subject": match.title, "userId": self.current_user.id, } self.write(member_info)
def _approve(self, member_id): # 通过审核 member = MatchMember.get_or_404(id=member_id) member.set_approved() member.save() self.write_success()
def _request_for_improved(self, member_id): # 提示用户完善信息 member = MatchMember.get_or_404(id=member_id) member.set_request_for_improved() member.save() self.write_success()
def _reject(self, member_id): # 审核不通过,如果已经付款的需要退款 member = MatchMember.get_or_404(id=member_id) member.set_reject() member.save() self.write_success()