예제 #1
0
    def __clean_order(self, order_type, day_num, uid_range):
        test_order = OrderMixes(order_type)
        test_order.batch_add_orders(day_num, uid_range)

        if order_type == PayTypeEnum.DEPOSIT:
            OrderDeposit.clean_hot_table()
            OrderDetailDeposit.clean_hot_table()
        else:
            OrderWithdraw.clean_hot_table()
            OrderDetailWithdraw.clean_hot_table()

        test_order.check_clean_result(day_num, uid_range)

        test_order.test_one_day_order_clean(day_num, uid_range)
        test_order.check_clean_result(day_num, uid_range)
예제 #2
0
    def order_alloc(cls, admin_account, order_id, merchant):
        """
        认领分配订单
        :return:
        """
        params = copy.deepcopy(locals())
        params.pop('cls')

        order = OrderWithdraw.query_by_order_id(merchant, order_id)
        if not order:
            msg = '%s, params: %s' % (NoSuchWithdrawOrderError.message, params)
            current_app.logger.error(msg)
            return NoSuchWithdrawOrderError()

        if order.state != OrderStateEnum.INIT:
            msg = '%s, params: %s' % (DoNotAllowedOrderError.message, params)
            current_app.logger.error(msg)
            return DoNotAllowedOrderError()

        order, ref_id = OrderUpdateCtl.update_order_event(
            order.order_id,
            uid=order.uid,
            merchant=merchant,
            state=OrderStateEnum.ALLOC,
            op_account=admin_account,
            alloc_time=DateTimeKit.get_cur_datetime(),
        )

        if not order:
            msg = '%s, params: %s' % (AllowedOrderError.message, params)
            current_app.logger.error(msg)
            return AllowedOrderError()

        return ResponseSuccess()
예제 #3
0
    def query_order_list(cls,
                         uid,
                         merchant,
                         begin_time,
                         end_time,
                         payment_type=None):
        """
        查询订单列表
        :param uid:
        :param merchant:
        :param begin_time:
        :param end_time:
        :param payment_type:
        :return:
        """
        order_items = []

        deposit_query = OrderDeposit.query_by_create_time(
            begin_time=begin_time, end_time=end_time,
            merchant=merchant).filter_by(uid=uid)

        withdraw_query = OrderWithdraw.query_by_create_time(
            begin_time=begin_time, end_time=end_time,
            merchant=merchant).filter_by(uid=uid)

        if payment_type == PayTypeEnum.DEPOSIT:
            order_items.extend(deposit_query)
        elif payment_type == PayTypeEnum.WITHDRAW:
            order_items.extend(withdraw_query)
        else:
            order_items.extend(deposit_query)
            order_items.extend(withdraw_query)

        return order_items
예제 #4
0
    def post(self):
        """
        人工出款: 用户银行信息查询
        :return:
        """
        form, error = WithDrawBankForm().request_validate()
        if error:
            return error.as_response()

        # 查询 提现订单表 获取提现金额, 提现用户指定行
        order = OrderWithdraw.query_by_order_id(merchant=form.merchant.data,
                                                order_id=form.order_id.data)
        if not order:
            return OrderInfoMissingError().as_response()

        if order.state not in [OrderStateEnum.ALLOC, OrderStateEnum.DEALING]:
            return BankOrderStateError().as_response()

        bank = order.get_bank_card()
        if not bank:
            return BankInfoMissingError().as_response()

        bank_entry = dict(amount=order.amount,
                          account_name=bank.account_name,
                          card_no=bank.card_no,
                          bank_name=bank.bank_name,
                          province=bank.province,
                          city=bank.city,
                          branch=bank.branch)
        return WithdrawBankEntryResult(bs_data=dict(
            bank_entry=bank_entry)).as_response()
예제 #5
0
    def manually_withdraw(cls, admin_user, merchant, order_id):
        """
        人工出款
        :return:
        """
        # 查询 提现订单表 获取提现金额, 提现用户指定行
        withdraw_entry = OrderWithdraw.query_by_order_id(merchant=merchant,
                                                         order_id=order_id)
        if not withdraw_entry:
            return OrderInfoMissingError()

        if withdraw_entry.state != OrderStateEnum.ALLOC:
            return BankOrderStateError()

        # 更新订单状态
        order, ref_id = OrderUpdateCtl.update_order_event(
            withdraw_entry.order_id,
            uid=int(withdraw_entry.uid),
            merchant=merchant,
            state=OrderStateEnum.DEALING,
            tx_amount=withdraw_entry.amount,
            deliver_type=DeliverTypeEnum.MANUALLY,
            deal_time=DateTimeKit.get_cur_datetime(),
            op_account=admin_user.account,
            mch_fee_id=withdraw_entry.mch_fee_id,
            commit=True)
        if not order:
            return WithdrawOrderStateChangeError()

        return ResponseSuccess()
