def test_channel_config_model(self): data = dict( channel_enum=ChannelConfigEnum.CHANNEL_1001, fee=Decimal("2.1"), fee_type=PaymentFeeTypeEnum.PERCENT_PER_ORDER, limit_per_min=Decimal("100.0"), limit_per_max=Decimal("1000.0"), limit_day_max=Decimal("500000.0"), trade_begin_hour=9, trade_begin_minute=0, trade_end_hour=20, trade_end_minute=30, settlement_type=SettleTypeEnum.D0, maintain_begin=DateTimeKit.str_to_datetime("2016-10-01 09:30", DateTimeFormatEnum.MINUTES_FORMAT), maintain_end=DateTimeKit.str_to_datetime("2016-10-02 21:30", DateTimeFormatEnum.MINUTES_FORMAT), priority=1 ) self.__update_channel(data, 1, 1) data['fee'] = Decimal("3.55") data['priority'] = 10 data['maintain_begin'] = DateTimeKit.str_to_datetime("2016-10-01 12:30", DateTimeFormatEnum.MINUTES_FORMAT) self.__update_channel(data, 1, 2) data['priority'] = -100 data['channel_enum'] = ChannelConfigEnum.CHANNEL_1002 self.__update_channel(data, 2, 3)
def get_clean_date(cls): """ 获取清理日期 :return: """ return DateTimeKit.get_cur_date() - DateTimeKit.time_delta( days=cls.HOT_DAYS)
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 get_expiration(self): """ 计算交易密码错误次数过期时间,过期时间为每天的午夜0点 :return: """ return DateTimeKit.gen_midnight_timestamp( ) - DateTimeKit.get_cur_timestamp()
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 covert_data_to_dict(cls, value): data = dict() for k, v in value.items(): if isinstance(v, Enum): v = v.to_dict() elif isinstance(v, Decimal): v = str(v) elif isinstance(v, (datetime.datetime, datetime.date)): v = DateTimeKit.datetime_to_timestamp(v) elif isinstance(v, dict): v = cls.covert_data_to_dict(v) elif isinstance(v, list): v_list = list() for x in v: if isinstance(x, Enum): x = x.to_dict() elif isinstance(x, Decimal): x = str(x) elif isinstance(x, (datetime.datetime, datetime.date)): x = DateTimeKit.datetime_to_timestamp(v) elif isinstance(x, dict): x = cls.covert_data_to_dict(x) v_list.append(x) v = v_list data[k] = v return data
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 query_by_create_time(cls, begin_time, end_time, **kwargs): """ 根据时间查询数据,包含 begin_time/end_time 边界值 :param begin_time: :param end_time: :return: 返回查询对象 """ begin_time = DateTimeKit.datetime_to_timestamp(begin_time) end_time = DateTimeKit.datetime_to_timestamp(end_time) return cls.query.filter(cls._create_time.between(begin_time, end_time))
def validate_end_datetime(self, value): if value.data == '': self.end_datetime.data = DateTimeKit.get_day_begin_end( DateTimeKit.get_cur_date())[1] else: try: self.end_datetime.data = DateTimeKit.str_to_datetime( value.data, DateTimeFormatEnum.SECONDS_FORMAT) except Exception as e: raise StopValidation('查询日期格式有误')
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 post(self): """ 用户列表 :return: """ form, error = UserListSelectForm.request_validate() if error: return error.as_response() kwargs = dict() if form.phone_number.data: phone_number = str(form.phone_number.data) if phone_number.find("+") < 0: phone_number = "{}{}".format("+", phone_number) kwargs['account'] = phone_number user_query = User.query.filter_by(**kwargs) if form.start_datetime.data: user_query = user_query.filter( User._create_time >= DateTimeKit.datetime_to_timestamp( form.start_datetime.data)) if form.end_datetime.data: user_query = user_query.filter( User._create_time <= DateTimeKit.datetime_to_timestamp( form.end_datetime.data)) paginate = user_query.paginate(form.page_index.data, form.page_size.data, False) total = paginate.total user_lst = paginate.items uid_lst = [u.id for u in user_lst] uid_merchant = UserBalance.query.filter( UserBalance.id.in_(uid_lst)).all() mer_bl_uid = { uid.id: dict(balance=uid.real_balance, merchant=uid.merchant) for uid in uid_merchant } data = [ dict(user_id=u.id, phone_number=PhoneNumberParser.hide_number(u.account), type='测试用户' if u.is_test_user else "普通用户", source=mer_bl_uid[u.id]['merchant'].name, available_bl=mer_bl_uid[u.id]['balance'], register_datetime=u.str_create_time, state=u.state.name) for u in user_lst ] return UserListResult( bs_data=dict(entries=data, total=total)).as_response()
def query_by_create_time(cls, begin_time, end_time, *args, **kwargs): """ 根据时间查询数据,包含begin_time, end_time,[begin_time, end_time] :param begin_time: :param end_time: :return: 返回查询对象 """ begin_ts = DateTimeKit.datetime_to_timestamp(begin_time) end_ts = DateTimeKit.datetime_to_timestamp(end_time) model_cls = kwargs.get('model_cls') or cls.get_model_cls( *args, **kwargs) return model_cls.query.filter(model_cls._create_time >= begin_ts, model_cls._create_time <= end_ts)
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 validate_done_end_time(self, value): try: if value.data: self.done_end_time.data = DateTimeKit.str_to_datetime( value.data, DateTimeFormatEnum.SECONDS_FORMAT) except Exception as e: raise StopValidation("时间格式不对 精确到秒")
def validate_maintain_end(self, value): try: if value.data: self.maintain_end.data = DateTimeKit.str_to_datetime( value.data) except Exception as e: raise StopValidation("无效的时间格式")
def post(self): """ 获取用户交易历史记录 """ form, error = UserOrderSelectForm().request_validate() if error: return error.as_response() uid = g.user.uid merchant = g.user.merchant try: begin_time, end_time = DateTimeKit.get_month_begin_end( int(form.year.data), int(form.mouth.data)) except Exception as e: return SelectDepositWithdrawDateError().as_response() if not MerchantMonthMix.is_valid_shard_date(begin_time): # 查询的时间太早,没有数据,直接返回空列表 return ResponseOrderEntryList(bs_data=dict( order_entry_list=[], order_entry_total=0)).as_response() order_entry_list, order_entry_total = TransactionListHelper.get_transaction_list( form.payment_type.data, uid, merchant, begin_time, end_time, TRANSACTION_PAGE_SIZE, form.page_index.data) return ResponseOrderEntryList( bs_data=dict(order_entry_list=order_entry_list, order_entry_total=order_entry_total)).as_response()
def query_one(cls, query_fields: dict, *args, **kwargs): """ 查询模型 :param query_fields: :param args: :param kwargs: :return: """ cls._check_required_params(**kwargs) kwargs['date'] = DateTimeKit.to_date(kwargs['date']) if not kwargs.get('only_hot') and ( kwargs.get('only_cold') or kwargs['date'] <= cls.get_clean_date()): # 只查冷表 model_cls = cls.get_cold_model_cls(*args, **kwargs) rst = super(MonthColdMix, cls).query_one(query_fields, model_cls=model_cls) else: model_cls = cls.get_model_cls(*args, **kwargs) rst = super(MonthColdMix, cls).query_one(query_fields, model_cls=model_cls) return rst
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 get_body(self, params_dict): body = '{"type":"%s","channel":"%s","order":"%s","currency":%s,"amount":"%s","time":"%s","timeout":"%s","product":{"subject":"%s","body":"%s"},"account_type":%s,"no":"%s","name":"%s"}' \ % (str(1), BANK_CODE_DICT[params_dict['bank_code']], params_dict['tx_id'], 1, str(params_dict['amount']), DateTimeKit.get_cur_timestamp(), 3600, "subject", "body", 1, params_dict['bank_number'], params_dict['bank_account'] ) return body
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 generate_token(cls, uid, **kwargs): """ 生成token :param uid: :return: 返回一个已经进行base64编码的token字符串 """ s = JSONWebSignatureSerializer(current_app.config['SECRET_KEY']) login_time = DateTimeKit.get_cur_timestamp(1000) data = dict( uid=uid, time=login_time, ip=IpKit.get_remote_ip(), ) data.update(kwargs) # 缓存登录状态 cache = cls.cache_cls(uid) cache.dumps(data) token = s.dumps(data) b64_token = base64.b64encode(token).decode('utf8') DEBUG_LOG and current_app.logger.debug( 'token generated, key: %s, ttl: %s, data: %s, token: %s, b64_token: %s', cache.get_cache_key(), cache.get_ttl(), data, token, b64_token) return b64_token
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 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 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 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 query_by_date(cls, someday, **kwargs): """ 在某天的范围内查询 :param someday: :param kwargs: :return: 返回查询对象 """ begin_time, end_time = DateTimeKit.get_day_begin_end(someday) return cls.query_by_create_time(begin_time, end_time, **kwargs)
def validate_end_datetime(self, value): if not value.data: self.end_datetime.data = None else: try: self.end_datetime.data = DateTimeKit.str_to_datetime( value.data, DateTimeFormatEnum.SECONDS_FORMAT) except Exception as e: raise StopValidation('查询日期格式有误')
def delete_by_someday(cls, someday, **kwargs): """ 根据创建时间删除记录 :param someday: :param kwargs: :return: """ begin_time, end_time = DateTimeKit.get_day_begin_end(someday) return cls.delete_by_create_time(begin_time, end_time, **kwargs)
def _parse_suffix(cls, *args, **kwargs): """ 表后缀 :param args: :param kwargs: :return: """ return DateTimeKit.datetime_to_str( kwargs['date'], DateTimeFormatEnum.TIGHT_MONTH_FORMAT)
def construct_request(self, order, params: dict): pay_params = { PayMethodEnum.ZHIFUBAO_H5: dict(channel=7, render_type=SdkRenderType.QR_CODE), PayMethodEnum.WEIXIN_H5: dict(channel=1, render_type=SdkRenderType.QR_CODE) } payment_method = self.channel_enum.conf['payment_method'] request_fields = ['pay_type', 'mch_id', 'order_id', 'channel_id', 'pay_amount', 'name', 'explain', 'remark', 'result_url', 'notify_url', 'client_ip', 'bank_cardtype', 'bank_code', 'is_qrimg', 'is_sdk', 'ts', 'sign', 'ext'] sorted_params = sorted(request_fields) request_body = {} for field in request_fields: if field == "pay_type": request_body[field] = 2 elif field == "mch_id": request_body[field] = int(self.third_config['mch_id']) elif field == "order_id": request_body[field] = order.sys_tx_id elif field == "channel_id": request_body[field] = pay_params[payment_method]['channel'] elif field == "pay_amount": request_body[field] = str(BalanceKit.round_4down_5up(order.amount)) elif field == "name": request_body[field] = "mch name" elif field == "explain": request_body[field] = "explain text" elif field == "remark": request_body[field] = "remark message" elif field == "result_url": request_body[field] = self.third_config['return_url'] elif field == "notify_url": request_body[field] = self.third_config['callback_url'] elif field == "client_ip": request_body[field] = params['client_ip'] elif field == 'is_qrimg': request_body[field] = 0 elif field == 'is_sdk': request_body[field] = 1 elif field == 'ts': request_body[field] = DateTimeKit.get_cur_timestamp() elif field == 'ext': request_body[field] = "ext message" sign_str = "&".join(["{}={}".format(k, request_body[k]) for k in sorted_params if request_body.get(k, False) or k in ["is_qrimg", "is_sdk"]]) # sign_str += self.third_config['secret_key'] print("sign string: ", sign_str) print("request body: ", request_body) render_type = pay_params[payment_method]['render_type'] return request_body, sign_str, render_type