class Paragraph(models.Model): content = models.TextField() section = models.BooleanField() economist = models.ForeignKey( Economist, on_delete=models.CASCADE, ) words = models.IntegerField( default=0, ) @classmethod def create(cls, content: str, **kwargs): try: return cls.objects.create( content=content, words=len(content.split(' ')), **kwargs ) except Exception: raise EconomistError.PARAGRAPH_CREATE @classmethod def get(cls, economist: Economist): return cls.objects.filter(economist=economist).order_by('pk') def d(self): return self.dictify('content', 'section')
class Member(models.Model): user = models.ForeignKey( 'User.User', on_delete=models.CASCADE, ) # 是否准备 is_ready = models.BooleanField(default=False) def d(self): return self.dictor('pk->mid', 'user', 'is_ready') def d_username(self): return self.user.d_username() def _readable_user(self): if self.user: return self.user.d() def ready(self): self.is_ready = True self.save() def unready(self): self.is_ready = False self.save() @classmethod def join_room(cls, user): print("######user:"******"___join_room") try: member = cls(user=user, ) member.save() except Exception: raise RoomError.JOIN_ROOM(user.username) user.entered_room() return member @classmethod def leave_room(cls, user): try: print("######user:"******"___leave_room") member = Member.objects.get(user=user) user.leave_room() member.delete() except Exception as err: raise RoomError.LEAVE_ROOM(debug_message=err)
class Album(Resource): space = models.ForeignKey( 'Space.Space', on_delete=models.CASCADE, null=False, ) parent = models.ForeignKey( 'Album', on_delete=models.CASCADE, default=None, null=True, ) name = models.CharField( max_length=20, null=True, ) grid_rows = models.IntegerField( min_value=8, max_value=20, default=10, ) auto_arrange = models.BooleanField( default=True, ) cover = models.ForeignKey( 'Image.Image', on_delete=models.SET_NULL, null=True, related_name='cover', ) def not_root_checker(self): if not self.parent: raise AlbumError.ROOT def born(self, name) -> 'Album': try: return Album.objects.create( space=self.space, parent=self, name=name, grid_rows=10, auto_arrange=True, cover=None, res_id=self.generate_res_id(), grid_position=None, ) except Exception: raise AlbumError.CREATE def delete(self, *args, **kwargs): if self.cover: self.cover.delete() super().delete(*args, **kwargs) def set_cover(self, image): self.cover = image self.save() def get_image_token(self): return Image.get_token( action=ImageUploadAction.ALBUM_COVER, album_id=self.res_id, ) def _readable_parent(self): if self.parent: return self.parent.res_id def d(self): d = dict(type='album') d.update(self.dictify('name', 'grid_rows', 'res_id', 'parent')) return d def d_layer(self): d = self.d() d['items'] = self.album_set.filter( cover__isnull=False).dict(Album.d_image) + self.image_set.dict(Image.d) return d def d_image(self): d = self.cover.d() d.update(self.d()) # res_id 不能交换顺序 return d def update(self, name, grid_rows): self.name = name self.grid_rows = grid_rows self.save()
class User(models.Model): """ 用户类 根超级用户id=1 """ ROOT_ID = 1 username = models.CharField( max_length=32, min_length=3, unique=True, blank=True, null=True, default=None, ) password = models.CharField( max_length=32, min_length=6, ) salt = models.CharField( max_length=10, default=None, ) pwd_change_time = models.FloatField( null=True, blank=True, default=0, ) inviter = models.ForeignKey('User.User', on_delete=models.CASCADE, default=None, null=True) invite_code = models.CharField( default=None, max_length=255, blank=True, null=True, ) enter_room = models.BooleanField(default=False) def is_ancestor(self, user: '******'): while user: if self == user: return True user = user.inviter return False @staticmethod def _valid_username(username): """验证用户名合法""" if username[0] not in string.ascii_lowercase + string.ascii_uppercase: raise UserError.INVALID_USERNAME_FIRST valid_chars = '^[A-Za-z0-9_]{3,32}$' if re.match(valid_chars, username) is None: raise UserError.INVALID_USERNAME @staticmethod def _valid_password(password): """验证密码合法""" valid_chars = '^[A-Za-z0-9!@#$%^&*()_+-=,.?;:]{6,16}$' if re.match(valid_chars, password) is None: raise UserError.INVALID_PASSWORD @staticmethod def hash_password(raw_password, salt=None): if not salt: salt = get_random_string(length=6) hash_password = User._hash(raw_password + salt) return salt, hash_password @classmethod def exist_with_username(cls, username): try: cls.objects.get(username=username) except cls.DoesNotExist: return raise UserError.USERNAME_EXIST @classmethod def inviterjsonArr(cls, data): rData = [] for item in data: item.__dict__.pop("_state") rData.append(item.__dict__) return rData @classmethod def return_inviter(cls, invite_code): try: cls.objects.get(invite_code=invite_code) except cls.DoesNotExist: raise UserError.NOT_FOUND_COD else: user = User.objects.get(invite_code=invite_code) print(user.username) return user @classmethod def create_invite(cls, username, password, invite_code): """ 创建用户(有邀请码) :param username: 用户名 :param password: 密码 :param invite_code: 邀请码 :return: Ret对象,错误返回错误代码,成功返回用户对象 """ salt, hashed_password = User.hash_password(password) User.exist_with_username(username) # User.exist_with_invitecode(invite_code) # print(invite_code) inviter = User.return_inviter(invite_code) # print(inviter.username) try: user = cls(username=username, password=hashed_password, salt=salt, inviter=inviter, invite_code=username + "666") user.save() except Exception: raise UserError.CREATE_USER return user @classmethod def create(cls, username, password): """ 创建用户 :param username: 用户名 :param password: 密码 :param invite_code: 邀请码 :return: Ret对象,错误返回错误代码,成功返回用户对象 """ cls.validator(locals()) salt, hashed_password = User.hash_password(password) User.exist_with_username(username) try: user = cls( username=username, password=hashed_password, salt=salt, ) user.save() except Exception: raise UserError.CREATE_USER return user def change_password(self, password, old_password): """修改密码""" self.validator(locals()) if self.password != User._hash(old_password): raise UserError.PASSWORD self.salt, self.password = User.hash_password(password) import datetime self.pwd_change_time = datetime.datetime.now().timestamp() self.save() @staticmethod def _hash(s): import hashlib md5_ = hashlib.md5() md5_.update(s.encode()) return md5_.hexdigest() @staticmethod def get_user_by_username(username): """根据用户名获取用户对象""" try: user = User.objects.get(username=username) except User.DoesNotExist: raise UserError.NOT_FOUND_USER return user @staticmethod def get_user_by_id(user_id): """根据用户ID获取用户对象""" try: user = User.objects.get(pk=user_id) except User.DoesNotExist: raise UserError.NOT_FOUND_USER return user def entered_room(self): self.enter_room = True self.save() def leave_room(self): print("######leave the room") self.enter_room = False self.save() @classmethod def authenticate(cls, username, password): """验证用户名和密码是否匹配""" cls.validator(locals()) try: user = User.objects.get(username=username) except User.DoesNotExist as err: raise UserError.NOT_FOUND_USER salt, hashed_password = User.hash_password(password, user.salt) if hashed_password == user.password: return user raise UserError.PASSWORD def is_beinviter(self, inviter): if self.username == inviter: return True else: inviters = User.objects.filter(inviter__exact=self.username) if len(inviters) > 0: for invite in inviters: # print(inviter.username) return invite.is_beinviter(inviter) else: return False def d(self): return self.dictor('pk->uid', 'username', 'inviter', 'enter_room') def d_invite(self): return self.dictor('pk->id', 'username') def d_username(self): return self.dictor('username') def _readable_inviter(self): if self.inviter: return self.inviter.d_base() def d_base(self): return self.dictor('pk->id', 'username')
class Article(models.Model): user = models.ForeignKey( 'User.MiniUser', on_delete=models.CASCADE, ) aid = models.CharField( verbose_name='文章ID', max_length=6, min_length=6, unique=True, ) origin = models.CharField( max_length=20, null=True, default=None, ) author = models.CharField( max_length=20, null=True, default=None, ) create_time = models.DateTimeField(auto_now_add=True, ) title = models.CharField(max_length=50, ) self_product = models.BooleanField( verbose_name='原创声明', default=False, null=True, ) require_review = models.BooleanField( verbose_name='需要审核', default=False, null=True, ) allow_open_reply = models.BooleanField( verbose_name='允许公开回复', default=False, null=True, ) @classmethod def get(cls, aid): try: return cls.objects.get(aid=aid) except cls.DoesNotExist: raise ArticleError.NOT_FOUND @classmethod def get_unique_id(cls): while True: aid = get_random_string(length=6) try: cls.get(aid) except E: return aid @classmethod def create(cls, user, author, origin, title, self_product, require_review, allow_open_reply): try: return cls.objects.create( user=user, title=title, author=author, origin=origin, self_product=self_product, require_review=require_review, allow_open_reply=allow_open_reply, aid=cls.get_unique_id(), ) except Exception as err: raise ArticleError.CREATE(debug_message=err) def update(self, title, origin, author): self.origin = origin self.title = title self.author = author self.save() def assert_belongs_to(self, user): if self.user != user: raise ArticleError.NOT_OWNER def remove(self): self.delete() def get_comments(self, show_all=False, selected=True): comments = self.comment_set.filter(reply_to=None) if not show_all: comments = comments.filter(selected=selected) return comments.order_by('pk') def _readable_create_time(self): return self.create_time.timestamp() def _readable_comments(self, show_all=False): if not self.require_review and show_all: return comments = self.comment_set.filter(reply_to=None) if self.require_review: comments = comments.filter(selected=True) return comments.order_by('pk').dict(Comment.d_replies, show_all) def _readable_my_comments(self, user): if self.require_review: return self.comment_set.filter(~Q(selected=True), user=user, reply_to=None).order_by('pk').dict( Comment.d_replies) else: return [] def _readable_user(self): return self.user.d() def d_base(self): return self.dictify('aid', 'title', 'origin', 'author', 'create_time', 'self_product', 'require_review', 'allow_open_reply') def d(self, user): d = self.d_base() if self.user == user: d.update( self.dictify('comments', 'user', ('comments->all_comments', True))) else: d.update(self.dictify('comments', 'user', ('my_comments', user))) return d def d_create(self): return self.dictify('aid') def comment(self, user, content): return Comment.create(self, user, content)
class Room(models.Model): """ 房间类 """ number = models.IntegerField( unique=True, null=False, ) password = models.CharField(max_length=5, min_length=4, null=True, default=None) is_public = models.BooleanField(default=True) owner = models.ForeignKey( 'Room.Member', related_name='owner', on_delete=models.SET_NULL, null=True, ) member_a = models.ForeignKey( 'Room.Member', related_name='member_a', on_delete=models.SET_NULL, null=True, default=None, ) member_b = models.ForeignKey( 'Room.Member', related_name='member_b', on_delete=models.SET_NULL, null=True, default=None, ) # 房间人数0/3 member_num = models.IntegerField(default=1, ) status = models.BooleanField(default=False) # 轮到说话的人(1、2、3)(1默认房主) speaker = models.IntegerField(default=1, ) create_time = models.DateTimeField(auto_now_add=True) def d(self): return self.dictor('pk->rid', 'is_public', 'number', 'password', 'owner', 'member_a', 'member_b', 'member_num', 'speaker', 'status', 'create_time') def d_room_list(self): return self.dictor('pk->rid', 'number', 'is_public', 'owner', 'member_a', 'member_b', 'member_num', 'create_time') def d_number(self): return self.dictor('number') def _readable_owner(self): if self.owner: return self.owner.d() def _readable_member_a(self): if self.member_a: return self.member_a.d() def _readable_member_b(self): if self.member_b: return self.member_b.d() def _readable_create_time(self): return self.create_time.timestamp() @classmethod def check_password(cls, room, password): """验证房间密码是否正确""" if not room.is_public: if room.password != password: raise RoomError.JOIN_ROOM_PASSWORD(room.number) @staticmethod def get_room_by_pk(pk): try: return Room.objects.get(pk=pk) except Exception: raise RoomError.GET_ROOM_BY_PK(pk) @staticmethod def get_room_by_number(number): try: room = Room.objects.get(number=number) except Room.DoesNotExist: raise RoomError.GET_ROOM_BY_NUMBER(number) return room @classmethod def get_room_list(cls): Rooms = [] for room in Room.objects.filter( create_time__lte=datetime.datetime.now()): Rooms.append(room.d_room_list()) return Rooms @classmethod def creat_room(cls, user, password): try: print(password) room = cls(number=random.randint(1000, 9999), owner=Member.join_room(user), password=password) if room.password is not None: room.is_public = False room.save() except Exception as err: user.leave_room() raise RoomError.Create_ROOM(debug_message=err) return room @classmethod def close_room(cls, room): try: if room.member_a is not None: Member.leave_room(room.member_a) if room.member_b is not None: Member.leave_room(room.member_b) if room.owner is not None: Member.leave_room(room.owner) room.delete() print("delete======ok") except Exception as err: raise RoomError.CLOSE_ROOM(room.number, debug_message=err) @classmethod def join_room(cls, user, room, password): Room.check_password(room, password) if room.member_num == 3: raise RoomError.ROOM_FULL(room.number) try: member_num = room.member_num if member_num == 1: room.member_a = Member.join_room(user) else: room.member_b = Member.join_room(user) room.member_num = member_num + 1 room.save() except Exception: raise RoomError.MEMBER_JOIN_ROOM(user.username, room.number) return room @classmethod def change_position(cls, room): if room.owner is None: if room.member_b is not None: print("b->owner") room.owner = room.member_b room.member_b = None elif room.member_a is not None: print("a->owner") room.owner = room.member_a room.member_a = None if room.member_a is None and room.member_b is not None: print("b->a") room.member_a = room.member_b room.member_b = None room.member_num = room.member_num - 1 print(room.member_num) room.save() if room.member_num == 0: cls.close_room(room) def get_room_member(self): return [self.owner, self.member_a, self.member_b] @staticmethod def room_ready_status(user, room, ready): if room.status: raise RoomError.ROOM_NOT_UNREADY(room.number) try: print(ready) for member in room.get_room_member(): if member.user == user: member.is_ready = ready member.save() return room except Exception as err: raise RoomError.ROOM_CHANGE_STATUS(room.number, user.username, debug_message=err) @staticmethod def room_begin(room): room.status = True room.save() return room def room_change_speaker(self): try: self.speaker = (self.speaker + 1) if self.speaker == 4: self.speaker = 1 self.save() except Exception: raise RoomError.ROOM_CHANGE_SPEAKER(self.number)
class SpaceMan(models.Model): class Meta: unique_together = ('user', 'space') user = models.ForeignKey( 'User.User', on_delete=models.CASCADE, ) avatar = models.ForeignKey( 'Image.Image', on_delete=models.SET_NULL, null=True, ) name = models.CharField( verbose_name='名字', max_length=20, ) space = models.ForeignKey( Space, on_delete=models.CASCADE, ) is_owner = models.BooleanField(default=False, ) @classmethod def get_by_union(cls, space_user_union: str): space_name, user_id = space_user_union.rsplit('-', 1) space = Space.get(space_name) user = User.get(user_id) return space.get_member(user) def get_union(self): return '-'.join([self.space.space_id, self.user.user_id]) def not_owner_checker(self): if self.is_owner: raise SpaceError.DELETE_OWNER def delete(self, *args, **kwargs): if self.avatar: self.avatar.delete() super(SpaceMan, self).delete(*args, **kwargs) # 星球居民头像 def set_avatar(self, image): if self.avatar: self.avatar.delete() self.avatar = image self.save() def get_avatar_token(self): return Image.get_token( action=ImageUploadAction.SPACEMAN, space_user=self.get_union(), ) def get_avatar(self): if self.avatar: return self.avatar.d_avatar() return dict(source=self.user.avatar, color='0xdddddd') def update(self, name): self.name = name self.save() def _readable_user(self): return self.user.d() def _readable_space(self, for_invite=False): if for_invite: return self.space.d_base() return self.space.d() def _readable_avatar(self): return self.get_avatar() def d_space(self): return self.dictify('avatar', 'name', 'is_owner', 'user') def d_user(self): return self.dictify('avatar', 'name', 'is_owner', 'space') def d_invite(self): return self.dictify('avatar', 'name', ('space', True)) def d_user_base(self): return self.space.d_base()
class Foto(models.Model): foto_id = models.CharField( max_length=6, min_length=6, unique=True, ) create_time = models.DateTimeField(auto_now_add=True, ) key = models.CharField( max_length=100, unique=True, ) width = models.IntegerField() height = models.IntegerField() color_average = models.CharField( max_length=20, null=True, default=None, ) mime_type = models.CharField(max_length=50, ) orientation = models.IntegerField(default=1, ) album = models.ForeignKey( Album, on_delete=models.SET_NULL, default=None, null=True, ) pinned = models.BooleanField(default=False, ) @classmethod def is_id_unique(cls, foto_id): try: cls.objects.get(foto_id=foto_id) return False except cls.DoesNotExist: return True @classmethod def generate_foto_id(cls): while True: foto_id = get_random_string(6) if cls.is_id_unique(foto_id): return foto_id @classmethod def orientation_str2int(cls, orientation: list): if orientation[0] == 'TOP': return 1 if orientation[1] == 'LEFT' else 2 elif orientation[0] == 'BOTTOM': return 4 if orientation[1] == 'LEFT' else 3 elif orientation[1] == 'TOP': return 5 if orientation[1] == 'LEFT' else 6 else: return 8 if orientation[1] == 'LEFT' else 7 @classmethod def orientation_int2str(cls, orientation: int): o = orientation - 1 s = [''] * 2 s[o // 4] = 'TOP' if orientation in [1, 2, 5, 6] else 'BOTTOM' s[1 - o // 4] = 'LEFT' if orientation in [1, 4, 5, 8] else 'RIGHT' return '-'.join(s) def set_album(self, album): self.album = album self.save() def toggle_pin(self): self.pinned = not self.pinned self.save() @classmethod def get(cls, foto_id): try: return cls.objects.get(foto_id=foto_id) except cls.DoesNotExist: return FotoError.NOT_FOUND @classmethod def create(cls, width, height, orientation, **kwargs): if orientation >= 5: width, height = height, width try: return cls.objects.create( **kwargs, width=width, height=height, orientation=orientation, foto_id=cls.generate_foto_id(), ) except Exception as err: raise FotoError.CREATE(debug_message=err) @classmethod def get_tokens(cls, num, **kwargs): key_prefix = hex(int(datetime.datetime.now().timestamp() * 1000)) tokens = [] for _ in range(num): key = key_prefix + '/' + get_random_string(length=16) tokens.append( qn_manager.get_upload_token(key=key, policy=policy.customize(**kwargs))) return tokens def get_source(self, expires=3600, auto_rotate=True, resize=None, quality=100): url = qn_manager.get_image(self.key, expires=expires, auto_rotate=auto_rotate, resize=resize, quality=quality) return url def get_exif(self, expires=3600): return qn_manager.get_image_exif(self.key, expires=expires) def resize(self): target = 1200 if self.width <= target and self.height <= target: return self.width, self.height if self.width > self.height: return target, target * self.height // self.width return target * self.width // self.height, target def get_sources(self): return dict(origin=self.get_source(auto_rotate=False, resize=None), square=self.get_source(auto_rotate=True, resize=(600, 600), quality=75), rotate=self.get_source(auto_rotate=True, resize=self.resize(), quality=75)) def remove(self): qn_manager.delete_res(self.key) self.delete() @classmethod def get_pinned_fotos(cls, space): return cls.objects.filter(pinned=True, album__space=space) def _readable_sources(self): return dict( color=self.color_average, origin=self.get_source(auto_rotate=False, resize=None), square=self.get_source(auto_rotate=True, resize=(600, 600), quality=75), rotate=self.get_source(auto_rotate=True, resize=self.resize(), quality=75), exif=self.get_exif(), ) def _readable_orientation(self): return [self.orientation, self.orientation_int2str(self.orientation)] def _readable_album(self): return self.album.name def d(self): return self.dictify('sources', 'width', 'height', 'foto_id', 'orientation', 'album', 'pinned') def d_base(self): return self.dictify( 'album', 'foto_id', )
class Meat(models.Model): """ 肉类(任务) """ # 天鹅肉 content = models.TextField( default=None, null=True, ) # 天鹅肉所有者 toad = models.ForeignKey( 'User.User', related_name='toad_user', on_delete=models.CASCADE, ) # 目标时间 target_time = models.DateTimeField( null=True ) # 创建时间 create_time = models.DateTimeField( auto_now_add=True ) # 肉质及状态 # 0还没熟 # 1熟了(已到时间) # 2吃上了 # 3下次一定 status = models.IntegerField( default=0 ) # 短信提醒开关 notification = models.BooleanField( # 是否开启短信提醒功能 default=False ) # 标记 achieve = models.BooleanField( default=False ) @classmethod def create(cls, user, content, target_time, notification): print("create++++++++++++++++") try: meat = cls( content=content, toad=user, target_time=target_time, create_time=datetime.datetime.now(), status=0, notification=notification, achieve=False ) meat.save() user.add_meat_quantity() except Exception as err: raise MeatError.CREATE_MEAT(debug_message=err) return meat @classmethod def change_meat_status(cls, meat, status): try: meat.status = status meat.save() except Exception: raise MeatError.CHANGE_MEAT_STATUS if not meat.achieve and status > 1: cls.change_meat_achieve(meat, True) return meat @classmethod def check_target_time(cls): try: meats = cls.objects.filter(achieve=False) now_time = datetime.datetime.now() change_list = [] for meat in meats: if now_time > meat.target_time: m = cls.change_meat_status(meat, 1) change_list.append(m.d_meat_list()) except Exception as err: raise MeatError.CHECK_TARGET_TIME(debug_message=err) return change_list @staticmethod def change_meat_achieve(meat, achieve): try: meat.achieve = achieve meat.save() except Exception: raise MeatError.CHANGE_MEAT_ACHIEVE meat.toad.reduce_meat_quantity() @classmethod def get_meat_by_pk(cls, tid): try: return cls.objects.get(pk=tid) except Exception: raise MeatError.GET_MEAT_BY_PK @classmethod def get_meat_list(cls, user): pass def d(self): return self.dictor('pk->mid', 'content', 'toad', 'target_time->time', 'create_time', 'status', 'notification', 'achieve') def d_meat_list(self): return self.dictor('pk->mid', 'content', 'status', 'achieve', 'target_time->time') def _readable_toad(self): if self.toad: return self.toad.d() def _readable_target_time(self): return self.target_time.timestamp() def _readable_create_time(self): return self.create_time.timestamp()
class UserApp(models.Model): """用户应用类""" user = models.ForeignKey( 'User.User', on_delete=models.CASCADE, ) app = models.ForeignKey( 'App.App', on_delete=models.CASCADE, ) user_app_id = models.CharField( max_length=16, verbose_name='用户在这个app下的唯一ID', unique=True, ) bind = models.BooleanField( default=False, verbose_name='用户是否绑定应用', ) last_auth_code_time = models.CharField( default=None, verbose_name='上一次申请auth_code的时间,防止被多次使用', max_length=20, ) frequent_score = models.FloatField( verbose_name='频繁访问分数,按分值排序为常用应用', default=0, ) last_score_changed_time = models.CharField( default=None, verbose_name='上一次分数变化的时间', max_length=20, ) mark = models.PositiveSmallIntegerField( verbose_name='此用户的打分,0表示没打分', default=0, ) def _readable_rebind(self): return float(self.last_auth_code_time) < self.app.field_change_time def d(self): return self.dictify('bind', 'mark', 'rebind', 'user_app_id') @classmethod def get_by_user_app(cls, user, app): try: return cls.objects.get(user=user, app=app) except Exception: raise AppError.USER_APP_NOT_FOUND @classmethod def get_by_id(cls, user_app_id, check_bind=False): try: user_app = cls.objects.get(user_app_id=user_app_id) except Exception: raise AppError.USER_APP_NOT_FOUND if check_bind and not user_app.bind: raise AppError.APP_UNBINDED return user_app @classmethod def get_unique_id(cls): while True: user_app_id = get_random_string(length=8) try: cls.get_by_id(user_app_id) except E as e: if e.eis(AppError.USER_APP_NOT_FOUND): return user_app_id @classmethod def do_bind(cls, user, app): if app.is_user_full(): raise AppError.USER_FULL premise_list = app.check_premise(user) for premise in premise_list: error = E.sid2e[premise['check']['identifier']] if not error.ok: raise error crt_timestamp = datetime.datetime.now().timestamp() try: user_app = cls.get_by_user_app(user, app) user_app.bind = True user_app.last_auth_code_time = crt_timestamp user_app.frequent_score += 1 user_app.last_score_changed_time = crt_timestamp user_app.save() except E as e: if e.eis(AppError.USER_APP_NOT_FOUND): try: user_app = cls( user=user, app=app, user_app_id=cls.get_unique_id(), bind=True, last_auth_code_time=crt_timestamp, frequent_score=1, last_score_changed_time=crt_timestamp, ) user_app.save() user_app.app.user_num += 1 user_app.app.save() except Exception as err: raise AppError.BIND_USER_APP(debug_message=err) else: raise e return JWT.encrypt(dict( user_app_id=user_app.user_app_id, type=JWType.AUTH_CODE, ctime=crt_timestamp ), replace=False, expire_second=5 * 60) @classmethod def check_bind(cls, user, app): try: user_app = cls.get_by_user_app(user, app) return user_app.bind except Exception: return False @classmethod def refresh_frequent_score(cls): from Config.models import Config crt_date = datetime.datetime.now().date() crt_time = datetime.datetime.now().timestamp() last_date = Config.get_value_by_key(CI.LAST_RE_FREQ_SCORE_DATE) last_date = datetime.datetime.strptime(last_date, '%Y-%m-%d').date() if last_date >= crt_date: raise AppError.SCORE_REFRESHED from OAuth.views import OAUTH_TOKEN_EXPIRE_TIME Config.update_value(CI.LAST_RE_FREQ_SCORE_DATE, crt_date.strftime('%Y-%m-%d')) for user_app in cls.objects.all(): if crt_time - float( user_app.last_auth_code_time) > OAUTH_TOKEN_EXPIRE_TIME + 24 * 60 * 60: if crt_time - float(user_app.last_score_changed_time) > OAUTH_TOKEN_EXPIRE_TIME: user_app.frequent_score /= 2 user_app.last_score_changed_time = crt_time user_app.save() def do_mark(self, mark): if mark < 1 or mark > 5: raise AppError.MARK original_mark = self.mark self.mark = mark self.save() mark_list = list(map(int, self.app.mark.split('-'))) if 5 >= original_mark > 0: mark_list[original_mark - 1] -= 1 mark_list[mark - 1] += 1 self.app.mark = '-'.join(map(str, mark_list)) self.app.save()
class User(models.Model): """ 用户类 根超级用户id=1 """ ROOT_ID = 1 VERIFY_STATUS_UNVERIFIED = 0 VERIFY_STATUS_UNDER_AUTO = 1 VERIFY_STATUS_UNDER_MANUAL = 2 VERIFY_STATUS_DONE = 3 VERIFY_STATUS_TUPLE = ( (VERIFY_STATUS_UNVERIFIED, '没有认证'), (VERIFY_STATUS_UNDER_AUTO, '自动认证阶段'), (VERIFY_STATUS_UNDER_MANUAL, '人工认证阶段'), (VERIFY_STATUS_DONE, '成功认证'), ) VERIFY_CHINA = 0 VERIFY_ABROAD = 1 VERIFY_TUPLE = ( (VERIFY_CHINA, '中国大陆身份证认证'), (VERIFY_ABROAD, '其他地区身份认证'), ) user_str_id = models.CharField( verbose_name='唯一随机用户ID', default=None, null=True, blank=True, max_length=32, unique=True, ) qitian = models.CharField( default=None, unique=True, max_length=20, min_length=4, ) phone = models.CharField( default=None, unique=True, max_length=20, ) password = models.CharField( max_length=32, min_length=6, ) salt = models.CharField( max_length=10, default=None, ) pwd_change_time = models.FloatField( null=True, blank=True, default=0, ) avatar = models.CharField( default=None, null=True, blank=True, max_length=1024, ) nickname = models.CharField( max_length=10, default=None, ) description = models.CharField( max_length=20, default=None, blank=True, null=True, ) qitian_modify_time = models.IntegerField( verbose_name='齐天号被修改的次数', help_text='一般只能修改一次', default=0, ) birthday = models.DateField( verbose_name='生日', default=None, null=True, ) email = models.EmailField( verbose_name='邮箱', default=None, null=True, ) verify_status = models.SmallIntegerField( verbose_name='是否通过实名认证', default=0, choices=VERIFY_STATUS_TUPLE, ) real_verify_type = models.SmallIntegerField( verbose_name='实名认证类型', default=None, null=True, ) real_name = models.CharField( verbose_name='真实姓名', default=None, max_length=32, null=True, ) male = models.NullBooleanField( verbose_name='是否为男性', default=None, null=True, ) idcard = models.CharField( verbose_name='身份证号', default=None, max_length=18, choices=VERIFY_TUPLE, null=True, ) card_image_front = models.CharField( verbose_name='身份证正面照', max_length=1024, default=None, null=True, ) card_image_back = models.CharField( verbose_name='身份证背面照', max_length=1024, default=None, null=True, ) is_dev = models.BooleanField( verbose_name='是否开发者', default=False, ) @classmethod def get_unique_id(cls): while True: user_str_id = get_random_string(length=6) try: cls.get_by_str_id(user_str_id) except E as e: if e.eis(UserError.USER_NOT_FOUND): return user_str_id @classmethod def get_unique_qitian(cls): while True: qitian_id = get_random_string(length=8) try: cls.get_by_qitian(qitian_id) except E as e: if e.eis(UserError.USER_NOT_FOUND): return qitian_id @staticmethod def _valid_qitian(qitian): """验证齐天号合法""" valid_chars = '^[A-Za-z0-9_]{4,20}$' if re.match(valid_chars, qitian) is None: raise UserError.INVALID_QITIAN @staticmethod def _valid_password(password): """验证密码合法""" valid_chars = '^[A-Za-z0-9!@#$%^&*()_+-=,.?;:]{6,16}$' if re.match(valid_chars, password) is None: raise UserError.INVALID_PASSWORD @staticmethod def _valid_birthday(birthday): """验证生日是否合法""" import datetime if birthday > datetime.datetime.now().date(): raise UserError.BIRTHDAY_FORMAT @staticmethod def hash_password(raw_password, salt=None): if not salt: salt = get_random_string(length=6) hash_password = User._hash(raw_password + salt) return salt, hash_password @classmethod def create(cls, phone, password): salt, hashed_password = User.hash_password(password) User.exist_with_phone(phone) try: user = cls( qitian=cls.get_unique_qitian(), phone=phone, password=hashed_password, salt=salt, avatar=None, nickname='', description=None, qitian_modify_time=0, user_str_id=cls.get_unique_id(), birthday=None, verify_status=cls.VERIFY_STATUS_UNVERIFIED, is_dev=False, ) user.save() except Exception as err: raise UserError.CREATE_USER(debug_message=err) return user def modify_password(self, password): self.salt, self.password = User.hash_password(password) import datetime self.pwd_change_time = datetime.datetime.now().timestamp() self.save() def change_password(self, password, old_password): """修改密码""" if self.password != User._hash(old_password + self.salt): raise UserError.PASSWORD self.salt, self.password = User.hash_password(password) import datetime self.pwd_change_time = datetime.datetime.now().timestamp() self.save() @staticmethod def _hash(s): from Base.common import md5 return md5(s) @classmethod def get_by_str_id(cls, user_str_id): try: return cls.objects.get(user_str_id=user_str_id) except cls.DoesNotExist: raise UserError.USER_NOT_FOUND @classmethod def get_by_phone(cls, phone): """根据手机号获取用户对象""" try: return cls.objects.get(phone=phone) except cls.DoesNotExist: raise UserError.USER_NOT_FOUND('手机号未注册') @classmethod def exist_with_phone(cls, phone): try: cls.objects.get(phone=phone) except cls.DoesNotExist: return raise UserError.PHONE_EXIST @classmethod def get_by_qitian(cls, qitian_id): try: return cls.objects.get(qitian=qitian_id) except cls.DoesNotExist: raise UserError.USER_NOT_FOUND('不存在的齐天号') @classmethod def exist_with_qitian(cls, qitian_id): try: cls.objects.get(qitian=qitian_id) except cls.DoesNotExist: return raise UserError.QITIAN_EXIST @classmethod def get_by_id(cls, user_id): """根据用户ID获取用户对象""" try: return cls.objects.get(pk=user_id) except cls.DoesNotExist: raise UserError.USER_NOT_FOUND def allow_qitian_modify(self): return self.qitian_modify_time == 0 def _readable_avatar(self): return self.get_avatar_url() def _readable_birthday(self): return self.birthday.strftime('%Y-%m-%d') if self.birthday else None def _readable_allow_qitian_modify(self): return int(self.allow_qitian_modify()) def d_oauth(self): return self.dictify('avatar', 'nickname', 'description') def d_base(self): return self.dictify('user_str_id', 'avatar', 'nickname', 'description') def d(self): return self.dictify('birthday', 'user_str_id', 'qitian', 'avatar', 'nickname', 'description', 'allow_qitian_modify', 'verify_status', 'verify_type', 'is_dev') @classmethod def authenticate(cls, qitian, phone, password): """验证手机号和密码是否匹配""" if qitian: user = cls.get_by_qitian(qitian) else: user = cls.get_by_phone(phone) salt, hashed_password = User.hash_password(password, user.salt) if hashed_password == user.password: return user raise UserError.PASSWORD def get_avatar_url(self, small=True): """获取用户头像地址""" if self.avatar is None: return None from Base.qn import qn_public_manager key = "%s-small" % self.avatar if small else self.avatar return qn_public_manager.get_resource_url(key) def get_card_urls(self): from Base.qn import qn_res_manager front_url = qn_res_manager.get_resource_url(self.card_image_front) \ if self.card_image_front else None back_url = qn_res_manager.get_resource_url(self.card_image_back) \ if self.card_image_back else None return dict( front=front_url, back=back_url, ) def modify_avatar(self, avatar): """修改用户头像""" if self.avatar: from Base.qn import qn_public_manager qn_public_manager.delete_res(self.avatar) self.avatar = avatar self.save() def upload_verify_front(self, card_image_front): from Base.qn import qn_res_manager if self.card_image_front: qn_res_manager.delete_res(self.card_image_front) self.card_image_front = card_image_front self.save() qn_res_manager.get_resource_url(self.card_image_front + '-small') def upload_verify_back(self, card_image_back): from Base.qn import qn_res_manager if self.card_image_back: qn_res_manager.delete_res(self.card_image_back) self.card_image_back = card_image_back self.save() qn_res_manager.get_resource_url(self.card_image_back + '-small') def modify_info(self, nickname, description, qitian, birthday): """修改用户信息""" if nickname is None: nickname = self.nickname if description is None: description = self.description if qitian is None: qitian = self.qitian if birthday is None or (self.verify_status and self.real_verify_type == User.VERIFY_CHINA): birthday = self.birthday if self.allow_qitian_modify(): if self.qitian != qitian: self.exist_with_qitian(qitian) self.qitian = qitian self.qitian_modify_time += 1 self.nickname = nickname self.description = description self.birthday = birthday self.save() def update_card_info(self, real_name, male, idcard, birthday): self.real_name = real_name self.male = male self.idcard = idcard self.birthday = birthday try: self.save() except Exception as err: return IDCardError.AUTO_VERIFY_FAILED(debug_message=err) def update_verify_status(self, status): self.verify_status = status self.save() def update_verify_type(self, verify_type): self.real_verify_type = verify_type self.save() def developer(self): self.is_dev = True self.save()