예제 #6
0
    def manually_withdraw_success(cls, admin_user, merchant, order_id,
                                  channel_cost, comment):
        """
        完成人工出款
        :return:
        """

        withdraw_entry = OrderWithdraw.query_by_order_id(merchant=merchant,
                                                         order_id=order_id)
        if not withdraw_entry:
            return OrderInfoMissingError()

        if withdraw_entry.state != OrderStateEnum.DEALING:
            return BankOrderStateError()

        detail = OrderDetailWithdraw.query_by_order_id(
            order_id=withdraw_entry.order_id,
            merchant=merchant,
            create_time=withdraw_entry.create_time)
        if not detail:
            return NosuchOrderDetailDataError()

        if detail.fee == 0:
            return WithdrawFeeEmptyError()

        # 更新订单状态

        profit = FeeCalculator.calc_profit(detail.fee, channel_cost)

        order, ref_id = OrderUpdateCtl.update_order_event(
            withdraw_entry.order_id,
            uid=int(withdraw_entry.uid),
            merchant=merchant,
            tx_amount=withdraw_entry.amount,
            state=OrderStateEnum.SUCCESS,
            deliver_type=DeliverTypeEnum.MANUALLY,
            op_account=admin_user.account,
            comment=comment,
            cost=channel_cost,
            profit=profit,
            mch_fee_id=withdraw_entry.mch_fee_id,
            commit=True)
        if not order:
            return WithdrawOrderStateChangeError()

        cls.do_notify(
            order=order,
            op_account=admin_user.account,
            comment=comment,
        )

        return ResponseSuccess()
예제 #7
0
def clean_order_table():
    # 每天凌晨4点清理数据
    from app.main import flask_app

    print('job clean_order_table running')

    with flask_app.app_context():
        rst = OrderDeposit.clean_hot_table()
        print(rst)
        rst = OrderWithdraw.clean_hot_table()
        print(rst)
        rst = OrderDetailDeposit.clean_hot_table()
        print(rst)
        rst = OrderDetailWithdraw.clean_hot_table()
        print(rst)
예제 #8
0
    def get(self):
        """
        检查热表数据
        :return:
        """
        if not request.args:
            return ResponseSuccess(
                message="参数规则:?merchant=test&date=20190901").as_response()

        try:
            merchant = MerchantEnum.from_name(request.args['merchant'])
        except:
            return ResponseSuccess(message="请输入正确的商户名称,有效的商户名称包括:%s" %
                                   MerchantEnum.get_names()).as_response()

        try:
            date = request.args.get('date')
            if date:
                date = DateTimeKit.str_to_datetime(
                    date, DateTimeFormatEnum.TIGHT_DAY_FORMAT, to_date=True)
            else:
                date = DateTimeKit.get_cur_date()
        except:
            return ResponseSuccess(
                message="请输入有效的查询日期,格式为:20190901").as_response()

        kwargs = dict(
            date=date,
            merchant=merchant,
            only_hot=request.args.get('only_hot'),
            only_cold=request.args.get('only_cold'),
        )

        rst = dict(
            OrderDeposit=OrderDeposit.query_by_date(date, **kwargs).count(),
            OrderWithdraw=OrderWithdraw.query_by_date(date, **kwargs).count(),
            OrderDetailDeposit=OrderDetailDeposit.query_by_date(
                date, **kwargs).count(),
            OrderDetailWithdraw=OrderDetailWithdraw.query_by_date(
                date, **kwargs).count(),
        )
        kwargs['merchant'] = merchant.name
        kwargs['date'] = DateTimeKit.datetime_to_str(date)
        rst['kwargs'] = kwargs

        return ResponseSuccess(bs_data=rst).as_response()
예제 #9
0
    def get_order_by_order_id(cls, order_id):
        """
        根据order id查询订单
        :param order_id:
        :return:
        """
        g_order_id = GlobalOrderId.query_global_id(order_id)
        if not g_order_id:
            msg = '无法从 GlobalOrderId 查到订单,order_id: %s' % order_id
            current_app.config['SENTRY_DSN'] and current_app.logger.fatal(
                'msg: %s', msg)
            return None

        order = OrderWithdraw.query_by_order_id(merchant=g_order_id.merchant,
                                                order_id=order_id)
        if not order:
            msg = '无法从 OrderWithdraw 查到订单,order_id: %s' % order_id
            current_app.config['SENTRY_DSN'] and current_app.logger.fatal(
                'msg: %s', msg)
            return None

        return order
