def get(self): """ 后台管理员操作日志 :return: """ if not request.args: return ResponseSuccess( message="参数规则:?account=panda&date=20190901&export=1" ).as_response() account = request.args['account'] if account: user = AdminUser.query_user(account=account) if not user: return ResponseSuccess(message="用户不存在,请检查参数。account:%s" % account).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() events = AdminLog.query_by_date(date) if account: events = events.filter_by(account=account) rst = list() for event in events: rst.append( dict( create_time=event.create_time, account=event.account, url=event.url, ip=event.ip, module=event.module.desc, model=event.model, model_id=event.model_id, data_before=event.data_before, data_after=event.data_after, )) rst = sorted(rst, key=lambda x: x['create_time'], reverse=True) for x in rst: x['create_time'] = DateTimeKit.datetime_to_str(x['create_time']) if rst and request.args.get('export'): filename = 'admin_log_%s.csv' % DateTimeKit.datetime_to_str( date, DateTimeFormatEnum.TIGHT_DAY_FORMAT) return CsvKit.send_csv(rst, filename=filename, fields=rst[0].keys()) return ResponseSuccess(bs_data=rst).as_response()
def _parse_suffix(cls, *args, **kwargs): """ 表后缀 :param args: :param kwargs: :return: """ return DateTimeKit.datetime_to_str( kwargs['date'], DateTimeFormatEnum.TIGHT_MONTH_FORMAT)
def test_datetime_kit(self): t = DateTimeKit.gen_midnight_timestamp() self.print("gen_midnight_timestamp, t: %s" % t) self.assertIsInstance(t, int) d = DateTimeKit.timestamp_to_datetime(t) self.print("timestamp_to_datetime, d: %s" % d) self.assertIsInstance(d, datetime.datetime) t = DateTimeKit.datetime_to_timestamp(d) self.print("datetime_to_timestamp, t: %s" % t) self.assertIsInstance(t, int) s = DateTimeKit.datetime_to_str(d) self.print("datetime_to_str, s: %s" % s) self.assertIsInstance(s, str) d = DateTimeKit.str_to_datetime(s) self.print("str_to_datetime, d: %s" % d) self.assertIsInstance(d, datetime.datetime) d = DateTimeKit.get_cur_date() self.print("get_cur_date, d: %s" % d) self.assertIsInstance(d, datetime.date) t = DateTimeKit.datetime_to_timestamp(d) self.print("datetime_to_timestamp, t: %s" % t) self.assertIsInstance(t, int) s = DateTimeKit.datetime_to_str(d) self.print("datetime_to_str, s: %s" % s) self.assertIsInstance(s, str) cur_time = DateTimeKit.get_cur_datetime() rst = DateTimeKit.is_month_begin_time(cur_time) self.assertFalse(rst) someday = DateTimeKit.to_date(year=2019, month=1, day=1) rst = DateTimeKit.is_month_begin_time(someday) self.assertTrue(rst) someday = DateTimeKit.to_date(year=2019, month=2, day=2) rst = DateTimeKit.is_month_begin_time(someday) self.assertFalse(rst)
class MonthMix(BaseMix): """ 按月分表的模型 """ MAX_MONTHS = TABLE_MAX_MONTHS __OLDEST_DATE = TABLE_BEGIN_TIME TABLE_SUFFIXES = [ DateTimeKit.datetime_to_str(x, DateTimeFormatEnum.TIGHT_MONTH_FORMAT) for x in DateTimeKit.gen_month_range(__OLDEST_DATE, months=MAX_MONTHS) ] @classmethod def is_valid_shard_date(cls, date): """ 判断日期是否小于最小的分表日期 :param date: :return: """ if date.year < cls.__OLDEST_DATE.year or ( date.year == cls.__OLDEST_DATE.year and date.month < cls.__OLDEST_DATE.month): return False return True @classmethod def _check_required_params(cls, **kwargs): """ 必填参数检查 :param kwargs: :return: """ date = kwargs.get('date') if not date or not DateTimeKit.is_datetime(date): raise ShardParamsException( 'date is required for cls: %s, kwargs: %s' % (cls.__name__, kwargs)) if not cls.is_valid_shard_date(date): raise ShardDateException( 'sharding date must gte %s, cls: %s, kwargs: %s' % (cls.__OLDEST_DATE, cls.__name__, kwargs)) @classmethod def _parse_suffix(cls, *args, **kwargs): """ 表后缀 :param args: :param kwargs: :return: """ return DateTimeKit.datetime_to_str( kwargs['date'], DateTimeFormatEnum.TIGHT_MONTH_FORMAT)
def withdraw_epay_tong(): from app.main import flask_app with flask_app.app_context(): tasks = list(OrderTasks.query_all()) for task in tasks: order_id = task.order_id batch_date = DateTimeKit.datetime_to_str(task.create_time, DateTimeFormatEnum.TIGHT_DAY_FORMAT) current_app.logger.info('EpayTong withdraw check: order_id: %s, batch_date: %s', order_id, batch_date) order = WithdrawTransactionCtl.get_order_by_order_id(order_id) current_app.logger.info('EpayTong withdraw check: order_state: %s, state_type: %s', order.state, type(order.state)) if not order: current_app.logger.info('EpayTong withdraw check, order_id: %s', order_id) OrderTasks.delete_task(task_id=task.id) continue if order.state.name == OrderStateEnum.SUCCESS.name or order.state.name == OrderStateEnum.FAIL.name: OrderTasks.delete_task(task_id=task.id) continue elif order.state.name != "DEALING": current_app.logger.info('EpayTong withdraw check, order_id: %s, order_state: %s', order_id, order.state) continue current_app.logger.info('EpayTong withdraw check, order_id: %s, order_state: %s', order_id, order.state) params = { "tx_id": order.sys_tx_id, "batch_date": batch_date } rst = EpayTongWithdrawRequest(channel_enum=ChannelConfigEnum.CHANNEL_6013).launch_pay(params) if rst['code'] == 0: tx_amount = rst['data']['tx_amount'] code = rst['data']['tradeFeedbackcode'] print(code, "******************") if code == "成功": if WithdrawTransactionCtl.order_success(order, tx_amount): OrderTasks.delete_task(task_id=task.id) elif code == "失败": if WithdrawTransactionCtl.order_fail(order): OrderTasks.delete_task(task_id=task.id)
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()
def export_deposit_list_csv(cls, order_list, order_detail_dict, all_channels): """ 导出充值订单列表 :param order_list: :param order_detail_dict: :param all_channels: :return: """ # 用户id、系统订单号、商户订单号、创建时间、完成时间、商户、通道商户号、通道、发起金额、实际支付金额、优惠金额、手续费、 # 成本金额、收入金额、订单状态、充值类型 data = list() for order in order_list: channel = all_channels[order.channel_id] order_detail = order_detail_dict[order.order_id] data.append({ "用户ID": order.uid, "系统订单号": order.sys_tx_id, "商户订单号": order.mch_tx_id, "创建时间": order.str_create_time, "完成时间": order_detail.str_done_time, "商户": order.merchant.name, "通道商户号": channel.channel_enum.conf['mch_id'], "通道": channel.channel_enum.desc, "发起金额": str(order.amount), "实际支付金额": str(order.tx_amount), "优惠金额": str(order_detail.offer), "手续费": str(order_detail.fee), "成本金额": str(order_detail.cost), "收入金额": str(order_detail.profit), "订单状态": order.state.desc, "充值类型": order.source.desc, }) filename = 'epay_deposit_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())
def launch_pay(self, params_dict: dict): """ 发起代付 :param params_dict: :return: """ url = self.gen_url(params_dict['tx_id']) params_dict['batch_date'] = DateTimeKit.datetime_to_str( DateTimeKit.get_cur_datetime(), DateTimeFormatEnum.TIGHT_DAY_FORMAT) request_dict, sign_str = self.construct_request(params_dict) sign = CallbackEpayTong.generate_sign(sign_str) request_dict["sign"] = sign request_dict["signType"] = "SHA" try: headers = {"Content-Type": "application/x-www-form-urlencoded"} current_app.logger.info('EpayTong withdraw, url: %s, data: %s', url, request_dict) resp = requests.post(url=url, data=request_dict, headers=headers) current_app.logger.info( 'EpayTong withdraw, status_code: %s, content: %s', resp.status_code, resp.text) except Exception as e: current_app.logger.fatal(traceback.format_exc()) return dict( code=-100, msg="http请求失败", data=dict(), ) print(resp.json(), resp.text, resp.content) return self.parse_response(resp, params_dict)
def export_withdraw_list_csv(cls, order_list, order_detail_dict, all_channels): """ 导出提现订单列表 :param order_list: :param order_detail_dict: :param all_channels: :return: """ # 用户id、系统订单号、商户订单号、创建时间、完成时间、商户、通道商户号、通道、提现金额、手续费、成本金额、收入金额、订单状态、出款类型、备注 data = list() for order in order_list: channel = all_channels.get(order.channel_id) order_detail = order_detail_dict[order.order_id] data.append({ "用户ID": order.uid, "系统订单号": order.sys_tx_id, "商户订单号": order.mch_tx_id, "创建时间": order.str_create_time, "完成时间": order_detail.str_done_time, "商户": order.merchant.name, "通道商户号": channel.channel_enum.conf['mch_id'] if channel else '', "通道": channel.channel_enum.desc if channel else '', "提现金额": str(order.amount), "手续费": str(order_detail.fee), "成本金额": str(order_detail.cost), "收入金额": str(order_detail.profit), "订单状态": order.state.desc, "出款类型": order.source.desc, "备注": order_detail.comment or '', }) 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())
def __test_api_withdraw(self): """ 1. 新建用户提现订单 :return: """ order_cls = OrderWithdraw uid = 1000 channel_enum = ChannelConfigEnum.CHANNEL_1001 banks = [ PaymentBankEnum(int(bank)) for bank in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] ] proxy_channel = dict(fee=Decimal("2.5"), fee_type=PaymentFeeTypeEnum.PERCENT_PER_ORDER, limit_per_min=300, limit_per_max=1000, limit_day_max=0, trade_begin_hour=0, trade_begin_minute=0, trade_end_hour=23, trade_end_minute=59, maintain_begin=DateTimeKit.str_to_datetime( "2019-12-11 09:00:00", DateTimeFormatEnum.SECONDS_FORMAT), maintain_end=DateTimeKit.str_to_datetime( "2025-12-20 23:00:00", DateTimeFormatEnum.SECONDS_FORMAT), state=ChannelStateEnum.TESTING, banks=banks) ProxyChannelConfig.update_channel(channel_enum, **proxy_channel) merchant = MerchantEnum.TEST # 准备配置数据 bank = BankCard.add_bank_card( merchant, uid=uid, bank_name="中国工商银行", bank_code="ICBC", card_no="6212260405014627955", account_name="张三", branch="广东东莞东莞市长安镇支行", province="广东省", city="东莞市", ) OrderMixes.add_one_channel_config(channel_enum) OrderMixes.add_one_merchant_config(merchant, channel_enum, payment_way=PayTypeEnum.WITHDRAW) channel_config = ChannelConfig.query_latest_one( dict(channel_enum=channel_enum)) merchant_fee_config = MerchantFeeConfig.query_latest_one( dict( merchant=merchant, payment_way=PayTypeEnum.WITHDRAW, payment_method=channel_enum.conf.payment_method, )) amount = Decimal("500") fee = BalanceKit.round_4down_5up( Decimal(merchant_fee_config.value) * amount / Decimal(100)) # 创建提现订单 params = dict( uid=uid, merchant=merchant, channel_id=channel_config.channel_id, mch_fee_id=merchant_fee_config.config_id, source=OrderSourceEnum.TESTING, order_type=PayTypeEnum.WITHDRAW, in_type=InterfaceTypeEnum.CASHIER_H5, amount=amount, bank_id=bank.id, fee=fee, ) order, ref_id = OrderCreateCtl.create_order_event(**params) event = OrderEvent.query_one(dict(ref_id=ref_id), merchant=merchant, date=order.create_time) data = order_cls.query_by_order_id(order_id=event.order_id, merchant=merchant) begin_time, end_time = DateTimeKit.get_month_begin_end( year=int(DateTimeKit.get_cur_datetime().year), month=int(DateTimeKit.get_cur_datetime().month)) withdraw_params = dict(merchant_name="TEST", page_size=10, page_index=1, begin_time=DateTimeKit.datetime_to_str( begin_time, DateTimeFormatEnum.SECONDS_FORMAT), end_time=DateTimeKit.datetime_to_str( end_time, DateTimeFormatEnum.SECONDS_FORMAT), state="0") self.path = "/trade_manage/withdraw/list" # 通过接口 查询提现订单 response = self.do_request(json_data=withdraw_params) print(response.json, "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&") self.assertEqual("1", response.json['data']['total']) self.assertEqual("待认领", response.json['data']['entries'][0]['state']) self.path = "/trade_manage/order/allowed" # 通过接口, 认领订单 allowed_params = dict(order_id=order.id, merchant_name="TEST") response = self.do_request(allowed_params) self.assertEqual(ResponseSuccess.code, response.status_code) self.assertEqual(ResponseSuccess.error_code, response.json['error_code']) # 查询当前订单状态是否已修改为已认领 data = order_cls.query_by_order_id(order_id=event.order_id, merchant=merchant) self.assertEqual(OrderStateEnum.ALLOC, data.state) # 通过接口查询 审核列表 已有的认领订单为1 self.path = '/trade_manage/withdraw/review/list' request_review_params = dict( year=str(DateTimeKit.get_cur_datetime().year), mouth=str(DateTimeKit.get_cur_datetime().month)) response = self.do_request(json_data=request_review_params) self.assertEqual(1, len(response.json['data']['entries'])) self.assertEqual("已认领", response.json['data']['entries'][0]['state']) # 通过接口查询 当前可用的 代付通道 proxy_channel_suppor = dict(bank_type=bank.bank_enum.name, merchant_name="TEST", amount=str(amount)) self.path = "/trade_manage/withdraw/available/channel" response = self.do_request(json_data=proxy_channel_suppor) self.assertEqual(WithdrawBankEntryResult.code, response.status_code) self.assertEqual(WithdrawBankEntryResult.error_code, response.json['error_code']) self.assertEqual( channel_enum.conf['provider'] + channel_enum.conf['mch_id'], response.json['data']['entries'][0]['key']) # 测试人工出款 处理订单 self.path = '/trade_manage/withdraw/person/execute' execute_params = dict(order_id=order.order_id, merchant="Test") response = self.do_request(json_data=execute_params) self.assertEqual(ResponseSuccess.code, response.status_code) self.assertEqual(ResponseSuccess.error_code, response.json['error_code']) data = order_cls.query_by_order_id(order_id=event.order_id, merchant=merchant) self.assertEqual(OrderStateEnum.DEALING, data.state) # 测试人工出款 出款 self.path = "/trade_manage/withdraw/person/done" done_params = dict(order_id=order.order_id, merchant='TEST', comment='测试', fee='5') response = self.do_request(json_data=done_params) self.assertEqual(ResponseSuccess.code, response.status_code) self.assertEqual(ResponseSuccess.error_code, response.json['error_code']) data = order_cls.query_by_order_id(order_id=event.order_id, merchant=merchant) self.assertEqual(OrderStateEnum.SUCCESS, data.state)
def get(self): """ 订单修改日志 :return: """ if not request.args: return ResponseSuccess( message= "参数规则:?merchant=test&date=20190901&order_id=123&uid=123&ref_id=xxx&export=1," "必填参数:merchant," "可选参数:date,order_id,uid,ref_id,export," "当不填写date时,默认查询当天所有的数据").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() q_params = dict() order_id = request.args.get('order_id') try: order_id = OrderUtils.parse_tx_id(order_id) except: pass if order_id: q_params['order_id'] = order_id uid = request.args.get('uid') if uid: q_params['uid'] = uid ref_id = request.args.get('ref_id') if ref_id: q_params['ref_id'] = ref_id if not q_params: return ResponseSuccess( message="必须输入 order_id/uid/ref_id 其中一个或多个参数").as_response() events = OrderEvent.query_model(query_fields=q_params, date=date) rst = list() for event in events: rst.append( dict( create_time=event.create_time, order_id=event.order_id, uid=event.uid, ref_id=event.ref_id, data_before=event.data_before, data_after=event.data_after, )) rst = sorted(rst, key=lambda x: x['create_time'], reverse=True) for x in rst: x['create_time'] = DateTimeKit.datetime_to_str(x['create_time']) if rst and request.args.get('export'): filename = 'order_events_%s.csv' % DateTimeKit.datetime_to_str( date, DateTimeFormatEnum.TIGHT_DAY_FORMAT) return CsvKit.send_csv(rst, filename=filename, fields=rst[0].keys()) return ResponseSuccess(bs_data=rst).as_response()
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())
def get(self): """ 查询用户余额变更流水 :return: """ if not request.args: return ResponseSuccess( message= "参数规则:?merchant=test&account=8618912341234&date=20190901&export=1" ).as_response() try: account = request.args.get('account') if account: account = '+' + account.strip('+').strip() if not PhoneNumberParser.is_valid_number(account): raise except: return ResponseSuccess( message="请输入正确的用户手机号码,必须有完整区号,不填+号,如:8613812349999" ).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() rst = dict( data=list(), sum_value=0, ) events = UserBalanceEvent.query_by_date(date, merchant=merchant, date=date) if account: user = User.query_user(merchant, account=account) if not user: return ResponseSuccess(message="用户不存在,请检查参数。商户:%s,手机号码:%s" % (merchant.name, account)) user_balance = UserBalance.query_balance(user.uid, merchant).first() rst.update(user=dict( account=user.account, uid=user.uid, user_balance=str(user_balance.real_balance), )) events = events.filter_by(uid=user.uid) rst['sql'] = str(events) for event in events: rst['sum_value'] += event.value_real rst['data'].append( dict( create_time=event.create_time, uid=event.uid, ref_id=event.ref_id, order_type=event.order_type.desc, source=event.source.desc, bl_type=event.bl_type.desc, value=str(event.value_real), ad_type=event.ad_type.desc, tx_id=event.tx_id, # 大整数,转为字符串 comment=event.comment, extra=event.raw_extra, )) rst['data'] = sorted(rst['data'], key=lambda x: x['create_time'], reverse=True) for x in rst['data']: x['create_time'] = DateTimeKit.datetime_to_str(x['create_time']) if rst['data'] and request.args.get('export'): filename = 'user_balance_events_%s.csv' % DateTimeKit.datetime_to_str( date, DateTimeFormatEnum.TIGHT_DAY_FORMAT) return CsvKit.send_csv(rst['data'], filename=filename, fields=rst['data'][0].keys()) rst['sum_value'] = str(rst['sum_value']) return ResponseSuccess(bs_data=rst).as_response()
def get(self): """ 查询商户余额变更流水 :return: """ if not request.args: return ResponseSuccess( message="参数规则:?merchant=test&date=20190901&export=1" ).as_response() try: merchant = MerchantEnum.from_name(request.args['merchant']) merchant_info = MerchantInfo.query_merchant(merchant) if not merchant_info: raise 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() balance = dict( balance_total=str(merchant_info.balance_total), balance_available=str(merchant_info.balance_available), balance_income=str(merchant_info.balance_income), balance_frozen=str(merchant_info.balance_frozen), ) events = MerchantBalanceEvent.query_by_date(date, merchant=merchant, date=date) rst = dict( data=list(), sum_value=0, balance=balance, ) rst['sql'] = str(events) for event in events: rst['sum_value'] += event.value_real rst['data'].append( dict( create_time=event.create_time, ref_id=event.ref_id, order_type=event.order_type.desc, source=event.source.desc, bl_type=event.bl_type.desc, value=str(event.value_real), ad_type=event.ad_type.desc, tx_id=event.tx_id, comment=event.comment, )) rst['data'] = sorted(rst['data'], key=lambda x: x['create_time'], reverse=True) for x in rst['data']: x['create_time'] = DateTimeKit.datetime_to_str(x['create_time']) if rst['data'] and request.args.get('export'): filename = 'merchant_balance_events_%s.csv' % DateTimeKit.datetime_to_str( date, DateTimeFormatEnum.TIGHT_DAY_FORMAT) return CsvKit.send_csv(rst['data'], filename=filename, fields=rst['data'][0].keys()) rst['sum_value'] = str(rst['sum_value']) return ResponseSuccess(bs_data=rst).as_response()
def post(self): """ 商户充值订单查询数据导出 :return: """ form, error = DepositOrderSelectForm().request_validate() if error: return error.as_response() user = g.user try: order_list_query = OrderDeposit.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["sys_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() items = [] channel_lst = list(set([o.channel_id for o in entries])) channel_items = { Channel.id: Channel for Channel in ChannelConfig.query.filter( ChannelConfig.id.in_(channel_lst)).all() } kwargs = {} if form.order_id.data and entries: kwargs['id'] = entries[0].order_id order_items = { item.id: item for item in OrderDetailDeposit.query_by_create_time( begin_time=form.start_datetime.data, end_time=form.end_datetime.data, merchant=MerchantEnum(user.mid)).filter_by(**kwargs).all() } for order in entries: item_channel = channel_items.get(order.channel_id, {}) detail = order_items.get(order.order_id, {}) if not item_channel or not detail: continue items.append( dict(mch_tx_id=order.mch_tx_id, sys_tx_id=order.sys_tx_id, payment_type=item_channel.channel_enum. conf['payment_type'].desc, amount=detail.amount, tx_amount=detail.tx_amount, fee=detail.fee, 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'], "支付方式": item['payment_type'], "发起金额": str(item['amount']), "实际支付金额": str(item['tx_amount']), "手续费": str(item['fee']), "创建时间": item['create_time'], "完成时间": item['done_time'], "订单状态": item['state'], "通知状态": item['deliver'] }) filename = 'epay_deposit_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())
def construct_request(self, params_dict: dict): tx_id = params_dict['tx_id'] amount = Decimal(params_dict['amount']) bank_code = params_dict['bank_code'] bank_account = params_dict['bank_account'] bank_number = params_dict['bank_number'] request_fields = ['merchantId', 'batchNo', 'batchRecord', 'currencyCode', 'totalAmount', 'payDate', 'isWithdrawNow', 'notifyUrl', 'signType', 'sign', 'detailList'] detail_list = ['receiveType', 'accountType', 'serialNo', 'amount', 'bankName', 'bankNo', 'receiveName'] fields = sorted(request_fields) request_dict = {} for field in fields: if field == 'merchantId': request_dict[field] = self.third_config['mch_id'] elif field == 'batchNo': request_dict[field] = tx_id elif field == 'batchRecord': request_dict[field] = 1 elif field == 'currencyCode': request_dict[field] = 'CNY' # 交易类型 固定值 elif field == 'totalAmount': tx_amount = amount tx_amount = str(tx_amount) if tx_amount.find('.') < 0: tx_amount += '.00' request_dict[field] = tx_amount # 交易金额 单位 分 elif field == 'payDate': request_dict[field] = DateTimeKit.datetime_to_str(DateTimeKit.get_cur_datetime(), DateTimeFormatEnum.TIGHT_DAY_FORMAT) # 后台回调地址 elif field == 'isWithdrawNow': request_dict[field] = '3' # 开户人名称 elif field == 'notifyUrl': request_dict[field] = self.third_config['callback_url'] elif field == 'signType': request_dict[field] = 'RSA' elif field == 'detailList': detail = request_dict.get(field, []) detail_dict = {} for item in detail_list: if item == 'receiveType': detail_dict[item] = '个人' elif item == 'accountType': detail_dict[item] = '储蓄卡' elif item == 'serialNo': detail_dict[item] = tx_id elif item == 'amount': tx_amount = str(amount) if tx_amount.find('.') < 0: tx_amount += '.00' detail_dict[item] = tx_amount elif item == 'bankName': detail_dict[item] = bank_code elif item == 'bankNo': detail_dict[item] = bank_number elif item == 'receiveName': detail_dict[item] = bank_account detail.append(detail_dict) request_dict[field] = detail no_need_fields = ['detailList', 'signType', 'sign'] request_body_str = "&".join(["{}={}".format(k, request_dict[k]) for k in fields if k not in no_need_fields and request_dict.get(k, False)]) sign = CallbackOnePay.gen_sign(body_str=request_body_str, third_config=self.third_config) request_dict['sign'] = sign return request_dict
def str_alloc_time(self): if self.alloc_time: return DateTimeKit.datetime_to_str(self.alloc_time) return ''
def __test_api_channel(self): # 充值通道管理 新增通道 channel_enum = ChannelConfigEnum.CHANNEL_1001 post_data = dict(channel_id=channel_enum.value, fee="2.3", fee_type="1", limit_per_min="2000", limit_per_max="50000", limit_day_max="50000", start_time="09:00", end_time="23:59", state="10", settlement_type="1", priority="1") post_data["state"] = 10 # 数值型参数类型错误 self.path = '/channel/deposit/add' response = self.do_request(post_data) self.assertEqual(response.status_code, ParameterException.code) self.assertEqual(response.json['error_code'], ParameterException.error_code) # print(response.json['message'], "this field must be String type") post_data["state"] = "10" # 测试交易日期为空的情况 response = self.do_request(post_data) print(response.json) self.assertEqual(ResponseSuccess.code, response.status_code, response.json['message']) self.assertEqual(ResponseSuccess.error_code, response.json['error_code'], response.json['message']) # print(response.json['message'], "this field must be String type") post_data['maintain_begin'] = "2019-09-27 09:10:00" post_data['maintain_end'] = "2019-10-20 23:09:00" # 测试每笔交易上限大于日交易上限 post_data['limit_day_max'] = "40000" response = self.do_request(post_data) print(response.json) self.assertEqual(PerLimitMustLittleDayLimitError.code, response.status_code) self.assertEqual(PerLimitMustLittleDayLimitError.error_code, response.json['error_code']) # print(response.json['message'], "this field must be String type") # self.assertEqual(response.json['message'], "单笔交易最大值必须小于当日交易限额") post_data['limit_day_max'] = "60000" # 时间类型参数业务数据不对 post_data["maintain_begin"] = "2020-08-23 09:30:00" response = self.do_request(post_data) self.assertEqual(response.status_code, DateStartMoreThanError.code) self.assertEqual(response.json['error_code'], DateStartMoreThanError.error_code) self.assertEqual(response.json['message'], DateStartMoreThanError.message) post_data["maintain_begin"] = "2019-09-27 09:00:00" # 业务类型数据不对 post_data["limit_per_min"] = "100000" response = self.do_request(post_data) self.assertEqual(response.status_code, DataStartMoreThanError.code) self.assertEqual(response.json['error_code'], DataStartMoreThanError.error_code) self.assertEqual(response.json['message'], DataStartMoreThanError.message) post_data["limit_per_min"] = "2000" # 时间类型数据格式错误 post_data["maintain_begin"] = "2019/09/27 09:00:00" response = self.do_request(post_data) self.assertEqual(response.status_code, ParameterException.code) self.assertEqual(response.json['error_code'], ParameterException.error_code) # self.assertEqual(response.json['message'], "无效的时间格式") post_data["maintain_begin"] = "2019-09-27 09:00:00" # 测试成功添加数据 response = self.do_request(post_data) self.assertEqual(response.status_code, ResponseSuccess.code) channel = ChannelConfig.query_latest_one( dict(channel_enum=channel_enum)) self.assertEqual(channel.channel_enum.value, post_data['channel_id']) self.assertEqual(channel.settlement_type.value, int(post_data['settlement_type'])) # 渠道管理:编辑通道 self.path = '/channel/deposit/edit' post_data['settlement_type'] = '3' response = self.do_request(post_data) self.assertEqual(response.status_code, ResponseSuccess.code) channel = ChannelConfig.query_latest_one( dict(channel_enum=channel_enum)) self.assertEqual(channel.channel_enum.value, post_data['channel_id']) self.assertEqual(channel.settlement_type.value, 3) # 测试 不支持的 枚举类型数据 self.path = '/channel/deposit/edit' post_data['state'] = '110' response = self.do_request(post_data) self.assertEqual(response.status_code, ParameterException.code) self.assertEqual(response.json['error_code'], ParameterException.error_code) # self.assertEqual(response.json['message'], "无效的通道状态") post_data['state'] = "10" # 代付通道管理: 新增代付通道 self.path = "/channel/withdraw/add" withdraw_postdata = dict(channel_id=channel_enum.value, fee="2.3", fee_type="1", limit_per_min="2000", limit_per_max="50000", limit_day_max="50000", start_time="09:00", end_time="23:59", maintain_begin="2019-09-27 09:00:00", maintain_end="2019-10-20 23:00:00", state="10", banks=["1", "2", "4", "6", "3", "5", "15"]) # 测试参数类型错误 withdraw_postdata['channel_id'] = '123' response = self.do_request(withdraw_postdata) self.assertEqual(response.status_code, ParameterException.code) self.assertEqual(response.json['error_code'], ParameterException.error_code) # self.assertEqual(response.json['message'], str({'channel_id': 'this field must be Integer type'})) # 测试时间格式错误 withdraw_postdata['channel_id'] = channel_enum.value withdraw_postdata["maintain_begin"] = "2019/09/27 09:00:00" response = self.do_request(withdraw_postdata) self.assertEqual(response.status_code, ParameterException.code) self.assertEqual(response.json['error_code'], ParameterException.error_code) # self.assertEqual(response.json['message'], "无效的时间格式") withdraw_postdata["maintain_begin"] = "2019-09-27 09:00:00" # 测试成功添加代付通道 ProxyChannelConfig.delete_all() response = self.do_request(withdraw_postdata) self.assertEqual(response.status_code, ResponseSuccess.code) self.assertEqual(response.json['error_code'], ResponseSuccess.error_code) channel = ProxyChannelConfig.query_latest_one( dict(channel_enum=channel_enum)) self.assertEqual(withdraw_postdata['maintain_begin'], DateTimeKit.datetime_to_str(channel.maintain_begin)) self.assertEqual(withdraw_postdata['channel_id'], channel.channel_enum.value) # 测试编辑 代付通道 self.path = "/channel/withdraw/edit" withdraw_postdata['banks'] = ["4", "6", "3"] withdraw_postdata['limit_day_max'] = '180000' withdraw_postdata['maintain_begin'] = "2019-10-30 09:30:01" withdraw_postdata['maintain_end'] = "2019-12-30 09:30:01" response = self.do_request(withdraw_postdata) self.assertEqual(response.status_code, ResponseSuccess.code) self.assertEqual(response.json['error_code'], ResponseSuccess.error_code) channel = ProxyChannelConfig.query_latest_one( dict(channel_enum=channel_enum)) self.assertEqual(channel_enum.value, channel.channel_enum.value) self.assertEqual(withdraw_postdata['maintain_begin'], DateTimeKit.datetime_to_str(channel.maintain_begin)) self.assertEqual(withdraw_postdata['maintain_end'], DateTimeKit.datetime_to_str(channel.maintain_end)) self.assertEqual( [PaymentBankEnum(4), PaymentBankEnum(6), PaymentBankEnum(3)], channel.banks) self.assertEqual(180000, channel.limit_day_max) # 测试代付列表 self.path = "/channel/withdraw/list" response = self.do_request() self.assertEqual(response.status_code, ResponseSuccess.code) self.assertEqual(response.json['error_code'], ResponseSuccess.error_code) self.assertEqual('1', response.json['data']['counts']) self.assertEqual(channel_enum.value, response.json['data']['withdraws'][0]['channel_id']) ProxyChannelConfig.delete_all()
def str_done_time(self): if self.done_time: return DateTimeKit.datetime_to_str(self.done_time) return ''
def get_suffix(self, cur_datetime=None): cur_datetime = cur_datetime or DateTimeKit.get_cur_datetime() return DateTimeKit.datetime_to_str(cur_datetime, self.suffix_format)
def str_create_time(self): if self.create_time: return DateTimeKit.datetime_to_str(self.create_time) return ''
def str_update_time(self): return DateTimeKit.datetime_to_str(self.update_time)