Beispiel #1
0
class ProxyChannelConfig(ABSChannelConfig):
    """
    代付通道配置
    """
    _banks = db.Column('banks',
                       db.String(length=128),
                       comment="支持的银行",
                       nullable=False,
                       default="")

    __table_args__ = (
        # 联合唯一索引
        db.UniqueConstraint('channel',
                            'version',
                            name='uix_proxy_channel_config_channel_version'), )

    @property
    def banks(self):
        bank_list = list()
        for value in StringParser(to_type=int).split(self._banks):
            bank_list.append(PaymentBankEnum(value))
        return bank_list

    @banks.setter
    def banks(self, bank_list: List[PaymentBankEnum]):
        self._banks = StringParser().join([x.value for x in bank_list])
Beispiel #2
0
class ChannelConfig(ABSChannelConfig):
    """
    通道配置
    """
    _settlement_type = db.Column('st_type',
                                 db.Integer,
                                 comment="结算方式",
                                 nullable=False,
                                 default=0)
    priority = db.Column(db.Integer,
                         comment="优先级(用于路由)",
                         nullable=False,
                         default=0)

    __table_args__ = (
        # 联合唯一索引
        db.UniqueConstraint('channel',
                            'version',
                            name='uix_channel_config_channel_version'), )

    @property
    def settlement_type(self) -> SettleTypeEnum:
        return SettleTypeEnum(self._settlement_type)

    @settlement_type.setter
    def settlement_type(self, value: SettleTypeEnum):
        self._settlement_type = value.value
Beispiel #3
0
    def new_cls(cls, abs_cls, table_name, cls_name, bind_key=None):
        """
        生成模型类
        :param abs_cls:
        :param table_name:
        :param cls_name:
        :param bind_key:
        :return:
        """
        # 使用抽象类来生成子类
        indexes = list()
        for v in abs_cls.UNION_UNIQUE_INDEX:
            indexes.append(
                db.UniqueConstraint(*v,
                                    name='uix_' + table_name + '_' +
                                    '_'.join(v)))
        for v in abs_cls.UNION_INDEX:
            indexes.append(db.Index('ix_' + table_name + '_' + '_'.join(v),
                                    *v))

        return type(
            cls_name, (abs_cls, ),
            dict(
                __module__=abs_cls.__module__,
                __name__=cls_name,
                __tablename__=table_name,
                __bind_key__=bind_key,
                __table_args__=tuple(indexes),
            ))
class Target(db.Model):
    id = db.Column(db.Integer(), primary_key=True)

    target_from = db.Column(db.Float(), nullable=True)
    target_to = db.Column(db.Float(), nullable=True)

    indicator_id = db.Column(db.Integer(),
                             db.ForeignKey('indicator.id'),
                             nullable=False)
    objective_id = db.Column(db.Integer(),
                             db.ForeignKey('objective.id', ondelete='CASCADE'),
                             nullable=False)

    username = db.Column(db.String(120), default='')
    created = db.Column(db.DateTime(), default=datetime.utcnow)
    updated = db.Column(db.DateTime(),
                        onupdate=datetime.utcnow,
                        default=datetime.utcnow)

    __table_args__ = (db.UniqueConstraint(
        'indicator_id',
        'objective_id',
        name='target_indicator_id_objective_id_key'), )

    def get_owner(self):
        return self.objective.product.product_group.name

    def __repr__(self):
        return '<Target {} | {} - {}>'.format(self.objective.product.name,
                                              self.target_from, self.target_to)
Beispiel #5
0
class AliasMailbox(db.Model, ModelMixin):
    __table_args__ = (
        db.UniqueConstraint("alias_id", "mailbox_id", name="uq_alias_mailbox"),
    )

    alias_id = db.Column(db.ForeignKey(Alias.id, ondelete="cascade"), nullable=False)
    mailbox_id = db.Column(
        db.ForeignKey(Mailbox.id, ondelete="cascade"), nullable=False
    )
Beispiel #6
0
class SocialAuth(db.Model, ModelMixin):
    """Store how user authenticates with social login"""

    user_id = db.Column(db.ForeignKey(User.id, ondelete="cascade"), nullable=False)

    # name of the social login used, could be facebook, google or github
    social = db.Column(db.String(128), nullable=False)

    __table_args__ = (db.UniqueConstraint("user_id", "social", name="uq_social_auth"),)
Beispiel #7
0
class AliasUsedOn(db.Model, ModelMixin):
    """Used to know where an alias is created"""

    __table_args__ = (db.UniqueConstraint("gen_email_id",
                                          "hostname",
                                          name="uq_alias_used"), )

    gen_email_id = db.Column(db.ForeignKey(GenEmail.id, ondelete="cascade"),
                             nullable=False)

    hostname = db.Column(db.String(1024), nullable=False)
Beispiel #8
0
class AliasUsedOn(db.Model, ModelMixin):
    """Used to know where an alias is created"""

    __table_args__ = (
        db.UniqueConstraint("alias_id", "hostname", name="uq_alias_used"),
    )

    alias_id = db.Column(db.ForeignKey(Alias.id, ondelete="cascade"), nullable=False)
    user_id = db.Column(db.ForeignKey(User.id, ondelete="cascade"), nullable=False)

    alias = db.relationship(Alias)

    hostname = db.Column(db.String(1024), nullable=False)
Beispiel #9
0
class SetEntry(Base):
    __table__name = 'set_entry'
    __table_args__ = (db.UniqueConstraint('set_num',
                                          'entry_id',
                                          name='_set_num_entry_uc'), )

    entry_id = db.Column(db.Integer,
                         db.ForeignKey('exercise_entry.id'),
                         nullable=False)

    set_num = db.Column(db.Integer, nullable=False)
    reps = db.Column(db.Integer, nullable=False)
    weight = db.Column(db.Integer, nullable=False)
    comment = db.Column(db.Text)
    bodyweight = db.Column(db.Boolean, default=False)
Beispiel #10
0
class ExerciseEntry(Base):
    __table__name = 'exercise_entry'
    __table_args__ = (db.UniqueConstraint('exercise_id', 'workout_id',
                                          name='_exercise_workout_uc'),)

    workout_id = db.Column(db.Integer,
                           db.ForeignKey('workout.id'), nullable=False)

    ex_num = db.Column(db.Integer, nullable=False)
    exercise = db.relationship('Exercise')
    exercise_id = db.Column(db.Integer,
                            db.ForeignKey('exercise.id'), nullable=False)

    sets = db.relationship('SetEntry', backref="exercise_entry",
                           cascade="all, delete-orphan",
                           order_by=('(SetEntry.set_num)'))