예제 #10
0
    def post(self):
        """
        运营代付审核列表:
        :return:
        """

        form, error = YearMouthForm().request_validate()
        if error:
            return error.as_response()

        # 遍历系统所有商户
        begin_time, end_time = DateTimeKit.get_month_begin_end(
            year=int(form.year.data), month=int(form.mouth.data))
        if not MerchantMonthMix.is_valid_shard_date(begin_time):
            # 查询的时间太早,没有数据,直接返回空列表
            return ReviewWithdrawResult(bs_data=dict(
                entries=[], total=len(
                    []), operator=g.user.account)).as_response()

        withdraw_list = []

        detail_list = OrderDetailWithdraw.query_by_create_time(
            begin_time=begin_time, end_time=end_time)
        detail_dict = dict([(o.order_id, o) for o in detail_list])

        order_list_query = OrderWithdraw.query_by_create_time(
            begin_time=begin_time, end_time=end_time)
        query = order_list_query.filter(
            or_(
                OrderWithdraw._state == OrderStateEnum.ALLOC.value,
                OrderWithdraw._state == OrderStateEnum.DEALING.value,
            )).all()

        for order in query:
            detail_order = detail_dict[order.order_id]
            if detail_order.op_account != g.user.account:
                continue

            bank = order.get_bank_card(valid_check=False)
            if not bank:
                current_app.logger.error(
                    'bank not exit, order.sys_tx_id: %s, order.bank_id: %s, order.bank_info: %s',
                    order.sys_tx_id, order.bank_id, order.bank_info)
                continue

            user_flag = UserFlagCache(order.uid).get_flag()

            withdraw_list.append(
                dict(
                    uid=order.uid,
                    order_id=order.order_id,
                    sys_tx_id=order.sys_tx_id,
                    merchant=order.merchant.name,
                    source=order.source.desc,
                    state=order.state.desc,
                    amount=order.amount,
                    bank_name=bank.bank_name,
                    bank_type=bank.bank_enum.name,
                    create_time=order.str_create_time,
                    user_flag=user_flag.name if user_flag else None,
                ))

        withdraw_list = sorted(withdraw_list,
                               key=lambda item: item['create_time'])

        return ReviewWithdrawResult(
            bs_data=dict(entries=withdraw_list,
                         total=len(withdraw_list),
                         operator=g.user.account)).as_response()
예제 #11
0
 def get_user_latest_order(cls, uid, order_type: PayTypeEnum):
     g_order_id = GlobalOrderId.query_latest_one(uid, order_type)
     return OrderWithdraw.query_by_order_id(merchant=g_order_id.merchant,
                                            order_id=g_order_id.order_id)
예제 #12
0
    def query_withdraw_order_list(cls, form, export=False):
        """
        查询提现订单列表
        :param form:
        :param export:
        :return:
        """

        merchant = form.merchant_name.data

        if not form.begin_time.data:
            begin_time, end_time = DateTimeKit.get_day_begin_end(DateTimeKit.get_cur_date())
        else:
            begin_time = form.begin_time.data
            end_time = form.end_time.data

        kwargs = {}
        if form.state.data != "0":
            kwargs["_state"] = form.state.data.value

        tx_id = form.order_id.data
        if tx_id:
            if OrderUtils.is_sys_tx_id(tx_id):
                kwargs["sys_tx_id"] = tx_id
            else:
                kwargs["mch_tx_id"] = tx_id

        try:
            order_list = OrderWithdraw.query_by_create_time(begin_time=begin_time, end_time=end_time,
                                                            merchant=merchant).filter_by(**kwargs)
        except MultiMonthQueryException:
            return MultiMonthQueryError().as_response()

        all_channels = dict([(x.channel_id, x) for x in ChannelConfig.query_all()])
        order_detail_dict = dict()

        order_list = OrderFilters.filter_from_order_list(order_list, filters=[
            functools.partial(OrderFilters.filter_tx_id, tx_id),
            functools.partial(OrderFilters.filter_channel, all_channels, form.channel.data),
        ])

        if order_list:
            # 订单详情列表
            order_detail_list = OrderDetailWithdraw.query_by_create_time(begin_time=begin_time, end_time=end_time,
                                                                         merchant=merchant)

            # 订单列表和订单详情列表相互过滤
            order_list, order_detail_dict = OrderFilters.filter_from_detail_list(
                order_list, order_detail_list,
                filters=[
                    functools.partial(
                        OrderFilters.filter_done_time,
                        form.done_begin_time.data,
                        form.done_end_time.data),
                ])

            # 按时间倒序
            order_list = sorted(order_list, key=itemgetter('create_time'), reverse=True)

        if export and order_list:
            return CsvOrderExport.export_withdraw_list_csv(order_list, order_detail_dict, all_channels)

        return cls.render_withdraw_list(form, order_list, order_detail_dict)
