Пример #1
0
    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)
Пример #2
0
    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)
Пример #3
0
    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()
Пример #4
0
    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()
Пример #5
0
    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()
Пример #6
0
    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)
Пример #7
0
    def get(self, match_id):
        """ 我的状态
        """

        member = MatchMember.get_or_none(match_id=match_id,
                                         user_id=self.current_user.id)

        self.write(member.info)
Пример #8
0
    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)
Пример #9
0
    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)
Пример #10
0
 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, "未加入赛事, 无须退出")
Пример #11
0
 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
Пример #12
0
    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"]})
Пример #13
0
    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})
Пример #14
0
    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)
Пример #15
0
    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()
Пример #16
0
 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, "您已参赛, 无须重复参加")
Пример #17
0
    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)
Пример #18
0
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("调用派队推送接口失败"))
Пример #19
0
    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)
Пример #20
0
    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)
Пример #21
0
 def _approve(self, member_id):
     # 通过审核
     member = MatchMember.get_or_404(id=member_id)
     member.set_approved()
     member.save()
     self.write_success()
Пример #22
0
 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()
Пример #23
0
 def _reject(self, member_id):
     # 审核不通过,如果已经付款的需要退款
     member = MatchMember.get_or_404(id=member_id)
     member.set_reject()
     member.save()
     self.write_success()