Beispiel #11
0
class ForwardEmail(db.Model, ModelMixin):
    """
    Store configuration of sender (website-email) and alias.
    """

    __table_args__ = (
        db.UniqueConstraint("gen_email_id", "website_email", name="uq_forward_email"),
    )

    gen_email_id = db.Column(
        db.ForeignKey(GenEmail.id, ondelete="cascade"), nullable=False
    )

    # used to be envelope header, should be mail header from instead
    website_email = db.Column(db.String(512), nullable=False)

    # the email from header, e.g. AB CD <*****@*****.**>
    # nullable as this field is added after website_email
    website_from = db.Column(db.String(1024), nullable=True)

    # when user clicks on "reply", they will reply to this address.
    # This address allows to hide user personal email
    # this reply email is created every time a website sends an email to user
    # it has the prefix "reply+" to distinguish with other email
    reply_email = db.Column(db.String(512), nullable=False)

    gen_email = db.relationship(GenEmail, backref="forward_emails")

    def website_send_to(self):
        """return the email address with name.
        to use when user wants to send an email from the alias"""
        from app.email_utils import get_email_name

        if self.website_from:
            name = get_email_name(self.website_from)
            if name:
                return name + " " + self.website_email + f" <{self.reply_email}>"

        return self.website_email.replace("@", " at ") + f" <{self.reply_email}>"

    def last_reply(self) -> "ForwardEmailLog":
        """return the most recent reply"""
        return (
            ForwardEmailLog.query.filter_by(forward_id=self.id, is_reply=True)
            .order_by(desc(ForwardEmailLog.created_at))
            .first()
        )
Beispiel #12
0
class Message(db.Model):
    __tablename__ = "messages"
    __table_args__ = (db.UniqueConstraint("message_id", "chat_id"),)

    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.String(), nullable=False)
    chat_id = db.Column(db.String(), nullable=False)
    message_id = db.Column(db.String(), nullable=False)
    created_date = db.Column(db.DateTime, default=datetime.now)

    def __init__(self, user_id, chat_id, message_id):
        self.user_id = user_id
        self.chat_id = chat_id
        self.message_id = message_id

    def __repr__(self):
        return "<Message {}>".format(self.id)
class Indicator(db.Model):
    id = db.Column(db.Integer(), primary_key=True)

    name = db.Column(db.String(120), nullable=False, index=True)
    source = db.Column(db.JSON(), nullable=False)
    unit = db.Column(db.String(20), nullable=False, default='')
    aggregation = db.Column(db.String(80), default='average')
    is_deleted = db.Column(db.Boolean(),
                           default=False,
                           index=True,
                           server_default=false())

    product_id = db.Column(db.Integer(),
                           db.ForeignKey('product.id'),
                           nullable=False,
                           index=True)

    slug = db.Column(db.String(120), nullable=False, index=True)

    targets = db.relationship('Target',
                              backref=db.backref('indicator', lazy='joined'),
                              lazy='dynamic')
    values = db.relationship('IndicatorValue',
                             backref='indicator',
                             lazy='dynamic',
                             passive_deletes=True)

    username = db.Column(db.String(120), default='')
    created = db.Column(db.DateTime(), default=datetime.utcnow)
    updated = db.Column(db.DateTime(),
                        onupdate=datetime.utcnow,
                        default=datetime.utcnow)

    __table_args__ = (db.UniqueConstraint(
        'name',
        'product_id',
        'is_deleted',
        name='indicator_name_product_id_key'), )

    def get_owner(self):
        return self.product.product_group.name

    def __repr__(self):
        return '<SLI {} | {} | {}>'.format(self.product.name, self.name,
                                           self.source)
Beispiel #14
0
class Mailbox(db.Model, ModelMixin):
    user_id = db.Column(db.ForeignKey(User.id, ondelete="cascade"), nullable=False)
    email = db.Column(db.String(256), nullable=False)
    verified = db.Column(db.Boolean, default=False, nullable=False)
    force_spf = db.Column(db.Boolean, default=True, server_default="1", nullable=False)

    # used when user wants to update mailbox email
    new_email = db.Column(db.String(256), unique=True)

    pgp_public_key = db.Column(db.Text, nullable=True)
    pgp_finger_print = db.Column(db.String(512), nullable=True)

    __table_args__ = (db.UniqueConstraint("user_id", "email", name="uq_mailbox_user"),)

    def nb_alias(self):
        return Alias.filter_by(mailbox_id=self.id).count()

    @classmethod
    def delete(cls, obj_id):
        # Put all aliases belonging to this mailbox to global trash
        try:
            for alias in Alias.query.filter_by(mailbox_id=obj_id):
                # special handling for alias that has several mailboxes and has mailbox_id=obj_id
                if len(alias.mailboxes) > 1:
                    # use the first mailbox found in alias._mailboxes
                    first_mb = alias._mailboxes[0]
                    alias.mailbox_id = first_mb.id
                    alias._mailboxes.remove(first_mb)
                else:
                    # only put aliases that have mailbox as a single mailbox into trash
                    DeletedAlias.create(email=alias.email)
                db.session.commit()
        # this can happen when a previously deleted alias is re-created via catch-all or directory feature
        except IntegrityError:
            LOG.error("Some aliases have been added before to DeletedAlias")
            db.session.rollback()

        cls.query.filter(cls.id == obj_id).delete()
        db.session.commit()

    def __repr__(self):
        return f"<Mailbox {self.email}>"
Beispiel #15
0
class Membership(db.Model):
    """
    整个表可以认为zone_id 就是主键,因为逻辑是先保存zone表数据,才有zone_id,
    所有数据zone_id绝对不会有重复值,
    所以在删除的时候,需要以zone_id 查询,先删除该表数据,再删除zone表数据。
    """
    game_id = db.Column(db.Integer,
                        db.ForeignKey('t_games.id'),
                        primary_key=True)
    channel_id = db.Column(db.Integer,
                           db.ForeignKey('t_channels.id'),
                           primary_key=True)
    zone_id = db.Column(db.Integer,
                        db.ForeignKey("t_zones.id"),
                        primary_key=True)

    db.UniqueConstraint('game_id', 'channel_id', 'zone_id')
    db.relationship('Games',
                    uselist=False,
                    backref='memberships',
                    lazy='dynamic')
    db.relationship('Channels',
                    uselist=False,
                    backref='memberships',
                    lazy='dynamic')
    db.relationship('Zones',
                    uselist=False,
                    backref='memberships',
                    lazy='dynamic')

    def __init__(self, game, channel, zone):
        self.game_id = game.id
        self.channel_id = channel.id
        self.zone_id = zone.id

    def __repr__(self):
        return "<Membership %s, %s, %s>" % (self.game_id, self.channel_id,
                                            self.zone_id)