예제 #13
0
    def post(self):
        """
        用户最近一周充值提现交易记录
        :return:
        """

        form, error = UserTransactionForm().request_validate()
        if error:
            return error.as_response()

        uid = form.uid.data
        pay_type = form.pay_type.data
        page_size = form.page_size.data
        page_index = form.page_index.data

        entry = User.query.filter_by(**dict(id=uid)).first()
        if not entry:
            return AdjustUserBalanceError(message="系统找不到该用户").as_response()

        # 获取用户最近一周的充值提现记录
        query_datetime_lst = []
        c_date = DateTimeKit.get_cur_datetime()
        s_date = c_date + DateTimeKit.time_delta(days=-7)
        if not DateTimeKit.is_same_month(s_date,
                                         DateTimeKit.get_cur_datetime()):
            _, e_m_date = DateTimeKit.get_month_begin_end(year=s_date.year,
                                                          month=s_date.month)
            s_m_date, _ = DateTimeKit.get_month_begin_end(year=c_date.year,
                                                          month=c_date.month)
            s_date, _ = DateTimeKit.get_day_begin_end(s_date)
            query_datetime_lst.append([s_date, e_m_date])
            query_datetime_lst.append([s_m_date, c_date])
        else:
            s_date, _ = DateTimeKit.get_day_begin_end(s_date)
            query_datetime_lst.append([s_date, c_date])

        order_list = []
        for s_e_date in query_datetime_lst:

            if pay_type == PayTypeEnum.DEPOSIT:
                base_order = OrderDeposit.query_by_create_time(
                    begin_time=s_e_date[0],
                    end_time=s_e_date[1],
                    merchant=entry.merchant).filter_by(**dict(
                        uid=entry.uid)).all()

                # order_detail = OrderDetailDeposit.query_by_create_time(begin_time=s_e_date[0],
                #                                                        end_time=s_e_date[1],
                #                                                        merchant=entry.merchant).filter_by(
                #     uid=entry.uid).all()
                # detail_dict = [{detail.order_id: detail} for detail in order_detail]
                [
                    order_list.append(
                        dict(mch_tx_id=item.mch_tx_id,
                             sys_tx_id=item.sys_tx_id,
                             pay_method=item.pay_method.desc,
                             amount=item.amount,
                             tx_amount=item.tx_amount,
                             create_time=item.str_create_time,
                             state=item.state.get_back_desc(
                                 PayTypeEnum.DEPOSIT))) for item in base_order
                ]
            elif pay_type == PayTypeEnum.WITHDRAW:

                base_order = OrderWithdraw.query_by_create_time(
                    begin_time=s_e_date[0],
                    end_time=s_e_date[1],
                    merchant=entry.merchant).filter_by(**dict(
                        uid=entry.uid)).all()
                detail_order = OrderDetailWithdraw.query_by_create_time(
                    begin_time=s_e_date[0],
                    end_time=s_e_date[1],
                    merchant=entry.merchant).filter_by(**dict(
                        uid=entry.uid)).all()
                detail_dict = {
                    detail.order_id: detail
                    for detail in detail_order
                }

                bank_lst = [order.bank_id for order in base_order]

                bank_info = BankCard.query.filter(
                    BankCard.id.in_(bank_lst)).all()

                bank_dct = {bank.id: bank for bank in bank_info}

                [
                    order_list.append(
                        dict(mch_tx_id=item.mch_tx_id,
                             sys_tx_id=item.sys_tx_id,
                             amount=item.amount,
                             fee=detail_dict[item.order_id].fee,
                             bank_name=bank_dct[item.bank_id].bank_name,
                             card_no=bank_dct[
                                 item.bank_id].card_no_short_description,
                             state=item.state.get_back_desc(
                                 PayTypeEnum.WITHDRAW),
                             done_time=item.update_time,
                             create_time=item.str_create_time))
                    for item in base_order
                ]

        items, total = Pagination.paginate_list(items=order_list,
                                                page_index=page_index,
                                                page_size=page_size,
                                                sort_key="create_time")

        if pay_type == PayTypeEnum.WITHDRAW:
            return UserDepositResult(bs_data=dict(
                depositInfo=dict(entries=[], total=0),
                withdrawInfo=dict(entries=items, total=total))).as_response()
        elif pay_type == PayTypeEnum.DEPOSIT:
            return UserDepositResult(bs_data=dict(
                depositInfo=dict(entries=items, total=total),
                withdrawInfo=dict(entries=[], total=0))).as_response()
