def check_and_clean(self): delete_files = list() for name in os.listdir(self.log_path): # print(name) if name == self.filename: continue try: date = self.parse_suffix(name) except: continue delta_days = (DateTimeKit.get_cur_datetime() - date).days # print(delta_days, keep_days) if delta_days < self.keep_days: print('keep file: ', name) continue delete_files.append(name) for name in delete_files: file = os.path.join(self.log_path, name) print('to remove', file) if os.path.exists(file): print('removed ', file) os.remove(file)
def is_in_trade_time( self, end_delta: datetime.timedelta = datetime.timedelta(minutes=5)): """ 判断当前通道是否处于交易时间内 :param end_delta: :return: """ if (self.trade_begin_hour == 0 and self.trade_begin_minute == 0) and ( (self.trade_end_hour == 0 and self.trade_end_minute == 0) or (self.trade_end_hour == 23 and self.trade_end_minute == 59)): # 未设置交易时间,或者结束时间为23点59 return True begin_time = self.begin_trade_time end_time = self.end_trade_time if end_delta: end_time -= end_delta cur_datetime = DateTimeKit.get_cur_datetime() if end_time < begin_time: # 如果设置的结束时间小于开始时间,比如交易周期是早上9点到凌晨的2点,那么交易时间是9点到0点以及0点到2点 return cur_datetime <= end_time or cur_datetime >= begin_time return begin_time <= cur_datetime <= end_time
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()
def create_order(cls): merchant = MerchantEnum.TEST order_id = OrderTstGid.generate_order_id() create_time = DateTimeKit.get_cur_datetime() fields = dict( uid=0, create_time=create_time, update_time=create_time, order_id=order_id, amount=Decimal("234142.33"), mch_tx_id=OrderUtils.generate_mch_tx_id(order_id), sys_tx_id=OrderUtils.generate_sys_tx_id(order_id), source=OrderSourceEnum.MANUALLY, state=OrderStateEnum.INIT, op_account='test', pay_method=PayMethodEnum.ZHIFUBAO_SAOMA, notify_url="https://google.com", result_url="https://google.com", extra=json.dumps(dict(x=1, y=2, z=3)), ) rst = cls.add_model(fields, merchant=merchant, date=create_time, commit=True) return rst
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()
def check_and_clean(cls, keep_days=7): full_path = os.path.join(BASEDIR, log_path) delete_files = list() for name in os.listdir(full_path): # print(name) if log_prefix not in name: continue try: date_str = name.split('.')[-1] date = DateTimeKit.str_to_datetime( date_str, DateTimeFormatEnum.DAY_FORMAT) except: continue delta_days = (DateTimeKit.get_cur_datetime() - date).days # print(delta_days, keep_days) if delta_days < keep_days: print('keep file: ', name) continue delete_files.append(name) for name in delete_files: file = os.path.join(full_path, name) print('removed ', file) os.remove(file)
def drop_all_records(cls): create_time = DateTimeKit.get_cur_datetime() with db.auto_commit(): model_cls = cls.get_model_cls(date=create_time) model_cls.query.delete() model_cls = cls.get_cold_model_cls(date=create_time) model_cls.query.delete()
def count_all_records(cls): merchant = MerchantEnum.TEST create_time = DateTimeKit.get_cur_datetime() begin, end = DateTimeKit.get_month_begin_end(create_time.year, create_time.month) return cls.query_by_create_time(begin, end, merchant=merchant, date=create_time).count()
def __change_balance(self, data: dict, result: int, balance_check: dict, event_check: dict): """ 修改 :param data: :param result:merchant_balance_event_test_91 :param balance_check: :param event_check: :return: """ # 修改余额 data = copy.deepcopy(data) ref_id = OrderUtils.gen_unique_ref_id() data['ref_id'] = ref_id event_check['tx_id'] = data['tx_id'] = OrderUtils.gen_normal_tx_id(int(data['value'])) rst, msg = MerchantBalanceEvent.update_balance(**data) self.assertEqual(result, rst) if rst == 0: event_check['ref_id'] = ref_id # 验证余额 merchant = MerchantInfo.query_merchant(data['merchant']) self.assertIsNotNone(merchant) balance_total = sum(balance_check.values()) self.assertEqual(balance_total, merchant.balance_total) self.assertEqual(balance_check['balance_available'], merchant.balance_available) self.assertEqual(balance_check['balance_frozen'], merchant.balance_frozen) self.assertEqual(balance_check['balance_income'], merchant.balance_income) if rst != 0: # 更新失败,就不用验证结果事件了 return rst event = MerchantBalanceEvent.query_event(merchant=merchant.merchant, create_time=DateTimeKit.get_cur_datetime(), ref_id=ref_id).first() value = event_check['value'] if event_check['ad_type'] == BalanceAdjustTypeEnum.MINUS: # 做减法 value = -event_check['value'] self.assertEqual(event_check['merchant'], event.merchant) self.assertEqual(event_check['source'], event.source) self.assertEqual(event_check['bl_type'], event.bl_type) self.assertEqual(event_check['ad_type'], event.ad_type) self.assertEqual(value, event.value_real) self.assertEqual(event_check['tx_id'], event.tx_id) self.assertEqual(event_check['comment'], event.comment) self.assertEqual(event_check['ref_id'], event.ref_id) return rst
def __change_balance(self, data: dict, result: int, balance_check: dict): """ 修改 :param data: :param result: :param balance_check: :return: """ # 修改余额 data = copy.deepcopy(data) ref_id = OrderUtils.gen_unique_ref_id() data['ref_id'] = ref_id data['tx_id'] = OrderUtils.gen_normal_tx_id(data['uid']) rst, msg = UserBalanceEvent.update_user_balance(**data) self.assertEqual(result, rst) # 验证余额 balance = UserBalance.query_balance(data['uid'], data['merchant']).first() self.assertIsNotNone(balance) self.assertEqual(balance_check['balance_available'], balance.real_balance) if rst != 0: # 更新失败,就不用验证结果事件了 return rst event = UserBalanceEvent.query_event( uid=data['uid'], merchant=balance.merchant, date=DateTimeKit.get_cur_datetime(), ref_id=ref_id).first() value = data['value'] if data['ad_type'] == BalanceAdjustTypeEnum.MINUS: # 做减法 value = -data['value'] self.assertEqual(data['uid'], event.uid) self.assertEqual(data['merchant'], event.merchant) self.assertEqual(data['source'], event.source) self.assertEqual(data['bl_type'], event.bl_type) self.assertEqual(data['ad_type'], event.ad_type) self.assertEqual(value, event.value_real) self.assertEqual(ref_id, event.ref_id) if data['source'] == OrderSourceEnum.MANUALLY: self.assertEqual(data['comment'], event.comment) return rst
def is_in_maintain_time( self, end_delta: datetime.timedelta = datetime.timedelta(minutes=5)): """ 判断当前通道是否处于维护时间内 :param end_delta: :return: """ begin_time = self.maintain_begin end_time = self.maintain_end if not begin_time or not end_time: # 未设置维护时间 return False if end_delta: end_time -= end_delta return begin_time <= DateTimeKit.get_cur_datetime() <= end_time
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)
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 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 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 __init__(self): self.create_time = DateTimeKit.get_cur_datetime()
def update_order_event( cls, order_id, uid, merchant: MerchantEnum, state: OrderStateEnum = None, # 订单状态 tx_amount=0, # 实际支付金额 pay_method: PayMethodEnum = None, # 支付方法 ref_id=None, # 票据ID channel_tx_id=None, # 通道交易ID,支付成功后填写 settle: SettleStateEnum = None, # 结算状态 deliver: DeliverStateEnum = None, # 给下游商户的发货(通知)状态 channel_id: int = None, # 通道费率ID,ABSChannelConfig 表中的主键ID,确认出款时必填 mch_fee_id: int = None, # 商户费率配置ID,MerchantFeeConfig 表的主键ID,确认出款时必填 op_account=None, # 后台管理员账号 comment=None, # 管理后台修改备注 offer=None, # 优惠金额 fee=None, # 手续费 cost=None, # 成本金额 profit=None, # 利润(收入)金额 deliver_type: DeliverTypeEnum = None, # 出款类型 alloc_time=None, # 提款订单分配时间 deal_time=None, # 提款订单处理时间 done_time=None, # 订单完成时间 commit=True, # 是否立即提交事务 ): update_time = DateTimeKit.get_cur_datetime() g_order_id = GlobalOrderId.query_global_id(order_id) create_time = g_order_id.create_time order_type = g_order_id.order_type if order_type == PayTypeEnum.WITHDRAW: if state == OrderStateEnum.DEALING: if deliver_type == DeliverTypeEnum.MANUALLY: if not (mch_fee_id and deal_time): raise RuntimeError("人工出款时必填 mch_fee_id/deal_time") else: if not deal_time: raise RuntimeError("出款时必填 deal_time") if state == OrderStateEnum.ALLOC and not alloc_time: raise RuntimeError("认领订单必填认领时间") elif order_type == PayTypeEnum.DEPOSIT: pass if state in [OrderStateEnum.SUCCESS, OrderStateEnum.FAIL ] and not done_time: # 订单交易完成 done_time = update_time params = copy.deepcopy(locals()) params.pop('cls') if not ref_id: ref_id = OrderConstraint.apply_ref_id(order_id, order_type, state) if not ref_id: # 票据申请失败 return None, ref_id params['ref_id'] = ref_id try: # 订单事件 rst = cls.__create_event(**params) order_event = rst['model'] rst, fields = cls.__update_order(**params) if rst['code'] != 0: OrderConstraint.revoke_order_state(order_id, state) current_app.logger.error('rst: %s, params: %s', rst, params) return None, '' models = list(rst['model'].values()) order = rst['model']['hot'] # 订单详情 rst, fields = cls.__update_order_detail(**params) if rst['code'] != 0: OrderConstraint.revoke_order_state(order_id, state) current_app.logger.error('rst: %s, params: %s', rst, params) return None, '' models.extend(rst['model'].values()) order_detail = rst['model']['hot'] # 订单修改事件 rst = cls.__create_event(**params) order_event = rst['model'] order_event.data_before = [ order.get_before_fields(), order_detail.get_before_fields() ] order_event.data_after = [ order.get_after_fields(), order_detail.get_after_fields() ] models.append(order_event) # 提交事务 assert len(models) == 5 ModelOpMix.commit_models(models=models, commit=commit) except Exception as e: # 任何非期望的异常都要回滚状态 OrderConstraint.revoke_order_state(order_id, state) current_app.logger.error(traceback.format_exc()) return None, ref_id return order, ref_id
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 update_balance(cls, merchant: MerchantEnum, ref_id, source: OrderSourceEnum, bl_type: BalanceTypeEnum, value: float, order_type: PayTypeEnum, tx_id, ad_type: BalanceAdjustTypeEnum = None, comment: str = '', create_time=None, commit=True): """ 修改余额 :param merchant: :param ref_id: 32位的md5ID :param source: 操作来源 :param order_type: 订单类型 :param bl_type: 余额类型 :param ad_type: 当人工操作时,必填调整类型;订单类型的调整不需要填写ad_type :param value: 始终传入正整数 :param comment: 当人工调整时,必填备注信息 :param create_time: :param tx_id: 交易流水号 :param commit: 是否立即提交事务 :return: (result, msg) """ # 记录一个参数日志, locals自动收集这行代码之前出现过的局部变量 params = copy.deepcopy(locals()) params.pop('cls') # current_app.config['SENTRY_DSN'] and current_app.logger.info('update_balance, params: %s', params) rst, msg = cls.__check_event_params(params) if rst != 0: return rst, msg if ad_type is None: if source == PayTypeEnum.DEPOSIT: # 存款+ ad_type = BalanceAdjustTypeEnum.PLUS else: # 提款- ad_type = BalanceAdjustTypeEnum.MINUS # 金额入库乘100转换为分钱 value = BalanceKit.multiple_hundred(value) if ad_type == BalanceAdjustTypeEnum.MINUS: # 做减法 value = -value try: with db.auto_commit(commit): # 生成修改事件 create_time = create_time or DateTimeKit.get_cur_datetime() fields = dict( source=source, order_type=order_type, bl_type=bl_type, value=value, tx_id=tx_id, ad_type=ad_type, comment=comment, ref_id=ref_id, create_time=create_time, extra=dict(), ) rst = cls.add_model(fields, merchant=merchant, date=create_time) db.session.add(rst['model']) # 更新商户余额 before_merchant, after_merchant = cls.__update_balance( merchant, source, order_type, bl_type, ad_type, value) # 添加admin日志 if source == OrderSourceEnum.MANUALLY: log_model = cls.add_admin_log( data_before=before_merchant.get_before_fields(), data_after=after_merchant.get_after_fields(), ) log_model and db.session.add(log_model) except RuntimeError as e: # 捕获异常,返回失败 msg = "%s, params: %s" % (str(e), params) current_app.config['SENTRY_DSN'] and current_app.logger.fatal(msg) return -100, str(e) except InsufficientBalanceException as e: msg = str(e) if bl_type == BalanceTypeEnum.AVAILABLE: msg = "可用余额不足" if bl_type == BalanceTypeEnum.INCOME: msg = "在途余额不足" if bl_type == BalanceTypeEnum.FROZEN: if ad_type == BalanceAdjustTypeEnum.PLUS: msg = "可用余额不足" else: msg = "冻结余额不足" return -101, msg return 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()
def __test_admin_log(self): begin_time = DateTimeKit.get_cur_datetime() data = dict( account='lucy', ip='127.0.0.1', module=AdminModuleEnum.SYSTEM, data_before=dict(x=1, y=2, c="你好"), data_after=dict(x=3, y=4, c="哈哈"), ) AdminLog.add_log(**data) # time.sleep(1) end_time = DateTimeKit.get_cur_datetime() logs = list(AdminLog.query_log(begin_time, end_time)) log = logs[0] self.assertEqual(len(logs), 1) self.assertEqual(log.account, data['account']) self.assertEqual(log.ip, data['ip']) self.assertEqual(log.module, data['module']) self.assertEqual(log.data_before, data['data_before']) self.assertEqual(log.data_after, data['data_after']) data = dict( account='clark', ip='192.168.12.233', module=AdminModuleEnum.TRADE, data_before=dict(x=1, y=2, c="你好"), data_after=dict(x=3, y=4, c="哈哈"), ) AdminLog.add_log(**data) # time.sleep(1) end_time = DateTimeKit.get_cur_datetime() logs = list(AdminLog.query_log(begin_time, end_time)) log = logs[1] self.assertEqual(len(logs), 2) self.assertEqual(log.account, data['account']) self.assertEqual(log.ip, data['ip']) self.assertEqual(log.module, data['module']) self.assertEqual(log.data_before, data['data_before']) self.assertEqual(log.data_after, data['data_after']) # 以日期查询,当天0点到第二天0点 logs = list( AdminLog.query_log( begin_time.date(), end_time.date() + DateTimeKit.time_delta(days=1))) log = logs[1] self.assertEqual(len(logs), 2) self.assertEqual(log.account, data['account']) self.assertEqual(log.ip, data['ip']) self.assertEqual(log.module, data['module']) self.assertEqual(log.data_before, data['data_before']) self.assertEqual(log.data_after, data['data_after']) logs = list( AdminLog.query_by_account(begin_time, end_time, account='clark')) log = logs[0] self.assertEqual(len(logs), 1) self.assertEqual(log.account, data['account']) self.assertEqual(log.ip, data['ip']) self.assertEqual(log.module, data['module']) self.assertEqual(log.data_before, data['data_before']) self.assertEqual(log.data_after, data['data_after']) logs = list(AdminLog.query_all()) self.assertEqual(len(logs), 2) AdminLog.delete_all() logs = list(AdminLog.query_all()) self.assertEqual(len(logs), 0)
def create_order_event( cls, uid, amount, merchant: MerchantEnum, source: OrderSourceEnum, # 订单来源 order_type: PayTypeEnum, # 订单类型 in_type: InterfaceTypeEnum, # 商户接入类型 pay_method: PayMethodEnum = None, # 支付方法 create_time=None, bank_id=None, # 用户提现银行卡ID ref_id=None, # 票据ID op_account=None, # 后台管理员账号,后台人工修改数据时必填 comment=None, # 管理后台修改备注,后台人工修改数据时必填 mch_tx_id=None, # 商户交易ID,当in_type为API时必填,其它时候选填 channel_id: int = None, # 通道费率ID,ABSChannelConfig 表中的主键ID,充值必填,提款不填 mch_fee_id: int = None, # 商户费率配置ID,MerchantFeeConfig 表的主键ID,充值必填,提款不填 ip=None, fee=None, # 提现订单,创建的时候就扣了手续费 notify_url=None, result_url=None, extra=None, bank_info=None, # 银行卡信息,用于API商户 cost_type=None, commit=True, # 是否立即提交事务 ): if source == OrderSourceEnum.MANUALLY and not (op_account and comment): raise RuntimeError("人工修改订单必填管理员账号和备注") if in_type == InterfaceTypeEnum.API and not mch_tx_id: raise RuntimeError("开放API必须提供商户交易ID") if order_type == PayTypeEnum.DEPOSIT and not (channel_id and mch_fee_id): raise RuntimeError("充值必须填写channel_id/mch_fee_id/bank_id") if order_type == PayTypeEnum.WITHDRAW: if not (fee and mch_fee_id): raise RuntimeError("提现必须填写 fee/mch_fee_id") if not (bank_info or bank_id): raise RuntimeError("提现必须填写 bank_info 或 bank_id") create_time = create_time or DateTimeKit.get_cur_datetime() order_id = GlobalOrderId.generate_order_id(uid, order_type, create_time, merchant) sys_tx_id = OrderUtils.generate_sys_tx_id(order_id) if not mch_tx_id: mch_tx_id = OrderUtils.generate_mch_tx_id(order_id) params = copy.deepcopy(locals()) params.pop('cls') if not ref_id: ref_id = OrderConstraint.apply_ref_id(order_id, order_type, OrderStateEnum.INIT) if not ref_id: return None, '' params['ref_id'] = ref_id try: # 订单模型 rst, fields = cls.__create_order(**params) if rst['code'] != 0: OrderConstraint.revoke_order_state(order_id, OrderStateEnum.INIT) current_app.logger.error('failed to create order model, rst: %s, params: %s', rst, params) return None, ref_id models = list(rst['model'].values()) order = rst['model']['hot'] # 订单详情 rst, fields = cls.__create_order_detail(**params) if rst['code'] != 0: OrderConstraint.revoke_order_state(order_id, OrderStateEnum.INIT) current_app.logger.error('failed to create order detail model, rst: %s, params: %s', rst, params) return None, ref_id models.extend(rst['model'].values()) order_detail = rst['model']['hot'] # 订单修改事件 rst = cls.__create_event(**params) order_event = rst['model'] order_event.data_after = [order.get_after_fields(), order_detail.get_after_fields()] models.append(order_event) # 提交事务 assert len(models) == 5 ModelOpMix.commit_models(models=models, commit=commit) except IntegrityError as e: OrderConstraint.revoke_order_state(order_id, OrderStateEnum.INIT) # sqlalchemy.exc.IntegrityError: (pymysql.err.IntegrityError) (1062, "Duplicate entry '4155' for key 'mch_tx_id'") msg = str(e) if 'Duplicate' in msg and 'mch_tx_id' in msg: # 订单号重复时不能重置订单状态 msg = "商户订单号重复" current_app.logger.error(str(msg), exc_info=True) raise Exception(msg) except Exception as e: # 任何非期望的异常都要回滚状态 OrderConstraint.revoke_order_state(order_id, OrderStateEnum.INIT) current_app.logger.error(str(e), exc_info=True) raise e return order, ref_id
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 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 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 __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 post(self): """ 代付通道管理: 编辑代付通道 :return: """ form, error = WithdrawAddForm().request_validate() if error: return error.as_response() if form.start_time.data >= form.end_time.data: return DateStartMoreThanError().as_response() if form.maintain_begin.data: if form.maintain_begin.data >= form.maintain_end.data or form.maintain_begin.data < DateTimeKit.get_cur_datetime( ): return DateStartMoreThanError().as_response() if Decimal(form.limit_per_min.data) >= Decimal( form.limit_per_max.data): return DataStartMoreThanError().as_response() if form.limit_day_max.data and Decimal( form.limit_per_max.data) > Decimal(form.limit_day_max.data): return PerLimitMustLittleDayLimitError().as_response() banks = [PaymentBankEnum(int(bank)) for bank in form.banks.data] kwargs = dict( fee=form.fee.data, fee_type=form.fee_type.data, limit_per_min=form.limit_per_min.data, limit_per_max=form.limit_per_max.data, limit_day_max=form.limit_day_max.data if form.limit_day_max.data != "" else 0, trade_begin_hour=form.start_time.data.hour, trade_begin_minute=form.start_time.data.minute, trade_end_hour=form.end_time.data.hour, trade_end_minute=form.end_time.data.minute, maintain_begin=form.maintain_begin.data if form.maintain_begin else "", maintain_end=form.maintain_end.data if form.maintain_end else "", state=form.state.data, banks=banks) rst, error = ProxyChannelConfig.update_channel(form.channel_id.data, **kwargs) if error: return error.as_response() # 同步缓存 # ChannelLimitCacheCtl(PayTypeEnum.WITHDRAW).sync_db_channels_to_cache() return ResponseSuccess().as_response()
def post(self): """ 通道管理: 删除通道 :return: """ form, error = ChannelAddForm().request_validate() if error: return error.as_response() # if form.start_time.data >= form.end_time.data: # return DateStartMoreThanError().as_response() if form.maintain_begin.data: if form.maintain_begin.data >= form.maintain_end.data or form.maintain_begin.data < DateTimeKit.get_cur_datetime( ): return DateStartMoreThanError().as_response() if int(form.limit_per_min.data) >= int(form.limit_per_max.data): return DataStartMoreThanError().as_response() if form.limit_day_max.data and Decimal( form.limit_per_max.data) > Decimal(form.limit_day_max.data): return PerLimitMustLittleDayLimitError().as_response() kwargs = dict(fee=form.fee.data, fee_type=form.fee_type.data, limit_per_min=form.limit_per_min.data, limit_per_max=form.limit_per_max.data, limit_day_max=form.limit_day_max.data if form.limit_day_max.data != "" else 0, trade_begin_hour=form.start_time.data.hour, trade_begin_minute=form.start_time.data.minute, trade_end_hour=form.end_time.data.hour, trade_end_minute=form.end_time.data.minute, maintain_begin=form.maintain_begin.data, maintain_end=form.maintain_end.data, settlement_type=form.settlement_type.data, state=form.state.data, priority=form.priority.data, valid=ModelBase.INVALID) rst, error = ChannelConfig.update_channel(form.channel_id.data, **kwargs) if error: return error.as_response() # 同步缓存 # ChannelLimitCacheCtl(PayTypeEnum.DEPOSIT).sync_db_channels_to_cache() return ResponseSuccess().as_response()
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()
def __add_event(self, uid, merchant, channel_enum, order_type): order_cls = OrderDeposit if order_type == PayTypeEnum.DEPOSIT else OrderWithdraw channel_config = ChannelConfig.query_latest_one( dict(channel_enum=channel_enum)) merchant_fee_config = MerchantFeeConfig.query_latest_one( dict( merchant=merchant, payment_way=PayTypeEnum.DEPOSIT, payment_method=channel_enum.conf.payment_method, )) 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=order_type, in_type=InterfaceTypeEnum.CASHIER_H5, amount=Decimal("500"), comment='谢谢', op_account='xxx', bank_id=123, fee=10 if order_type == PayTypeEnum.WITHDRAW else 0, ) order, ref_id = OrderCreateCtl.create_order_event(**params) self.assertIsNotNone(order) event = OrderEvent.query_one(dict(ref_id=ref_id), merchant=merchant, date=order.create_time) self.assertIsNotNone(event) self.assertEqual(order.order_id, event.order_id) self.assertEqual(order.uid, event.uid) self.assertEqual(ref_id, event.ref_id) order = order_cls.query_by_order_id(order_id=event.order_id, merchant=merchant) self.assertIsNotNone(order) self.assertEqual(params['uid'], order.uid) self.assertEqual(params['merchant'], order.merchant) self.assertEqual(params['channel_id'], order.channel_id) self.assertEqual(params['source'], order.source) self.assertEqual(params['amount'], order.amount) self.assertTrue(len(order.mch_tx_id) > 0) self.assertTrue(len(order.sys_tx_id) > 0) # 更新订单 order, ref_id = OrderUpdateCtl.update_order_event( order_id=order.order_id, uid=order.uid, merchant=merchant, state=OrderStateEnum.SUCCESS if order_type == PayTypeEnum.DEPOSIT else OrderStateEnum.ALLOC, tx_amount=Decimal("500.32"), channel_tx_id='1232283838229929292', settle=SettleStateEnum.DONE, deliver=DeliverStateEnum.DONE, channel_id=channel_config.channel_id, mch_fee_id=merchant_fee_config.config_id, op_account='xxxx', comment='改了改了', offer=Decimal('1.22'), fee=Decimal('1.22'), cost=Decimal('1.22'), profit=Decimal('1.22'), deliver_type=DeliverTypeEnum.PROXY, alloc_time=DateTimeKit.get_cur_datetime(), deal_time=DateTimeKit.get_cur_datetime(), ) self.assertIsNotNone(order) event = OrderEvent.query_one(dict(ref_id=ref_id), merchant=merchant, date=order.create_time) self.assertEqual(order.order_id, event.order_id) self.assertEqual(order.uid, event.uid) self.assertEqual(ref_id, event.ref_id) order3 = order_cls.query_by_order_id(order_id=event.order_id, merchant=merchant) self.assertIsNotNone(order3) self.assertEqual(params['uid'], order3.uid) self.assertEqual(params['merchant'], order3.merchant) self.assertEqual(params['channel_id'], order3.channel_id) self.assertEqual(params['source'], order3.source) self.assertEqual(Decimal("500.32"), order3.tx_amount) self.assertEqual(order.order_id, order3.order_id) self.assertEqual(order.mch_tx_id, order3.mch_tx_id) self.assertEqual(order.sys_tx_id, order3.sys_tx_id) order3 = order_cls.query_by_tx_id(tx_id=order.sys_tx_id) self.assertIsNotNone(order3) self.assertEqual(params['uid'], order3.uid) self.assertEqual(params['merchant'], order3.merchant) self.assertEqual(params['channel_id'], order3.channel_id) self.assertEqual(params['source'], order3.source) self.assertEqual(Decimal("500.32"), order3.tx_amount) self.assertEqual(order.order_id, order3.order_id) self.assertEqual(order.mch_tx_id, order3.mch_tx_id) self.assertEqual(order.sys_tx_id, order3.sys_tx_id) order3 = order_cls.query_by_uid_someday( merchant=merchant, uid=uid, someday=order.create_time).all()[0] self.assertIsNotNone(order3) self.assertEqual(params['uid'], order3.uid) self.assertEqual(params['merchant'], order3.merchant) self.assertEqual(params['channel_id'], order3.channel_id) self.assertEqual(params['source'], order3.source) self.assertEqual(Decimal("500.32"), order3.tx_amount) self.assertEqual(order.order_id, order3.order_id) self.assertEqual(order.mch_tx_id, order3.mch_tx_id) self.assertEqual(order.sys_tx_id, order3.sys_tx_id) begin_time, end_time = DateTimeKit.get_day_begin_end(order.create_time) orders = order_cls.query_by_create_time(begin_time, end_time, merchant=merchant, uid=uid).all() order3 = orders[-1] self.assertIsNotNone(order3) self.assertFalse(order3.is_cold_table()) self.assertEqual(params['uid'], order3.uid) self.assertEqual(params['merchant'], order3.merchant) self.assertEqual(params['channel_id'], order3.channel_id) self.assertEqual(params['source'], order3.source) self.assertEqual(Decimal("500.32"), order3.tx_amount) self.assertEqual(order.order_id, order3.order_id) self.assertEqual(order.mch_tx_id, order3.mch_tx_id) self.assertEqual(order.sys_tx_id, order3.sys_tx_id) # 冷表查询测试 begin_time, end_time = DateTimeKit.get_day_begin_end(order.create_time) clean_date = order_cls.get_clean_date() if clean_date.month == begin_time.month: begin_time = clean_date orders = order_cls.query_by_create_time(begin_time, end_time, merchant=merchant, uid=uid).all() order3 = orders[-1] self.assertIsNotNone(order3) self.assertTrue(order3.is_cold_table()) self.assertEqual(params['uid'], order3.uid) self.assertEqual(params['merchant'], order3.merchant) self.assertEqual(params['channel_id'], order3.channel_id) self.assertEqual(params['source'], order3.source) self.assertEqual(Decimal("500.32"), order3.tx_amount) self.assertEqual(order.order_id, order3.order_id) self.assertEqual(order.mch_tx_id, order3.mch_tx_id) self.assertEqual(order.sys_tx_id, order3.sys_tx_id)