class SensitiveWordResource(Resource): method_decorators = { 'get': [mis_permission_required('sensitive-word-get')], 'put': [mis_permission_required('sensitive-word-put')], 'delete': [mis_permission_required('sensitive-word-delete')], } def get(self, target): """ 获取敏感词信息 :param target: 敏感词id :return: """ word = SensitiveWord.query.filter_by(id=target).first() if not word: return {'message': '{} is not exists'.format(target)}, 403 return marshal(word, SensitiveWordListResource.word_fields) def put(self, target): """ 修改敏感词信息 :param target: 敏感词id :return: """ args_parser = RequestParser() args_parser.add_argument('word', required=True, location='json') args_parser.add_argument('weights', type=inputs.natural, required=True, location='json') args = args_parser.parse_args() word = SensitiveWord.query.filter_by(id=target).first() if not word: return {'message': '{} is not exists'.format(target)}, 403 print(word.word, args.word) if args.word is not None and args.word != word.word: if SensitiveWord.query.filter_by(word=args.word).first(): return {'message': '{} already exists'.format(args.word)}, 403 word.word = args.word if args.weights: word.weights = args.weights word.utime = datetime.now() db.session.add(word) db.session.commit() return marshal(word, SensitiveWordListResource.word_fields), 201 def delete(self, target): """ 删除敏感词信息 :param target: 敏感词id """ SensitiveWord.query.filter_by(id=target).delete( synchronize_session=False) db.session.commit() return {}, 204
class StatisticsSearchTotalResource(Resource): """ 搜索统计-总数 """ method_decorators = { 'get': [mis_permission_required('statistics-search-total-get')], } def get(self): # 搜索总用户数 query = db.session.query(func.sum(StatisticsSearchTotal.user_count)) search_user_count = int(query.scalar() or 0) # 总用户数 query = User.query user_count = int(query.count() or 0) # 搜索总数 query = db.session.query(func.sum(StatisticsSearchTotal.count)) count = int(query.scalar() or 0) return { 'search_user_count': search_user_count, 'count': count, 'user_count': user_count }
class ChannelResource(Resource): """ 频道 """ method_decorators = { 'put': [mis_permission_required('channel-list-put')], 'delete': [mis_permission_required('channel-list-delete')], } def put(self, target): """ 修改频道 """ json_parser = RequestParser() json_parser.add_argument('name', required=True, location='json') json_parser.add_argument('is_visible', type=inputs.int_range(0, 1), required=True, location='json') json_parser.add_argument('sequence', type=inputs.positive, location='json') args = json_parser.parse_args() channel = Channel.query.filter_by(id=target).first() if not channel: return {'message': 'Invalid channel id.'}, 400 if args.name is not None and args.name != channel.name: if Channel.query.filter_by(name=args.name).first(): return {'message': '{} already exists'.format(args.name)}, 403 channel.name = args.name if args.is_visible is not None: channel.is_visible = args.is_visible if args.sequence is not None: channel.sequence = args.sequence db.session.add(channel) db.session.commit() return marshal(channel, ChannelListResource.channel_fields), 201 def delete(self, target): """ 删除频道 """ if Article.query.filter_by(channel_id=target).first(): return {'message': 'Please delete the article first.'}, 400 Channel.query.filter_by(id=target).delete(synchronize_session=False) return {'message': 'OK'}, 204
class ArticleResource(Resource): method_decorators = { 'get': [mis_permission_required('article-get')], } def get(self, target): # 获取文章完整信息 article = Article.query.filter_by(id=target).first() if not article: return {'messaget': 'article is not exits.'} ret = marshal(article, ArticleListResource.article_fields) ret['content'] = article.content.content return ret
class UserResource(Resource): """ 单用户管理 """ method_decorators = { 'get': [mis_permission_required('user-get')], } def get(self, target): """ 获取用户信息 """ user = User.query.filter_by(id=target).first() if not user: return {'message': 'Invalid user id.'}, 400 return marshal(user, UserListResource.user_fields)
class LegalizeResource(Resource): method_decorators = { 'get': [mis_permission_required('legalize-get')], } def get(self, target): """ 用户认证申请记录-详细信息 """ legal = LegalizeLog.query.filter_by(id=target).first() if not legal: return {'message': 'Invalid legalize id.'}, 400 ret = marshal(legal, LegalizeListResource.legalize_fields) if legal.type == LegalizeLog.TYPE.REAL_NAME: up = UserProfile.query.filter_by(id=legal.user_id).first() ret['user_profile'] = dict( user_id=up.id, gender=up.gender, birthday=up.birthday.strftime('%Y-%m-%d') if up.birthday else '', real_name=up.real_name, id_number=up.id_number, id_card_front=up.id_card_front, id_card_back=up.id_card_back, id_card_handheld=up.id_card_handheld, area=up.area, company=up.company, career=up.career) elif legal.type == LegalizeLog.TYPE.QUALIFICATION: qa = legal.qualification ret['qualification'] = dict(qualification_id=qa.id, user_id=qa.user_id, name=qa.name, id_number=qa.id_number, industry=qa.industry, company=qa.company, position=qa.position, add_info=qa.add_info, id_card_front=qa.id_card_front, id_card_back=qa.id_card_back, id_card_handheld=qa.id_card_handheld, qualification_img=qa.qualification_img) return ret
class StatisticsReadSourceTotalResource(Resource): """ 阅读来源统计-总数 """ method_decorators = { 'get': [mis_permission_required('statistics-read-source-total-get')], } def get(self): srsts = StatisticsReadSourceTotal.query.order_by(StatisticsReadSourceTotal.count.desc()).all() ret = { 'read_sources': [], 'total_read_count': 0 } for srst in srsts: ret['total_read_count'] += srst.count ret['read_sources'].append({ 'source': srst.source, 'source_name': StatisticsReadSourceTotal.SOURCE.source_map[srst.source], 'count': srst.count, 'count_20_down': srst.count_20_down, 'count_20_80': srst.count_20_80, 'count_80_up': srst.count_80_up, }) return ret def post(self): for source in StatisticsReadSourceTotal.SOURCE.source_map: srst = StatisticsReadSourceTotal.query.filter_by(source=source).first() if not srst: srst = StatisticsReadSourceTotal(source=source, count=3, count_20_down=1, count_20_80=1, count_80_up=1) db.session.add(srst) db.session.commit() return {}
class StatisticsSalesTotalResource(Resource): """ 销售额统计-总数 """ method_decorators = { 'get': [mis_permission_required('statistics-sales-total-get')], } def get(self): """ 地区销售额排行榜 :return: """ ssts = StatisticsSalesTotal.query.order_by( StatisticsSalesTotal.money.desc()).all() ret = {'area_sales': [], 'total_money': 0} for sst in ssts: ret['total_money'] += sst.money ret['area_sales'].append( dict(area=sst.area, area_name=StatisticsArea.area_map[sst.area], money=sst.money)) return ret def post(self): for area in StatisticsArea.area_map: import random money = random.randint(100, 1000) * 100 sst = StatisticsSalesTotal.query.filter_by(area=area).first() if not sst: sst = StatisticsSalesTotal(area=area, money=0) sst.money += money db.session.add(sst) db.session.commit() return {}
class ArticleListResource(Resource): """ 文章列表管理 """ article_fields = { 'article_id': fields.Integer(attribute='id'), # 文章id 'title': fields.String(attribute='title'), # 文章名称 'name': fields.String(attribute='user.name'), # 作者 'channel_name': fields.String(attribute='channel.name'), # 所属频道 'comment_count': fields.Integer(attribute='comment_count'), # 评论数 'update_time': fields.DateTime(attribute='ctime', dt_format='iso8601'), # 更新时间 'status': fields.Integer(attribute='status'), # 状态 } method_decorators = { 'get': [mis_permission_required('article-list-get')], 'put': [mis_permission_required('article-list-put')], } def get(self): """ 获取文章列表 """ args_parser = RequestParser() args_parser.add_argument('page', type=inputs.positive, required=False, location='args') args_parser.add_argument('per_page', type=inputs.int_range(constants.PER_PAGE_MIN, constants.PER_PAGE_MAX, 'per_page'), required=False, location='args') args_parser.add_argument('title', location='args') args_parser.add_argument('channel', location='args') args_parser.add_argument('status_list', location='args') args_parser.add_argument('begin', type=parser.date_time, location='args') args_parser.add_argument('end', type=parser.date_time, location='args') args_parser.add_argument('order_by', location='args') args = args_parser.parse_args() page = constants.DEFAULT_PAGE if args.page is None else args.page per_page = constants.DEFAULT_PER_PAGE if args.per_page is None else args.per_page articles = Article.query if args.title is not None: articles = articles.filter( Article.title.like('%' + args.title + '%')) if args.channel is not None: channels = Channel.query.filter( Channel.name.like('%' + args.channel + '%')).all() channel_ids = [i.id for i in channels] articles = articles.filter(Article.channel_id.in_(channel_ids)) if args.status_list is not None: try: status_list = [int(i) for i in args.status_list.split(',')] except: return {'message': 'status_list parameter error.'}, 403 articles = articles.filter(Article.status.in_(status_list)) if args.begin and args.end and args.end >= args.begin: articles = articles.filter( Article.ctime.between(args.begin, args.end)) if args.order_by is not None: if args.order_by == 'id': articles = articles.order_by(Article.id.asc()) else: articles = articles.order_by(Article.ctime.desc()) total_count = articles.count() articles = articles.offset(per_page * (page - 1)).limit(per_page).all() ret = marshal(articles, ArticleListResource.article_fields, envelope='articles') ret['total_count'] = total_count return ret def put(self): """ 批量修改文章状态 :return: """ json_parser = RequestParser() json_parser.add_argument('reject_reason', location='json') json_parser.add_argument('status', type=inputs.int_range(1, 6), required=True, location='json') json_parser.add_argument('article_ids', action='append', type=inputs.positive, required=True, location='json') args = json_parser.parse_args() articles = Article.query.filter(Article.id.in_(args.article_ids)).all() need_kafka_send_msgs = [] need_rebuild_es_indexes = [] for article in articles: if args.status == Article.STATUS.BANNED: # 封禁 if article.status != Article.STATUS.APPROVED: return { 'message': '"%s" 文章不是审核状态, 不能封禁' % article.title }, 403 elif args.status == 6: # 解封 if article.status != Article.STATUS.BANNED: return { 'message': '"%s" 文章不是封禁状态, 不能解封' % article.title }, 403 article.status = Article.STATUS.APPROVED elif args.status == Article.STATUS.APPROVED: # 通过审核 if article.status != Article.STATUS.UNREVIEWED: return { 'message': '"%s" 文章不是待审核状态, 不能通过审核' % article.title }, 403 article.review_time = datetime.now() article.reviewer_id = g.administrator_id # 记录需要kafka推送的消息 need_kafka_send_msgs.append('{},{}'.format( article.channel_id, article.id)) # 记录需要es添加索引 need_rebuild_es_indexes.append(article) elif args.status == Article.STATUS.FAILED: # 审核失败(驳回) if article.status != Article.STATUS.UNREVIEWED: return { 'message': '"%s" 文章不是待审核状态, 不能驳回' % article.title }, 403 article.reviewer_id = g.administrator_id article.review_time = datetime.now() article.reject_reason = args.reject_reason if args.reject_reason else '系统审核' elif args.status == Article.STATUS.DELETED: # 删除 article.delete_time = datetime.now() else: return {'message': '错误的状态操作'}, 403 article.status = args.status if args.status != 6 else Article.STATUS.APPROVED db.session.add(article) db.session.commit() # 文章通过审核,kafka推送消息给推荐系统 for msg in need_kafka_send_msgs: current_app.kafka_producer.send('new-article', msg.encode()) # 添加ES索引 for article in need_rebuild_es_indexes: doc = { 'article_id': article.id, 'user_id': article.user_id, 'title': article.title, 'content': article.content.content, 'status': article.status, 'create_time': article.ctime } try: current_app.es.index(index='articles', doc_type='article', body=doc, id=article.id) except Exception as e: current_app.logger.error(e) return {'message': 'OK'}, 201
class LegalizeListResource(Resource): """ 用户认证申请记录 """ legalize_fields = { 'legalize_id': fields.Integer(attribute='id'), # 申请记录id 'account': fields.String(attribute='user.account'), # 账号 'name': fields.String(attribute='user.name'), # 用户名 'create_time': fields.DateTime(attribute='ctime', dt_format='iso8601'), # 申请认证时间 'type': fields.Integer(attribute='type'), # 认证类型 'status': fields.Integer(attribute='status'), # 状态 } method_decorators = { 'get': [mis_permission_required('legalize-list-get')], 'post': [mis_permission_required('legalize-list-post')], 'put': [mis_permission_required('legalize-list-put')], } def get(self): """ 获取用户认证申请记录 """ args_parser = RequestParser() args_parser.add_argument('page', type=inputs.positive, required=False, location='args') args_parser.add_argument('per_page', type=inputs.int_range(constants.PER_PAGE_MIN, constants.PER_PAGE_MAX, 'per_page'), required=False, location='args') args_parser.add_argument('status', type=inputs.positive, location='args') args_parser.add_argument('order_by', location='args') args = args_parser.parse_args() page = constants.DEFAULT_PAGE if args.page is None else args.page per_page = constants.DEFAULT_PER_PAGE if args.per_page is None else args.per_page legalizes = LegalizeLog.query if args.status is not None: legalizes = legalizes.filter_by(status=args.status) if args.order_by is not None: if args.order_by == 'id': legalizes = legalizes.order_by(LegalizeLog.id.asc()) else: legalizes = legalizes.order_by(LegalizeLog.utime.desc()) else: legalizes = legalizes.order_by(LegalizeLog.utime.desc()) total_count = legalizes.count() legalizes = legalizes.offset(per_page * (page - 1)).limit(per_page).all() ret = marshal(legalizes, LegalizeListResource.legalize_fields, envelope='legalizes') ret['total_count'] = total_count return ret def post(self): """ *** 测试 """ user_id = 8 # qual = Qualification(user_id=user_id, # name='wangzq', # id_number='440982199006091854', # industry='软件', # company='cz', # position='python', # add_info='nothing', # id_card_front='id_card_front', # id_card_back='id_card_back', # id_card_handheld='id_card_handheld', # qualification_img='qualification_img', # ) # db.session.add(qual) # db.session.commit() legal = LegalizeLog(user_id=user_id, type=LegalizeLog.TYPE.REAL_NAME, status=LegalizeLog.STATUS.PROCESSING) db.session.add(legal) db.session.commit() return marshal(legal, LegalizeListResource.legalize_fields), 201 def put(self): """ 批量通过/驳回 """ json_parser = RequestParser() json_parser.add_argument('legalize_ids', action='append', type=inputs.positive, required=True, location='json') json_parser.add_argument('status', type=inputs.int_range(2, 3), required=True, location='json') json_parser.add_argument('reject_reason', location='json') args = json_parser.parse_args() legalizes = LegalizeLog.query.filter( LegalizeLog.id.in_(args.legalize_ids)) user_ids = [legal.user_id for legal in legalizes.all()] count = legalizes.update({'status': args.status}, synchronize_session=False) if args.status == LegalizeLog.STATUS.REJECT: legalizes.update( {'reject_reason': args.reject_reason or '资料不通过,驳回'}, synchronize_session=False) User.query.filter(User.id.in_(user_ids)).update( {'is_media': False}, synchronize_session=False) elif args.status == LegalizeLog.STATUS.APPROVED: User.query.filter(User.id.in_(user_ids)).update( {'is_media': True}, synchronize_session=False) db.session.commit() return {'count': count}, 201
class UserListResource(Resource): """ 用户管理 """ user_fields = { 'user_id': fields.Integer(attribute='id'), # 用户id 'account': fields.String(), # 账号 'name': fields.String(attribute='name'), # 用户名 'email': fields.String(), # 邮箱 'mobile': fields.String(attribute='mobile'), # 手机号 'last_login': fields.DateTime(attribute='last_login', dt_format='iso8601'), # 最后活动时间 'is_verified': fields.Integer(attribute='is_verified'), # 是否实名认证 'is_media': fields.Integer(attribute='is_media'), # 是否资质认证 'status': fields.Integer(attribute='status'), # 状态 } method_decorators = { 'get': [mis_permission_required('userlist-get')], 'post': [mis_permission_required('userlist-post')], 'put': [mis_permission_required('userlist-put')], } def get(self): """ 获取用户列表 """ args_parser = RequestParser() args_parser.add_argument('page', type=inputs.positive, required=False, location='args') args_parser.add_argument('per_page', type=inputs.int_range(constants.PER_PAGE_MIN, constants.PER_PAGE_MAX, 'per_page'), required=False, location='args') args_parser.add_argument('keyword', location='args') args_parser.add_argument('begin', type=parser.date_time, location='args') args_parser.add_argument('end', type=parser.date_time, location='args') args_parser.add_argument('status', type=inputs.int_range(0, 1), location='args') args_parser.add_argument('order_by', location='args') args = args_parser.parse_args() page = constants.DEFAULT_PAGE if args.page is None else args.page per_page = constants.DEFAULT_PER_PAGE if args.per_page is None else args.per_page users = User.query if args.keyword: users = users.filter( or_(User.mobile.like('%' + args.keyword + '%'), User.account.like('%' + args.keyword + '%'))) if args.status is not None: users = users.filter(User.status == args.status) if args.begin and args.end and args.end > args.begin: users = users.filter(User.last_login.between(args.begin, args.end)) if args.order_by is not None: if args.order_by == 'id': users = users.order_by(User.id.asc()) else: users = users.order_by(User.last_login.desc()) else: users = users.order_by(User.last_login.asc()) total_count = users.count() users = users.offset(per_page * (page - 1)).limit(per_page).all() ret = marshal(users, UserListResource.user_fields, envelope='users') ret['total_count'] = total_count return ret def post(self): """ 新增用户 """ json_parser = RequestParser() json_parser.add_argument('account', type=parser.mis_account, required=True, location='json') json_parser.add_argument('password', type=parser.mis_password, required=True, location='json') json_parser.add_argument('name', type=inputs.regex(r'^.{1,7}$'), required=True, location='json') json_parser.add_argument('profile_photo', type=parser.image_base64, required=False, location='json') json_parser.add_argument('introduction', required=False, location='json') json_parser.add_argument('email', type=parser.email, required=False, location='json') json_parser.add_argument('mobile', type=parser.mobile, required=True, location='json') json_parser.add_argument('gender', type=inputs.int_range(0, 1), required=True, location='json') json_parser.add_argument('area', required=False, location='json') json_parser.add_argument('company', required=False, location='json') json_parser.add_argument('career', required=False, location='json') args = json_parser.parse_args() if User.query.filter_by(account=args.account).first(): return {'message': '{} already exists'.format(args.account)}, 403 if User.query.filter_by(mobile=args.mobile).first(): return {'message': '{} already exists'.format(args.mobile)}, 403 user = User(account=args.account, password=generate_password_hash(args.password), name=args.name, introduction=args.introduction, email=args.email, mobile=args.mobile, status=User.STATUS.ENABLE, last_login=datetime.now()) if args.profile_photo: try: photo_url = upload_image(args.profile_photo) user.profile_photo = photo_url except Exception as e: current_app.logger.error('upload failed {}'.format(e)) return { 'message': 'Uploading profile photo image failed.' }, 507 db.session.add(user) db.session.commit() user_profile = UserProfile(id=user.id, gender=args.gender, area=args.area, company=args.company, career=args.career) db.session.add(user_profile) db.session.commit() return marshal(user, UserListResource.user_fields), 201 def put(self): """ 批量冻结/解冻 """ json_parser = RequestParser() json_parser.add_argument('user_ids', action='append', type=inputs.positive, required=True, location='json') json_parser.add_argument('status', type=inputs.int_range(0, 1), required=True, location='json') args = json_parser.parse_args() count = User.query.filter(User.id.in_(args.user_ids)).update( {'status': args.status}, synchronize_session=False) db.session.commit() return {'count': count}, 201
class GroupResource(Resource): """ 对指定管理员组管理 """ method_decorators = { 'get': [mis_permission_required('group-get')], 'put': [mis_permission_required('group-put')], 'delete': [mis_permission_required('group-delete')], } def get(self, target): """ 获取id=target组的信息 """ group = MisAdministratorGroup.query.filter_by(id=target).first() if not group: return {'message': 'Invalid group id.'}, 400 ret = marshal(group, GroupListResource.group_fields) permission_tree = get_permission_tree(group.id) ret['permission_tree'] = permission_tree return ret def put(self, target): """ 修改id=target管理员组信息 """ json_parser = RequestParser() json_parser.add_argument('name', location='json') json_parser.add_argument('remark', location='json') json_parser.add_argument('status', type=inputs.int_range(0, 1), location='json') json_parser.add_argument('permission_ids', action='append', type=inputs.positive, location='json') args = json_parser.parse_args() print(args) group = MisAdministratorGroup.query.filter_by(id=target).first() if not group: return {'message': 'Invalid group id.'}, 400 if args.name and args.name != group.name: if MisAdministratorGroup.query.filter_by(name=args.name).first(): return {'message': '{} already exists'.format(args.name)} group.name = args.name if args.status is not None: group.status = args.status if args.remark: group.remark = args.remark if args.permission_ids is not None: inc_red_group_permission(group.id, args.permission_ids) db.session.add(group) db.session.commit() return marshal(group, GroupListResource.group_fields), 201 def delete(self, target): """ 删除id=target管理员组信息 """ if MisAdministrator.query.filter_by(group_id=target).first(): return {'message': 'Delete administrator first.'}, 400 # 删除组权限信息 MisGroupPermission.query.filter_by(group_id=target).delete( synchronize_session=False) # 删除组信息 MisAdministratorGroup.query.filter_by(id=target).delete( synchronize_session=False) try: db.session.commit() except: db.session.rollback() raise return {'message': 'OK'}, 204
class GroupListResource(Resource): """ 管理员角色/组管理 """ method_decorators = { 'get': [mis_permission_required('group-list-get')], 'post': [mis_permission_required('group-list-post')], } group_fields = { 'group_id': fields.Integer(attribute='id'), 'name': fields.String(attribute='name'), 'remark': fields.String(attribute='remark'), 'status': fields.Integer(attribute='status'), } def get(self): """ 获取所有组 """ args_parser = RequestParser() args_parser.add_argument('page', type=inputs.positive, required=False, location='args') args_parser.add_argument('per_page', type=inputs.int_range(constants.PER_PAGE_MIN, constants.PER_PAGE_MAX, 'per_page'), required=False, location='args') args_parser.add_argument('keyword', location='args') args_parser.add_argument('order_by', location='args') args = args_parser.parse_args() page = constants.DEFAULT_PAGE if args.page is None else args.page per_page = constants.DEFAULT_PER_PAGE if args.per_page is None else args.per_page groups = MisAdministratorGroup.query if args.keyword: groups = groups.filter( MisAdministratorGroup.name.like('%' + args.keyword + '%')) if args.order_by is not None: if args.order_by == 'id': groups = groups.order_by(MisAdministratorGroup.id.asc()) else: groups = groups.order_by(MisAdministratorGroup.utime.desc()) total_count = groups.count() groups = groups.offset(per_page * (page - 1)).limit(per_page).all() ret = marshal(groups, GroupListResource.group_fields, envelope='groups') ret['total_count'] = total_count return ret def post(self): """ 添加组 """ json_parser = RequestParser() json_parser.add_argument('name', required=True, location='json') json_parser.add_argument('remark', location='json') json_parser.add_argument('status', type=inputs.int_range(0, 1), location='json') json_parser.add_argument('permission_ids', action='append', type=inputs.positive, location='json') args = json_parser.parse_args() if MisAdministratorGroup.query.filter_by(name=args.name).first(): return { 'message': 'Group {} already exists.'.format(args.name) }, 403 group = MisAdministratorGroup( name=args.name, remark=args.remark if args.remark else '', status=args.status if args.status is not None else 1) try: db.session.add(group) db.session.commit() print(args.permission_ids) if args.permission_ids: for index, permission_id in enumerate(args.permission_ids): db.session.add( MisGroupPermission(group_id=group.id, permission_id=permission_id)) print(group.id, permission_id) if (index + 1) % 1000 == 0: db.session.commit() db.session.commit() except Exception: db.session.rollback() raise return {'group_id': group.id, 'name': args.name}
class StatisticsBasicResource(Resource): """ 基本统计 """ method_decorators = { 'get': [mis_permission_required('statistics-basic-get')], } def _get_size_hour(self, statistics_type, args): # 获取粒度为小时的数据 ret = [] begin = args.begin while begin <= args.end + timedelta(hours=23): query = db.session.query(func.sum(StatisticsBasic.count)) \ .filter(StatisticsBasic.type == statistics_type, and_(StatisticsBasic.date_time >= begin, StatisticsBasic.date_time < begin + timedelta(hours=1)) ) hour_count = int(query.scalar() or 0) ret.append( dict(date_time=begin.strftime('%Y-%m-%d %H:%M:%S'), count=hour_count)) begin += timedelta(hours=1) return ret def _get_size_day(self, statistics_type, args): # 获取粒度为天的数据 ret = [] begin = args.begin while begin <= args.end: query = db.session.query(func.sum(StatisticsBasic.count)) \ .filter(StatisticsBasic.type == statistics_type, and_(StatisticsBasic.date_time >= begin, StatisticsBasic.date_time < begin + timedelta(days=1)) ) day_count = int(query.scalar() or 0) ret.append(dict(date=begin.strftime('%Y-%m-%d'), count=day_count)) begin += timedelta(days=1) return ret def _get_size_month(self, statistics_type): # 获取粒度为月的全年数据 ret = [] now = datetime.now() month = 1 while True: if month > 12: break begin = datetime(now.year, month=month, day=1) if month == 12: end = datetime(now.year + 1, month=1, day=1) else: end = datetime(now.year, month=month + 1, day=1) query = db.session.query(func.sum(StatisticsBasic.count)) \ .filter(StatisticsBasic.type == statistics_type, and_(StatisticsBasic.date_time >= begin, StatisticsBasic.date_time < end) ) month_count = int(query.scalar() or 0) ret.append(dict(month=month, count=month_count)) month += 1 return ret def get(self): args_parser = RequestParser() args_parser.add_argument('type', type=parser.statistics_type, required=True, location='args') args_parser.add_argument('size', type=parser.statistics_size, location='args') args_parser.add_argument('begin', type=parser.date, location='args') args_parser.add_argument('end', type=parser.date, location='args') args = args_parser.parse_args() statistics_type = StatisticsType.type_map[args.type] if args.size is not None: # 不同粒度的图表数据 if args.size == 'month': return self._get_size_month(statistics_type) elif args.size == 'day': if not args.begin or not args.end or args.begin > args.end: return {'message': '(begin or end) parameter error.'}, 403 return self._get_size_day(statistics_type, args) elif args.size == 'hour': if not args.begin or not args.end or args.begin > args.end: return {'message': '(begin or end) parameter error.'}, 403 return self._get_size_hour(statistics_type, args) else: # 获取基本统计数据 # 1.当天总数 2.周涨/降幅百分比 3.日涨/降幅百分比 4.日平均数 5.总天数 6.总数量 now = datetime.now() today = datetime(year=now.year, month=now.month, day=now.day) # 总数 query = db.session.query(func.sum(StatisticsBasic.count)) \ .filter(StatisticsBasic.type == statistics_type) total_count = int(query.scalar() or 0) # 总天数 first_day = StatisticsBasic.query.order_by( StatisticsBasic.date_time.asc()).first() total_days = 0 if first_day is not None: total_days = (now - first_day.date_time).days if (now - first_day.date_time).seconds > total_days * 3600 * 24: total_days += 1 # 当天总数 query = db.session.query(func.sum(StatisticsBasic.count))\ .filter(StatisticsBasic.type == statistics_type, StatisticsBasic.date_time.between(today, now) ) day_count = int(query.scalar() or 0) # 昨天总数 yesterday = today - timedelta(days=1) query = db.session.query(func.sum(StatisticsBasic.count))\ .filter(StatisticsBasic.type==statistics_type, and_(StatisticsBasic.date_time >= yesterday, StatisticsBasic.date_time < today) ) yesterday_count = int(query.scalar() or 0) # 上周数量 lask_week_begin = today - timedelta(days=13) lask_week_end = today - timedelta(days=6) query = db.session.query(func.sum(StatisticsBasic.count))\ .filter(StatisticsBasic.type==statistics_type, and_(StatisticsBasic.date_time >= lask_week_begin, StatisticsBasic.date_time < lask_week_end) ) lask_week_count = int(query.scalar() or 0) # 本周数量 week_begin = lask_week_end query = db.session.query(func.sum(StatisticsBasic.count)) \ .filter(StatisticsBasic.type == statistics_type, and_(StatisticsBasic.date_time >= week_begin, StatisticsBasic.date_time < now) ) week_count = int(query.scalar() or 0) # 周同比 week_percent = round( (week_count - lask_week_count) / (lask_week_count if lask_week_count else 1) * 100, 2) # 日环比 day_percent = round( (day_count - yesterday_count) / (yesterday_count if yesterday_count else 1) * 100, 2) # 日均数 day_average = round( total_count / (total_days if total_days else 1), 2) ret = dict(day_count=day_count, week_percent=week_percent, day_percent=day_percent, day_average=day_average, total_days=total_days, total_count=total_count) return ret
class ChannelListResource(Resource): """ 频道列表 """ channel_fields = { 'channel_id': fields.String(attribute='id'), # 频道ID 'name': fields.String(attribute='name'), # 频道名称 'is_visible': fields.Integer(attribute='is_visible'), # 状态: 是否可见 } method_decorators = { 'get': [mis_permission_required('channel-list-get')], 'post': [mis_permission_required('channel-list-post')], } def get(self): """ 获取所有频道信息 """ args_parser = RequestParser() args_parser.add_argument('page', type=inputs.positive, required=False, location='args') args_parser.add_argument('per_page', type=inputs.int_range(constants.PER_PAGE_MIN, constants.PER_PAGE_MAX, 'per_page'), required=False, location='args') args_parser.add_argument('channel_id', location='args') args_parser.add_argument('keyword', location='args') args_parser.add_argument('is_visible', location='args') args_parser.add_argument('order_by', location='args') args = args_parser.parse_args() page = constants.DEFAULT_PAGE if args.page is None else args.page per_page = constants.DEFAULT_PER_PAGE if args.per_page is None else args.per_page channels = Channel.query if args.channel_id is not None: channels = channels.filter_by(channel_id=args.channel_id) if args.is_visible is not None: channels = channels.filter_by(is_visible=args.is_visible) if args.keyword is not None: channels = channels.filter(Channel.name.like('%' + args.keyword + '%')) if args.order_by is not None: if args.order_by == 'id': channels = channels.order_by(Channel.id.asc()) else: channels = channels.order_by(Channel.utime.desc()) total_count = channels.count() channels = channels.offset(per_page * (page - 1)).limit(per_page).all() ret = marshal(channels, ChannelListResource.channel_fields, envelope='channels') for channel in ret['channels']: channel['article_count'] = Article.query.filter_by(channel_id=channel['channel_id']).count() ret['total_count'] = total_count return ret def post(self): """ 新增频道 """ json_parser = RequestParser() json_parser.add_argument('name', required=True, location='json') json_parser.add_argument('is_visible', type=inputs.int_range(0, 1), required=True, location='json') json_parser.add_argument('sequence', type=inputs.positive, location='json') args = json_parser.parse_args() if Channel.query.filter_by(name=args.name).first(): return {'message': '{} already exists'.format(args.name)}, 403 channel = Channel(name=args.name, is_visible=args.is_visible, sequence=args.sequence if args.sequence is not None else 0, ) db.session.add(channel) db.session.commit() return marshal(channel, ChannelListResource.channel_fields), 201
class SensitiveWordListResource(Resource): """ 敏感词 """ word_fields = { 'id': fields.Integer(attribute='id'), # 敏感词id 'word': fields.String(attribute='word'), # 敏感词 'weights': fields.Integer(attribute='weights'), # 权重 'hold_count': fields.Integer(attribute='hold_count'), # 拦截次数 'create_time': fields.DateTime(attribute='ctime', dt_format='iso8601'), # 创建时间 'update_time': fields.DateTime(attribute='utime', dt_format='iso8601') # 更新时间 } method_decorators = { 'get': [mis_permission_required('sensitive-word-list-get')], 'post': [mis_permission_required('sensitive-word-list-post')], } def get(self): """ 获取敏感词列表 """ args_parser = RequestParser() args_parser.add_argument('page', type=inputs.positive, required=False, location='args') args_parser.add_argument('per_page', type=inputs.int_range(constants.PER_PAGE_MIN, constants.PER_PAGE_MAX, 'per_page'), required=False, location='args') args_parser.add_argument('word', location='args') args_parser.add_argument('order_by', location='args') args = args_parser.parse_args() page = constants.DEFAULT_PAGE if args.page is None else args.page per_page = constants.DEFAULT_PER_PAGE if args.per_page is None else args.per_page words = SensitiveWord.query if args.word is not None: words = words.filter(SensitiveWord.word.like('%' + args.word + '%')) if args.order_by is not None: if args.order_by == 'id': words = words.order_by(SensitiveWord.id.asc()) else: words = words.order_by(SensitiveWord.utime.desc()) total_count = words.count() words = words.offset(per_page * (page - 1)).limit(per_page).all() ret = marshal(words, SensitiveWordListResource.word_fields, envelope='words') ret['total_count'] = total_count return ret def post(self): """ 添加敏感词 """ args_parser = RequestParser() args_parser.add_argument('word', required=True, location='json') args_parser.add_argument('weights', type=inputs.natural, required=True, location='json') args = args_parser.parse_args() if SensitiveWord.query.filter_by(word=args.word).first(): return {'message': '{} already exists'.format(args.word)}, 403 word = SensitiveWord(word=args.word, weights=args.weights, hold_count=0) db.session.add(word) db.session.commit() return marshal(word, SensitiveWordListResource.word_fields), 201
class AdministratorResource(Resource): """ 管理员管理 """ method_decorators = { 'get': [mis_permission_required('administrator-get')], 'put': [mis_permission_required('administrator-put')], 'delete': [mis_permission_required('administrator-delete')], } def get(self, target): """ 获取管理员详情 """ administrator = MisAdministrator.query.filter_by(id=target).first() if not administrator: return {'message': 'Invalid administrator id.'}, 400 return marshal(administrator, AdministratorListResource.administrators_fields) def put(self, target): """ 修改id=target管理员信息 """ json_parser = RequestParser() json_parser.add_argument('account', type=parser.mis_account, location='json') json_parser.add_argument('password', type=parser.mis_password, location='json') json_parser.add_argument('name', location='json') json_parser.add_argument('group_id', type=parser.mis_group_id, location='json') json_parser.add_argument('status', type=inputs.int_range(0, 1), location='json') json_parser.add_argument('email', type=parser.email, location='json') json_parser.add_argument('mobile', type=parser.mobile, location='json') json_parser.add_argument('current_password', type=parser.mis_password, location='json') args = json_parser.parse_args() print(args) administrator = MisAdministrator.query.filter_by(id=target).first() if not administrator: return {'message': 'Invalid administrator id.'}, 403 if args.account and args.account != administrator.account: if MisAdministrator.query.filter_by(account=args.account).first(): return {'message': '{} already exists'.format(args.account)} administrator.account = args.account if args.password: if target == g.administrator_id \ and administrator.password != generate_password_hash(args.current_password): return {'message': 'Current password error.'}, 403 administrator.password = generate_password_hash(args.password) if args.name: administrator.name = args.name if args.group_id: administrator.group_id = args.group_id if args.status is not None: administrator.status = args.status if args.email: administrator.email = args.email if args.mobile: administrator.mobile = args.mobile try: db.session.add(administrator) db.session.commit() except: db.session.rollback() raise return marshal(administrator, AdministratorListResource.administrators_fields), 201 def delete(self, target): """ 删除id=target管理员信息 """ MisAdministrator.query.filter_by(id=target).delete(synchronize_session=False) db.session.commit() return {'message': 'OK'}, 204
class StatisticsSearchResource(Resource): """ 搜索统计 """ method_decorators = { 'get': [mis_permission_required('statistics-search-get')], } def get(self): args_parser = RequestParser() args_parser.add_argument('page', type=inputs.positive, required=False, location='args') args_parser.add_argument('per_page', type=inputs.int_range(constants.PER_PAGE_MIN, constants.PER_PAGE_MAX, 'per_page'), required=False, location='args') args = args_parser.parse_args() page = constants.DEFAULT_PAGE if args.page is None else args.page per_page = constants.DEFAULT_PER_PAGE if args.per_page is None else args.per_page ssts = StatisticsSearchTotal.query.order_by( StatisticsSearchTotal.count.desc()) total_count = ssts.count() ret = {'keywords': [], 'total_count': total_count} ssts = ssts.offset(per_page * (page - 1)).limit(per_page).all() for sst in ssts: lask_week_count, week_count, week_percent = self._get_week_percent( sst.keyword) ret['keywords'].append({ 'keyword': sst.keyword, 'user_count': sst.user_count, 'week_percent': week_percent, # 'lask_week_count': lask_week_count, # 'week_count': week_count }) return ret # def post(self): # now = datetime.now() # today = datetime(year=now.year, month=now.month, day=now.day) # begin = today - timedelta(days=14) # while begin <= now: # for word in ['java', 'python', 'php']: # import random # count = user_count = 1 # ss = StatisticsSearch.query.filter_by(year=begin.year, # month=begin.month, # day=begin.day, # hour=begin.hour, # keyword=word).first() # if not ss: # ss = StatisticsSearch(year=begin.year, # month=begin.month, # day=begin.day, # hour=begin.hour, # keyword=word, # count=0, # user_count=0, # date_time=datetime(year=begin.year,month=begin.month, # day=begin.day,hour=begin.hour)) # sst = StatisticsSearchTotal.query.filter_by(keyword=word).first() # if not sst: # sst = StatisticsSearchTotal(keyword=word, user_count=0, count=0) # ss.count += count # ss.user_count += user_count # sst.count += count # sst.user_count += user_count # db.session.add(ss) # db.session.add(sst) # db.session.commit() # begin += timedelta(hours=1) # return {} def _get_week_percent(self, keyword): """ 获取搜索关键字的周涨幅 :param keyword: 搜索关键字 :return: 周涨幅百分比 """ now = datetime.now() today = datetime(year=now.year, month=now.month, day=now.day) # 上周用户数量 lask_week_begin = today - timedelta(days=13) lask_week_end = today - timedelta(days=6) query = db.session.query(func.sum(StatisticsSearch.user_count)) \ .filter(StatisticsSearch.keyword == keyword, and_(StatisticsSearch.date_time >= lask_week_begin, StatisticsSearch.date_time < lask_week_end) ) lask_week_count = int(query.scalar() or 0) # 本周用户数量 week_begin = lask_week_end query = db.session.query(func.sum(StatisticsSearch.user_count)) \ .filter(StatisticsSearch.keyword == keyword, and_(StatisticsSearch.date_time >= week_begin, StatisticsSearch.date_time < now) ) week_count = int(query.scalar() or 0) # 周涨幅 week_percent = round((week_count - lask_week_count) / (lask_week_count if lask_week_count else 1) * 100, 2) return lask_week_count, week_count, week_percent
class AdministratorListResource(Resource): """ 管理员管理 """ administrators_fields = { 'administrator_id': fields.Integer(attribute='id'), 'account': fields.String(attribute='account'), 'email': fields.String(attribute='email'), 'mobile': fields.String(attribute='mobile'), 'name': fields.String(attribute='name'), 'group_name': fields.String(attribute='group.name'), 'group_id': fields.Integer(attribute='group_id'), 'access_count': fields.Integer(attribute='access_count'), 'status': fields.Integer(attribute='status'), 'last_login': fields.Integer(attribute='last_login') } method_decorators = { 'get': [mis_permission_required('administrator-list-get')], 'post': [mis_permission_required('administrator-list-post')], } def post(self): """ 添加管理员 """ json_parser = RequestParser() json_parser.add_argument('account', type=parser.mis_account, required=True, location='json') json_parser.add_argument('password', type=parser.mis_password, required=True, location='json') json_parser.add_argument('group_id', type=parser.mis_group_id, required=True, location='json') json_parser.add_argument('name', required=True, location='json') args = json_parser.parse_args() administrator = MisAdministrator.query.filter_by(account=args.account).first() if administrator: return {'message': '{} already exists'.format(args.account)}, 403 administrator = MisAdministrator(account=args.account, password=generate_password_hash(args.password), name=args.name, group_id=args.group_id) try: db.session.add(administrator) db.session.commit() except Exception: db.session.rollback() raise return {'account': args.account, 'name': args.name}, 201 def get(self): """ 管理员查询 """ args_parser = RequestParser() args_parser.add_argument('order_by', location='args') args_parser.add_argument('keyword', location='args') args_parser.add_argument('status', type=inputs.int_range(0, 1), location='args') args_parser.add_argument('page', type=inputs.positive, required=False, location='args') args_parser.add_argument('per_page', type=inputs.int_range(constants.PER_PAGE_MIN, constants.PER_PAGE_MAX, 'per_page'), required=False, location='args') args = args_parser.parse_args() page = constants.DEFAULT_PAGE if args.page is None else args.page per_page = constants.DEFAULT_PER_PAGE if args.per_page is None else args.per_page administrators = MisAdministrator.query if args.status is not None: administrators = administrators.filter_by(status=args.status) if args.keyword: administrators = administrators.filter(or_(MisAdministrator.account.like('%' + args.keyword + '%'), MisAdministrator.name.like('%' + args.keyword + '%'))) if args.order_by is not None: if args.order_by == 'id': administrators = administrators.order_by(MisAdministrator.id.asc()) else: administrators = administrators.order_by(MisAdministrator.utime.desc()) total_count = administrators.count() administrators = administrators.offset(per_page * (page - 1)).limit(per_page).all() ret = marshal(administrators, AdministratorListResource.administrators_fields, envelope='administrators') ret['total_count'] = total_count return ret def delete(self): """ 批量删除管理员 """ json_parser = RequestParser() json_parser.add_argument('administrator_ids', action='append', type=inputs.positive, required=True, location='json') args = json_parser.parse_args() MisAdministrator.query.filter(MisAdministrator.id.in_(args.administrator_ids)).delete(synchronize_session=False) db.session.commit() return {'message': 'OK'}, 204
class OperationLogListResource(Resource): """ 运营日记 """ logs_fields = { 'operation_log_id': fields.Integer(attribute='id'), 'administrator_id': fields.Integer(attribute='administrator_id'), 'administrator_name': fields.String(attribute='administrator.name'), 'ip': fields.String(attribute='ip'), 'operation': fields.String(attribute='operation'), 'description': fields.String(attribute='description'), 'ctime': fields.DateTime(attribute='ctime', dt_format='iso8601'), } method_decorators = {'get': [mis_permission_required('operationlog-get')]} def get(self): """ 获取运营日志列表 """ args_parser = RequestParser() args_parser.add_argument('page', type=inputs.positive, required=False, location='args') args_parser.add_argument('per_page', type=inputs.int_range(constants.PER_PAGE_MIN, constants.PER_PAGE_MAX, 'per_page'), required=False, location='args') args_parser.add_argument('keyword', location='args') args_parser.add_argument('order_by', location='args') args_parser.add_argument('begin', type=parser.date_time, location='args') args_parser.add_argument('end', type=parser.date_time, location='args') args = args_parser.parse_args() page = constants.DEFAULT_PAGE if args.page is None else args.page per_page = constants.DEFAULT_PER_PAGE if args.per_page is None else args.per_page logs = MisOperationLog.query if args.keyword: logs = logs.filter( MisOperationLog.operation.like('%' + args.keyword + '%'), MisOperationLog.description.like('%' + args.keyword + '%')) if args.begin and args.end and args.end > args.begin: logs = logs.filter( MisOperationLog.ctime.between(args.begin, args.end)) if args.order_by is not None: if args.order_by == 'id': logs = logs.order_by(MisOperationLog.id.desc()) else: logs = logs.order_by(MisOperationLog.ctime.desc()) total_count = logs.count() logs = logs.offset(per_page * (page - 1)).limit(per_page).all() ret = marshal(logs, OperationLogListResource.logs_fields, envelope='operationlogs') ret['total_count'] = total_count add_log('查询', '查询: 运营日志') return ret