Beispiel #16
0
class TeamMember(db.Model):
    """
    Team-member database model.
    """
    # pylint: disable=no-member
    __tablename__ = 'team_member'

    team_id = db.Column(db.Integer, db.ForeignKey('team.id'), primary_key=True)
    team = db.relationship('Team')
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True)
    user = db.relationship('User', backref='teams_membership')

    is_leader = db.Column(db.Boolean, default=False, nullable=False)

    __table_args__ = (
        db.UniqueConstraint('team_id', 'user_id', name='_team_user_uc'),
    )

    def check_owner(self, user):
        return self.user == user

    def check_supervisor(self, user):
        return self.team.check_owner(user)
Beispiel #17
0
class TeamMember(db.Model):
    """
    Team-member database model.
    """
    __tablename__ = 'team_member'

    team_id = db.Column(db.Integer, db.ForeignKey('team.id'), primary_key=True)
    team = db.relationship('Team')
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True)
    user = db.relationship(
        'User',
        backref=db.backref('teams_membership', cascade='delete, delete-orphan')
    )

    is_leader = db.Column(db.Boolean, default=False, nullable=False)

    __table_args__ = (
        db.UniqueConstraint('team_id', 'user_id', name='_team_user_uc'),
    )

    def __repr__(self):
        return (
            "<{class_name}("
            "team_id={self.team_id}, "
            "user_id=\"{self.user_id}\", "
            "is_leader=\"{self.is_leader}\""
            ")>".format(
                class_name=self.__class__.__name__,
                self=self
            )
        )

    def check_owner(self, user):
        return self.user == user

    def check_supervisor(self, user):
        return self.team.check_owner(user)
Beispiel #18
0
class ClientUser(db.Model, ModelMixin):
    __table_args__ = (
        db.UniqueConstraint("user_id", "client_id", name="uq_client_user"),
    )

    user_id = db.Column(db.ForeignKey(User.id, ondelete="cascade"), nullable=False)
    client_id = db.Column(db.ForeignKey(Client.id, ondelete="cascade"), nullable=False)

    # Null means client has access to user original email
    gen_email_id = db.Column(
        db.ForeignKey(GenEmail.id, ondelete="cascade"), nullable=True
    )

    # user can decide to send to client another name
    name = db.Column(
        db.String(128), nullable=True, default=None, server_default=text("NULL")
    )

    # user can decide to send to client a default avatar
    default_avatar = db.Column(
        db.Boolean, nullable=False, default=False, server_default="0"
    )

    gen_email = db.relationship(GenEmail, backref="client_users")

    user = db.relationship(User)
    client = db.relationship(Client)

    def get_email(self):
        return self.gen_email.email if self.gen_email_id else self.user.email

    def get_user_name(self):
        if self.name:
            return self.name
        else:
            return self.user.name

    def get_user_info(self) -> dict:
        """return user info according to client scope
        Return dict with key being scope name. For now all the fields are the same for all clients:

        {
          "client": "Demo",
          "email": "*****@*****.**",
          "email_verified": true,
          "id": 1,
          "name": "Son GM",
          "avatar_url": "http://s3..."
        }

        """
        res = {
            "id": self.id,
            "client": self.client.name,
            "email_verified": True,
            "sub": str(self.id),
        }

        for scope in self.client.get_scopes():
            if scope == Scope.NAME:
                if self.name:
                    res[Scope.NAME.value] = self.name
                else:
                    res[Scope.NAME.value] = self.user.name
            elif scope == Scope.AVATAR_URL:
                if self.user.profile_picture_id:
                    if self.default_avatar:
                        res[Scope.AVATAR_URL.value] = URL + "/static/default-avatar.png"
                    else:
                        res[Scope.AVATAR_URL.value] = self.user.profile_picture.get_url(
                            AVATAR_URL_EXPIRATION
                        )
                else:
                    res[Scope.AVATAR_URL.value] = None
            elif scope == Scope.EMAIL:
                # Use generated email
                if self.gen_email_id:
                    LOG.debug(
                        "Use gen email for user %s, client %s", self.user, self.client
                    )
                    res[Scope.EMAIL.value] = self.gen_email.email
                # Use user original email
                else:
                    res[Scope.EMAIL.value] = self.user.email

        return res