예제 #14
0
    def post(self):
        """
        商户提现订单查询
        :return:
        """
        form, error = WithdrawOrderSelectForm().request_validate()
        if error:
            return error.as_response()

        user = g.user

        try:
            order_list_query = OrderWithdraw.query_by_create_time(
                begin_time=form.start_datetime.data,
                end_time=form.end_datetime.data,
                merchant=MerchantEnum(user.mid))

        except MultiMonthQueryException as e:
            return MultiMonthQueryError().as_response()

        kwargs = {}
        if form.order_id.data:
            kwargs["mch_tx_id"] = form.order_id.data

        if form.state.data != "0":
            kwargs["_state"] = form.state.data.value

        entries = order_list_query.filter_by(**kwargs).all()

        bank_lst = list(set([o.bank_id for o in entries]))
        kwargs = {}

        if form.order_id.data and entries:
            kwargs['id'] = entries[0].order_id
        if not MerchantEnum(user.mid).is_api_merchant:
            bank_items = {
                bank.id: bank
                for bank in BankCard.query.filter(BankCard.id.in_(
                    bank_lst)).all()
            }
        order_items = {
            item.id: item
            for item in OrderDetailWithdraw.query_by_create_time(
                begin_time=form.start_datetime.data,
                end_time=form.end_datetime.data,
                merchant=MerchantEnum(user.mid)).filter_by(**kwargs).all()
        }

        items = []
        for order in entries:
            if not MerchantEnum(user.mid).is_api_merchant:
                bank = bank_items.get(order.bank_id, {})
            else:
                bank = order.get_bank_card()
            detail = order_items.get(order.order_id, {})
            if not bank or not detail:
                continue
            items.append(
                dict(mch_tx_id=order.mch_tx_id,
                     sys_tx_id=order.sys_tx_id,
                     amount=detail.amount,
                     fee=detail.fee,
                     account_name=bank.account_name,
                     bank_name=bank.bank_name,
                     branch="{}{}{}".format(bank.province, bank.city,
                                            bank.branch),
                     card_no=bank.card_no,
                     create_time=order.str_create_time,
                     done_time=order.update_time,
                     state=order.state.desc,
                     deliver=order.deliver.desc))
        items = sorted(items, key=lambda item: item['create_time'])

        if items:
            data = list()
            for item in items:
                data.append({
                    "商户订单号": item['mch_tx_id'],
                    "提现金额": str(item['amount']),
                    "手续费": str(item['fee']),
                    "开户名": item['account_name'],
                    "开户银行": item['bank_name'],
                    "开户地址": item['branch'],
                    "银行卡号": item['card_no'],
                    "创建时间": item['create_time'],
                    "完成时间": item['done_time'],
                    "订单状态": item['state'],
                    "通知状态": item['deliver']
                })

            filename = 'epay_withdraw_record_%s.csv' % (
                DateTimeKit.datetime_to_str(
                    DateTimeKit.get_cur_datetime(),
                    DateTimeFormatEnum.TIGHT_DAY_FORMAT))

            return CsvKit.send_csv(data,
                                   filename=filename,
                                   fields=data[0].keys())
