class Endorsement(db.Model): __tablename__ = 'endorsement' id = db.Column(db.BigInteger, nullable=False, unique=True, autoincrement=True, primary_key=True) uid = db.Column(db.BigInteger, db.ForeignKey("users.id"), nullable=False, unique=True, index=True) total_contacts = db.Column(db.Integer, nullable=False, default=0) niubility = db.Column(db.Integer, nullable=False, default=0) reliability = db.Column(db.Integer, nullable=False, default=0) @staticmethod def add(uid): # When create a new user, must create a endorsement accordingly endorsement = Endorsement(uid=uid) db.session.add(endorsement) db.session.commit() @staticmethod def find_by_uid(uid): return Endorsement.query.filter(Endorsement.uid == uid).one_or_none() @staticmethod def update_niubility_count(uid, cnt): Endorsement._update_count(uid, "niubility", cnt) @staticmethod def update_reliability_count(uid, cnt): Endorsement._update_count(uid, "reliability", cnt) @staticmethod def update_total_contacts_count(uid, cnt): Endorsement._update_count(uid, "total_contacts", cnt) @staticmethod def _update_count(uid, attr, cnt): if abs(cnt) != 1: # cnt must be 1 or -1 return endorsement = Endorsement.query.filter(Endorsement.uid == uid).one_or_none() if endorsement: count = getattr(endorsement, attr) + cnt if count >= 0: setattr(endorsement, attr, count) db.session.commit() def to_dict(self): return { "total_contacts": self.total_contacts, "niubility_count": self.niubility, "reliability_count": self.reliability }
class Organization(db.Model): __tablename__ = 'organizations' id = db.Column(db.BigInteger, nullable=False, unique=True, autoincrement=True, primary_key=True) name = db.Column(db.String(255), unique=True, index=True) icon = db.Column(db.TEXT) verified = db.Column(db.Boolean) alias = db.Column(db.String(255)) info = db.Column(db.Text) created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) @staticmethod def add(new_one): db.session.add(new_one) db.session.commit() @staticmethod def update(): db.session.commit() @staticmethod def find(id): return Organization.query.filter(Organization.id == id).one_or_none() @staticmethod def find_by_name(name): return Organization.query.filter( Organization.name == name).one_or_none() @staticmethod def like(keyword, count): like_query = "%{}%".format(keyword) return Organization.query \ .filter(Organization.name.like(like_query)) \ .offset(0).limit(count).all() def to_dict(self): return { "id": encode_id(self.id), "name": self.name, "icon": self.icon, "alias": self.alias }
class Feed(db.Model): id = db.Column(db.BigInteger, nullable=False, unique=True, autoincrement=True, primary_key=True) uid = db.Column(db.BigInteger, db.ForeignKey('users.id'), nullable=False) user = db.relationship("colleague.models.user.User", lazy="selectin") type = db.Column(db.SMALLINT, nullable=False, default=0) # images are ids of table 'image.id' images = db.Column(db.JSON, nullable=False, comment=u"图片id") text = db.Column(db.TEXT, nullable=False) like_count = db.Column(db.Integer, nullable=False, default=0) comment_count = db.Column(db.Integer, nullable=False, default=0) create_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) @staticmethod def find_by_cursor(last_id, size): if last_id: feeds = Feed.query \ .filter(Feed.id < last_id) \ .order_by(db.desc(Feed.id)).offset(0).limit(size).all() else: feeds = Feed.query \ .order_by(db.desc(Feed.id)).offset(0).limit(size).all() return feeds @staticmethod def find(id): return Feed.query.filter(Feed.id == id).one_or_none() @staticmethod def add(obj): db.session.add(obj) db.session.commit() def to_dict(self): return { 'id': encode_id(self.id), 'user': self.user.to_dict(), 'type': self.type, 'text': self.text, 'like_count': self.like_count, 'comment_count': self.comment_count, 'create_at': datetime_to_timestamp(self.create_at) }
class FeedLike(db.Model): id = db.Column(db.BigInteger, nullable=False, unique=True, autoincrement=True, primary_key=True) uid = db.Column(db.BigInteger, db.ForeignKey('users.id'), nullable=False) feed_id = db.Column(db.BigInteger, db.ForeignKey('feed.id'), nullable=False) status = db.Column(db.SMALLINT, nullable=False, default=0) created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) updated_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) db.UniqueConstraint(uid, feed_id) def update(self): db.session.commit() @staticmethod def find_by_uid(uid, feed_id): return FeedLike.query \ .filter(FeedLike.uid == uid, FeedLike.feed_id == feed_id) \ .one_or_none() @staticmethod def add(obj): db.session.add(obj) db.session.commit()
class Image(db.Model): id = db.Column(db.BigInteger, nullable=False, unique=True, autoincrement=True, primary_key=True) uid = db.Column(db.BigInteger, db.ForeignKey("users.id"), nullable=False) path = db.Column(db.String(255), unique=True) width = db.Column(db.Integer) height = db.Column(db.Integer) size = db.Column(db.Integer) location = db.Column(db.SMALLINT, default=0, comment="0:local, 1: aliyun oss") info = db.Column(db.String(255)) created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) @staticmethod def add(obj): db.session.add(obj) db.session.commit() @staticmethod def find(id): return Image.query.filter(Image.id == id).one_or_none() @staticmethod def find_by_ids(ids): images = Image.query.filter(Image.id.in_(ids)).all() image_dict = list_to_dict(images, "id") ordered_images = [] for id in ids: image = image_dict.get(id) if image: ordered_images.append(image) return ordered_images @staticmethod def find_by_path(path): return Image.query.filter(Image.path == path).one_or_none() @staticmethod def path_to_url(path, location=MediaLocation.AliyunOSS): if location == MediaLocation.AliyunOSS: return os.path.join(os.getenv("SERVER_NAME"), path) def to_dict(self): return { "id": Image.encode_id(self.id), "url": Image.path_to_url(self.path), "width": self.width, "height": self.height, } @staticmethod def encode_id(id): return encode_id("image" + str(id)) @staticmethod def decode_id(id): return int(decode_id(id)[5:])
class User(db.Model): __tablename__ = "users" id = db.Column(db.BigInteger, nullable=False, unique=True, autoincrement=True, primary_key=True) mobile = db.Column(db.String(50), nullable=False, unique=True, index=True) password_hash = db.Column(db.String(512), nullable=False) user_name = db.Column(db.String(256)) gender = db.Column(db.Integer) avatar = db.Column(db.String(1024)) colleague_id = db.Column(db.String(255), unique=True, nullable=True, comment=u'同事id') status = db.Column(db.Integer) title = db.Column(db.String(1024), nullable=True) company_id = db.Column(db.BigInteger, db.ForeignKey("organizations.id"), nullable=True) company = db.relationship("colleague.models.work.Organization") endorsement = db.relationship("Endorsement", uselist=False) created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) last_login_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) @staticmethod def search_users(search_string): if search_string.isdigit(): mobile = search_string user = User.find_by_mobile(mobile) if user: return [user.to_dict()] else: users = User.query.filter(db.or_(User.user_name == search_string, User.colleague_id == search_string)) return [user.to_dict() for user in users] @staticmethod def find(id): return User.query.filter(User.id == id).one_or_none() @staticmethod def find_by_mobile(mobile): return User.query.filter(User.mobile == mobile).one_or_none() @staticmethod def find_by_ids(ids): return User.query.filter(User.id.in_(ids)).all() @staticmethod def add(mobile, password): user = User(mobile=mobile, status=UserStatus.Confirmed) user.hash_password(password) db.session.add(user) db.session.commit() return user def update_user(self, **kwargs): if not kwargs: return for key, value in kwargs.iteritems(): if key in ["password", "user_name", "gender", "colleague_id", "avatar"] and value: if key == 'password': self.hash_password(value) if key == 'colleague_id': if self.colleague_id: st_raise_error(ErrorCode.COLLEAGUE_ID_ALREADY_SET) exist_colleague_id = User.query.filter(User.colleague_id == value).one_or_none() if exist_colleague_id: st_raise_error(ErrorCode.ALREADY_EXIST_COLLEAGUE_ID) self.colleague_id = value else: setattr(self, key, value) db.session.commit() return self.to_dict() def update_title(self, company_id, title): self.company_id = company_id self.title = title db.session.commit() def hash_password(self, password): self.password_hash = pwd_context.encrypt(password) def verify_password(self, password): return pwd_context.verify(password, self.password_hash) def login_on(self, device_id): self.last_login_at = arrow.utcnow().naive self.status = UserStatus.Confirmed db.session.commit() payload = self._generate_token_metadata(device_id) access_token = create_access_token(identity=payload) refresh_token = create_refresh_token(identity=payload) return { 'access_token': access_token, 'refresh_token': refresh_token } def _generate_token_metadata(self, device_id): return { 'user_id': encode_id(self.id), 'device_id': device_id, 'timestamp': arrow.get(self.last_login_at).timestamp } def verify_token_metadata(self, metadata, device_id): expected = self._generate_token_metadata(device_id) return metadata == expected def is_available(self): return self.status not in [UserStatus.Blocked, UserStatus.Deleted] def is_logged_out(self): return self.status == UserStatus.Logout def logout(self): self.status = UserStatus.Logout db.session.commit() @property def avatar_url(self): return os.path.join(settings["SERVER_NAME"], self.avatar) if self.avatar else "" def to_dict_with_mobile(self): d = self.to_dict() d['mobile'] = self.mobile return d def to_dict(self): return { "id": encode_id(self.id), "user_name": self.user_name, "gender": self.gender, "avatar": self.avatar_url, "colleague_id": self.colleague_id, "title": self.title, "company": self.company.to_dict() if self.company else None, "endorsement": self.endorsement.to_dict() if self.endorsement else None }
class UserEndorse(db.Model): __tablename__ = 'user_endorse' id = db.Column(db.BigInteger, nullable=False, unique=True, autoincrement=True, primary_key=True) uid = db.Column(db.BigInteger, db.ForeignKey('users.id'), nullable=False, comment=u"to uid") from_uid = db.Column(db.BigInteger, db.ForeignKey('users.id'), nullable=False, comment=u"from uid") fromUser = db.relationship('colleague.models.user.User', foreign_keys=from_uid, lazy="selectin") type = db.Column(db.SMALLINT, nullable=False, comment=u"1: 大牛, 2: 靠谱") status = db.Column(db.SMALLINT, nullable=False, comment=u"0: 开启, 1: 取消") create_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) update_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) db.UniqueConstraint(uid, from_uid, type) @staticmethod def find_by_from_uid(uid, from_uid, type): return UserEndorse.query \ .filter(UserEndorse.uid == uid, UserEndorse.from_uid == from_uid, UserEndorse.type == type, UserEndorse.status == EndorseStatus.Supported) \ .one_or_none() @staticmethod def find_by_cursor(uid, type, latest_id, size): if latest_id: return UserEndorse.query \ .filter(UserEndorse.uid == uid, UserEndorse.type == type, UserEndorse.status == EndorseStatus.Supported, UserEndorse.id > latest_id) \ .order_by(db.desc(UserEndorse.id)) \ .offset(0).limit(size).all() else: return UserEndorse.query \ .filter(UserEndorse.uid == uid, UserEndorse.type == type, UserEndorse.status == EndorseStatus.Supported) \ .order_by(db.desc(UserEndorse.id)) \ .offset(0).limit(size).all() def to_dict(self): return { 'id': encode_id(self.id), 'from_user': self.fromUser.to_dict(), 'type': self.type, 'create_at': datetime_to_timestamp(self.create_at) } @staticmethod def update(uid, from_uid, type, status): endorse = UserEndorse.query.filter(UserEndorse.uid == uid, UserEndorse.from_uid == from_uid, UserEndorse.type == type).one_or_none() endorse_status = EndorseStatus.Supported if status else EndorseStatus.Removed if endorse and endorse.status == endorse_status: # duplicate request return if endorse_status == EndorseStatus.Removed and not endorse: # endorse must be existed when u tries to remove return if not endorse: endorse = UserEndorse(uid=uid, from_uid=from_uid, type=type) db.session.add(endorse) endorse.status = endorse_status endorse.update_at = arrow.utcnow().naive cnt = 1 if endorse_status == EndorseStatus.Supported else -1 if type == EndorseType.Niubility: Endorsement.update_niubility_count(uid, cnt) elif type == EndorseType.Reliability: Endorsement.update_reliability_count(uid, cnt) db.session.commit()
class EndorseComment(db.Model): __tablename__ = 'endorse_comment' id = db.Column(db.BigInteger, nullable=False, unique=True, autoincrement=True, primary_key=True) uid = db.Column(db.BigInteger, db.ForeignKey('users.id'), nullable=False, comment=u"to uid") # user = db.relationship('colleague.models.user.User', foreign_keys=[uid]) from_uid = db.Column(db.BigInteger, db.ForeignKey('users.id'), nullable=False, comment=u"from uid") from_user = db.relationship('colleague.models.user.User', foreign_keys=from_uid, lazy="selectin") text = db.Column(db.TEXT, nullable=True) status = db.Column(db.SMALLINT, nullable=False, default=0, comment=u'0: supported, 1: removed') create_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) update_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) db.UniqueConstraint(uid, from_uid) @staticmethod def update(uid, from_uid, text): """ Update the endorse comment, if text is empty, means user removed the endorse comment :param uid: :param from_uid: :param text: comment :return: """ comment = EndorseComment.query \ .filter(EndorseComment.uid == uid, EndorseComment.from_uid == from_uid).one_or_none() if not comment: comment = EndorseComment(uid=uid, from_uid=from_uid) db.session.add(comment) comment.update_at = arrow.utcnow().naive if text is not None: text = text.strip() comment.text = text comment.status = EndorseStatus.Supported if len(text) > 0 else EndorseStatus.Removed db.session.commit() @staticmethod def find_by_from_uid(uid, from_uid): return EndorseComment.query \ .filter(EndorseComment.uid == uid, EndorseComment.from_uid == from_uid) \ .one_or_none() @staticmethod def find_latest_by_uid(uid): return EndorseComment.query \ .filter(EndorseComment.uid == uid, EndorseComment.status == EndorseStatus.Supported) \ .order_by(db.desc(EndorseComment.id)) \ .first() @staticmethod def find_by_cursor(uid, latest_id, size): if latest_id: return EndorseComment.query \ .filter(EndorseComment.uid == uid, EndorseComment.status == EndorseStatus.Supported, EndorseComment.id > latest_id) \ .order_by(db.desc(EndorseComment.id)) \ .offset(0).limit(size).all() else: return EndorseComment.query \ .filter(EndorseComment.uid == uid, EndorseComment.status == EndorseStatus.Supported) \ .order_by(db.desc(EndorseComment.id)) \ .offset(0).limit(size).all() def to_dict(self): return { 'id': encode_id(self.id), 'from_user': self.from_user.to_dict(), 'text': self.text, 'update_at': datetime_to_timestamp(self.update_at) }
class Contact(db.Model): __tablename__ = 'contact' id = db.Column(db.BigInteger, nullable=False, unique=True, autoincrement=True, primary_key=True) uidA = db.Column(db.BigInteger, name="uid_a", nullable=False) uidB = db.Column(db.BigInteger, name="uid_b", nullable=False) status = db.Column(db.Integer, nullable=False, comment=u'0: 已删除, 1: 正常', default=1) type = db.Column(db.Integer, nullable=False, comment=u'1: 自己添加, 2: 熟人推荐') created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow, comment=u'第一次添加时间') updated_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow, comment=u'更新时间') removed_at = db.Column(db.DateTime, nullable=True) @staticmethod def find_by_cursor(from_uid, last_update_date, size): if last_update_date: contacts = Contact.query \ .filter((Contact.uidA == from_uid) | (Contact.uidB == from_uid), Contact.status == ContactStatus.Connected, Contact.updated_at < last_update_date) \ .order_by(db.desc(Contact.updated_at)).offset(0).limit(size).all() else: contacts = Contact.query \ .filter((Contact.uidA == from_uid) | (Contact.uidB == from_uid), Contact.status == ContactStatus.Connected) \ .order_by(db.desc(Contact.updated_at)).offset(0).limit(size).all() return contacts @staticmethod def add(uidA, uidB, type): result = False contact = Contact.find_by_uid(uidA, uidB) if contact is None: uid_a, uid_b = Contact._ordered_uid(uidA, uidB) contact = Contact(uidA=uid_a, uidB=uid_b, type=type) db.session.add(contact) result = True # 由于联系人的请求有两种来源:自己添加和朋友推荐 # 这两种情况可能同时会存在,但是以用户添加为主 if type == ContactRequestType.Added: contact.type = ContactRequestType.Added contact.status = ContactStatus.Connected contact.updated_at = arrow.utcnow().naive db.session.commit() return result @staticmethod def find_by_uid(uidA, uidB): uid_a, uid_b = Contact._ordered_uid(uidA, uidB) return Contact.query.filter(Contact.uidA == uid_a, Contact.uidB == uid_b).one_or_none() @staticmethod def _ordered_uid(a, b): return (a, b) if a < b else (b, a)
class ContactRequest(db.Model): """ 关系请求。 关系请求目前分为两种: 1, 用户主动添加. 2: 用户推荐认识 uid: 关系发起者,如果是主动添加,则为主动添加的用户。如果是推荐,则为推荐人 uidA: 关系的一方,如果是主动添加,则为主动添加人uid, 可以理解为A 把"自己"推荐给 B。如果是推荐,则为被推荐人 uidB: 关系的一方,关系的接受者 """ __tablename__ = 'contact_request' id = db.Column(db.BigInteger, nullable=False, unique=True, autoincrement=True, primary_key=True) # 如果用户主动添加,uidA = 用户id,可以理解为用户把自己推荐给 B # 如果用户推荐 A 给 B, 就按照字面解释 uid = db.Column(db.BigInteger, db.ForeignKey("users.id"), nullable=False, comment=u"请求发起者") uidA = db.Column(db.BigInteger, db.ForeignKey("users.id"), name="uid_a", nullable=False, comment=u"被推荐人") uidB = db.Column(db.BigInteger, db.ForeignKey("users.id"), name="uid_b", nullable=False, comment=u"被添加人") type = db.Column(db.Integer, nullable=False, comment=u"1:添加,2:推荐") comment = db.Column(db.Text, nullable=True, comment=u'如果由第三方推荐,推荐语') status = db.Column(db.Integer, nullable=False, default=0, comment=u"0:pending, 1: 接受, 2:拒绝") created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) end_at = db.Column(db.DateTime, comment=u"接受/拒绝 时间") def to_dict(self): return { "id": encode_id(self.id), "user": self.user.to_dict(), "userA": self.userA.to_dict(), "userB": self.userB.to_dict(), "type": self.type, "status": self.status, "comment": self.comment, "create_at": datetime_to_timestamp(self.created_at) } @staticmethod def find_by_cursor(uid, last_id, size=20): if last_id: requests = ContactRequest.query \ .filter(ContactRequest.uidB == uid) \ .filter((ContactRequest.status == ContactRequestStatus.Accepted) | ( ContactRequestStatus == ContactRequestStatus.Pending)) \ .filter(ContactRequest.id < last_id) \ .order_by(db.desc(ContactRequest.id)) \ .offset(0) \ .limit(size).all() else: requests = ContactRequest.query \ .filter(ContactRequest.uidB == uid) \ .filter((ContactRequest.status == ContactRequestStatus.Accepted) | ( ContactRequest.status == ContactRequestStatus.Pending)) \ .order_by(db.desc(ContactRequest.id)) \ .offset(0) \ .limit(size).all() return requests @staticmethod def add(uid, uidA, uidB, comment): result = False contact = Contact.find_by_uid(uidA, uidB) if contact and contact.status == ContactStatus.Connected: st_raise_error(ErrorCode.RELATIONSHIP_ALREADY_CONNECTED) type = ContactRequestType.Added if uid == uidA else ContactRequestType.Recommended if type == ContactRequestType.Added: # the two user must have been in at least one same company if add directly. work_experiences_A = set(WorkExperience.get_company_ids(uidA)) work_experiences_B = set(WorkExperience.get_company_ids(uidB)) if len(work_experiences_A & work_experiences_B) == 0: st_raise_error(ErrorCode.ADD_RELATIONSHIP_NOT_COMMON_COMPANY) # TODO: 过期时间 request = ContactRequest.query.filter( ContactRequest.uid == uid, ContactRequest.uidA == uidA, ContactRequest.uidB == uidB, ).one_or_none() if request is None: request = ContactRequest(uid=uid, uidA=uidA, uidB=uidB, type=type, comment=comment, status=ContactRequestStatus.Pending) db.session.add(request) result = True db.session.commit() return result
class WorkExperience(db.Model): __tablename__ = "work_experience" id = db.Column(db.BigInteger, primary_key=True, autoincrement=True) uid = db.Column(db.BigInteger, index=True, nullable=False) start_year = db.Column(db.SMALLINT, nullable=False, comment=u'开始-年') start_month = db.Column(db.SMALLINT, nullable=False, comment=u'开始-月') end_year = db.Column(db.SMALLINT, nullable=False, comment=u'2999表示至今') end_month = db.Column(db.SMALLINT, nullable=True) title = db.Column(db.String(255), nullable=False, comment=u'职位') company_id = db.Column(db.BigInteger, db.ForeignKey("organizations.id"), nullable=False) company = db.relationship("Organization") status = db.Column(db.SMALLINT, nullable=False, default=0, comment=u"0: normal, 1: deleted") create_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) update_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) delete_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) @staticmethod def add(new_one): db.session.add(new_one) db.session.commit() def update(self): db.session.commit() @staticmethod def find_by_uid_id(uid, id): return WorkExperience.query.filter( WorkExperience.id == id, WorkExperience.uid == uid).one_or_none() @staticmethod def find_all_for_user(uid): return WorkExperience.query.filter( WorkExperience.uid == uid, WorkExperience.status == WorkExperienceStatus.Normal).all() @staticmethod def get_company_ids(uid): return [ _[0] for _ in WorkExperience.query.with_entities( db.distinct(WorkExperience.company_id)).filter( WorkExperience.uid == uid, WorkExperience.status == WorkExperienceStatus.Normal).all() ] @staticmethod def delete(uid, id): we = WorkExperience.find_by_uid_id(uid, id) if we: we.status = WorkExperienceStatus.Deleted we.delete_date = datetime.utcnow() db.session.commit() def to_dict(self): return { "id": encode_id(self.id), "start_year": self.start_year, "start_month": self.start_month, "end_year": self.end_year, "end_month": self.end_month, "title": self.title, "company": self.company.to_dict() }