Beispiel #19
0
class BankCard(MerchantBase):
    """
    用户银行卡表,按商户分库
    """
    # __abstract__ = True

    valid = db.Column(db.SmallInteger, comment='是否删除', nullable=False)
    uid = db.Column(db.Integer, comment='用户ID', nullable=False)
    bank_name = db.Column(db.String(128), comment="银行名称例如中国工商银行", nullable=False)
    bank_code = db.Column(db.String(32), comment="银行简称例如ICBC", nullable=False)
    card_no = db.Column(db.String(32), comment="银行卡卡号例如:6212260405014627955", nullable=False)
    account_name = db.Column(db.String(20), comment="开户人姓名例如:张三", nullable=False)
    branch = db.Column(db.String(100), comment="支行名称例如:广东东莞东莞市长安镇支行", nullable=True)
    province = db.Column(db.String(32), comment="省份 例如:湖北省", nullable=False)
    city = db.Column(db.String(32), comment="市 例如:深圳市", nullable=False)

    __table_args__ = (
        # 联合唯一索引
        db.UniqueConstraint('card_no', 'merchant', name='uix_bank_card_no_merchant'),
        # 联合索引
        # db.Index('ix_user_account_mch_name', 'account', 'merchant'),
    )

    _merchant = db.Column('merchant', db.Integer, comment="商户ID", nullable=False)

    @property
    def merchant(self) -> MerchantEnum:
        return MerchantEnum(self._merchant)

    @merchant.setter
    def merchant(self, merchant: MerchantEnum):
        self._merchant = merchant.value

    @property
    def bank_enum(self):
        return PaymentBankEnum.get_bank_by_code(self.bank_code)

    @property
    def card_id(self):
        return self.id

    @property
    def short_description(self):
        return self.bank_name + '(' + self.card_no[-4:] + ')' + ' *' + self.account_name[1:]

    @property
    def card_no_short_description(self):
        card_hide = "**** **** **** {}".format(self.card_no[-4:])
        # card_list = [self.card_no[index:index + 4] for index in range(0, len(self.card_no), 4)]
        # card_hide = ""
        # for index, num in enumerate(card_list):
        #     if index != len(card_list)-1:
        #         card_hide += "**** "
        #     else:
        #         card_hide += "{}".format(num)
        return card_hide

    @property
    def bank_address(self):
        return (self.province or '') + (self.city or '') + (self.branch or '')

    @classmethod
    def generate_model(cls, **kwargs):
        bank_card = cls.get_model_obj()
        bank_card.set_attr(kwargs)
        return bank_card

    @property
    def bank_info_dict(self):
        return dict(
            bank_name=self.bank_name,
            bank_code=self.bank_code,
            card_no=self.card_no,
            account_name=self.account_name,
            branch=self.branch,
            province=self.province,
            city=self.city,
        )

    @classmethod
    def add_bank_card(cls, merchant: MerchantEnum, uid: int, bank_name: str, bank_code: str, card_no: str,
                      account_name: str, branch: str, province: str, city: str):
        """
        添加用户银行卡
        :param merchant:
        :param uid:
        :param bank_name:
        :param bank_code:
        :param card_no:
        :param account_name:
        :param branch:
        :param province:
        :param city:
        :return:
        """
        with db.auto_commit():
            bank_card = cls.query_one(query_fields=dict(card_no=card_no, valid=cls.INVALID, _merchant=merchant.value))
            if not bank_card:
                bank_card = cls.get_model_obj(merchant=merchant)
            bank_card.valid = cls.VALID
            bank_card.uid = uid
            bank_card.bank_name = bank_name
            bank_card.bank_code = bank_code
            bank_card.card_no = card_no
            bank_card.account_name = account_name
            bank_card.branch = branch
            bank_card.province = province
            bank_card.city = city
            bank_card.merchant = merchant
            db.session.add(bank_card)

        return bank_card

    @classmethod
    def delete_bankcard_by_card_no(cls, merchant, card_no):
        """
        根据卡号删除用户银行卡
        :param merchant:
        :param card_no:
        :return:
        """
        with db.auto_commit():
            bank_card = cls.query_bankcard_by_card_no(merchant, card_no=card_no)
            bank_card.valid = cls.INVALID
            db.session.add(bank_card)

    @classmethod
    def delete_bankcard_by_card_id(cls, card_id):
        """
        根据卡号删除用户银行卡
        :param card_id:
        :return:
        """
        with db.auto_commit():
            bank_card = cls.query_bankcard_by_id(card_id=card_id)
            bank_card.valid = cls.INVALID
            db.session.add(bank_card)

    @classmethod
    def query_bankcard_by_card_no(cls, merchant, card_no):
        """
        根据卡号查询用户银行卡记录
        :param merchant:
        :param card_no:
        :return:
        """
        kwargs = dict(card_no=str(card_no), valid=cls.VALID, _merchant=merchant.value)
        return cls.query_one(query_fields=kwargs)

    @classmethod
    def query_bankcard_by_id(cls, card_id, valid_check=True):
        """
        根据卡id查询用户银行卡记录
        :param card_id:
        :param valid_check:
        :return:
        """
        if valid_check:
            kwargs = dict(id=str(card_id), valid=cls.VALID)
        else:
            kwargs = dict(id=str(card_id))
        return cls.query_one(query_fields=kwargs)

    @classmethod
    def query_bankcards_by_uid(cls, merchant, uid):
        """
        根据卡号查询用户银行卡记录
        :param merchant:
        :param uid:
        :return:
        """
        kwargs = dict(uid=int(uid), valid=cls.VALID)
        return cls.query_model(query_fields=kwargs, merchant=merchant).order_by(cls._create_time.desc()).all()

    @classmethod
    def query_bankcards_by_bank_ids(cls, merchant, bank_ids):
        """
        查询一批银行卡的信息
        :param merchant:
        :param bank_ids:
        :return:
        """
        model_cls = cls.get_model_cls(merchant=merchant)
        return model_cls.query.filter(model_cls.id.in_(bank_ids))