예제 #15
0
    def post(self):
        """
        商户提现订单查询
        :return:
        """
        form, error = WithdrawOrderSelectForm().request_validate()
        if error:
            return error.as_response()

        user = g.user

        try:
            order_list_query = OrderWithdraw.query_by_create_time(
                begin_time=form.start_datetime.data,
                end_time=form.end_datetime.data,
                merchant=MerchantEnum(user.mid))

        except MultiMonthQueryException as e:
            return MultiMonthQueryError().as_response()

        kwargs = {}
        tx_id = form.order_id.data
        if tx_id:
            if OrderUtils.is_sys_tx_id(tx_id):
                kwargs["sys_tx_id"] = tx_id
            else:
                kwargs["mch_tx_id"] = tx_id

        if form.state.data != "0":
            kwargs["_state"] = form.state.data.value

        query = order_list_query.filter_by(**kwargs)
        pagination = query.paginate(form.page_index.data, form.page_size.data,
                                    False)

        entries = pagination.items
        total = pagination.total

        bank_lst = list(set([o.bank_id for o in entries]))
        kwargs = {}

        if tx_id and entries:
            kwargs['id'] = entries[0].order_id

        bank_items = dict()
        if not MerchantEnum(user.mid).is_api_merchant:
            bank_items = {
                bank.id: bank
                for bank in BankCard.query.filter(BankCard.id.in_(
                    bank_lst)).all()
            }
        order_items = {
            item.id: item
            for item in OrderDetailWithdraw.query_by_create_time(
                begin_time=form.start_datetime.data,
                end_time=form.end_datetime.data,
                merchant=MerchantEnum(user.mid)).filter_by(**kwargs).all()
        }

        items = []
        for order in entries:
            if not MerchantEnum(user.mid).is_api_merchant:
                bank = bank_items.get(order.bank_id, {})
            else:
                bank = order.get_bank_card()
            detail = order_items.get(order.order_id, {})
            if not bank or not detail:
                continue
            items.append(
                dict(mch_tx_id=order.mch_tx_id,
                     sys_tx_id=order.sys_tx_id,
                     amount=detail.amount,
                     fee=detail.fee,
                     account_name=bank.account_name,
                     bank_name=bank.bank_name,
                     branch="{}{}{}".format(bank.province, bank.city,
                                            bank.branch),
                     card_no=bank.card_no,
                     create_time=order.str_create_time,
                     done_time=order.update_time,
                     state=order.state.desc,
                     deliver=order.deliver.desc))
        items = sorted(items, key=lambda item: item['create_time'])
        return MerchantWithdrawOrderResult(
            bs_data=dict(entries=items, total=total)).as_response()
예제 #16
0
    def manually_withdraw_failed(cls, admin_user, merchant, order_id):
        """
        手动更新提款状态为失败
        退款/审核拒绝
                流程:
            1. 获取创建订单时 扣除 用户及商户的费用
                获取订单 withdrawOrderDetail 数据, 获取 手续费 提现金额
            2. 给用户和商户新增费用
                更新 UserBalance 表
                更新 MerchantBalance表
            3. 修改订单状态
                更新 OrderUpdateCtl
        :param admin_user:
        :param merchant:
        :param order_id:
        :return:
        """
        # 查询该笔订单是否存在
        withdraw_entry = OrderWithdraw.query_by_order_id(merchant=merchant,
                                                         order_id=order_id)
        # 判断是否存在
        if not withdraw_entry:
            return OrderInfoMissingError()

        # 判断订单状态是否为 已认领 或 提现成功
        if withdraw_entry.state not in [
                OrderStateEnum.ALLOC, OrderStateEnum.SUCCESS
        ]:
            return BankOrderStateError()

        detail = OrderDetailWithdraw.query_by_order_id(
            order_id=withdraw_entry.order_id,
            merchant=merchant,
            create_time=withdraw_entry.create_time)
        if not detail:
            return NosuchOrderDetailDataError()

        # 提现订单 手续费 提现订单费用
        fee = detail.fee
        amount = detail.amount

        comment = "出款失败" if withdraw_entry.state == OrderStateEnum.SUCCESS else "系统拒绝"
        order_type = PayTypeEnum.REFUND if withdraw_entry.state == OrderStateEnum.SUCCESS else PayTypeEnum.MANUALLY

        merchant_config = MerchantFeeConfig.query_by_config_id(
            withdraw_entry.mch_fee_id)

        # 更新订单状态
        try:
            with db.auto_commit():

                order, ref_id = OrderUpdateCtl.update_order_event(
                    withdraw_entry.order_id,
                    uid=int(withdraw_entry.uid),
                    merchant=merchant,
                    state=OrderStateEnum.FAIL,
                    tx_amount=withdraw_entry.amount,
                    deliver_type=DeliverTypeEnum.MANUALLY,
                    op_account=admin_user.account,
                    commit=False)

                if not order:
                    msg = WithdrawOrderStateChangeError.message
                    current_app.logger.error(msg)
                    raise WithdrawOrderStateChangeError()

                # 加提现金额
                flag, msg = MerchantBalanceEvent.update_balance(
                    merchant=merchant,
                    ref_id=ref_id,
                    order_type=order_type,
                    bl_type=BalanceTypeEnum.AVAILABLE,
                    value=amount,
                    ad_type=BalanceAdjustTypeEnum.PLUS,
                    tx_id=order.sys_tx_id,
                    source=OrderSourceEnum.MANUALLY,
                    comment=comment,
                    commit=False,
                )
                if flag < 0:
                    msg = '%s' % ("提现退回增加商户余额失败, %s" % msg)
                    current_app.logger.error(msg)
                    raise DepositCallbackUserBalanceError()

                if merchant_config.cost_type == CostTypeEnum.MERCHANT:
                    # 给商户加手续费
                    flag, msg = MerchantBalanceEvent.update_balance(
                        merchant=merchant,
                        ref_id=OrderUtils.gen_unique_ref_id(),
                        order_type=PayTypeEnum.FEE,
                        bl_type=BalanceTypeEnum.AVAILABLE,
                        value=fee,
                        ad_type=BalanceAdjustTypeEnum.PLUS,
                        tx_id=order.sys_tx_id,
                        commit=False,
                        comment=comment,
                        source=OrderSourceEnum.MANUALLY)
                    if flag < 0:
                        msg = '%s' % ("提现退款增加商户手续费失败, %s" % msg)
                        current_app.logger.error(msg)
                        raise DepositCallbackUserBalanceError()

                refund_fee = amount
                if merchant_config.cost_type == CostTypeEnum.USER:
                    # 给用户退回手续费
                    refund_fee += fee

                # 增加用户余额
                flag, msg = UserBalanceEvent.update_user_balance(
                    uid=order.uid,
                    merchant=merchant,
                    ref_id=ref_id,
                    order_type=order_type,
                    bl_type=BalanceTypeEnum.AVAILABLE,
                    value=refund_fee,
                    ad_type=BalanceAdjustTypeEnum.PLUS,
                    tx_id=order.sys_tx_id,
                    commit=False,
                    comment=comment,
                    source=OrderSourceEnum.MANUALLY)
                if flag < 0:
                    msg = '%s' % ("提现退款增加用户余额失败, %s" % msg)
                    current_app.logger.error(msg)
                    raise DepositCallbackUserBalanceError()

        except APIException as e:
            current_app.logger.error(traceback.format_exc())
            return e

        cls.do_notify(
            order=order,
            op_account=admin_user.account,
            comment=comment,
        )

        return ResponseSuccess()