Beispiel #20
0
class MerchantFeeConfig(AdminLogMix, ModelBase):
    """
    商户费率控制
    """
    admin_log_model = AdminLog

    _merchant = db.Column('merchant', db.Integer, comment="商户", nullable=False)
    version = db.Column(db.Integer, comment="版本", nullable=False, default=1)
    valid = db.Column(db.SmallInteger, comment="是否被删除的标记", nullable=False)

    _p_way = db.Column('p_way',
                       db.SmallInteger,
                       comment="支付形式",
                       nullable=False)
    _p_method = db.Column('p_method',
                          db.SmallInteger,
                          comment="支付方法",
                          nullable=False,
                          default=0)
    _fee_type = db.Column('fee_type',
                          db.SmallInteger,
                          comment="费用类型",
                          nullable=False,
                          default=PaymentFeeTypeEnum.PERCENT_PER_ORDER.value)
    _value = db.Column('value',
                       db.Integer,
                       comment="值",
                       nullable=False,
                       default=0)
    _cost_type = db.Column('cost_type',
                           db.Integer,
                           comment="扣费类型",
                           nullable=True)

    __table_args__ = (
        # 联合唯一索引
        db.UniqueConstraint(
            'merchant',
            'version',
            'valid',
            'p_way',
            'p_method',
            name='uix_merchant_fee_version_valid_way_method'), )

    @property
    def short_description(self):
        s = 'version: ' + str(self.version) + ', ' + (
            self.payment_method.desc if self.payment_method else '') + \
            self.value_description + "(" + self.cost_type.desc + ")"
        return s

    @property
    def config_id(self):
        return self.id

    @property
    def merchant(self) -> MerchantEnum:
        return MerchantEnum(self._merchant)

    @merchant.setter
    def merchant(self, value: MerchantEnum):
        self._merchant = value.value

    @property
    def cost_type(self) -> CostTypeEnum:
        if not self._cost_type:
            return CostTypeEnum.MERCHANT
        return CostTypeEnum(self._cost_type)

    @cost_type.setter
    def cost_type(self, value: CostTypeEnum):
        self._cost_type = value.value

    @property
    def payment_way(self) -> PayTypeEnum:
        return PayTypeEnum(self._p_way)

    @payment_way.setter
    def payment_way(self, value: PayTypeEnum):
        self._p_way = value.value

    @property
    def payment_method(self) -> PayMethodEnum:
        if self._p_method:
            return PayMethodEnum(self._p_method)

    @payment_method.setter
    def payment_method(self, value: PayMethodEnum):
        if value:
            self._p_method = value.value

    @property
    def fee_type(self) -> PaymentFeeTypeEnum:
        return PaymentFeeTypeEnum(self._fee_type)

    @fee_type.setter
    def fee_type(self, value: PaymentFeeTypeEnum):
        self._fee_type = value.value

    @property
    def value(self):
        return BalanceKit.divide_hundred(self._value)

    @value.setter
    def value(self, value):
        self._value = BalanceKit.multiple_hundred(value)

    @property
    def value_description(self):
        return "{}{}".format(self.value, self.fee_type.desc)

    @classmethod
    def convert_query_fields(cls, query_fields):
        params = dict(_merchant=query_fields['merchant'].value)
        if query_fields.get('payment_way'):
            params['_p_way'] = query_fields['payment_way'].value
        if query_fields.get('payment_method'):
            params['_p_method'] = query_fields['payment_method'].value
        return params

    @classmethod
    def query_latest_one(cls, query_fields):
        """
        以version倒序,查询最新的一个配置
        :param query_fields:
        :return:
        """
        params = cls.convert_query_fields(query_fields)
        return cls.query_one_order_by(query_fields=params,
                                      order_fields=[cls.version.desc()])

    @classmethod
    def query_by_config_id(cls, config_id):
        """
        根据主键查询配置
        :param config_id:
        :return:
        """
        return cls.query_by_id(config_id)

    @classmethod
    def query_active_configs(cls, query_fields):
        """
        查询有效的配置
        :param query_fields:
        :return:
        """
        params = cls.convert_query_fields(query_fields)
        params['valid'] = cls.VALID
        return cls.query_model(params)

    @classmethod
    def update_fee_config(cls, merchant, params: list, models=None):
        """
        修改费率,批量操作
        :param merchant:
        :param params: [
            dict(merchant, payment_way, fee_type, value, payment_method),
            dict(merchant, payment_way, fee_type, value, payment_method),
        ]
        :param models: 商户和日志模型
        :return:
        """
        models = models or list()

        params_dict = cls.get_update_dict(params)
        all_configs = cls.query_active_configs(dict(merchant=merchant))
        for item in all_configs.all():
            if item.item_key not in params_dict:
                # 不在更新列表里面的,全部标记为删除
                item.valid = cls.INVALID
                models.append(item)

        for fields in params:
            fields = copy.deepcopy(fields)
            fields['valid'] = cls.VALID
            model = cls.query_latest_one(
                dict(
                    merchant=fields.get('merchant'),
                    payment_way=fields.get('payment_way'),
                    payment_method=fields.get('payment_method', None),
                ))
            if model:
                fields['version'] = model.version + 1
            else:
                fields['version'] = 1

            rst = cls.add_model(fields=fields)
            models.append(rst['model'])

            log_model = cls.add_admin_log(rst['model'])
            log_model and models.append(log_model)

        try:
            cls.commit_models(models=models)
        except IntegrityError as e:
            print(e)
            current_app.config['SENTRY_DSN'] and current_app.logger.fatal(
                traceback.format_exc())
            return False, SqlIntegrityError()

        return True, None

    @classmethod
    def filter_latest_items(cls, items):
        """
        过滤出最新的配置,同一个渠道下的旧的配置会被排除掉
        :param items:
        :return:
        """
        # 按version值倒序,version值越大,记录越新
        items = sorted(items, key=attrgetter('version'), reverse=True)

        latest = dict()

        for item in items:
            if not item.valid:
                # 已经删除的过滤掉
                continue
            key = item.item_key
            if key in latest:
                # 已经存在的配置直接跳过
                continue
            latest[key] = item

        return latest.values()

    @property
    def item_key(self):
        """
        唯一键
        :return:
        """
        return self._merchant, self._p_way, self._p_method

    @classmethod
    def get_update_dict(cls, update_list: list):
        tmp = dict()

        for fields in update_list:
            params = cls.convert_query_fields(fields)
            tmp[(params['_merchant'], params['_p_way'],
                 params.get('_p_method'))] = fields

        return tmp

    @classmethod
    def get_latest_active_configs(cls, merchant, payment_way):
        """
        获取商户最新的有效的费率配置
        :param merchant:
        :param payment_way:
        :return:
        """
        merchant_fees = MerchantFeeConfig.query_active_configs(
            query_fields=dict(
                merchant=merchant,
                payment_way=payment_way,
            )).all()
        return MerchantFeeConfig.filter_latest_items(merchant_fees)
Beispiel #21
0
class OrderConstraint(ModelBase):
    """
    订单状态唯一约束
    """
    order_id = db.Column(db.Integer, comment='订单ID', nullable=False)
    _state = db.Column('state',
                       db.SmallInteger,
                       comment="订单状态",
                       nullable=False)

    __table_args__ = (
        # 联合唯一索引
        db.UniqueConstraint('order_id',
                            'state',
                            name='uix_order_constraint_order_id_state'), )

    @property
    def state(self) -> OrderStateEnum:
        return OrderStateEnum(self._state)

    @state.setter
    def state(self, value: OrderStateEnum):
        self._state = value.value

    @classmethod
    def query_by_order_id(cls, order_id):
        return cls.query_one(dict(order_id=order_id))

    @classmethod
    def revoke_order_state(cls, order_id, state: OrderStateEnum):
        """
        回滚订单状态
        :param order_id:
        :param state:
        :return:
        """
        if not state:
            return

        cls.get_model_cls().query.filter_by(order_id=order_id).update(
            dict(_state=state.value))

    @classmethod
    def apply_ref_id(cls, order_id, order_type: PayTypeEnum,
                     state: OrderStateEnum):
        """
        申请修改订单的票据ID
        :param order_id:
        :param order_type:
        :param state:
        :return:
        """
        if not state:
            return OrderUtils.gen_unique_ref_id()

        params = copy.deepcopy(locals())
        params.pop('cls')

        if state == OrderStateEnum.INIT:
            # 初始化状态无需前置状态约束
            model = cls.get_model_obj()
            model.order_id = order_id
            model.state = state
            model.commit_models(model)
            return OrderUtils.gen_unique_ref_id()

        query_params = dict(order_id=order_id, )

        if order_type == PayTypeEnum.WITHDRAW:
            if state == OrderStateEnum.ALLOC:
                query_params.update(_state=OrderStateEnum.INIT.value)
            elif state == OrderStateEnum.DEALING:
                query_params.update(_state=OrderStateEnum.ALLOC.value)
            elif state == OrderStateEnum.SUCCESS:
                query_params.update(_state=OrderStateEnum.DEALING.value)
            elif state == OrderStateEnum.FAIL:
                # 状态变更为失败,无需前置状态约束
                pass
            else:
                raise ValueError('invalid state, params: %s' % params)

        elif order_type == PayTypeEnum.DEPOSIT:
            if state == OrderStateEnum.SUCCESS:
                query_params.update(_state=OrderStateEnum.INIT.value)
            elif state == OrderStateEnum.FAIL:
                # 状态变更为失败,无需前置状态约束
                pass
            else:
                raise ValueError('invalid state, params: %s' % params)

        elif order_type == PayTypeEnum.DEPOSIT:
            if state == OrderStateEnum.FAIL:
                # 只有状态成功后才有退款
                query_params.update(_state=OrderStateEnum.SUCCESS.value)
            else:
                raise ValueError('invalid state, params: %s' % params)

        # 让数据库来确保事务
        with db.auto_commit():
            effect = cls.get_model_cls().query.filter_by(
                **query_params).update(dict(_state=state.value))
            if effect != 1:
                msg = '修改订单状态的票据ID申请失败,query_params: %s, params: %s' % (
                    query_params, params)
                current_app.config['SENTRY_DSN'] and current_app.logger.error(
                    msg)
                return None

        return OrderUtils.gen_unique_ref_id()
Beispiel #22
0
                                                db.Integer,
                                                db.ForeignKey('document.id'),
                                                nullable=False),
                                      db.PrimaryKeyConstraint('rule_id', 'document_id'))

term_to_term_relationship = db.Table('term_to_term_relationship',
                                     db.Column('term_id',
                                               db.Integer,
                                               db.ForeignKey('term.id'),
                                               primary_key=True),
                                     db.Column('related_term_id',
                                               db.Integer,
                                               db.ForeignKey('term.id'),
                                               primary_key=True),
                                     db.UniqueConstraint('term_id',
                                                         'related_term_id',
                                                         name='unique_related_terms'))


class Term(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100))
    short_description = db.Column(db.String(200))
    long_description = db.Column(db.Text)
    abbreviation = db.Column(db.String(10))

    categories = db.relationship('Category', secondary=term_category_relationship, backref='terms')

    columns = db.relationship('Column', secondary=term_column_relationship, backref='terms')

    documents = db.relationship('Document', secondary=term_document_relationship, backref='terms')
Beispiel #23
0
class Contact(db.Model, ModelMixin):
    """
    Store configuration of sender (website-email) and alias.
    """

    __table_args__ = (
        db.UniqueConstraint("alias_id", "website_email", name="uq_contact"),
    )

    user_id = db.Column(db.ForeignKey(User.id, ondelete="cascade"), nullable=False)
    alias_id = db.Column(db.ForeignKey(Alias.id, ondelete="cascade"), nullable=False)

    name = db.Column(
        db.String(512), nullable=True, default=None, server_default=text("NULL")
    )

    website_email = db.Column(db.String(512), nullable=False)

    # the email from header, e.g. AB CD <*****@*****.**>
    # nullable as this field is added after website_email
    website_from = db.Column(db.String(1024), nullable=True)

    # when user clicks on "reply", they will reply to this address.
    # This address allows to hide user personal email
    # this reply email is created every time a website sends an email to user
    # it has the prefix "reply+" to distinguish with other email
    reply_email = db.Column(db.String(512), nullable=False)

    # whether a contact is created via CC
    is_cc = db.Column(db.Boolean, nullable=False, default=False, server_default="0")

    alias = db.relationship(Alias, backref="contacts")
    user = db.relationship(User)

    def website_send_to(self):
        """return the email address with name.
        to use when user wants to send an email from the alias
        Return
        "First Last | email at example.com" <ra+random_string@SL>
        """

        # Prefer using contact name if possible
        name = self.name

        # if no name, try to parse it from website_from
        if not name and self.website_from:
            try:
                from app.email_utils import parseaddr_unicode

                name, _ = parseaddr_unicode(self.website_from)
            except Exception:
                # Skip if website_from is wrongly formatted
                LOG.warning(
                    "Cannot parse contact %s website_from %s", self, self.website_from
                )
                name = ""

        # remove all double quote
        if name:
            name = name.replace('"', "")

        if name:
            name = name + " | " + self.website_email.replace("@", " at ")
        else:
            name = self.website_email.replace("@", " at ")

        # cannot use formataddr here as this field is for email client, not for MTA
        return f'"{name}" <{self.reply_email}>'

    def new_addr(self):
        """
        Replace original email by reply_email. Possible formats:
        - [email protected] via SimpleLogin <reply_email> OR
        - First Last - first at example.com <reply_email> OR
        - First Last - first(a)example.com <reply_email> OR
        - First Last - [email protected] <reply_email> OR
        And return new address with RFC 2047 format

        `new_email` is a special reply address
        """
        user = self.user
        if (
            not user
            or not SenderFormatEnum.has_value(user.sender_format)
            or user.sender_format == SenderFormatEnum.VIA.value
        ):
            new_name = f"{self.website_email} via SimpleLogin"
        elif user.sender_format == SenderFormatEnum.AT.value:
            name = self.name or ""
            new_name = (
                name + (" - " if name else "") + self.website_email.replace("@", " at ")
            ).strip()
        elif user.sender_format == SenderFormatEnum.A.value:
            name = self.name or ""
            new_name = (
                name + (" - " if name else "") + self.website_email.replace("@", "(a)")
            ).strip()
        elif user.sender_format == SenderFormatEnum.FULL.value:
            name = self.name or ""
            new_name = (name + (" - " if name else "") + self.website_email).strip()

        new_addr = formataddr((new_name, self.reply_email)).strip()
        return new_addr.strip()

    def last_reply(self) -> "EmailLog":
        """return the most recent reply"""
        return (
            EmailLog.query.filter_by(contact_id=self.id, is_reply=True)
            .order_by(desc(EmailLog.created_at))
            .first()
        )

    def __repr__(self):
        return f"<Contact {self.id} {self.website_email} {self.alias_id}>"