예제 #17
0
    def order_deal(cls,
                   admin_account,
                   order_id,
                   merchant,
                   channel_id,
                   test=False):
        """

        :param admin_account:
        :param order_id:
        :param merchant:
        :param channel_id:
        :param test: 单元测试填写
        :return:
        """
        params = copy.deepcopy(locals())
        params.pop('cls')

        if EnvironEnum.is_production(current_app.config['FLASK_ENV']) and test:
            # 非调试环境不该填写 channel_tx_id,应该发送请求到第三方去申请
            raise RuntimeError('invalid test param')

        order = OrderWithdraw.query_by_order_id(merchant, order_id)
        if not order:
            msg = '%s, params: %s' % (NoSuchWithdrawOrderError.message, params)
            current_app.logger.error(msg)
            return NoSuchWithdrawOrderError()

        if order.state != OrderStateEnum.ALLOC:
            msg = '%s, params: %s, state: %s, tx_id: %s' % (
                NotAllocOrderError.message, params, order.state.desc,
                order.sys_tx_id)
            current_app.logger.error(msg)
            return NotAllocOrderError(message=NotAllocOrderError.message +
                                      ", 订单状态:" + order.state.desc)

        channel_config = ProxyChannelConfig.query_by_channel_id(channel_id)
        if not channel_config:
            msg = '%s, params: %s' % (InvalidChannelError.message, params)
            current_app.logger.error(msg)
            return InvalidChannelError()

        bank_card = order.get_bank_card()
        if not bank_card:
            msg = '%s, params: %s' % (WithdrawBankNoExistError.message, params)
            current_app.logger.error(msg)
            return WithdrawBankNoExistError()

        # 开始更新订单,根据通道计算费率和成本
        # 通道收取的手续费
        channel_cost = FeeCalculator.calc_cost(order.amount,
                                               channel_config.fee_type,
                                               channel_config.fee)
        tx_amount = order.amount

        channel_enum = channel_config.channel_enum
        if channel_enum.plus_fee_for_withdraw():
            # 特殊通道,要发起金额要加上手续费,通道测扣除手续费才是实际到账金额
            # 实际提款金额=发起金额+通道手续费
            tx_amount += channel_cost

        # 发起支付
        launch_pay = channel_enum.get_launch_pay_func(PayTypeEnum.WITHDRAW)

        if not test:
            # 第三方返回由第三方生成交易ID:channel_tx_id
            rst = launch_pay(
                dict(order_id=order.order_id,
                     tx_id=order.sys_tx_id,
                     amount=tx_amount,
                     channel_cost=channel_cost,
                     bank_code=bank_card.bank_code,
                     bank_name=bank_card.bank_name,
                     bank_account=bank_card.account_name,
                     bank_number=bank_card.card_no,
                     bank_address=bank_card.bank_address,
                     bank_branch=bank_card.branch,
                     province=bank_card.province,
                     city=bank_card.city))

            current_app.logger.info('withdraw launch_pay, params: %s, rst: %s',
                                    params, rst)

            if rst['code'] != 0:
                # 不要改变订单状态,让客服去选择其它通道重试
                # cls.order_fail(order, client_ip)
                current_app.logger.error(
                    '%s, %s, params: %s' %
                    (FailedLaunchWithdrawError.message, rst['msg'], params))
                return FailedLaunchWithdrawError(message=rst['msg'])

        # 发起成功后更新状态为处理中
        order, _ = OrderUpdateCtl.update_order_event(
            order.order_id,
            uid=order.uid,
            merchant=merchant,
            state=OrderStateEnum.DEALING,
            channel_id=channel_id,
            cost=channel_cost,
            deal_time=DateTimeKit.get_cur_datetime(),
            op_account=admin_account,
        )
        if not order:
            msg = '%s, params: %s' % (WithdrawUpdateDealingError.message,
                                      params)
            current_app.logger.error(msg)
            return WithdrawUpdateDealingError()

        return ResponseSuccess()