Beispiel #24
0
class User(MerchantBase):
    """
    用户,按商户分库
    """
    id = db.Column(db.Integer,
                   primary_key=True,
                   autoincrement=False,
                   comment='用户ID,主键但不自增,由全局表生成id')
    _create_time = db.Column('create_time',
                             db.Integer,
                             nullable=False,
                             comment="创建时间",
                             index=True)

    account = db.Column(db.String(32), comment="账号,手机号码/邮箱", nullable=False)
    _ac_type = db.Column('ac_type',
                         db.SmallInteger,
                         default=AccountTypeEnum.NONE.value,
                         comment="账号类型,手机号码/邮箱")
    _state = db.Column('state',
                       db.SmallInteger,
                       default=AccountStateEnum.ACTIVE.value,
                       comment="账号状态")
    _login_pwd = db.Column('login_pwd', db.String(100), comment="登录密码,已加密存储")
    _trade_pwd = db.Column('trade_pwd', db.String(100), comment="支付密码,已加密存储")
    _flag = db.Column('flag', db.SmallInteger, comment="账号状态", nullable=True)
    _permissions = db.Column('permissions',
                             db.SmallInteger,
                             comment="账号权限",
                             nullable=True)

    __table_args__ = (
        # 联合唯一索引
        db.UniqueConstraint('account',
                            'merchant',
                            name='uix_user_account_mch_name'),
        # 联合索引
        # db.Index('ix_user_account_mch_name', 'account', 'merchant'),
    )

    _merchant = db.Column('merchant',
                          db.Integer,
                          comment="商户ID",
                          nullable=False)

    @property
    def merchant(self) -> MerchantEnum:
        return MerchantEnum(self._merchant)

    @merchant.setter
    def merchant(self, merchant: MerchantEnum):
        self._merchant = merchant.value

    @property
    def permissions(self) -> List[UserPermissionEnum]:
        if not self._permissions:
            return UserPermissionEnum.get_all_enums()
        return UserPermissionEnum.parse_permissions(self._permissions)

    @permissions.setter
    def permissions(self, values: List[UserPermissionEnum]):
        self._permissions = UserPermissionEnum.join_permissions(values)

    @property
    def permission_names(self):
        return [x.name for x in self.permissions]

    def has_permission(self, perm: UserPermissionEnum):
        """
        判断用户是否有权限
        :param perm:
        :return:
        """
        if not self._permissions:
            # 未设置权限,拥有所有权限
            return True
        return perm.has_permission(self._permissions)

    @property
    def flag(self) -> AccountFlagEnum:
        if not self._flag:
            return AccountFlagEnum.NORMAL
        return AccountFlagEnum(self._flag)

    @flag.setter
    def flag(self, value: AccountFlagEnum):
        if value:
            self._flag = value.value

    @property
    def is_official_auth(self):
        return self.flag == AccountFlagEnum.VIP

    @property
    def is_test_user(self):
        """
        是否是测试用户
        :return:
        """
        return self.merchant.is_test

    @property
    def uid(self):
        return self.id

    @property
    def is_active(self):
        return self._state == AccountStateEnum.ACTIVE.value

    @property
    def state(self) -> AccountStateEnum:
        return AccountStateEnum(self._state)

    @state.setter
    def state(self, value: AccountStateEnum):
        self._state = value.value

    @property
    def ac_type(self):
        """
        返回账户枚举类型
        :return:
        """
        return AccountTypeEnum(self._ac_type)

    @ac_type.setter
    def ac_type(self, e_value):
        """
        传入账户的类型
        :param e_value:
        :return:
        """
        if e_value:
            self._ac_type = e_value.value

    @property
    def login_pwd(self):
        """
        登录密码
        :return:
        """
        return self._login_pwd

    @login_pwd.setter
    def login_pwd(self, raw_pwd):
        """
        设置密码时要进行加密
        :param raw_pwd:
        :return:
        """
        if raw_pwd:
            self._login_pwd = generate_password_hash(raw_pwd)

    @property
    def trade_pwd(self):
        """
        交易密码
        :return:
        """
        return self._trade_pwd

    @trade_pwd.setter
    def trade_pwd(self, raw_pwd):
        """
        设置交易密码
        :param raw_pwd:
        :return:
        """
        if raw_pwd:
            self._trade_pwd = generate_password_hash(raw_pwd)

    def has_trade_pwd(self):
        return bool(self.trade_pwd)

    @classmethod
    def generate_model(cls,
                       merchant,
                       account,
                       ac_type: AccountTypeEnum = None,
                       login_pwd=None):
        """
        生成用户模型
        :param merchant:
        :param account:
        :param ac_type:
        :param login_pwd:
        :return:
        """
        user = cls.get_model_obj(merchant=merchant)
        user.account = account
        user.ac_type = ac_type
        user.login_pwd = login_pwd
        user.merchant = merchant
        user.id = 0
        return user

    @classmethod
    def register_account(cls,
                         merchant,
                         account,
                         ac_type: AccountTypeEnum = None,
                         login_pwd=None):
        """
        注册账号
        :param merchant:
        :param account:
        :param ac_type:
        :param login_pwd:
        :return:
        """
        uid = GlobalUid.make_uid(merchant)

        with db.auto_commit():
            user = cls.generate_model(merchant, account, ac_type, login_pwd)
            user.id = uid
            db.session.add(user)

            balance = UserBalance.generate_model(user.uid, merchant)
            db.session.add(balance)

        return user

    @classmethod
    def delete_account(cls, merchant, uid=None, account=None):
        """
        删除账号
        :param uid:
        :param account:
        :param merchant:
        :return:
        """
        with db.auto_commit():
            user = cls.query_user(merchant, uid=uid, account=account)
            db.session.delete(user)

    @classmethod
    def update_user_state(cls, merchant, account=None, uid=None, state=None):
        """
        修改用户状态
        :param merchant:
        :param account:
        :param uid:
        :param state:
        :return:
        """
        with db.auto_commit():
            if not account:
                user = cls.query_user(merchant=merchant, uid=uid)
            else:
                user = cls.query_user(merchant=merchant, account=account)
            if user is not None:
                user.state = state
                db.session.add(user)
                return True
            else:
                return False

    @classmethod
    def update_user_flag(cls,
                         merchant,
                         flag: AccountFlagEnum,
                         account=None,
                         uid=None):
        """
        修改用户标签
        """
        if account:
            user = cls.query_user(merchant=merchant, account=account)
        else:
            user = cls.query_user(merchant=merchant, uid=uid)

        if user is not None:
            user.flag = flag
            cls.commit_models(user)
            UserFlagCache(user.uid).set_flag(flag)
            return True

        return False

    @classmethod
    def update_user_permission(cls,
                               merchant,
                               permissions: List[UserPermissionEnum],
                               account=None,
                               uid=None):
        """
        修改用户权限
        """
        if account:
            user = cls.query_user(merchant=merchant, account=account)
        else:
            user = cls.query_user(merchant=merchant, uid=uid)

        if user is not None:
            user.permissions = permissions
            cls.commit_models(user)
            return True

        return False

    @classmethod
    def reset_password(cls, merchant, account=None, uid=None, login_pwd=None):
        """
        修改密码
        :param merchant:
        :param account:
        :param uid:
        :param login_pwd:
        :return:
        """
        with db.auto_commit():
            if not account:
                user = cls.query_user(merchant=merchant, uid=uid)
            else:
                user = cls.query_user(merchant=merchant, account=account)
            if user is not None:
                user.login_pwd = login_pwd
                db.session.add(user)
                return True
            else:
                return False

    @classmethod
    def verify_login(cls, merchant, account, password):
        """
        账号密码鉴权
        :param merchant:
        :param account:
        :param password:
        :return:
        """
        user = cls.query_user(merchant, account=account)
        if not user:
            return False

        return user.check_login_pwd(password)

    @classmethod
    def verify_password(cls, merchant, uid, password):
        """
        账号密码鉴权
        :param merchant:
        :param uid:
        :param password:
        :return:
        """
        user = cls.query_user(merchant, uid=uid)
        if not user:
            return False

        return user.check_login_pwd(password)

    def check_login_pwd(self, raw_pwd):
        return check_password_hash(self._login_pwd, raw_pwd)

    @classmethod
    def query_user(cls, merchant, uid=None, account=None):
        """
        查询用户
        :param merchant:
        :param uid:
        :param account:
        :return:
        """
        if uid:
            kwargs = dict(id=int(uid))
        elif account:
            kwargs = dict(account=account, )
        else:
            raise ValueError('parameter error')

        kwargs['_merchant'] = merchant.value

        return cls.query_one(query_fields=kwargs)

    @classmethod
    def set_payment_password(cls,
                             merchant,
                             account=None,
                             uid=None,
                             trade_pwd=None):
        """
        修改密码
        :param merchant:
        :param account:
        :param uid:
        :param trade_pwd:
        :return:
        """
        with db.auto_commit():
            if not account:
                user = cls.query_user(merchant=merchant, uid=uid)
            else:
                user = cls.query_user(merchant=merchant, account=account)
            if user is not None:
                user.trade_pwd = trade_pwd
                db.session.add(user)
                return True
            else:
                return False

    @classmethod
    def verify_payment_password(cls, merchant, uid, password):
        """
        支付密码鉴权
        :param merchant:
        :param uid:
        :param password:
        :return:
        """
        user = cls.query_user(merchant, uid=uid)
        if not user:
            return False

        return user.check_trade_pwd(password)

    def check_trade_pwd(self, raw_pwd):
        """
        验证交易密码
        :param raw_pwd:
        :return:
        """
        if not self._trade_pwd:
            return False

        return check_password_hash(self._trade_pwd, raw_pwd)