예제 #18
0
    def post(self):
        """
        提现订单详情
        :return:
        """
        form, error = WithDrawBankForm().request_validate()
        if error:
            return error.as_response()

        order_id = form.order_id.data
        order_map = GlobalOrderId.query_global_id(order_id)
        merchant = order_map.merchant
        entry = OrderWithdraw.query_by_order_id(order_id=order_id,
                                                merchant=merchant)
        detail = OrderDetailWithdraw.query_by_order_id(merchant=merchant,
                                                       order_id=order_id)
        detail_head = dict(source=entry.source.desc,
                           op_account=detail.op_account,
                           deliver_type=detail.deliver_type.desc
                           if detail.deliver_type else None,
                           create_time=entry.str_create_time,
                           alloc_time=detail.str_alloc_time,
                           deal_time=detail.str_deal_time,
                           done_time=detail.str_done_time,
                           mch_tx_id=entry.mch_tx_id,
                           sys_tx_id=entry.sys_tx_id,
                           state=entry.state.get_back_desc(
                               PayTypeEnum.WITHDRAW),
                           settle=entry.settle.desc,
                           deliver=entry.deliver.desc,
                           amount=entry.amount)
        order_merchant_info = dict(
            merchant_name=merchant.name,
            fee=detail.fee,
            cost=detail.cost,
            profit=detail.profit,
            withdraw_type="测试" if merchant.is_test else "用户提现")
        deliver_info = None
        if entry.channel_id:
            proxy_entry = ProxyChannelConfig.query_by_channel_id(
                entry.channel_id)
            channel_enum = proxy_entry.channel_enum

            deliver_info = dict(channel_name=channel_enum.desc,
                                mch_id=channel_enum.conf['mch_id'],
                                channel_tx_id=entry.channel_tx_id)
        user_info = dict(
            user_id=entry.uid,
            ip=detail.ip,
            location=GeoIpKit(detail.ip).location,
            device="",
        )

        event_entries = OrderEvent.query_model(
            query_fields=dict(order_id=entry.order_id), date=entry.create_time)

        event_log_list = list()

        for event in event_entries:
            order_event = event.data_after[0]
            order_event.update(event.data_after[1])

            if 'state' in order_event:
                state = list(order_event['state'].keys())[0]
                event_log_list.append(
                    dict(
                        operate_type=OrderStateEnum.from_name(
                            state).get_back_desc(PayTypeEnum.WITHDRAW),
                        operator=order_event.get('op_account') or '',
                        result="成功",
                        operate_time=DateTimeKit.timestamp_to_datetime(
                            order_event['update_time']),
                        comment=order_event.get('comment') or '',
                    ))

            if 'deliver' in order_event:
                deliver = list(order_event['deliver'].keys())[0]
                event_log_list.append(
                    dict(
                        operate_type=DeliverStateEnum.from_name(deliver).desc,
                        operator=order_event.get('op_account') or '',
                        result="成功",
                        operate_time=DateTimeKit.timestamp_to_datetime(
                            order_event['update_time']),
                        comment=order_event.get('comment') or '',
                    ))

        return WithdrawOrderDetailResult(
            bs_data=dict(detail_head=detail_head,
                         order_merchant_info=order_merchant_info,
                         deliver_info=deliver_info,
                         user_info=user_info,
                         event_log_list=event_log_list)).as_response()