Beispiel #25
0
class UserBindInfo(ModelBase):
    id = db.Column(db.Integer,
                   primary_key=True,
                   autoincrement=False,
                   comment='用户ID',
                   nullable=False)
    name = db.Column(db.String(64), comment="名称", nullable=False)
    account = db.Column(db.String(32),
                        comment="账号",
                        nullable=False,
                        index=True)
    _bind_type = db.Column('bind_type',
                           db.SmallInteger,
                           comment="绑定类型",
                           nullable=False)
    _merchant = db.Column('merchant',
                          db.Integer,
                          comment="商户ID",
                          nullable=False)

    __table_args__ = (
        # 联合唯一索引
        db.UniqueConstraint('name',
                            'bind_type',
                            'merchant',
                            name='uix_user_bind_name_bind_type_merchant'), )

    @property
    def uid(self):
        return self.id

    @uid.setter
    def uid(self, value):
        self.id = value

    @property
    def merchant(self) -> MerchantEnum:
        return MerchantEnum(self._merchant)

    @merchant.setter
    def merchant(self, merchant: MerchantEnum):
        self._merchant = merchant.value

    @property
    def bind_type(self):
        """
        返回账户枚举类型
        :return:
        """
        return AccountTypeEnum(self._bind_type)

    @bind_type.setter
    def bind_type(self, e_value):
        """
        传入账户的类型
        :param e_value:
        :return:
        """
        self._bind_type = e_value.value

    @classmethod
    def query_bind_by_uid(cls, uid):
        """
        根据用户ID查询绑定信息
        :param uid:
        :return:
        """
        return cls.query_one(query_fields=dict(id=uid))

    @classmethod
    def query_bind(cls,
                   merchant: MerchantEnum,
                   name,
                   bind_type: AccountTypeEnum = AccountTypeEnum.ACCOUNT):
        return cls.query_one(query_fields=dict(
            _merchant=merchant.value,
            name=name,
            _bind_type=bind_type.value,
        ))

    @classmethod
    def bind_account(cls,
                     uid,
                     merchant: MerchantEnum,
                     account,
                     name,
                     bind_type: AccountTypeEnum = AccountTypeEnum.ACCOUNT):
        obj = cls()
        obj.uid = uid
        obj.merchant = merchant
        obj.name = name
        obj.account = account
        obj.bind_type = bind_type
        cls.commit_models(obj)
        return obj

    @classmethod
    def unbind_account(cls, uid):
        obj = cls.query_bind_by_uid(uid)
        if not obj:
            return False
        cls.commit_models(obj, delete=True)
        return True