战略论坛
社交的进化(上)|需求演变与社交网络发展
最近,语音社交软件 Clubhouse 借助埃隆·马斯克(Elon Musk)的分享火爆全球。一时间,大批的听众蜂拥而至,直接让 Clubhouse 成为全球最受关注的社交软件之一。从互联网诞生之起,社交产品就一直热情不减,越来越多的人加入社交产品这场持久战。本文不讨论 Clubhouse ,想尝试从发展的角度聊聊社交的进化。一、人类社会对社交需求的演变人类社会对社交需求的演变经历了从最基本的物质和生存需求,到精神层面的需求,再到信息需求的过程。远古时期的人类社会,由于生产力低下,人们的行为以解决最简单的生存问题为主,即争夺水源、获取食物以及寻找休憩之地。而在这个过程中,合作与协调是最简单且有效的行为方式。这其实就是社交的起源。在当时没有语言体系的互动与交互下,人类通过社交帮助自己更好的生存,此时人类对社交的需求也仅仅是满足最基本的物质和生存需求。随着进化,人类逐渐“获得”智能。当懂得如何制作和使用工具时,人类社会开始变得有一点点不一样了。生产力的提高,使得人类的物质生活开始变得富足,自我意识也开始慢慢觉醒。就像人类简史所描述的,人类开始走向那个充满想象力的世界。随着人们构建出越来越多的故事,有了更多的不同的具体的场景,并且每个人都被赋予了更加具体的社会关系,此时的社交需求也逐渐地从物质转移到了精神层面。这里的精神层面是指是否认可彼此所在的故事,是否相信彼此的故事,以及是否愿意进入彼此的故事。在互联网时代,随着沟通成本的降低,人们的连接与互动方式更加丰富,人们也越发的愿意在互联网平台分享自己的故事,寻找属于自己新的故事,在更高的程度上追寻自己内心的认同感以及满足感。所以我们可以知道,社交的演变是从人类之间的互动开始的,并且依托于彼此之间的社会关系,产生了基于物质的需求以及精神的需求。直到如今,随着移动网络时代的不断深化,人们产生了对社交的第三个需求:信息需求。二、信息时代下网络社交与娱乐的发展上文提到的人类对信息的需求是我们当下这个时代的产物。从互联网社交与娱乐发展的历史维度来看:2002 年之前的互联网,Web 1.0 以匿名的信息获取功能为主;2002 年以后,Web 2.0 革命爆发,互联网越来越强调互动,论坛、博客、内容分享等社交媒体日益发展壮大,与现实生活的关系日益紧密,视频、音频等多媒体形式的地位也变得重要;Web 3.0 阶段,人们开始以完全的虚拟生活为娱乐主体,旨在满足精神世界,而现实生活反而仅仅是娱乐的辅助形式。智能设备和无线宽带的普及,使得随时随地浸入虚拟生活变得很容易实现。人们既是内容的消费者又是内容的生产者,人人皆媒体。我们正处于Web 3.0 这个阶段。对于当下的用户来说,新媒体不仅仅是他们获得信息的渠道,也是他们社会关系的一种新的依存空间。在复制、拓展现实中的社会结构与社会关系的同时,新媒体自身作为一种新型社会的属性也日益明晰,而线上、线下社会之间的界限也越来越模糊,逐渐形成了某种意义上的虚拟社会。虚拟与现实的关系,就像一个已经配平的化学方程式。我们可以从虚拟走向现实,也可以从现实通往虚拟。这在某种意义上正如Pony马化腾所言,我们正在走向一个“全真互联网”的新时代。三、“全真互联网”——未来社交的期待去年年底,腾讯内部出了一本2020年度特刊《三观》—— “以一年为单位,记录腾讯的成长和主要变化”,Pony(马化腾)亲自为这本书写了前言,其重要性不言自明。在这篇名为《以正为本,迎难而上》的文章当中,马化腾向腾讯人提出了三个要求:第一是向内看,以一种用户的心态去本能地捕捉用户价值,不是用理性,而是用本能。第二是去一线,无论To B还是To C,每个人都要打破传统界限,尽可能去一线寻找解决问题的方法和思路。第三是往前看,抓住关键机会。最近几年,腾讯深耕“产业互联网”,阿里提出“新零售”,百度则在发力“人工智能”,试图实现弯道超车,其它小巨头也纷纷提出了各自的发展策略,比如小米的“物联网”,京东的“智慧零售”……那么站在整个互联网行业高度,在马化腾的眼中,未来的关键机会是什么?站在2020年末尾,马化腾提出了他对未来的预判——“现在,一个令人兴奋的机会正在到来,移动互联网十年发展,即将迎来下一波升级,我们称之为全真互联网。”什么是“全真互联网”?百度百科目前还没有“全真互联网”的词条,其它搜索结果也显示,在马化腾之外,目前还没有人使用过这个全新概念。综合其文章来看,“全真互联网”的重点是两个字:全和真。所谓全,传统互联网经济大多直接面向消费者,也即是To C,而如今的互联网则在纷纷转型To B,试图去推动企业端进行升级。比如电商界都在推动产业带升级,提出智能制造,反向定制,扶持国产品牌,而腾讯和字节跳动则发布了腾讯会议、飞书等企业级产品。接下来,我们将进入一个打通消费互联网和产业互联网的时代。所谓真,传统的互联网经济,我们都叫它虚拟经济、线上经济。比如我们在淘宝买衣服,只能看到美化了之后的卖家秀,买回家之后的买家秀很可能惨不忍睹。当电商直播崛起,买家与卖家已经能够跳过过度ps的图片,直接上真实视频展示。未来随着“AI试衣”技术成熟,消费者则能“足不出门试遍天下衣”。对“全真”还有一个更为全面的理解是“虚实结合”。我的理解,这正呼应张正友博士提出的“虚实集成世界”。那什么是虚实集成世界?本文下篇会进行详细的介绍。小结随着社会演变和网络发展。当下实时通信、音视频、算法推荐等技术已经走向成熟,5G手机正在普及,各大商场则正在以低廉的价格向消费者普及VR、AR体验。底层技术的升级,新技术与新硬件、新软件的融合,将带来线上与线下的一体化,带来实体与电子在不同场景的新运用。按照Pony(马化腾)的观点:当虚拟世界和真实世界的大门打开,从消费者互联网到产业互联网,将产生无数的应用场景,不仅社交,通信、游戏……当下的各个行业,或许都值得再做一遍。 “我相信又一场大洗牌即将开始。就像移动互联网转型一样,上不了船的人将逐渐落伍。”参考文献资料rct studio:游戏中的社交与慰藉:在不断内卷的时代,寻找另一个孤独的自己经济消费洞察:下一个风口?马化腾提出的“全真互联网”到底啥意思?
SMP 2018今日开幕,做中国计算社会科学的先锋会议
AI 科技评论按:由中国中文信息学会社会媒体处理专委会主办、哈尔滨工业大学承办的第七届全国社会媒体处理大会(SMP 2018)于 2018 年 8 月 2 日- 4 日在哈尔滨召开。雷锋网作为独家战略媒体带来专题报道。 SMP 专注于以社会媒体处理为主题的科学研究与工程开发,为传播社会媒体处理最新的学术研究与技术成果提供广泛的交流平台,旨在构建社会媒体处理领域的产学研生态圈,成为中国乃至世界社会媒体处理的风向标。8 月 3 日,SMP 2018 在哈尔滨友谊宫拉开帷幕,大会程序委员会主席、清华大学副教授刘知远担任开幕式主持。当天,SMP 2018 大会指导委员会主席,中国中文信息学会前任理事长、现任名誉理事长,哈尔滨工业大学的李生教授为大会做开场致辞。他表示,人和人通过社交媒体,在物理世界之外产生了虚拟世界的进一步联系。社会媒体处理作为互联网和计算机技术的结合的产物,更进一步地实现和扩大了人机的融合、人和人的相连。「没有人与人的交流,就没有物种的进步,人类的进步,社会的发展,」李生教授认为,社会媒体加深和扩大了人与人之间的沟通和交流。他希望本届 SMP 大会能给大家提供一个互相交流和沟通的一个场所,形成产学研合作的一个生态环境。与此同时,李生教授对从事科学研究和技术工作的青年学生寄予了三点治学的建议。首先要实事求是,需要讲真话、讲实话、讲心里话、讲负责任的话;其次要独立思考,不要跟风做研究、做事情;再者要提高能力,不断提升自己的学术水平和判断能力,才能看得远,看得透。SMP 2018 大会主席、哈尔滨工业大学人工智能研究院副院长刘挺教授《「社媒派」——中国计算社会科学的先锋》为主题,对 SMP 大会及专委会情况做了概要介绍。他指出,社会媒体(social media)是大数据的重要来源,是社会计算的重要数据基础、是计算技术与社会科学历史性交汇的主要推动力,是人工智能技术的重要处理对象,也是智能社会的脑神经。SMP 专委会从学科建设、人才培养、校企合作及国家智库等多个方面担负起相应的责任,包括推动社会计算交叉学科的发展,培养社会媒体处理领域的青年人才培养,有力促进我国计算技术与社会科学学术界、企业界的交流与协作,并为我国政府在社会计算方面的规划献计献策。「以兴趣为基础,以情谊为纽带,」刘挺教授表示,SMP 是一个有理想的跨学科学术组织,秉承「清新、轻松、务实、守时」的工作风格,倡导学术化、技术化、去行政化的工作氛围,在学术交流中坚持认真严谨的道路。以「社会媒体」为聚焦,SMP 大会的议程主要划分为四个象限,包括:以自然语言处理、信息检索为代表的中文信息处理;
以社会学、传播学为代表的计算社会科学;
以多媒体、网络分析、数据库、可视化、高性能为代表的 SMP 相关计算技术;
以舆情分析、社交应用为代表的 SMP 产业应用。随后,刘挺教授回顾了自 2012 年至今,首届 SMP 大会到今天第七届大会的发展历程。如今,社会媒体处理成立了包括情感分析、数据挖掘、表示学习在内的三大核心技术专业组,以及特色浓厚的计算社会科学系列专业组,除计算社会学、计算传播学、智能金融、智能教育等专业组外,还有新成立的智能司法及计算历史学等新专业组。相应地,这两个专业组也会组织相应的论坛,在今年的 SMP 大会上首次与学者们见面。此外,计算文化学、计算心理学及计算管理学等三大专业组正在紧锣密鼓地筹办中。除却一年一届的 SMP 大会外,专委会也致力于以青年学院、学术沙龙、常委扩大会等多种形式壮大 SMP 于学术领域的影响力。刘挺教授也指出,国家从政策高度构建开放协同的人工智能科技创新体系,从多项指导意见及方针中,我们可以看到国家对认知科学、心理学、经济学、社会学、法学等多个学科的高度重视,对智能金融、智能商务、智能教育及智慧法庭的关注。令人欣喜的是,在国家基金委新增学科目录中,我们也看到了社交网络与社会计算、情感分析、社会媒体处理与跨媒体分析等崭新学科的出现。社会计算学科从 2010 年开始,呈现一个逐年发展的态势,并即将迎来社会计算发展的新高潮。不论是从国家科技立项的支持,或是学者研究兴趣的变化,社媒派正在计算技术与社会科学交汇的历史潮流中引领风骚。最后,刘挺教授表达了对 SMP 2018 大会赞助商的衷心感谢,并祝福 SMP 2018 大会圆满召开,为计算技术与社会科学的交叉融合不断努力。接下来,SMP 2018 程序委员会主席、哈尔滨工业大学的秦兵教授围绕大会概况做了报告。第七届社会媒体处理大会有四个「第一次」值得一提:第一次来到东北、第一次夏季举办、第一次摘要征稿,与此同时也是论坛数目最多的一次会议。SMP 大会延续往年形式多样的风格,包括大会报告、论坛报告、口头报告、海报展示、评测报告等多项精彩议程。本次共邀请到六位国内外著名学者做特邀报告,并举行八大专题论坛,今年新增了智慧司法和计算历史学两个全新论坛,也邀请到了许多知名学者参与交流。在三大技术评测论坛中,今年举办的首届「文本溯源技术评测」也获得了众多关注,共有 73 支队伍、184 位参赛者参与竞赛。第二届的「中文人机对话技术评测」共吸引 80 支队伍、248 位参赛者加入;而已经举办至第三届的「用户画像技术评测」最为壮大,共有 117 支队伍、374 位参赛者参与。刘挺教授也在方才的介绍中提及,专委会正在评估及规划更多的技术评测内容,包括情感分析、股票预测、媒体传播及社交网络关系等方向。SMP 2018 首次只征集论文摘要,不再要求提交论文全文,这也是会议的一大创新之处。本届大会共收到 87 篇投稿,其中录用口头报告 20 篇,录用率约为 23%;此外录用海报报告 31 篇。在 87 篇投稿中,有 68 篇投稿属于计算学领域,录用 36 篇;有 21 篇投稿属于社会学领域,录用 15 篇。今年共有 460 人注册了 SMP 2018,秦兵教授也对本届大会寄予了满满期待,并预祝今年大会顺利召开。雷锋网也将持续关注 SMP 2018 大会的进展,并带来后续报道。本文图片来源:哈工大 SCIR 李家琦,特此感谢。
用JAVA的DEA算法衡量社交媒体页面的流行度
Measuring the Social Media Popularity of Pages with DEA in JAVA原文作者:Vasilis Vryniotis原文地址:http://blog.datumbox.com/measuring-the-social-media-popularity-of-pages-with-dea-in-java/译者微博:@从流域到海域译者博客:blog.csdn.net/solo95用JAVA的DEA算法衡量社交媒体页面的流行度在前面的文章中,我们讨论了数据包络分析(Data Envelopment Analysis)技术,我们已经看到它如何被用作一个有效的非参数排序算法。在这篇博文中,我们将开发出一个JAVA数据包络分析的实例,我们将用它来评估网络上的网页和文章的社交媒体流行度。该代码是开源的(在GPL v3 license下),您可以从Github免费下载。更新:Datumbox机器学习框架现在是开源的,可以免费下载。查看包com.datumbox.framework.algorithms.dea以查看Java中Data Envelopment Analysis的实现。数据包络分析在JAVA中的实现代码是用JAVA编写的,可以直接从Github下载。它是根据GPLv3许可的,所以可以随意使用它,修改它,或者再分发。该代码实现了数据包络分析(Data Envelopment Analysis)算法,使用lp_solve库来解决线性规划问题,并使用Web搜索引擎优化分析(Web SEO Analytics )索引提取的数据,以构建基于Facebook,Google Plus和推特上分享的一个混合的社交媒体页面流行度矩阵。在前面的文章中介绍了算法的所有理论部分,在源代码中可以找到关于其实现的详细的javadoc注释。(原博文之后数据包络分析(Data Envelopment Analysis)算法及其实现全部简称了DEA,请读者注意,译者注。)下面我们提供一个关于其架构实现的高级别描述:1. lp_solve 5.5 library为了解决各种线性规划问题,我们使用一个名为lp\_solve的开源库。某些特定的lib是用ANSI C编写的,并使用JAVA包装来调用库方法。因此,在运行代码之前,您必须在您的系统上安装lp_solve。该库的二进制文件在[Linux和Windows都可以使用,您可以在lp_solve文档中阅读更多有关安装的信息。在尝试运行JAVA代码之前,请确保您的系统上安装了(相关的)特定库。有关安装和配置库的任何问题,请参阅lp_solve文档。2.DataEnvelopmentAnalysis Class这是DEA算法的主要实现类。它实现了一个名为estimateEfficiency()的公共方法,它获取记录的Map并返回它们的DEA得分。3. DeaRecord ObjectDeaRecord是一个特殊的对象,用于存储我们记录的数据。由于DEA需要分离输入和输出,因此DeaRecord对象将以DEA可以处理的方式分别存储我们的数据。4. SocialMediaPopularity ClassSocialMediaPopularity是一个应用程序,它使用DEA来评估社交媒体网络上Facebook的like,Google的 +1和twitter的Tweets的网页流行度。它实现了两个受保护的方法:calculatePopularity()和estimatePercentiles()以及两个公共方法loadFile()和getPopularity()。calculatePopularity()使用DEA实现根据社交媒体计数来估计页面的得分数。estimatedPercentiles()方法获取DEA分数并将其转换为百分位数。总的来说,百分比比DEA分数更容易解释; 因此当我们说一个网页的流行分数是70%时,这意味着该网页比70%的其他网页更受欢迎。为了能够估计一个特定页面的流行度,我们必须有一个包含其他页面的社交媒体数据的数据集。这是有原因的,因为需要预测哪个网页是受欢迎的,哪些不是,您必须能够将其与网络上的其他页面进行比较。为此,我们使用来自以txt格式提供的Web SEO分析索引的小型的匿名样本。您可以通过从网页上的更多页面提取社交媒体计数来构建自己的数据库。(社交媒体计数,比如点赞数、转发数、评论数)loadFile()方法用于加载DEA的上述统计信息,getPopularity()方法是一种易于使用的方法,可以获取Facebook的like,Google的+1和一个页面的Tweets数量,并以此评估其在社交媒体上的流行度。如何使用数据包络分析的JAVA实现在DataEnvelopmentAnalysisExample类中,我提供了2个不同的关于如何使用代码的例子。第一个例子直接使用DEA方法来根据它们的输出(ISSUES,RECEIPTS,REQS)和输入(STOCK,WAGES)来评估组织单位的效率。这个例子来自DEAzone.com的一篇文章。代码语言:txt复制Map<String, DeaRecord> records = new LinkedHashMap<>();
records.put("Depot1", new DeaRecord(new double[]{40.0,55.0,30.0}, new double[]{3.0,5.0}));
//...adding more records here...
DataEnvelopmentAnalysis dea = new DataEnvelopmentAnalysis();
Map<String, Double> results = dea.estimateEfficiency(records);
System.out.println((new TreeMap<>(results)).toString());第二个示例使用我们的社交媒体流行度应用程序,通过使用来自社交媒体的数据来评估页面的流行度,例如Facebook的like,Google的+1和Tweets。所有的社交媒体计数都被标记为输出,我们传递给DEA一个空的输入向量。代码语言:txt复制SocialMediaPopularity rank = new SocialMediaPopularity();
rank.loadFile(DataEnvelopmentAnalysisExample.class.getResource("/datasets/socialcounts.txt"));
Double popularity = rank.getPopularity(135, 337, 9079); //Facebook likes, Google +1s, Tweets
System.out.println("Page Social Media Popularity: "+popularity.toString());必要的扩展(上面)所提供的代码只是DEA如何被用作排名算法的一个例子。为了改进其实现,需要进行下面的扩展:1.加速(算法的)实现特定的DEA算法实现会评估数据库中所有记录的DEA得分。由于我们需要解决如同数据库中记录数量那样多的线性规划问题,这使得实现变得缓慢。如果我们不需要计算所有记录的分数,那么我们可以显著地加快执行速度。因此,该算法的小扩展可以使我们更好地控制哪些记录应该被解决掉,哪些只能被用作约束。2.扩大社交媒体统计数据库(这篇文章所)提供的社交媒体统计数据库由来自Web SEO Analytics索引的1111个样本组成。为了能够估计更准确的流行(度)分数,需要更大的样本。您可以通过统计来自网络上更多页面的社交媒体计数来创建自己的数据库。3.添加更多的社交媒体网络该实现使用Facebook的喜欢,Google的+1和推文的数量来评估文章的受欢迎程度。不过,来自其他社交媒体网络的指标可以很容易地被考虑在内。您只需要从您感兴趣的网络中构建一个社交媒体数据库,然后扩展SocialMediaPopularity类来处理它们。关于实施的最终意见为了能够扩展(算法的)实现,您必须对Data Envelopment Analysis的工作原理有一个很好的理解。这在前面的文章中已经介绍过了,所以在继续进行任何更改之前,请确保您阅读了之前的教程。此外,为了使用JAVA代码,您必须在您的系统中安装lp\_solve库(参见上文)。如果你在一个有趣的项目中使用这个实现,那么就给我们一条线索,我们将在我们的博客上展示你的项目。另外,如果你喜欢这篇文章,请花点时间在Twitter或Facebook分享。
潜伏6年,终露出水面:一探俄罗斯庞大的虚假信息活动
故事的主角是一个代号为Secondary Infektion的组织。最早可以追溯到2014年,而到了今年仍然在悄无声息地运作。它伪造虚假新闻、伪造文件,比如在论坛上假装“泄露”机密文件、“爆料”地缘政治丑闻(官员腐败或灰色交易等),大多数情况下,属于“打一枪就跑”,创建新账户发布虚假信息后会就弃号,活动更是遍布欧洲和北美。通常情况下,Secondary Infektion瞄准的是外交领域,通过虚假信息活动引发国家间的矛盾。然而,这个神秘又庞大的组织背后隐藏着谁?鲜为人知。2019年5月,Facebook安全团队首次发现了Secondary Infektion运营者的一小部分账户,并且将该组织归因于俄罗斯。此后,按照摸索到的线索,从2019年11月到2020年5月,社交媒体研究组织Graphika发现了Secondary Infektion组织发布的2500多条内容,时间跨度长达6年,覆盖7种语言,超过300个平台和网络论坛,从Facebook、Twitter、YouTube和Reddit等社交媒体巨头到巴基斯坦和澳大利亚的利基讨论论坛。前所未有的,一场庞大的虚假信息运动正在悄无声息地进行。美国参议员马可·卢比奥(Marco Rubio)的推文截图,其指责英国干涉2018年11月的美国中期选举。而Infektion将屏幕截图用作指控英国干预美国大选的文章的证据。虚假博客文章,抄袭“提及叙利亚冲突的原创博客”,然后改了一些内容,以此呼吁对涉嫌干预瑞典大选的“Mueller Commission”进行调查。根据分析,该组织所传播的的大部分内容基本是以下9个主题:乌克兰是失败的国家或不可靠的伙伴
美国和北约在其他国家具有侵略性和干涉性
欧洲疲软而分裂
俄罗斯政府的反对派在道义上腐败,存在酗酒或精神不稳定
穆斯林是侵略者
俄罗斯政府是西方阴谋的受害者
西方选举被操纵,但不该指责俄罗斯
土耳其是一个侵略和破坏稳定的国家
世界体育团体和竞赛不公平,不专业和反俄情绪此外,该组织还会根据攻击目标而更换内容语言。7种语言相比社交媒体,Secondary Infektion更偏爱博客,这可能也是其活动长达6年 仍然鲜为人知的一大原因。内容发布平台确实,尽管Secondary Infektion被归因于俄罗斯,但其幕后人员善于隐藏痕迹,截至2020年5月,该组织仍然没有被归因到特定参与者或实体。而因为活动的广泛,该组织的下一次活动时间和地点也难以预测。最后,揭露这一切的是社交媒体研究组织Graphika,该组织发布了一份长达120页的报告。此前,也曾经受到美国参议院的委托进行相关调查研究,在美伊相关报道、美俄就2016年美国大选问题等事件中,也看见过Graphika的影子。*本文作者:kirazhou,转载请注明来自FreeBuf.COM
基于 Go 语言开发在线论坛(三):访问论坛首页
1、整体流程
前面两篇教程学院君分别给大家介绍了基于 Go 语言构建在线论坛的整体设计以及数据表的创建、模型类的编写,今天我们来看看如何在服务端处理用户请求。用户请求的处理流程如下:客户端发送请求;服务端路由器(multiplexer)将请求分发给指定处理器(handler);处理器处理请求,完成对应的业务逻辑;处理器调用模板引擎生成 HTML 并将响应返回给客户端。接下来我们按照这个流程来编写服务端代码。2、定义路由器这里我们基于 gorilla/mux 来实现路由器,所以需要安装对应依赖:代码语言:javascript复制go get github.com/gorilla/mux然后我们遵循仿照 Laravel 框架对 Go 路由处理器代码进行拆分这篇教程介绍的组织架构将路由器定义在 routes 目录下的 router.go 中:
代码语言:javascript复制package routes
import "github.com/gorilla/mux"
// 返回一个 mux.Router 类型指针,从而可以当作处理器使用
func NewRouter() *mux.Router {
// 创建 mux.Router 路由器示例
router := mux.NewRouter().StrictSlash(true)
// 遍历 web.go 中定义的所有 webRoutes
for _, route := range webRoutes {
// 将每个 web 路由应用到路由器
router.Methods(route.Method).
Path(route.Pattern).
Name(route.Name).
Handler(route.HandlerFunc)
}
return route
}将所有路由定义在同一目录的 routes.go 中:代码语言:javascript复制package routes
import "net/http"
// 定义一个 WebRoute 结构体用于存放单个路由
type WebRoute struct {
Name string
Method string
Pattern string
HandlerFunc http.HandlerFunc
}
// 声明 WebRoutes 切片存放所有 Web 路由
type WebRoutes []WebRoute
// 定义所有 Web 路由
var webRoutes = WebRoutes{
}3、启动 HTTP 服务器最后在项目根目录下的 main.go 中引入上述路由器来启动 HTTP 服务器:代码语言:javascript复制package main
import (
. "github.com/xueyuanjun/chitchat/routes"
"log"
"net/http"
)
func main() {
startWebServer("8080")
}
// 通过指定端口启动 Web 服务器
func startWebServer(port string) {
r := NewRouter()
http.Handle("/", r) // 通过 router.go 中定义的路由器来分发请求
log.Println("Starting HTTP service at " + port)
err := http.ListenAndServe(":" + port, nil) // 启动协程监听请求
if err != nil {
log.Println("An error occured starting HTTP listener at port " + port)
log.Println("Error: " + err.Error())
}
}具体代码含义已经在注释中介绍清楚了,这里我们指定 HTTP 服务器监听 8080 端口,使用的路由器正是上述 router.go 中 NewRouter 方法返回的 mux.Router 指针类型实例,这里可以看到引用的时候并没有带上包名前缀,之所以可以这么做是因为通过如下这种方式引入的 routes 包:代码语言:javascript复制. "github.com/xueyuanjun/chitchat/routes"注意到前面的 . 别名,通过这种方式引入的包可以直接调用包中对外可见的变量、方法和结构体,而不需要加上包名前缀。还有一种方式是通过 _ 别名引入,这样一来只会调用该包里定义的 init 方法,我们在上篇教程引入 go-sql-driver/mysql 包时就是这么做的:代码语言:javascript复制_ "github.com/go-sql-driver/mysql"4、处理静态资源
在线论坛涉及到前端静态资源文件的处理,我们可以在 startWebServer 方法中新增如下这两行代码:代码语言:javascript复制r := NewRouter() // 通过 router.go 中定义的路由器来分发请求
// 处理静态资源文件
assets := http.FileServer(http.Dir("public"))
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", assets))
http.Handle("/", r) // 应用路由器到 HTTP 服务器
...其中 http.FileServer 用于初始化文件服务器和目录为当前目录下的 public 目录。
然后在第二段代码中指定静态资源路由及处理逻辑:将 /static/ 前缀的 URL 请求去除 static 前缀,然后在文件服务器查找指定文件路径是否存在(public 目录下的相对地址)。比如 URL 请求路径为 http://localhost:8080/static/css/bootstrap.min.css,对应的查找路径是:代码语言:javascript复制<application root>/public/css/bootstrap.min.css对于静态资源文件直接返回文件内容,不会进行额外处理。5、编写处理器实现1)首页处理器方法做好上述准备工作后,接下来,我们来创建论坛首页的路由处理器,在 handlers 目录下新增一个 index.go 来定义首页的处理器方法:代码语言:javascript复制package handlers
import (
"github.com/xueyuanjun/chitchat/models"
"html/template"
"net/http"
)
// 论坛首页路由处理器方法
func Index(w http.ResponseWriter, r *http.Request) {
files := []string{"views/layout.html", "views/navbar.html", "views/index.html",}
templates := template.Must(template.ParseFiles(files...))
threads, err := models.Threads();
if err == nil {
templates.ExecuteTemplate(w, "layout", threads)
}
}2)创建视图模板这里我们使用 Go 自带的 html/template 作为模板引擎,需要传入位于 views 目录下的视图模板文件,这里传入了多个模板文件,包括主布局文件 layout.html:代码语言:javascript复制{{ define "layout" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=9">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ChitChat</title>
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
<link href="/static/css/font-awesome.min.css" rel="stylesheet">
</head>
<body>
{{ template "navbar" . }}
<div class="container">
{{ template "content" . }}
</div> <!-- /container -->
<script src="/static/js/jquery-2.1.1.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
</body>
</html>
{{ end }}顶部导航模板 navbar.html:代码语言:javascript复制{{ define "navbar" }}
<div class="navbar navbar-default navbar-static-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">
<i class="fa fa-comments-o"></i>
ChitChat
</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="/">Home</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="/login">Login</a></li>
</ul>
</div>
</div>
</div>
{{ end }}以及首页视图模板 index.html:代码语言:javascript复制{{ define "content" }}
<p class="lead">
<a href="/thread/new">Start a thread</a> or join one below!
</p>
{{ range . }}
<div class="panel panel-default">
<div class="panel-heading">
<span class="lead"> <i class="fa fa-comment-o"></i> {{ .Topic }}</span>
</div>
<div class="panel-body">
Started by {{ .User.Name }} - {{ .CreatedAtDate }} - {{ .NumReplies }} posts.
<div class="pull-right">
<a href="/thread/read?id={{.Uuid }}">Read more</a>
</div>
</div>
</div>
{{ end }}
{{ end }}引入多个视图模板是为了提高模板代码的复用性,因为对于同一个应用的不同页面来说,可能基本布局、页面顶部导航和页面底部组件都是一样的,关于视图模板的细节,我们在后面视图模板部分会详细介绍,这里简单了解下即可。
3)渲染视图模板我们可以从数据库查询群组数据并将该数据传递到模板文件,最后将模板视图渲染出来,对应代码如下:代码语言:javascript复制threads, err := models.Threads();
if err == nil {
templates.ExecuteTemplate(w, "layout", threads)
}编译多个视图模板时,默认以第一个模板名作为最终视图模板名,所以这里第二个参数传入的是 layout,第三个参数传入要渲染的数据 threads,对应的渲染逻辑位于 views/index.html 中:代码语言:javascript复制{{ range . }}
<div class="panel panel-default">
<div class="panel-heading">
<span class="lead"> <i class="fa fa-comment-o"></i> {{ .Topic }}</span>
</div>
<div class="panel-body">
Started by {{ .User.Name }} - {{ .CreatedAtDate }} - {{ .NumReplies }} posts.
<div class="pull-right">
<a href="/thread/read?id={{.Uuid }}">Read more</a>
</div>
</div>
</div>
{{ end }}其中 {{ range . }} 表示将处理器方法传入的变量,这里是 threads 进行循环。4)注册首页路由最好,我们在 routes/routes.go 中注册首页路由及对应的处理器方法 Index:代码语言:javascript复制import "github.com/xueyuanjun/chitchat/handlers"
// 定义所有 Web 路由
var webRoutes = WebRoutes{
{
"home",
"GET",
"/",
handlers.Index,
},
}6、访问论坛首页访问论坛首页之前,我们将相应的前端资源文件拷贝到 public 目录下,此时项目整体目录结构如下:注:对应的前端资源可以从项目的 Github 仓库获取:https://github.com/nonfu/chitchat.git。然后我们在项目根目录下运行如下代码启动 HTTP 服务器:代码语言:javascript复制go run main.go然后我们在浏览器访问论坛首页 http://localhost:8080:一切与预期一致,下篇教程,我们将基于 Cookie + Session 实现用户认证并创建群组和主题。
Facebook发布虚拟社交平台Spaces,开启全新社交时代
Facebook在一年一度的开发者大会上发布了全新的虚拟社交平台Spaces,将拉近现实和虚拟的距离,实现无缝社交。时下的网络交友方式已经花样百出,人们再也不用担心孤身一人无人问津,只要愿意,随时可以结识天下好友。大名鼎鼎的Facebook如今也不满于单调的网络社交,他们发布了全新的虚拟社交平台Spaces,将结合VR技术拉近社交距离。戴上你的VR眼镜,进入Spaces中,可以利用简单步骤创造出代表自己的卡通人物,还可以自定义发型、脸型、五官、肤色等。但这些卡通人物的美化做的有些抽象,因此颜控玩家可能要对此失望了。创建好人物后,你可以让虚拟人物在Spaces进行社交行为,邀请其他脸书好友或者接受邀请来进入到虚拟世界中。乍一看这种模式有点像EA的模拟人生系列游戏。据悉,用户能够在Spaces中和朋友面对面聊天,用3D虚拟画笔涂鸦、观看影片,以及把玩物品。通过Messenger通讯功能,能够和网络对面的真实世界的朋友进行视频通话,甚至连AR滤镜也能够同步使用,增加了互动的趣味性。遗憾的是,该应用目前为测试版,需配合facebook旗下Oculus的Rift VR眼罩及Touch控制器来进行体验。如果你没有这个设备也没关系,可以受朋友邀请进去体验。Facebook Spaces目前只针对Oculus Rift推出,未来将会扩展到其他VR平台上,应用可以预见也会越来越丰富。
BBS论坛(十三)
13.1点击更换图形验证码(1)front/signup.html代码语言:javascript复制 <div class="form-group">
<div class="input-group">
<input type="text" class="form-control" name="graph_captcha" placeholder="图形验证码">
<span class="input-group-addon captcha-addon">
<img id="captcha-img" class="captcha-img" src="{{ url_for('front.graph_captcha') }}" alt="">
</span>
</div>
</div>(2)static/front/css/signup.css代码语言:javascript复制.sign-box {
width: 300px;
margin: 0 auto;
padding-top: 50px;
}
.captcha-addon {
padding: 0;
overflow: hidden;
}
.captcha-img {
width: 94px;
height: 32px;
cursor: pointer;
}代码语言:javascript复制body {
background: #f3f3f3;
}
.outer-box {
width: 854px;
background: #fff;
margin: 0 auto;
overflow: hidden;
}
.logo-box {
text-align: center;
padding-top: 40px;
}
.logo-box img {
width: 60px;
height: 60px;
}
.page-title {
text-align: center;
}
.sign-box {
width: 300px;
margin: 0 auto;
padding-top: 50px;
}
.captcha-addon {
padding: 0;
overflow: hidden;
}
.captcha-img {
width: 94px;
height: 32px;
cursor: pointer;
}(3)static/common/zlparam.js代码语言:javascript复制var zlparam = {
setParam: function (href,key,value) {
// 重新加载整个页面
var isReplaced = false;
var urlArray = href.split('?');
if(urlArray.length > 1){
var queryArray = urlArray[1].split('');
for(var i=0; i < queryArray.length; i++){
var paramsArray = queryArray[i].split('=');
if(paramsArray[0] == key){
paramsArray[1] = value;
queryArray[i] = paramsArray.join('=');
isReplaced = true;
break;
}
}
if(!isReplaced){
var params = {};
params[key] = value;
if(urlArray.length > 1){
href = href + '' + $.param(params);
}else{
href = href + '?' + $.param(params);
}
}else{
var params = queryArray.join('');
urlArray[1] = params;
href = urlArray.join('?');
}
}else{
var param = {};
param[key] = value;
if(urlArray.length > 1){
href = href + '' + $.param(param);
}else{
href = href + '?' + $.param(param);
}
}
return href;
}
};(4)static/front/js/signup.js代码语言:javascript复制$(function () {
$('#captcha-img').click(function (event) {
var self= $(this);
var src = self.attr('src');
var newsrc = zlparam.setParam(src,'xx',Math.random());
self.attr('src',newsrc);
});
});(5)front/signup.html中引用js和css代码语言:javascript复制 <script src="{{ static('common/zlparam.js') }}"></script>
<script src="{{ static('front/js/front_signup.js') }}"></script>
<link rel="stylesheet" href="{{ static('front/css/front_signup.css') }}">现在点击验证码,就可以更换验证码了。13.2.短信验证码(1)utils/alidayu.py代码语言:javascript复制# 仙剑论坛-阿里大于短信验证码sdk
import hashlib
from time import time
import logging
import requests
class AlidayuAPI(object):
APP_KEY_FIELD = 'ALIDAYU_APP_KEY'
APP_SECRET_FIELD = 'ALIDAYU_APP_SECRET'
SMS_SIGN_NAME_FIELD = 'ALIDAYU_SIGN_NAME'
SMS_TEMPLATE_CODE_FIELD = 'ALIDAYU_TEMPLATE_CODE'
def __init__(self, app=None):
self.url = 'https://eco.taobao.com/router/rest'
self.headers = {
'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8',
"Cache-Control": "no-cache",
"Connection": "Keep-Alive",
}
if app:
self.init_app(app)
def init_app(self,app):
config = app.config
try:
self.key = config[self.APP_KEY_FIELD]
self.secret = config[self.APP_SECRET_FIELD]
self.sign_name = config[self.SMS_SIGN_NAME_FIELD]
self.api_params = {
'sms_free_sign_name': config[self.SMS_SIGN_NAME_FIELD],
'sms_template_code': config[self.SMS_TEMPLATE_CODE_FIELD],
'extend': '',
'sms_type': "normal",
"method": "alibaba.aliqin.fc.sms.num.send",
"app_key": self.key,
"format": "json",
"v": "2.0",
"partner_id": "",
"sign_method": "md5",
}
except Exception as e:
logging.error(e.args)
raise ValueError('请填写正确的阿里大鱼配置!')
def send_sms(self,telephone,**params):
self.api_params['timestamp'] = str(int(time() * 1000))
self.api_params['sms_param'] = str(params)
self.api_params['rec_num'] = telephone
newparams = "".join(["%s%s" % (k, v) for k, v in sorted(self.api_params.items())])
newparams = self.secret + newparams + self.secret
sign = hashlib.md5(newparams.encode("utf-8")).hexdigest().upper()
self.api_params['sign'] = sign
resp = requests.post(self.url,params=self.api_params,headers=self.headers)
data = resp.json()
try:
result = data['alibaba_aliqin_fc_sms_num_send_response']['result']['success']
return result
except:
print('='*10)
print("阿里大于错误信息:",data)
print('='*10)
return False(2)exts.py代码语言:javascript复制alidayu = AlidayuAPI()(3)config.py代码语言:javascript复制ALIDAYU_APP_KEY = 'LTxxxxxxBBfT8Q'
ALIDAYU_APP_SECRET = 'SRxxxxxx8IL8LhJ'
ALIDAYU_SIGN_NAME = '仙剑论坛网站'
ALIDAYU_TEMPLATE_CODE = 'SMS_136xxx947'(4)perfect_bbs.py代码语言:javascript复制alidayu.init_app(app)(5)common/views.py代码语言:javascript复制# common/views.py
__author__ = 'derek'
from flask import Blueprint,request
from exts import alidayu
from utils import restful
from utils.captcha import Captcha
bp = Blueprint("common",__name__,url_prefix='/c')
@bp.route('/sms_captcha/')
def sms_captcha():
telephone = request.args.get('telephone')
if not telephone:
return restful.params_error(message='请输入手机号码')
#生成四位数的验证码
captcha = Captcha.gene_text(number=4)
if alidayu.send_sms(telephone,code=captcha):
return restful.success()
else:
# return restful.params_error(message='短信验证码发送失败!')
return restful.success()(6)signup.html代码语言:javascript复制 <script src="{{ static('common/zlajax.js') }}"></script>
<link rel="stylesheet" href="{{ static("common/sweetalert/sweetalert.css") }}">
<script src="{{ static("common/sweetalert/sweetalert.min.js") }}"></script>
<script src="{{ static("common/sweetalert/zlalert.js") }}"></script>
<script src="{{ static('common/zlparam.js') }}"></script>
<script src="{{ static('front/js/front_signup.js') }}"></script>
<link rel="stylesheet" href="{{ static('front/css/front_signup.css') }}">(7)front_signup.js代码语言:javascript复制$(function () {
$("#sms-captcha-btn").click(function (event) {
event.preventDefault();
var self = $(this);
//获取手机号码
var telephone = $("input[name='telephone']").val();
//使用js的正则判断手机号码,如果不合法,弹出提示框,直接return回去
if (!(/^1[3578]\d{9}$/.test(telephone))) {
zlalert.alertInfoToast('请输入正确的手机号');
return;
}
zlajax.get({
'url': '/c/sms_captcha?telephone='+telephone,
'success': function (data) {
if(data['code'] == 200){
zlalert.alertSuccessToast('短信验证码发送成功');
self.attr("disabled",'disabled');
var timeCount = 60;
var timer = setInterval(function () {
timeCount--;
self.text(timeCount);
if(timeCount <= 0){
self.removeAttr('disabled');
clearInterval(timer);
self.text('发送验证码');
}
},1000);
}else{
zlalert.alertInfoToast(data['message']);
}
}
});
});
});13.3.短信验证码加密(1)common/forms.py代码语言:javascript复制from apps.forms import BaseForm
from wtforms import StringField
from wtforms.validators import regexp,InputRequired
import hashlib
class SMSCaptchaForm(BaseForm):
salt='dfurtn5hdsesjc*^nd'
telephone=StringField(validators=[regexp(r'1[3578]\d{9}')])
timestamp=StringField(validators=[regexp(r'\d{13}')])
sign=StringField(validators=[InputRequired()])
def validate(self):
result=super(SMSCaptchaForm, self).validate()
if not result:
return False
telephone=self.telephone.data
timestamp=self.timestamp.data
sign=self.sign.data
sign2=hashlib.md5((timestamp+telephone+self.salt).encode('utf-8')).hexdigest()
if sign==sign2:
return True
else:
return False(2)front/views.py代码语言:javascript复制# common/views.py
__author__ = 'derek'
from flask import Blueprint,request
from exts import alidayu
from utils import restful
from utils.captcha import Captcha
from .form import SMSCaptchaForm
bp = Blueprint("common",__name__,url_prefix='/c')
# @bp.route('/sms_captcha/')
# def sms_captcha():
# telephone = request.args.get('telephone')
# if not telephone:
# return restful.params_error(message='请输入手机号码')
# #生成四位数的验证码
# captcha = Captcha.gene_text(number=4)
# if alidayu.send_sms(telephone,code=captcha):
# return restful.success()
# else:
# # return restful.params_error(message='短信验证码发送失败!')
# return restful.success()
@bp.route('/sms_captcha/',methods=['POST'])
def sms_captcha():
# telephone+timestamp+salt
form=SMSCaptchaForm(request.form)
if form.validate():
telephone=form.telephone.data
captcha=Captcha.gene_text(number=4)
if alidayu.send_sms(telephone,code=captcha):
return restful.success()
else:
# return restful.paramas_error(message='参数错误')
return restful.success()
else:
return restful.params_error(message='参数错误')(3)front_signup.js代码语言:javascript复制$(function () {
$("#sms-captcha-btn").click(function (event) {
event.preventDefault();
var self = $(this);
//获取手机号码
var telephone = $("input[name='telephone']").val();
//使用js的正则判断手机号码,如果不合法,弹出提示框,直接return回去
if (!(/^1[3578]\d{9}$/.test(telephone))) {
zlalert.alertInfoToast('请输入正确的手机号');
return;
}
var timestamp = (new Date).getTime();
var sign = md5(timestamp + telephone + 'dfurtn5hdsesjc*^nd');
zlajax.post({
'url': '/c/sms_captcha/',
'data': {
'telephone': telephone,
'timestamp': timestamp,
'sign': sign
},
'success': function (data) {
if (data['code'] == 200) {
zlalert.alertSuccessToast('短信验证码发送成功');
self.attr("disabled", 'disabled');
var timeCount = 60;
var timer = setInterval(function () {
timeCount--;
self.text(timeCount);
if (timeCount <= 0) {
self.removeAttr('disabled');
clearInterval(timer);
self.text('发送验证码');
}
}, 1000);
} else {
zlalert.alertInfoToast(data['message']);
}
}
});
});
});(4)front/signup.html代码语言:javascript复制 <meta name="csrf-token" content="{{ csrf_token() }}">
<script src="https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.min.js"></script>13.4.验证码缓存把front/views里面的图形验证码放到common/views.py下面common/views.py代码语言:javascript复制# common/views.py
__author__ = 'derek'
from flask import Blueprint, request,make_response
from exts import alidayu
from utils import restful, zlcache
from .form import SMSCaptchaForm
from utils.captcha import Captcha
from io import BytesIO
bp = Blueprint("common", __name__, url_prefix='/c')
# @bp.route('/sms_captcha/')
# def sms_captcha():
# telephone = request.args.get('telephone')
# if not telephone:
# return restful.params_error(message='请输入手机号码')
# #生成四位数的验证码
# captcha = Captcha.gene_text(number=4)
# if alidayu.send_sms(telephone,code=captcha):
# return restful.success()
# else:
# # return restful.params_error(message='短信验证码发送失败!')
# return restful.success()
@bp.route('/sms_captcha/', methods=['POST'])
def sms_captcha():
# telephone+timestamp+salt
form = SMSCaptchaForm(request.form)
if form.validate():
telephone = form.telephone.data
captcha = Captcha.gene_text(number=4)
if alidayu.send_sms(telephone, code=captcha):
zlcache.set(telephone, captcha) # 验证码保存到缓存中
return restful.success()
else:
# return restful.paramas_error(message='参数错误')
zlcache.set(telephone, captcha) # 测试用
return restful.success()
else:
return restful.params_error(message='参数错误')
@bp.route('/captcha/')
def graph_captcha():
text,image = Captcha.gene_graph_captcha()
zlcache.set(text.lower(),text.lower())
out = BytesIO()
image.save(out,'png') #指定格式为png
out.seek(0) #把指针指到开始位置
resp = make_response(out.read())
resp.content_type = 'image/png'
return resp
BBS论坛(十七)
17.首页导航条实现和代码抽离(1)temlates/common/_head.html代码语言:javascript复制<meta name="csrf-token" content="{{ csrf_token() }}">
<script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
<link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="{{ url_for('static',filename='common/zlparam.js') }}"></script>
<script src="{{ url_for('static',filename="common/zlajax.js") }}"></script>
<link rel="stylesheet" href="{{ url_for('static',filename='common/sweetalert/sweetalert.css') }}">
<script src="{{ url_for('static',filename='common/sweetalert/sweetalert.min.js') }}"></script>
<script src="{{ url_for('static',filename='common/sweetalert/zlalert.js') }}"></script>(2)cms/cms_base.html代码语言:javascript复制<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>
<link rel="stylesheet" href="{{ static('cms/css/base.css') }}">
<script src="{{ static('cms/js/base.js') }}"></script>
{% include "common/_head.html" %}
{% block head %}{% endblock %}
</head>(3)front/front_base.html代码语言:javascript复制<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
{% include "common/_head.html" %}
<title>{% block title %}{% endblock %}</title>
{% block head %}{% endblock %}
</head>
<body>
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">仙剑论坛</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active"><a href="#">首页 <span class="sr-only">(current)</span></a></li>
</ul>
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="请输入关键字">
</div>
<button type="submit" class="btn btn-default">搜索</button>
</form>
<ul class="nav navbar-nav navbar-right">
<li><a href="{{ url_for('front.signin') }}">登录</a></li>
<li><a href="{{ url_for('front.signup') }}">注册</a></li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
{% block body %}
{% endblock %}
</body>
</html>(4)front/front_index.html代码语言:javascript复制{% extends "front/front_base.html" %}
{% block title %}
仙剑论坛
{% endblock %}
{% block head %}
{% endblock %}
{% block body %}
论坛首页
{% endblock %}(5)front/views.y代码语言:javascript复制@bp.route('/')
def index():
return render_template('front/front_index.html')
BBS论坛(十一)
11.1.前台用户模型创建(1)apps/front/models.py首先安装:pip install shortuuid代码语言:javascript复制class FrontUser(db.Model):
__tablename__ = "front_user"
id = db.Column(db.String(100),primary_key=True,default=shortuuid.uuid)
telephone = db.Column(db.String(11),nullable=False,unique=True)
username = db.Column(db.String(50),nullable=False)
_password = db.Column(db.String(100),nullable=False)
email = db.Column(db.String(50),unique=True)
realname = db.Column(db.String(50))
avatar = db.Column(db.String(100))
signature = db.Column(db.String(100))
gender = db.Column(db.Enum(GenderEnum),default=GenderEnum.UNKNOW)
join_time = db.Column(db.DateTime,default=datetime.now)
def __init__(self,*args,**kwargs):
#如果传入的参数里面有‘password’,就单独处理
if "password" in kwargs:
self.password = kwargs.get("password")
#处理完后把password pop出去
kwargs.pop("password")
#剩下的参数交给父类去处理
super(FrontUser, self).__init__(*args,**kwargs)
@property
def password(self):
return self._password
#保存密码的时候加密
@password.setter
def password(self, raw_password):
self._password = generate_password_hash(raw_password)
def check_password(self, raw_password):
result = check_password_hash(self.password, raw_password)
return result(2)manage.py代码语言:javascript复制@manager.option('-t','--telephone',dest='telephone')
@manager.option('-u','--username',dest='username')
@manager.option('-p','--password',dest='password')
def create_front_user(telephone,username,password):
user = FrontUser(telephone=telephone,username=username,password=password)
db.session.add(user)
db.session.commit()(3)生成表和添加前台用户代码语言:javascript复制python manage.py db migrate
python manage.py db upgrade添加用户代码语言:javascript复制python manage.py create_front_user -t 18888888888 -u huge -p 11111111.2.注册界面完成(1)front/views.py代码语言:javascript复制class SignupView(views.MethodView):
def get(self):
return render_template('front/signup.html')
bp.add_url_rule('/signup/',view_func=SignupView.as_view('signup'))(2)common/images/logo.jpg放一张logo图片(3)front/front_signup.html代码语言:javascript复制{% from 'common/_macros.html' import static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>仙剑账号注册</title>
<script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
<link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<style>
body{
background:#f3f3f3;
}
.outer-box{
width: 854px;
background: #fff;
margin: 0 auto;
overflow: hidden;
}
.logo-box{
text-align: center;
padding-top: 40px;
}
.logo-box img{
width: 60px;
height: 60px;
}
.page-title{
text-align: center;
}
.sign-box{
width: 300px;
margin: 0 auto;
padding-top: 50px;
}
</style>
</head>
<body>
<div class="outer-box">
<div class="logo-box">
<a href="/">
<img src="{{ static('common/images/logo.jpg') }}" alt="">
</a>
</div>
<h2 class="page-title">仙剑账号注册</h2>
<div class="sign-box">
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control" name="telephone" placeholder="手机号码">
<span class="input-group-btn">
<button class="btn btn-default">发送验证码</button>
</span>
</div>
</div>
<div class="form-group">
<input type="text" class="form-control" name="sms_captcha" placeholder="短信验证码">
</div>
<div class="form-group">
<input type="text" class="form-control" name="username" placeholder="用户名">
</div>
<div class="form-group">
<input type="password" class="form-control" name="password1" placeholder="密码">
</div>
<div class="form-group">
<input type="password" class="form-control" name="password2" placeholder="确认密码">
</div>
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control" name="graph_captcha" placeholder="图形验证码">
<span class="input-group-addon">
验证码
</span>
</div>
</div>
<div class="form-group">
<button class="btn btn-warning btn-block">立即注册</button>
</div>
</div>
</div>
</body>
</html>效果:
BBS论坛(四)
4.1.cms登录页面csrf保护(1)Perfect_bbs.py代码语言:javascript复制from flask_wtf import CSRFProtect
CSRFProtect(app)添加csrf保护后,现在再去登录 (2)cms/cms_login.html添加csrf保护代码语言:javascript复制<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">4.2.cms后台修改密码界面布局完成(1)cms.views.py代码语言:javascript复制class ResetPwdView(views.MethodView):
decorators = [login_required]
def get(self):
return render_template('cms/cms_resetpwd.html')
def post(self):
pass
bp.add_url_rule('/resetpwd/',view_func=ResetPwdView.as_view('resetpwd'))(2)cms/cms_base.html代码语言:javascript复制 <li><a href="{{ url_for('cms.resetpwd') }}">修改密码</a></li>(3)cms/cms_resetpwd.html代码语言:javascript复制{% extends 'cms/cms_base.html' %}
{% block title %}
修改密码
{% endblock %}
{% block page_title %}
{{ self.title() }}
{% endblock %}
{% block head %}
<style>
.form-container{
width: 300px;
}
</style>
{% endblock %}
{% block main_content %}
<form method="post">
<div class="form-container">
<div class="form-group">
<div class="input-group">
<span class="input-group-addon">旧密码</span>
<input type="password" class="form-control" name="oldpwd" placeholder="请输入旧密码">
</div>
</div>
<div class="form-group">
<div class="input-group">
<span class="input-group-addon">新密码</span>
<input type="password" class="form-control" name="newpwd" placeholder="请输入新密码">
</div>
</div>
<div class="form-group">
<div class="input-group">
<span class="input-group-addon">确认新密码</span>
<input type="password" class="form-control" name="newpwd2" placeholder="请确认新密码">
</div>
</div>
<div class="form-group">
<button class="btn btn-primary">立即保存</button>
</div>
</div>
</form>
{% endblock %}效果:
BBS论坛(十五)
15.1.登录界面完成(1)front/signbase.html代码语言:javascript复制{% from 'common/_macros.html' import static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{% block title %}{% endblock %}</title>
<script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
<link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="{{ static('common/zlajax.js') }}"></script>
<link rel="stylesheet" href="{{ static("common/sweetalert/sweetalert.css") }}">
<script src="{{ static("common/sweetalert/sweetalert.min.js") }}"></script>
<script src="{{ static("common/sweetalert/zlalert.js") }}"></script>
<script src="{{ static('common/zlparam.js') }}"></script>
<script src="https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.min.js"></script>
<link rel="stylesheet" href="{{ static('front/css/front_signbase.css') }}">
{% block head %}
{% endblock %}
</head>
<body>
<div class="outer-box">
<div class="logo-box">
<a href="/">
<img src="{{ static('common/images/logo.jpg') }}" alt="">
</a>
</div>
<h2 class="page-title">
{% block h2_block %}
{% endblock %}
</h2>
<div class="sign-box">
{% block signbox %}
{% endblock %}
</div>
<span style="display: none" id="return-to-span">{{ return_to }}</span>
</div>
</body>
</html>(2)front/signup代码语言:javascript复制{% extends 'front/signbase.html' %}
{% from 'common/_macros.html' import static %}
{% block title %}
仙剑论坛注册
{% endblock %}
{% block head %}
<script src="{{ static('front/js/front_signup.js') }}"></script>
{% endblock %}
{% block h2_block %}
仙剑论坛注册
{% endblock %}
{% block signbox %}
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control" name="telephone" placeholder="手机号码">
<span class="input-group-btn">
<button id="sms-captcha-btn" class="btn btn-default">发送验证码</button>
</span>
</div>
</div>
<div class="form-group">
<input type="text" class="form-control" name="sms_captcha" placeholder="短信验证码">
</div>
<div class="form-group">
<input type="text" class="form-control" name="username" placeholder="用户名">
</div>
<div class="form-group">
<input type="password" class="form-control" name="password1" placeholder="密码">
</div>
<div class="form-group">
<input type="password" class="form-control" name="password2" placeholder="确认密码">
</div>
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control" name="graph_captcha" placeholder="图形验证码">
<span class="input-group-addon captcha-addon">
<img id="captcha-img" class="captcha-img" src="{{ url_for('common.graph_captcha') }}" alt="">
</span>
</div>
</div>
<div class="form-group">
<button id="submit-btn" class="btn btn-warning btn-block">立即注册</button>
</div>
{% endblock %}(3)front/css/front_signbase.css把之前signup中的css放到公共的地方,登录页面也需要代码语言:javascript复制body {
background: #f3f3f3;
}
.outer-box {
width: 854px;
background: #fff;
margin: 0 auto;
overflow: hidden;
}
.logo-box {
text-align: center;
padding-top: 40px;
}
.logo-box img {
width: 60px;
height: 60px;
}
.page-title {
text-align: center;
}
.sign-box {
width: 300px;
margin: 0 auto;
padding-top: 50px;
}
.captcha-addon {
padding: 0;
overflow: hidden;
}
.captcha-img {
width: 94px;
height: 32px;
cursor: pointer;
}(4)front/signin.html代码语言:javascript复制{% extends 'front/signbase.html' %}
{% from 'common/_macros.html' import static %}
{% block title %}
仙剑论坛登录
{% endblock %}
{% block head %}
<style>
.resetpwd-link{
float:right;
}
</style>
{% endblock %}
{% block h2_block %}
仙剑论坛账号登录
{% endblock %}
{% block signbox %}
<div class="form-group">
<input class="form-control" type="text" name="telephone" placeholder="手机号" >
</div>
<div class="form-group">
<input class="form-control" type="password" name="password" placeholder="密码" >
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="remember" value="1">记住我
</label>
</div>
<div class="form-group">
<button id="submit-btn" class="btn btn-warning btn-block">立即登录</button>
</div>
<div class="form-group">
<a href="{{ url_for('front.signup') }}" class="signup-link">没有账号?立即注册</a>
<a href="#" class="resetpwd-link">忘记密码?</a>
</div>
{% endblock %}
BBS论坛(三)
3.1.cms用户名渲染和注销功能实现显示登录的用户名(1)app/cms/hooks.py代码语言:javascript复制from .views import bp
import config
from flask import session,g
from .models import CMSUser
@bp.before_request
def before_request():
if config.CMS_USER_ID in session:
user_id = session.get(config.CMS_USER_ID)
user = CMSUser.query.get(user_id)
if user:
g.cms_user = user(2)app/cms/init.py要导入一下才可以,否则不会执行hooks.py里面的代码代码语言:javascript复制import apps.cms.hooks(3)cms/cms_index.html代码语言:javascript复制<li><a href="#">{{ g.cms_user.username }}<span>[超级管理员]</span></a></li>注销功能cms/views.py代码语言:javascript复制@bp.route('/logout/')
@login_required
def logout():
del session[config.CMS_USER_ID]
return redirect(url_for('cms.login'))cms/cms_index.html代码语言:javascript复制 <li><a href="{{ url_for('cms.logout') }}">注销</a></li>3.2.cms模板抽离和个人信息页面完成(1)cms/cms_base.html代码语言:javascript复制{% from "common/_macros.html" import static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>
<script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
<link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="{{ static('cms/css/base.css') }}">
<script src="{{ static('cms/js/base.js')}}"></script>
{% block head %}{% endblock %}
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Zhang_derek论坛管理后台</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li><a href="#">{{ g.cms_user.username }}<span>[超级管理员]</span></a></li>
<li><a href="{{ url_for('cms.logout') }}">注销</a></li>
</ul>
<form class="navbar-form navbar-right">
<input type="text" class="form-control" placeholder="查找...">
</form>
</div>
</div>
</nav>
<div class="container-fluid">
<div class="row">
<div class="col-sm-3 col-md-2 sidebar">
<ul class="nav-sidebar">
<li class="unfold"><a href="{{ url_for('cms.index') }}">首页</a></li>
<li class="profile-li">
<a href="#">个人中心<span></span></a>
<ul class="subnav">
<li><a href="{{ url_for('cms.profile') }}">个人信息</a></li>
<li><a href="#">修改密码</a></li>
<li><a href="#">修改邮箱</a></li>
</ul>
</li>
<li class="nav-group post-manage"><a href="#">帖子管理</a></li>
<li class="comments-manage"><a href="#">评论管理</a></li>
<li class="board-manage"><a href="#">板块管理</a></li>
<li class="nav-group user-manage"><a href="#">用户管理</a></li>
<li class="nav-group cmsuser-manage"><a href="#">CMS用户管理</a></li>
<li class="cmsrole-manage"><a href="#">CMS组管理</a></li>
</ul>
</div>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<h1>{% block page_title %}{% endblock %}</h1>
<div class="main_content">
{% block main_content %}{% endblock %}
</div>
</div>
</div>
</div>
</body>
</html>(2)cms/cms_index.html代码语言:javascript复制{% extends 'cms/cms_base.html' %}
{% block title %}
Zhang_derek论坛管理后台
{% endblock %}
{% block page_title %}
我的论坛
{% endblock %}(3)cms/views.py代码语言:javascript复制@bp.route('/profile/')
@login_required
def profile():
return render_template('cms/cms_profile.html')(4)cms/cms_profile.html代码语言:javascript复制{% extends 'cms/cms_base.html' %}
{% block title %}
个人信息
{% endblock %}
{% block page_title %}
{{ self.title() }}
{% endblock %}
{% block main_content %}
{% set user=g.cms_user %}
<table class="table table-bordered">
<tbody>
<tr>
<td>用户名:</td>
<td>{{ user.username }}</td>
</tr>
<tr>
<td>邮箱:</td>
<td>{{ user.email }}</td>
</tr>
<tr>
<td>角色:</td>
<td>xxxxxxx</td>
</tr>
<tr>
<td>权限:</td>
<td>xxxxxx</td>
</tr>
<tr>
<td>加入时间:</td>
<td>{{ user.join_time }}</td>
</tr>
</tbody>
</table>
{% endblock %}效果:
BBS论坛(九)
9.1.权限和角色模型定义(1)cms/models代码语言:javascript复制class CMSPermission(object):
ALL_PERMISSION = 0b11111111
# 1.访问者的权限
VISITOR = 0b00000001
# 2.管理帖子的权限
POSTER = 0b00000010
# 3.管理评论的权限
COMMENTER = 0b00000100
# 4.管理板块的权限
BOARDER = 0b00001000
# 5.管理前台用户的权限
FRONTUSER = 0b00010000
# 6.管理后台用户的权限
CMSUSER = 0b00100000
# 7.管理后台管理员的权限
ADMINER = 0b01000000
cms_role_user = db.Table(
'cms_role_user',
db.Column('cms_role_id',db.Integer,db.ForeignKey('cms_role.id'),primary_key=True),
db.Column('cms_user_id',db.Integer,db.ForeignKey('cms_user.id'),primary_key=True)
)
class CMSRole(db.Model):
__tablename__ = 'cms_role'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(50), nullable=False)
desc = db.Column(db.String(200),nullable=True)
create_time = db.Column(db.DateTime,default=datetime.now)
permissions = db.Column(db.Integer,default=CMSPermission.VISITOR)
users = db.relationship('CMSUser',secondary=cms_role_user,backref='roles')生成到数据库代码语言:javascript复制python manage.py db migrate
python manage.py db upgrade(2)manage.py代码语言:javascript复制CMSRole = cms_models.CMSRole
CMSPermission = cms_models.CMSPermission
@manager.command
def create_role():
# 1.访问者(可以修改个人信息)
visitor = CMSRole(name='访问者',desc='只能访问数据,不能修改')
visitor.permissions = CMSPermission.VISITOR
# 2.运营人员(修改个人信息,管理帖子,管理评论,管理前台用户)
operator = CMSRole(name='运营',desc='管理帖子,管理评论,管理前台用户,')
operator.permissions = CMSPermission.VISITOR|CMSPermission.POSTER\
|CMSPermission.COMMENTER|CMSPermission.FRONTUSER
# 3.管理员(拥有所有权限)
admin = CMSRole(name='管理员',desc='拥有本系统所有权限')
admin.permissions = CMSPermission.VISITOR|CMSPermission.POSTER|CMSPermission.CMSUSER\
|CMSPermission.COMMENTER|CMSPermission.FRONTUSER|CMSPermission.BOARDER
# 4.开发者
developer = CMSRole(name='开发者',desc='开发人员专用角色')
developer.permissions = CMSPermission.ALL_PERMISSION
db.session.add_all([visitor,operator,admin,developer])
db.session.commit()创建角色代码语言:javascript复制python manage.py create_role9.2.封装权限判断功能(1)cms/models.py代码语言:javascript复制class CMSUser(db.Model):
#.......
@property
def permissions(self):
#用户拥有的权限
if not self.roles:
return 0
all_permissions = 0
#用户所有的角色
for role in self.roles:
#取出用户所有角色的所有权限
permissions = role.permissions
#把所有权限通过“|=”整合到all_permissions
all_permissions |= permissions
return all_permissions
def has_permission(self,permission):
# 判断用户是否有‘xxx’权限
#通过与操作,判断用户是否有‘permission’;得到的结果相等返回true,不相等返回false
return self.permissionspermission == permission
@property
def is_developer(self):
#判断是不是开发者
return self.has_permission(CMSPermission.ALL_PERMISSION)(2)manage.py代码语言:javascript复制@manager.option('-e','--email',dest='email') #用户邮箱
@manager.option('-n','--name',dest='name') #角色名字
def add_user_to_role(email,name):
'''添加用户到某个角色'''
user = CMSUser.query.filter_by(email=email).first()
if user:
role = CMSRole.query.filter_by(name=name).first()
if role:
#把用户添加到角色里面
role.users.append(user)
db.session.commit()
print("用户添加到角色成功!")
else:
print("没有这个角色:%s" %role)
else:
print("%s邮箱没有这个用户!"%email)
@manager.command
def test_permission():
'''测试用户是否有xxx权限'''
user = CMSUser.query.first()
if user.has_permission(CMSPermission.VISITOR):
print("这个用户有访问者权限")
else:
print("这个用户没有访问者权限")(3)cms终端把用户添加到角色代码语言:javascript复制#添加用户到角色
python manage.py add_user_to_role -e [email protected] -n 访问者
#测试
python manage.py test_permission
关于网上论坛
昨天,jQuery的创始人John Resig怒气冲冲地宣布,不再使用Google Groups。他写了一篇长达2000个单词的文章,详细解释了为什么。请注意,2000个单词啊,打印在A4纸上,足足有三页。像他这样炙手可热的顶级程序员,愿意坐下来,写那么长的文章表达不满,可见真的是忍无可忍。我也是Google Groups的用户,他所说的那些问题,我都遇到过,所以对他的抱怨深有同感。下面我就谈谈我的看法。一、Google Groups是什么?为了帮助那些不熟悉这个话题的朋友跟上后面的讨论,我先简单介绍一下Google Groups。它是Google公司提供的网上论坛服务,你可以利用它,快速架起一个多人讨论区,在里面发起新的话题或者回复别人的话题。更妙的是,它还有邮件列表的功能,可以用Email,张贴自己的发言或者接收别人的发言。当然,从网上论坛的角度看,它的功能是比较弱的,与流行的论坛软件没法比。比如,它不允许对话题分组,因此没有板块功能,而且你不能在发言中插入图片,唯一的办法是发送附件。此外,它也不提供数据导出功能,基本上只要你使用了,就等于把所有数据都送给google公司了,自己没法再利用。但是不管怎么说,它提供了基本的讨论功能,而且出于我后面要谈到的原因,大家依然愿意使用它。另外,我也必须提一句,它别的功能也许不行,但是全文搜索功能绝对强大,不愧是google出品。(插入语:Google Groups在国内好像被屏蔽了,有时就连加密的https连接都会断。欢迎大家告诉我,当地是否可以正常使用这项服务。)二、Google Groups最大的缺点John Resig最不满的地方,也是其他用户最不满的地方,就是Google Groups里面的spam(垃圾帖)实在是太多了。我开设的讨论组基本上无人知晓,但是每天也会收到2~3条spam,不知道这些人从哪里找到我的网址的;John Resig开设的讨论组有2万个成员,属于著名讨论组,你可想而知他每天要收到多少条spam!更可气的是,一旦收到了spam,你毫无办法,只能一条条手工删除,然后第二天还会照样收到。想一想吧,让一个顶级程序员,日复一日,手工对抗灌水机的进攻,将时间浪费这种事情上面,怎能不让他郁闷呢!真难以相信,Google公司就听任spam横行,不提供任何有效的过滤措施,你唯一能做的,就是打开审核设置,在帖子发表之前对它进行审核。难怪John Resig要质疑,为什么Gmail可以自动过滤垃圾邮件,而Google Groups就做不到呢?这算不算产品歧视呢?好吧,在走投无路的情况下,你只有打开审核设置了。但是,这样做虽然可以净化讨论环境,但是却让整个事情变得更糟了。首先,你的工作量增大了,你不得不手工批准每一条发言;其次,用户的感受变差了,因为没有人喜欢自己的发言被审查,有的用户可能会因此攻击你,与你大谈美国宪法第一修正案;最后,假定你的讨论组非常受欢迎,每过五分钟,都有用户从地球的某个角落跳出来发言,那么你去睡觉的时候,谁来审核呢?你只能任命一些管理员,让他们按照时区顺序分担审核责任,并且为他们制定出统一的审核政策,这就让论坛的管理和人际关系变得复杂了。所以到头来,只能有两种结果,一种是你被累死了,另一种是你将论坛设置为封闭式,只有经过批准的成员才能加入和发言。两种结果都无法接受,John Resig只好选择退出了。三、项目开发者需要什么?当你开发出一个项目以后,你需要有一个地方,与用户直接交流。用户提出问题,你来回答,就是这么简单。这就是你需要网上论坛提供的所有功能。Google Groups那么简陋,大家还是愿意用它,原因就在这里,因为你不想为了这么单纯的需求,自己去架设一个全功能的论坛。试想一下,如果你自己搭建一个论坛,要干哪些事情:1)你要选择一个论坛软件,但是一旦用上以后,就等于被绑架了。论坛软件的每一次升级,你都必须跟上;如果将来你想改用其他软件,或者原来的软件停止开发,你就要愁死了,因为这意味着,你不得不自己写程序去转换数据。2)你要架设一个后台数据库,通常是MySQL,这就是说,你又多了一项管理数据库的任务。要知道世界上有一种人叫做DBA,他们的全职工作就是管理数据库,经常有半夜三点被叫到机房的经历,所以管理数据库真的是一种费心费力的活,不到万不得已,你千万不要把这种活揽到自己头上。3)你不得不绞尽脑汁,将每一项论坛的后台设置都设好。我最喜欢的论坛软件叫做vBulletin,它的后台设置多达2000多项,大部分我从来没有搞清楚过。我曾经设想,要是我把每一项设置都搞清楚,我就可以去开一家VBulltin的咨询公司了。所以,如果你选择自己架设论坛,你的精力就会被分散,你用于开发项目的宝贵时间,就被论坛的维护和管理消耗掉。你的初衷只是要找一个可以与用户对话的地方,没必要把一切搞得这么复杂。既然使用Google Groups,能够在5分钟里搭好一个网上论坛,那么大家当然就去用了。遗憾的是,Google这个产品并不理想。四、论坛软件的尴尬之处John Resig的烦恼,在更深的层次上,我认为反映了论坛软件在开发上的困境。今天的论坛软件,在模式上与10年前没有本质区别,主要功能和运作模式都是那时定下的。以国内市场占有率最高的Discuz!为例,最新的版本是7.0版,但是实际上它同最早的版本相比,除了功能更强大以外,没有太大的区别,两者都是在做同样的事情。我不是说现在的论坛模式不好,而是说这种模式不适合进一步发展,所以开发就走入了死胡同,只能做功能的增强,而不能做模式的创新。现有的论坛模式不具备可扩展性(scalable)。由于对数据的实时性要求太高,导致不能对论坛软件使用缓存,这就是说,基本上网上论坛都是一个单机系统,CDN和反向代理都用不上。单机系统的结果,就是论坛软件不可能负担太多的访问者。像vbullentin这样的软件,同时在线人数不能超过10000~15000人,否则就会有性能问题。不用打听你就知道,10000多人属于访问量比较小的网站,所以论坛都做不大。如今的时代是Web 2.0的时代,它的特征包括提供API接口、前台界面的多样化和去中心化等等。Facebook和Twitter,都不把自己称为网站,而是把自己叫做平台,意思就是说,我只提供基础设施,随便你怎么造房子。我就常常想,论坛的2.0时代什么时候到来呢,会是什么样子,又怎么来实现呢?五、论坛软件可能的创新之处我记得,以前看到过有人提议,可不可以基于BT下载的模式(或者点对点协议),做出一个论坛。这样做的好处是,数据在用户之间进行交换,这样就不存在中心服务器了,因此论坛可以做得很大;坏处是一条发言想被所有人看到,也许有一个很漫长的数据交换过程,即时的对话和讨论基本上不可能发生。退一步说,usenet就是一个去中心化的讨论区,的确规模非常大,但是大家也知道,在上面讨论问题,效率不高。我设想中,未来的论坛应该提供这样几个功能:1)支持openID,这样就省略了注册步骤。2)开放API编程接口,允许用户进行再开发,可以远程发言和调用数据。Google Groups如果开放API的话,防spam的问题就很容易解决了,还可以附加很多别的功能,我相信到时其他论坛软件恐怕都要死定了。3)数据储存和前端逻辑必须分离。从MVC的角度说,就是Model(模型)要同View(视图)和Controller(控制)分离,只要把各自的接口规定好就行了。这样一来,数据的维护和平台开发都会变得非常容易。说实话,Amazon公司提供一种SimpleDB服务,你可以将数据都放到那里去嘛,然后前台的论坛程序,其实就干两件事:控制用户行为和同SimpleDB交换数据。(完)
BBS论坛(六)
6.1.优化json数据的返回(1)新建utils/restful.py代码语言:javascript复制# utils/restful.py
from flask import jsonify
class HttpCode(object):
ok = 200
unautherror = 401
paramserror = 400
servererror = 500
def restful_result(code,message,data):
return jsonify({"code":code,"message":message,"data":data or {}})
def success(message="",data=None):
return restful_result(code=HttpCode.ok,message=message,data=data)
def unauth_error(message=""):
return restful_result(code=HttpCode.unautherror,message=message,data=None)
def params_error(message=""):
return restful_result(code=HttpCode.paramserror,message=message,data=None)
def server_error(message=""):
return restful_result(code=HttpCode.servererror,message=message or "服务器内部错误",data=None)(2)cms/views.py代码语言:javascript复制class ResetPwdView(views.MethodView):
.
.
.
.
if user.check_password(oldpwd):
user.password = newpwd
db.session.commit()
return restful.success()
else:
return restful.params_error("旧密码错误")
else:
return restful.params_error(form.get_error())6.2.sweetalert优化修改密码结果反馈(1)把sweetalert文件夹放到static/common下面(2)在cms/cms_base.html中引用代码语言:javascript复制 <link rel="stylesheet" href="{{ static("common/sweetalert/sweetalert.css") }}">
<script src="{{ static("common/sweetalert/sweetalert.min.js") }}"></script>
<script src="{{ static("common/sweetalert/zlalert.js") }}"></script>(3)cms/js/resetpwd.js代码语言:javascript复制/**
* Created by derek on 2018/6/3.
*/
$(function () {
$("#submit").click(function (event) {
//阻止按钮的提交表单的事件
event.preventDefault();
//分别获取三个标签
var oldpwdE = $("input[name=oldpwd]");
var newpwdE = $("input[name=newpwd]");
var newpwd2E = $("input[name=newpwd2]");
var oldpwd = oldpwdE.val();
var newpwd = newpwdE.val();
var newpwd2 = newpwd2E.val();
//1.要在模板的meta标签中渲染一个csrf-token
//2.在ajax请求的头部汇总设置X-CSRFtoken
zlajax.post({
'url':'/cms/resetpwd',
'data':{
'oldpwd':oldpwd,
'newpwd':newpwd,
'newpwd2':newpwd2
},
'success':function (data) {
if (data['code'] == 200){
zlalert.alertSuccessToast("恭喜!密码修改成功!");
oldpwd.val('');
newpwd.val('');
newpwd2.val('');
}else {
var message = data['message'];
zlalert.alertInfo(message);
}
},
'fail':function (error) {
zlalert.alertNetworkError(error);
}
});
});
});修改密码弹出的信息旧密码输错的提示信息
BBS论坛(三十)
30.显示评论和添加评论功能完成(1)apps/models.py代码语言:javascript复制class CommentModel(db.Model):
__tablename__='comment'
id=db.Column(db.Integer,primary_key=True,autoincrement=True)
content=db.Column(db.Text,nullable=False)
create_time=db.Column(db.DateTime,default=datetime.now)
post_id=db.Column(db.Integer,db.ForeignKey('post.id'))
author_id = db.Column(db.String(50), db.ForeignKey('front_user.id'), nullable=False)
post=db.relationship('PostModel',backref='comments')
author=db.relationship('FrontUser',backref='comments')生成到数据库代码语言:javascript复制python manage.py migrate
python manage.py upgrade(2)front/forms.py代码语言:javascript复制class AddCommentForm(BaseForm):
content=StringField(validators=[InputRequired(message='请输入评论内容')])
post_id=IntegerField(validators=[InputRequired(message='请输入评论内容')])(3)front/views.py代码语言:javascript复制@bp.route('/acomment/',methods=['POST'])
@login_requried
def add_comment():
form=AddCommentForm(request.form)
if form.validate():
content=form.content.data
post_id=form.post_id.data
post=PostModel.query.get(post_id)
if post:
comment=CommentModel(content=content)
comment.post=post
comment.author=g.front_user
db.session.add(comment)
db.session.commit()
return restful.success()
else:
return restful.params_error(message='没有这个帖子')
else:
return restful.params_error(form.get_error())(4)front/front_base.html代码语言:javascript复制<span id="login-tag" data-is-login="1"></span>(5)front/front_pdetail.html代码语言:javascript复制{% extends 'front/front_base.html' %}
{% from 'common/_macros.html' import static %}
{% block title %}
{{ post.title }}
{% endblock %}
{% block head %}
<script src="{{ static('ueditor/ueditor.config.js') }}"></script>
<script src="{{ static('ueditor/ueditor.all.min.js') }}"></script>
<script src="{{ static('front/js/front_pdetail.js') }}"></script>
<link rel="stylesheet" href="{{ static('front/css/front_pdetail.css') }}">
{% endblock %}
{% block body %}
<div class="lg-container">
<div class="post-container">
<h2>{{ post.title }}</h2>
<p class="post-info-group">
<span>发表时间:{{ post.create_time }}</span>
<span>作者:{{ post.author.username }}</span>
<span>版块:{{ post.board.name }}</span>
<span>阅读数:{{ post.read_count }}</span>
<span>评论数:0</span>
</p>
<article class="post-content" id="post-content" data-id="{{ post.id }}">
{{ post.content|safe }}
</article>
</div>
<div class="comment-group">
<h4>评论列表:</h4>
<ul class="comment-list-group">
{% for comment in post.comments %}
<li>
<div class="avatar-group">
<img src="{{ comment.author.avatar or static('common/images/logo.jpg') }}" alt="">
</div>
<div class="comment-content">
<p class="author-info">
<span>{{ comment.author.username }}</span>
<span>{{ comment.create_time }}</span>
</p>
<p class="comment-txt">
{{ comment.content|safe }}
</p>
</div>
</li>
{% endfor %}
</ul>
</div>
<div class="add-comment-group">
<h3>添加评论</h3>
<script id="editor" style="height:100px;" type="text/plain"></script>
<div class="add_comment_btn">
<button class="btn btn-primary" id='comment-btn'>发表评论</button>
</div>
</div>
</div>
<div class="sm-container"></div>
{% endblock %}(6)front/css/front_pdetail.css代码语言:javascript复制.comment-group{
border:1px solid #e8e8e8;
margin-top: 20px;
padding: 10px;
}
.add-comment-group{
border:1px solid #e8e8e8;
margin-top: 20px;
}
.add_comment_btn{
text-align: right;
margin: 10px;
}
.add-comment-group h3{
margin: 10px;
}
.comment-list-group li{
overflow:hidden;
border-bottom:1px solid #e8e8e8;
}
.avatar-group{
float:left;
}
.avatar-group img{
width: 50px;
height: 50px;
border-radius: 50%;
}
.comment-content{
float: left;
margin-left: 20px;
}
.comment-content .author-info{
font-size: 12px;
color:#8c8c8c;
}
.author-info span{
margin-right:10px;
}
.comment-content .comment-txt{
margin-top: 10px;
}(7)front/front_pdetail.js代码语言:javascript复制$(function(){
var ue=UE.getEditor('editor',{
'serverUrl':'/ueditor/upload/',
"toolbars": [
[
'undo', //撤销
'redo', //重做
'bold', //加粗
'italic', //斜体
'blockquote', //引用
'selectall', //全选
'fontfamily', //字体
'fontsize', //字号
'simpleupload', //单图上传
'emotion' //表情
]
]
});
window.ue=ue;
});
$(function(){
$('#comment-btn').on('click',function(event){
event.preventDefault();
var login_tag=$('#login-tag').attr('data-is-login');
if (! login_tag){
window.location='/signin/'
}else{
var content=window.ue.getContent();
var post_id=$('#post-content').attr('data-id');
zlajax.post({
'url':'/acomment/' ,
'data':{
'content':content,
'post_id':post_id
},
'success':function(data){
if(data['code']==200){
zlalert.alertSuccessToast(msg='评论发表成功');
window.location.reload();
}else{
zlalert.alertInfo(data['message']);
}
}
});
}
}) ;
});
BBS论坛(五)
5.1.cms后台修改密码功能完成(1)新建app/forms.py代码语言:javascript复制# app/forms.py
from wtforms import Form
class BaseForm(Form):
def get_error(self):
message = self.errors.popitem()[1][0]
return message(2)cms/forms.py代码语言:javascript复制# cmd/forms.py
from wtforms import StringField,IntegerField
from wtforms.validators import Email,InputRequired,Length,EqualTo
from ..forms import BaseForm
class LoginForm(BaseForm):
email = StringField(validators=[Email(message='请输入正确的邮箱格式'),
InputRequired(message='请输入邮箱')])
password = StringField(validators=[Length(6,20,message='密码长度不够或超出')])
remember = IntegerField()
class ResetpwdForm(BaseForm):
oldpwd = StringField(validators=[Length(6,20,message="请输入正确格式的旧密码")])
newpwd = StringField(validators=[Length(6,20,message="请输入正确格式的新密码")])
newpwd2 = StringField(validators=[EqualTo('newpwd',message="两次输入的密码不一致")])(3)cms/views.py代码语言:javascript复制class ResetPwdView(views.MethodView):
decorators = [login_required]
def get(self):
return render_template('cms/cms_resetpwd.html')
def post(self):
form = ResetpwdForm(request.form)
if form.validate():
oldpwd = form.oldpwd.data
newpwd = form.newpwd.data
user = g.cms_user
if user.check_password(oldpwd):
user.password = newpwd
db.session.commit()
return jsonify({"code":200,"message":""})
else:
return jsonify({"code": 400, "message": "旧密码错误"})
else:
message = form.get_error()
return jsonify({"code": 400, "message": message})
bp.add_url_rule('/resetpwd/',view_func=ResetPwdView.as_view('resetpwd'),strict_slashes=False)(4)新建static/common/zlajax,js代码语言:javascript复制// 对jquery的ajax的封装
'use strict';
var zlajax = {
'get':function(args) {
args['method'] = 'get';
this.ajax(args);
},
'post':function(args) {
args['method'] = 'post';
this.ajax(args);
},
'ajax':function(args) {
// 设置csrftoken
this._ajaxSetup();
$.ajax(args);
},
'_ajaxSetup': function() {
$.ajaxSetup({
'beforeSend':function(xhr,settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) !this.crossDomain) {
var csrftoken = $('meta[name=csrf-token]').attr('content');
// console.log(csrftoken);
xhr.setRequestHeader("X-CSRFToken", csrftoken)
}
}
});
}
};(5)cms/js/resetpwd.js代码语言:javascript复制/**
* Created by Administrator on 2018/6/3.
*/
$(function () {
$("#submit").click(function (event) {
//阻止按钮的提交表单的事件
event.preventDefault();
//分别获取三个标签
var oldpwdE = $("input[name=oldpwd]");
var newpwdE = $("input[name=newpwd]");
var newpwd2E = $("input[name=newpwd2]");
var oldpwd = oldpwdE.val();
var newpwd = newpwdE.val();
var newpwd2 = newpwd2E.val();
//1.要在模板的meta标签中渲染一个csrf-token
//2.在ajax请求的头部汇总设置X-CSRFtoken
zlajax.post({
'url':'/cms/resetpwd',
'data':{
'oldpwd':oldpwd,
'newpwd':newpwd,
'newpwd2':newpwd2
},
'success':function (data) {
console.log(data);
},
'fail':function (error) {
console.log(error);
}
});
});
});(6)cms/cmc_base.html代码语言:javascript复制 <meta name="csrf-token" content="{{ csrf_token() }}">
<script src="{{ static('common/zlajax.js') }}"></script>(7)cms/cms_resetpwd.html代码语言:javascript复制{% from 'common/_macros.html' import static %}
<script src="{{ static('cms/js/resetpwd.js')}}"></script>测试看能不能修改密码成功和输入错误的密码会不会提示密码不对
视频社交洞悉(下)
前言在 视频社交洞悉(上)我们通过调研视频社交市场现状,分析和拆解了视频社交的概念。得出“视频社交”的本质一种“实时社交”的结论,进而分析了视频社交的优点和缺点。本篇我们将讨论视频社交的用户特点以及可能的机会点。微信、微博、贴吧、陌陌、探探……,过去几年是“异步社交”狂飙突进的几年。现实生活中很多人内心敏感、与人相处谨小慎,社交压力已经很大。所以异步社交的模式降低了他们的社交成本,同时掩盖了社交能力的不足,也更符合现代人碎片化的生活习惯。那么高压力的同步社交会有机会吗?这要从这部分新用户的特点说起。视频社交用户的特点先看几个典型场景:你是广西人,高中辍学来东莞4年了。当年的同学很多都大学毕业在城市写字楼里上班。你喜欢的穿白衬衫的女生去了广州读大学,朋友圈的发的照片都很好看。这几年你从橡胶厂到了塑料厂又到了玩具厂。一天工作12小时两班倒没社保没医保,加班费低得可怜,在机床从你早上站到晚上下班耳边都是轰隆隆是机器声音,除了下班后三三两两的啤酒局你也没地方说话。半夜下了班你坐在台阶上,给伊对的“红娘”和“相亲对象”开始打赏,一百接一百,你开始讲起学生时代时的故事,讲你为了那个白衬衫女生在街头单挑一群人...没人知道你为了考进一个这种类型的事业编付出了多少努力。刚来一个月你就明显不适应。一方面,你融入不到在你身边每天讨论婆媳关系和小孩上学的同事中去;另一方面,岗位职责之外杂七杂八的工作令你疲惫,你终于明白招聘启事里“完成领导交代的其他任务”的含义了。白天你要和其他比你年长数岁的大哥大姐叔叔阿姨关在一个办公室里,他们的话题你完全插不上嘴;但在晚上,你十点半睡觉前会专门空出一个小时,点开 Soul 的脸基尼匹配,希望在面具的遮盖下,遇到一个能陪你说话的同龄人。你是一个“深二代“,去年被被爸妈送到美国来读高中。爸妈是零几年大学毕业来深圳定居的。你最有印象的是小时候你们家小区之前周围都是土堆工地,你和小伙伴们都在放学后跑游戏厅一起打游戏。你最喜欢的颜色是绿色,所以今年生日爸爸送给你的礼物是这部墨绿色的苹果手机。每天下午的课间时间你会用Monkey匹配女生聊天儿,然后和同学们比较谁匹配到的女生更漂亮。还扬言一定要约到哪个最漂亮的。其实你最喜欢昨天匹配到的那个隔壁学校的女生,因为她长得很像艾丽·范宁。看看这些场景归纳一下会发现一个共同点:这些用户都是年轻人。我们可以尽力去回想一下那些年我们的青春期,然后不难发现这些年轻用户的特点大概可以概括为以下。年轻用户更多元。伴随着社会不可避免的阶层分化,年轻人也不可避免地走向分化。不同地域、不同阶层家庭的孩子表现出巨大的不同,这在未来会是一个越发明显的趋势。有大都市初入职场的白领,有下沉市场中的厂工厂妹,有青春无处安放的在校学生…所以我们越来越难以做一个统一明确的画像来描述年轻用户或00后就是怎样的。年轻用户时间更多。他们不会像我们那样追求效率和结果,可能更加走心和兴趣导向。所以“杀时间”是一个很重要的需求。这就好像我们今天约一个人出来,总是有着明确的目标性,否则就是“无效社交”,而他们不会,他们有大把的时间去浪费和消耗。在这里不用说什么“一寸光阴一寸金”,你下班之后还不是要打几盘游戏或者看几集网综?年轻用户的社交冲动更强。社交需求更旺盛,“嘴巴上”也会更大胆。因为他们以往的圈子比较单一狭窄,所以好奇心十足更愿意尝试新鲜事物,去看看不同的人和世界。而我们大多数人圈子已经定型,出现了社交倦怠,宁可宅着也不想太多社交。年轻人涉世未深没吃过多少亏,所以相对而言戒备心更弱更能放得开;而我们的戒备心更足,社交中的神经敏感性更强。关于机会点的论证视频社交有市场吗?机会点在哪里?我们需要注意几个改变:“异步社交”需求已经高度满足
从微信、微博,到陌陌、快手,异步AIO消息已经几乎是今天绝大多数社交或者社区类新创产品的标配,人们对这种“低压力、无需立即回复”的模式已经司空见惯。
相反,优秀的“同步”社交产品反而是稀缺的。snapchat在北美高中生群体爆火,但国内的复制品却遭遇水土不服。尚未成功不代表这种模式本身存在问题,需求一定是存在的。而长期的稀缺会不会已经累积了新的需求势能呢?实时娱乐产业正在兴起
这些年的年互联网红利主要集中在工具类和平台领域,解决的是“效率”的问题。现在,效率已经前所未有的提升,伴随AI技术的出现和应用,某些领域甚至有些效率过剩了。
在这样的背景下,消解孤独、缓解焦虑、杀时间的娱乐类产品出现了爆发的态势。
对年轻人来说,看一个产品,往往短期内更看重好玩,而非实用。从以往的单机游戏到实时联网手游,从录播到直播,从刷贴盖楼到屏幕实时弹幕……王者荣耀、狼人杀、B站,这些产品的特点广泛普适、碎片场景、在多人沟通中的实时娱乐化。智能手机、5G移动网络等基础建设提供更多支持
智能手机快速普及,伴随5G的来临,运营商资费越来越廉价。让视频这种社交媒介有了具备普及的基础要求。技术的演进也会带来新的沟通与生活方式,这个在下面会具体展开讨论。以上三点,人们对“异步”已经腻了,对“同步”的需求正在增强,加之硬件和网络基建越来越好。这就是未来视频社交的机会点,直播的爆火已经是一次启蒙。实时社交体验好坏的关键,就是看当下获得的互动反馈数量和质量。是划了无数卡片一个没聊到,还是至少和一个妹子真正聊满5分钟?对于时间稀缺的上班族来说,碎片化的异步社交是能更好地适应。但是对时间大把的学生党和下沉用户群体来说,他们敢于尝试更有压力却也更加刺激的实时同步社交。我们肯定会担心即便人们喜欢用视频社交,可是会不会就像陌陌、探探一样,只是当作“发现新关系”的工具?而一旦新关系稳固深化,又会转移到qq、微信上?之所以会这样,一个很重要的原因是其沟通方式和功能(IM、图文)和微信没差别,从方便和集中的角度当然微信更好。但是视频聊天是有不同的:当下仅仅是需要一个人的实时陪伴,吐槽也好闲聊也罢就刚好遇到这个人而已,至于是谁并不重要,当下的需求满足了,过期不候。所以通过视频社交获得的关系,没必要转到熟人平台。视频聊天工具一旦加了“美颜”,无疑女性用户会更愿意留下,因为微信的真实画面没法直视。在美颜的烘托下任何女生都会获得男生的搭讪,这解决了部分女性在现实生活中无人问津的情况,满足了虚荣心。一般使用文字语音,更多是作为消除尴尬、试探性的缓冲。一旦双方一上来已经使用了“最刺激”的沟通方式,再转到微信用文字图片这样降维的聊天,其实已经意义不大。
生活方式变迁与技术演进的推动生活方式变迁与技术演进会推动视频沟通逐渐成为熟人的日常沟通方式。视频聊天成为主流,也许在我们心中几乎不可想象,但是对于“互联网原住民”的“Z世代”也许大有可为。对我们来说,使用免费、盗版几乎是我们青春期最正常的体验,所以今天很多内容要付费,真是有一点违和感。但是互联网原住民的一代,版权意识、付费习惯就明显要好很多。那么,他们是否也更容易接受视频聊天成为一种生活方式呢?只有新的生活方式的形成,才是最好的护城河。如果这个假设是对的,那就算加了微信、qq号也没事,一来因为后两者的视频聊天体验太差,二来这两个产品目前都是越来越复杂,越来越重。如果做一个轻量化的视频工具,用户想要视频了还是会回来。另一方面,在人来历史上,技术的演进对人的沟通方式产生了数次的变革。从信件、电报到有线电话,人类实现了跨距离的实时沟通。BB机(寻呼机)的出现又解决了电话固定而人是移动的问题,被呼叫、然后回拨在某种程度上保证了一定的时效性。移动电话的发明完全解决了随时随地实时沟通的时效性问题。互联网IM工具的发明,实现了多媒体和实时画面传输。也产生了陌生人之间的社交。智能手机和移动网络的普及,使得视频这种社交媒介有了具备普及的基础要求。目前的问题是受制于手机这样的设备,实时视频在某些场景下是不方便不容易进行的。比如手机忘记带了,比如在拥挤的地铁上或会议室等。也许未来借助眼镜、手表等可穿戴设备、加上全息投影等技术,可以实现随时随地可接通且为安全的状态。结语至此,文章已经接近尾声。感谢你的阅读!总结上下两篇全文的内容和观点如下:视频社交市场上三类产品形态:内容社区,陌生社交,熟人工具。视频社交可以拆解为关系的发现、建立、维护。分别对应上述三种产品形态。视频社交的本质一种“实时社交”,具备明显清晰的优势和劣势。视频社交用户是具备多元化、杀时间、强社交冲动的年轻人。视频社交从市场机会、用户需求、技术基础三方面论证,存在机会点。视频社交会因为生活方式变迁与技术演进而逐渐成为熟人的日常沟通方式。最后想引用Tiki创始人吴永辉的一段话:「Tiki创造的是一个用户完全实时在线的场景,产品的重心在于实时而不在于视频。用户在Tiki上的行为逻辑分为两个层面:首先是匹配聊天,之后才是共同游戏,深化用户的社交行为。」归根到底,“视频社交”的本质一种“实时社交”。但反过来“实时社交”包含但不限于“视频社交”或者陌生人社交。实时社交未来也许成为下一个QQ或者微信。未来实时社交可能会切入到更多的丰富的场景和人群当中去。它像是一个未来版本的电话,每个人可能会通过这种方式来进行通讯。你如何看待视频社交的未来呢?欢迎留言交流。如果这篇文章对你有用,欢迎点击右下角「在看」并分享到朋友圈。朋友们我们下期再见。
社群经济与粉丝经济:误解和澄清
胡泳 北京大学新闻与传播学院教授 宋宇齐 北京大学新闻与传播学院研究生 随着社会交流方式特别是网络的发展,社群的涵义产生了极大的变化。经典理论下的社群有一定的特点和限制条件,诸如:社群内成员的构成要以一定的社会关系作为基础;其成员从事的社会活动有一定的地域限制;社群内部应形成一定的文化,并能使社群成员产生归属感与认同感;同时,社群的建立与维护需要一定的基础设施。但在互联网时代,交流与沟通打破了原有的时空限制,使得社群可以不依托现实社会中的某些条件而建立。在地域上,因为互联网的全球性,社群成员的组成可以摆脱原有的地域限制,做到异地的交流沟通;在时间上,社群成员可以通过网络进行非实时的交流。社群活动也因此不仅仅局限于特定时间地点的特定活动,极大地便利了社群的维护和发展。 社群经济正是基于社群而形成的一种经济思维与模式,它依靠社群成员对社群的归属感和认同感而建立,借由社群内部的横向沟通,发现社群及成员的需求,其重点在于通过服务这些需求而获得相应的增值,并进一步建立社群内部的生态系统。这种内部生态的满足也使得社群本身通过优化社群环境、增加社群认同感等方式产生增值,从而有了更强的外拓资本。在互联网环境下,超强的传播效应与社群本身超低的边际成本使得社群的拓展具备更大的经济价值。 国内知名时尚网站P1在成立之初以街拍来为摄影爱好者及时尚人士提供摄影和穿着方面的交流平台,这就形成了一个以摄影和时尚为核心的社群。其账号体系便是利用社群经济的一个典型案例:P1并不开放注册账号,在用户心理上,P1通过不开放注册的方式设立准入门槛为其社群成员构建了较高的自我认同感。这种高自我认同感在社群成员的社会交流中会被作为一种资本进行表述,从而使有意加入该社群的非社群成员对获得账号有了更高的渴望,这种渴望极易在现实中产生经济价值,也就出现了倒卖P1邀请码的情况。 图片说明:P1街拍 如前文所述,社群经济的方式是通过对内部生态的满足产生经济价值并进一步外延。目前有相当一部分理论将粉丝经济等同于社群经济。其实,粉丝经济是一种单项的价值流通,它通过塑造一个品牌(这个品牌可以是人、物、观念等),来笼络对该品牌有较高认知度与喜好的受众成为其粉丝,这样的关系构成使得其内部的信息传递具有单向性的特点,即品牌传达某些信息,粉丝接受信息并向品牌做出反馈。这样的纵向信息传递方式有着极强的向心性及非理性因素。粉丝因缺少横向交流及获取外界信息的渠道与方式而产生对品牌主体的盲目崇拜。纵使品牌主体没有从物理上屏蔽粉丝获取外界信息的渠道,但其对粉丝灌输的价值体系使其在心理上自觉排斥了外界信息。这一点被用在很多的营销案例中,从早期的歌手粉丝到现今诸如苹果、小米类的品牌粉丝。它们的核心都是旨在营造一种目标受众的狂热崇拜。在这种狂热的崇拜下,品牌主体及其运营者通过开发品牌价值及周边产品获得经营性创收。 在互联网时代,社交媒体同样为粉丝经济的发展创造了更好的发展条件。它有效拉近了品牌主体与粉丝间的距离,扩大了品牌主体的信息传播范围,降低了其传播难度,使得品牌主体能够聚集更多粉丝,获得更大收益。同时,社交媒体与电商、互联网金融的结合也为粉丝经济产品的销售提供了便利有效的途径,进一步增加了收益。 例如,小米便是利用社交媒体和粉丝经济的一把好手。小米利用社交媒体进行粉丝吸引和维护的方式可谓应有尽有,几乎涵盖了当下所有的主流社交媒体。在微博上,雷军的个人账号成为推广小米的最主要力量。作为一个标榜创新的科技公司,其掌舵者的个人效应受到广泛关注,这个掌舵者自然而然成为品牌主体下的子品牌。雷军微博粉丝1237万,每日更新微博,其微博在小米品牌推广初期几乎条条与小米有关。在微信方面,小米各子品牌开设微信服务号,但由于服务号一月只能进行一次推送,其账号作用并不侧重于粉丝的招募宣传,而是在于既有粉丝的维护。在腾讯空间方面,小米拥有大量关注者。腾讯的QQ空间历来是一块粉丝营销不怎么投入的阵地,但是小米在QQ空间聚集了两千余万粉丝,说说的转发率在几千到上万不等,流量相当可观。同时,据网络数据分析,经由空间跳转至小米官网的流量占小米官网流量的5%以上。 虽然无论是在微博、微信还是QQ空间上,粉丝理论上都能与品牌主体进行互动并进行粉丝间交流,但实际能够与主体进行互动的粉丝仅占极少数,粉丝与粉丝间的有效交流更是寥寥无几。他们对品牌主体的凝集力几乎全部由品牌主体进行单方向维护。这与社群的构成要素及社群成员凝聚力的构建要素存在很大差异。 社群经济与粉丝经济之比较 社群经济与粉丝经济的最直观的差异在于,社群经济以社群间内部成员的横向交流为纽带,通过对社群的服务与创造社群价值获得经济效益;这显著区别于粉丝经济通过粉丝对品牌主体的向心性依托而获得经营性收益的做法。不过,因社交媒体互动性强的交流特点,粉丝经济与社群经济的界限并非全然泾渭分明。好的社群能够树立自有品牌从而聚集人气,形成一定的品牌向心性;粉丝经济也可以利用社交媒体的互动机制为粉丝提供一种类似于社群的归属感。这也就导致了社群经济与粉丝经济在社交媒体中容易混淆的现象。但如果我们深入来看,二者不难区分,可以通过以下三种方法进行分辨: (1)成员之间的互动程度 社群依托社群成员间的联系形成,社交媒体在网络时代起到的正是交流平台的作用,平台给予社群成员自由交流与沟通的方式和渠道。而社交媒体对于粉丝与品牌之间起到的大多是信息传播载体的作用,其信息有较强的单向性传播特点。以微博为例,社群的主体微博多发布与社群主题相关的内容供社群成员阅读、转发。社群成员间通过交流相结识,进一步建立网状关系。社群主体的运营者即微博运营者通过组织线上线下活动、服务社群成员而获得经济效益。但在粉丝经济中,品牌主体以发布某一新产品为内容核心,敦促粉丝通过接受其内容产生消费。其成员极少在主体发布的内容下有深入交流,多为自说自话的评论,粉丝间也很难通过该平台进行相互间的联系。 最典型的例子便是明星微博账号。明星微博账号主体是明星及其公关团队发布的用于维护明星价值的资讯消息,为的是让明星粉丝被动接受以维持崇拜心理。然而,基于明星粉丝群体而形成的诸多明星粉丝团与后援会却是社群形式,其通过微博发布明星信息及社群活动信息获得粉丝群体关注,并将粉丝群体迁移到微信群等即时通讯平台中,为粉丝间提供线上线下的交流平台,最终通过组织粉丝相关活动获得经济效益。 (2)成员与主体互动深入度 社群与社群主体间的互动程度要远远高于粉丝与品牌主体间的互动程度,其同成员间互动程度的本质一样,仍然与两者的构成机制与交流方式相关,即社群是基于社群成员的相同喜好、认知而建立的自发性互动性组织,而粉丝则是基于对品牌主体的崇拜而建立的一种上下关系。当社交媒体账号主体在社交媒体中发布消息后,社群中会产生相关话题,社群成员与账号主体的交流能够得到相应的回复与问题解答;而在粉丝群体中,品牌主体往往仅进行相关信息发布,而不会与粉丝产生除转发以外的多余互动,在消息评论中,粉丝往往也不会做出交流举动,而是以单纯表达崇拜为主。当然,在某些情况下,二者可以相互融合与共存,这主要取决于成员个体对于平台的自身诉求。 小米论坛便是集粉丝经济和社群经济为一体的一个BBS平台。其间有针对小米粉丝的在粉丝经济思想指导下的营销内容,也有针对技术与产品问题的互动交流内容。营销内容毫无疑问是提高粉丝购买欲的粉丝经济方式,而反馈贴及技术展望内容则实现了成员与社群主体即小米的沟通,用户通过产品问题的反馈和对新产品的预想展望为小米提供了直接的问题与期望采样资料,从而使小米能够对产品做出有针对性的改进研发,获得更大的市场收益。 (3)主体账号的运营维护方式 主体账号的维护方式也是区分粉丝经济和社群经济的重要标志。社群的主体账号以服务社群成员获得增值,其重点更多是在建立和维护交流平台上,账号的主要运营者通常也是社群成员,以业余时间进行平台的维护和拓展,社群成员也会自发加入到账号的内容维护中,发布社群主题相关内容与社群成员互动。因此社群账号也有着随意性强的特点。因其更多地承载平台职能,在一些社交媒体中社群不以明显的账号形式存在,比如百度贴吧、论坛类社交媒体。而粉丝经济中的主体账号因承担的是信息发布与传递的属性,故其会在成本允许的情况下雇佣专人或团队维护主体账号,撰写内容,选择合适时机发布。所以粉丝经济的主体账号维护方式有着一定的程式化及操作规律可循,并因其需求延伸出了相关行业。但是,随着社群知名度的提高及社群成员基数的发展等因素的推动,社群的经济效益逐步显现,为满足日常运营需求,社群主体账号也会出现专职维护人员。这也就与粉丝经济主体账号产生了一定的相似性,在这种情况下,就需要通过主体账号运作的目的性来进行甄别和判断。 社群经济在社交媒体中的发展现状及展望 因为社群经济在我国起步较晚,且社群相对独立分散,在缺乏专业运营人员及团队的情况下,社群自身的经济价值并未在社群内部被有效开发。更多的社群经济开发被社群所在社交媒体平台通过细分市场的形式进行广告推送以获得盈利。这也是社群经济较发展较为成熟的一个方向,社群账号的平台提供商通过对社群主题及成员的甄别划分,进行点到点、点到面的广告推送,以获得较常规广告投放更收益更高的增值收益。社交软件陌陌就通过群信息标签中的特点分类为群成员提供相关广告推送,并在这类广告推送的基础上增加微商接口,引入微商,打通微商到客户的对应渠道,获得相关金融流转收益。 这种联通平台用户及线下商业网络的模式也成为了近年来各大平台提供商针对社群经济进行拓展的主要方向,但这种发展完全抛开了社群这一经济载体,实质是开发社群聚集的账号价值进行增值。以汽车论坛汽车之家为例,其利用论坛用户规模及汽车相关性推出养车之家等客户端,上线线下养车服务商,提供养车服务及配件售卖,以获得线上交易带来的现金流。作为宽泛的社群来说,汽车之家论坛本身即为一个大的汽车用户及汽车爱好者社群。但就狭义社群概念而言,其社群的实际存在形式却在论坛分类里得到细分,社群成员的交流及归属感也多集中于细分的论坛之中。因此,其将社群的增值作用偷换为用户聚集效应产生的增殖作用其实有失偏颇。 本篇开头所介绍的P1时尚论坛的社群经济发展模式则有一定的可取性。它利用社群的品牌价值获得针对社群成员及准社群成员的经济价值。其经济价值由社群成员对于社群的价值认知和定位产生,围绕社群本身进行增值,而不是利用用户搭建平台获得现金流。在对被街拍的人“如何穿,穿什么”的关注中,社群成员自然会对品牌进行探究,再适当以帖子、介绍等形式引入品牌宣传内容,从而可以在不被社群成员反感的基础上获得广告收入,并依据成员的社群主题需求(在这里是时尚)展示诸如照片分享、拍照软件等内容进行社群经济的开发。 与此同时,如何将粉丝经济转化为社群经济是当今环境下所面临的重要问题。当粉丝被伪社群的宣传营造出的社群认同感欺骗时,真正的社群会被受到伤害的粉丝群体所质疑,从而使社会对社群失去部分信心。另一方面,粉丝经济的强大聚集效应带来的向心力便于提高经济效益,这使得将粉丝经济转化为社群经济变成了一条发展社群经济的捷径。依据前文中对于社群经济及粉丝经济差异性的分析,我们认为,在社交媒体中将粉丝转化为社群成员、从而使粉丝经济转化为社群经济的核心,是增强群体间的互动交流,增加价值认同,使得群体获得超出信息接收者的参与感。 谷歌的模块手机便是将谷歌安卓系统粉丝转化为社群成员的一个重要手段。在经历了从非智能机到智能机的迭代期后,谷歌和苹果各自拥有了自己的粉丝群,但这样的粉丝经济下产品用户的消费习惯过度依赖于对于品牌的崇拜,而模块手机的推出使得安卓系统的粉丝能够自由配置手机模块,玩出不同花样,在满足粉丝动手需求与个性需求的基础上,其进一步满足的社交需求无疑是粉丝群体在乎的一项重要诉求,粉丝间及不同粉丝群体间能够以此为话题进行深入的、有自我意识的交流。这无形中弥补了粉丝经济中依赖于垂直信息传递而忽略粉丝间横向交流的不足,将粉丝群在保持其粉丝经济价值的基础上迁移至社群,有效地发展了社群经济,并依靠社群成员的自发性及高认知度特点,进一步避免了粉丝流失。 前文简述了社群平台提供商的盈利模式及发展策略,但更多的中小型社群依然各自为营,较难开发经济价值,这也就需要从社群本质属性入手,利用社交媒体的时空便利,有针对性地为社群成员提供适合于他们的增值服务来挖掘价值。
BBS论坛(十二)
12.1.图形验证码生成(1)utils/captcha/init.py代码语言:javascript复制import random
import string
# Image:一个画布
# ImageDraw:一个画笔
# ImageFont:画笔的字体
from PIL import Image,ImageDraw,ImageFont
# pip install pillow
# Captcha验证码
class Captcha(object):
# 生成几位数的验证码
number = 4
# 验证码图片的宽度和高度
size = (100,30)
# 验证码字体大小
fontsize = 25
# 加入干扰线的条数
line_number = 2
# 构建一个验证码源文本
SOURCE = list(string.ascii_letters)
for index in range(0, 10):
SOURCE.append(str(index))
#用来绘制干扰线
@classmethod
def __gene_line(cls,draw,width,height):
begin = (random.randint(0, width), random.randint(0, height))
end = (random.randint(0, width), random.randint(0, height))
draw.line([begin, end], fill = cls.__gene_random_color(),width=2)
# 用来绘制干扰点
@classmethod
def __gene_points(cls,draw,point_chance,width,height):
chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100]
for w in range(width):
for h in range(height):
tmp = random.randint(0, 100)
if tmp > 100 - chance:
draw.point((w, h), fill=cls.__gene_random_color())
# 生成随机的颜色
@classmethod
def __gene_random_color(cls,start=0,end=255):
random.seed()
return (random.randint(start,end),random.randint(start,end),random.randint(start,end))
# 随机选择一个字体
@classmethod
def __gene_random_font(cls):
fonts = [
'msyh.ttf',
'msyhbd.ttf',
'simkai.ttf',
'simsun.ttc',
'webdings.ttf'
]
font = random.choice(fonts)
# print(font)
return 'utils/captcha/'+font
# 用来随机生成一个字符串(包括英文和数字)
@classmethod
def gene_text(cls, number):
# number是生成验证码的位数
return ''.join(random.sample(cls.SOURCE, number))
#生成验证码
@classmethod
def gene_graph_captcha(cls):
# 验证码图片的宽和高
width,height = cls.size
# 创建图片
# R:Red(红色)0-255
# G:G(绿色)0-255
# B:B(蓝色)0-255
# A:Alpha(透明度)
image = Image.new('RGBA',(width,height),cls.__gene_random_color(0,100))
# 验证码的字体
font = ImageFont.truetype(cls.__gene_random_font(),cls.fontsize)
# 创建画笔
draw = ImageDraw.Draw(image)
# 生成字符串
text = cls.gene_text(cls.number)
# 获取字体的尺寸
font_width, font_height = font.getsize(text)
# 填充字符串
draw.text(((width - font_width) / 2, (height - font_height) / 2),text,font= font,fill=cls.__gene_random_color(150,255))
# 绘制干扰线
for x in range(0, cls.line_number):
cls.__gene_line(draw, width, height)
# 绘制噪点
cls.__gene_points(draw, 10, width, height)
# with open('captcha.png','wb') as fp:
# image.save(fp)
return (text,image)(2)在utils/captcha文件下面放几种字体(3)front/views.py代码语言:javascript复制@bp.route('/captcha/')
def graph_captcha():
text,image = Captcha.gene_graph_captcha()
out = BytesIO()
image.save(out,'png') #指定格式为png
out.seek(0) #把指针指到开始位置
resp = make_response(out.read())
resp.content_type = 'image/png'
return resp(4)生成验证码运行项目,浏览器访问:http://127.0.0.1:5000/captcha/,可以看到生成的随机验证码
用JAVA的DEA算法衡量社交媒体页面的流行度
Measuring the Social Media Popularity of Pages with DEA in JAVA原文作者:Vasilis Vryniotis原文地址:http://blog.datumbox.com/measuring-the-social-media-popularity-of-pages-with-dea-in-java/译者微博:@从流域到海域译者博客:blog.csdn.net/solo95用JAVA的DEA算法衡量社交媒体页面的流行度在前面的文章中,我们讨论了数据包络分析(Data Envelopment Analysis)技术,我们已经看到它如何被用作一个有效的非参数排序算法。在这篇博文中,我们将开发出一个JAVA数据包络分析的实例,我们将用它来评估网络上的网页和文章的社交媒体流行度。该代码是开源的(在GPL v3 license下),您可以从Github免费下载。更新:Datumbox机器学习框架现在是开源的,可以免费下载。查看包com.datumbox.framework.algorithms.dea以查看Java中Data Envelopment Analysis的实现。数据包络分析在JAVA中的实现代码是用JAVA编写的,可以直接从Github下载。它是根据GPLv3许可的,所以可以随意使用它,修改它,或者再分发。该代码实现了数据包络分析(Data Envelopment Analysis)算法,使用lp_solve库来解决线性规划问题,并使用Web搜索引擎优化分析(Web SEO Analytics )索引提取的数据,以构建基于Facebook,Google Plus和推特上分享的一个混合的社交媒体页面流行度矩阵。在前面的文章中介绍了算法的所有理论部分,在源代码中可以找到关于其实现的详细的javadoc注释。(原博文之后数据包络分析(Data Envelopment Analysis)算法及其实现全部简称了DEA,请读者注意,译者注。)下面我们提供一个关于其架构实现的高级别描述:1. lp_solve 5.5 library为了解决各种线性规划问题,我们使用一个名为lp\_solve的开源库。某些特定的lib是用ANSI C编写的,并使用JAVA包装来调用库方法。因此,在运行代码之前,您必须在您的系统上安装lp_solve。该库的二进制文件在[Linux和Windows都可以使用,您可以在lp_solve文档中阅读更多有关安装的信息。在尝试运行JAVA代码之前,请确保您的系统上安装了(相关的)特定库。有关安装和配置库的任何问题,请参阅lp_solve文档。2.DataEnvelopmentAnalysis Class这是DEA算法的主要实现类。它实现了一个名为estimateEfficiency()的公共方法,它获取记录的Map并返回它们的DEA得分。3. DeaRecord ObjectDeaRecord是一个特殊的对象,用于存储我们记录的数据。由于DEA需要分离输入和输出,因此DeaRecord对象将以DEA可以处理的方式分别存储我们的数据。4. SocialMediaPopularity ClassSocialMediaPopularity是一个应用程序,它使用DEA来评估社交媒体网络上Facebook的like,Google的 +1和twitter的Tweets的网页流行度。它实现了两个受保护的方法:calculatePopularity()和estimatePercentiles()以及两个公共方法loadFile()和getPopularity()。calculatePopularity()使用DEA实现根据社交媒体计数来估计页面的得分数。estimatedPercentiles()方法获取DEA分数并将其转换为百分位数。总的来说,百分比比DEA分数更容易解释; 因此当我们说一个网页的流行分数是70%时,这意味着该网页比70%的其他网页更受欢迎。为了能够估计一个特定页面的流行度,我们必须有一个包含其他页面的社交媒体数据的数据集。这是有原因的,因为需要预测哪个网页是受欢迎的,哪些不是,您必须能够将其与网络上的其他页面进行比较。为此,我们使用来自以txt格式提供的Web SEO分析索引的小型的匿名样本。您可以通过从网页上的更多页面提取社交媒体计数来构建自己的数据库。(社交媒体计数,比如点赞数、转发数、评论数)loadFile()方法用于加载DEA的上述统计信息,getPopularity()方法是一种易于使用的方法,可以获取Facebook的like,Google的+1和一个页面的Tweets数量,并以此评估其在社交媒体上的流行度。如何使用数据包络分析的JAVA实现在DataEnvelopmentAnalysisExample类中,我提供了2个不同的关于如何使用代码的例子。第一个例子直接使用DEA方法来根据它们的输出(ISSUES,RECEIPTS,REQS)和输入(STOCK,WAGES)来评估组织单位的效率。这个例子来自DEAzone.com的一篇文章。代码语言:txt复制Map<String, DeaRecord> records = new LinkedHashMap<>();
records.put("Depot1", new DeaRecord(new double[]{40.0,55.0,30.0}, new double[]{3.0,5.0}));
//...adding more records here...
DataEnvelopmentAnalysis dea = new DataEnvelopmentAnalysis();
Map<String, Double> results = dea.estimateEfficiency(records);
System.out.println((new TreeMap<>(results)).toString());第二个示例使用我们的社交媒体流行度应用程序,通过使用来自社交媒体的数据来评估页面的流行度,例如Facebook的like,Google的+1和Tweets。所有的社交媒体计数都被标记为输出,我们传递给DEA一个空的输入向量。代码语言:txt复制SocialMediaPopularity rank = new SocialMediaPopularity();
rank.loadFile(DataEnvelopmentAnalysisExample.class.getResource("/datasets/socialcounts.txt"));
Double popularity = rank.getPopularity(135, 337, 9079); //Facebook likes, Google +1s, Tweets
System.out.println("Page Social Media Popularity: "+popularity.toString());必要的扩展(上面)所提供的代码只是DEA如何被用作排名算法的一个例子。为了改进其实现,需要进行下面的扩展:1.加速(算法的)实现特定的DEA算法实现会评估数据库中所有记录的DEA得分。由于我们需要解决如同数据库中记录数量那样多的线性规划问题,这使得实现变得缓慢。如果我们不需要计算所有记录的分数,那么我们可以显著地加快执行速度。因此,该算法的小扩展可以使我们更好地控制哪些记录应该被解决掉,哪些只能被用作约束。2.扩大社交媒体统计数据库(这篇文章所)提供的社交媒体统计数据库由来自Web SEO Analytics索引的1111个样本组成。为了能够估计更准确的流行(度)分数,需要更大的样本。您可以通过统计来自网络上更多页面的社交媒体计数来创建自己的数据库。3.添加更多的社交媒体网络该实现使用Facebook的喜欢,Google的+1和推文的数量来评估文章的受欢迎程度。不过,来自其他社交媒体网络的指标可以很容易地被考虑在内。您只需要从您感兴趣的网络中构建一个社交媒体数据库,然后扩展SocialMediaPopularity类来处理它们。关于实施的最终意见为了能够扩展(算法的)实现,您必须对Data Envelopment Analysis的工作原理有一个很好的理解。这在前面的文章中已经介绍过了,所以在继续进行任何更改之前,请确保您阅读了之前的教程。此外,为了使用JAVA代码,您必须在您的系统中安装lp\_solve库(参见上文)。如果你在一个有趣的项目中使用这个实现,那么就给我们一条线索,我们将在我们的博客上展示你的项目。另外,如果你喜欢这篇文章,请花点时间在Twitter或Facebook分享。
社交网络成受黑客攻击重灾区
据报道,随着社交媒体在人们生活中作用的迅速提升,网络黑客已经利用电脑技术开发并出售虚假用户评价,比如“好评”(likes)和“追随者”(followers)。而Facebook、照片分享应用Instagram、Twitter、谷歌(微博)YouTube、职业社交网站 LinkedIn以及其他热门社交网站都不幸成为黑客制造僵尸粉的重点攻击对象。 网络安全专家表示,最近一款名为Zeus的专门盗取信用卡数据的电脑木马病毒已经变体成能够制造虚假Instagram“好评”的僵尸病毒。 在互联网黑客论坛上,这些僵尸粉被打包成每份1000个进行出售,黑客还在论坛上兜售信用卡账号和从用户个人电脑上窃取来的其他信息。根据RSA的统计,在黑客论坛上,1000个Instagram虚假“追随者”售价为15美元,而1000个Instagram虚假“好评”的售价则高达30美元。与此形成鲜明对比的是,1000个信用卡账号的售价则低至6美元。虚假社交媒体账户比真实的信用卡账号更值钱,这似乎有些不可思议。但互联网营销专家则表示,出于自身原因或业务需要,有些人愿意在互联网上投资以便制造轰动效应,比如让一款新产品看起来颇受消费者欢迎。 数据分析师维克多·潘(Victor Pan)指出:“人们认识到了趋势的重要性。这就是所谓的‘从众效应’(bandwagon effect)”。 Facebook在去年斥资10亿美元收购了Instagram,并表示该公司正在对Instagram的安全系统进行升级。Instagram的新闻发言人迈克尔·科克兰德(Michael Kirkland)也表示,Instagram将拥有与Facebook同等级别的安全系统。Facebook的全球用户已经接近12亿,而Instagram的活跃用户也接近1.3亿。他还指出:“我们一直在努力减少服务中包含的垃圾邮件,并禁止通过非授权或自动方式新建用户账户。”他还鼓励用户通过Facebook网站和应用里的链接报告可疑的网络行为。 长期追踪网络犯罪行为的专业人士指出,变体后的Zeus病毒是截至目前发现的第一款专门在社交网络上制造僵尸粉的恶意软件。使用自动软件程序制造虚假“好评”成为黑客普遍采用的方式。变体后的Zeus病毒可以通过中央服务器控制受感染电脑,迫使电脑向具体用户发送“好评”。根据RSA的调查,经过精心设计的Zeus病毒还可以介入其它网络活动或下载其它类型的恶意软件。 目前,变体后的病毒已经开始把攻击目标锁定在Instagram。这进一步突显出社交媒体在市场营销过程中不断上升的重要性,而黑客也在不断提升利用这种趋势进行牟利的复杂性。 专家们同时指出,操纵社交网络的行为很难自动消失。创建虚假社交媒体账户的目的比制造僵尸粉更加邪恶,比如窃取用户的身份信息。 加州大学伯克利分校的电脑安全专家克里斯·格里尔(Chris Grier)指出:“这些虚假账户仅仅是接触终端用户的一种途径而已。网络罪犯总是希望借此来牟利。”格里尔领导的团队已经花费了一年时间来调查Twitter上的虚假账户情况。
密友社交设计探索
腾讯ISUXisux.tencent.com社交用户体验设计
1 社交现状与痛点保持距离、经营人设、礼貌分寸,我们努力维系与迎合,去成为在社交关系中受欢迎的角色。曾几何时,人们在使用社交APP时越来越忐忑,“这句话这样说是否合适?”“是否影响到了我的公众形象?””这样会不会太矫情?”又或是,大家开始抱怨社交产品上的红点提示,似乎永远也删不完。想联系的人找不到,不想联系的,却又碍于社交礼仪,只能礼貌的客套。许多人在问,那个想说什么就说什么的“我”去了哪儿?2 痛点分析美国社会学家格兰诺维特曾提出,人和人之间的连接分为“强”“弱”两种关系。强关系常产生于家庭成员、同事、同学等之间,他们在生活和工作中有较多的互动机会。由于已经有了固定的情感基础,社交动机以具体的”人“为维度,沟通与互动是主要场景。弱关系中,人与人之间联系较少,可能只是聊过几句或是打过招呼,人们关注的更多是信息的质量和信息的传播。主流社交APP,在产品早期主要聚焦于强关系链。但随着用户数量的持续大量增长,私密性和封闭性逐步被打破。原本只承载强社交关系的圈子,变成了一个充满看客的强弱社交关系混合的广场。因此,虽然刺激用户社交的功能一直在不停的迭代与增加,用户却因为社交环境的变化,导致分享沟通更有负担。3 解决方案与设计目标关系,决定了人与人之间产生交互;场景,决定了人与人之间产生什么样行为。具体的社交行为,是关系和场景这两个要素的叠加。为尝试解决现有的社交困境,DOV定位于“强关系”这一明确的关系链,清晰分隔混合的社交环境,聚焦于核心的密友社交场景,探索搭建合适的APP,为用户提供新的社交帮助。4 引导用户导入建立合适的强关系群体第一步,需要引导用户导入和建立合适的强关系联系人。我们将“强关系”定义为一个用户更好理解的概念--“密友”,根据基本的用户使用地图,我们在推荐渠道,新手引导和功能设定三个步骤上,突出“密友”的概念。4.1 从推荐渠道建立认知首先,在各个推荐渠道中强调“密友”概念。用户需要通过一份”真假朋友大考验“,才能获得友谊的通行券。在诸多运营活动页面的设计中,DOV使用了同一套字体系统和类似的四方连续底纹,保证设计风格和氛围的统一化。整体设计风格偏娱乐化,同时兼顾DOV的品牌风格,深化产品在用户心中的形象定位,帮助用户首次接触DOV这个产品时,留下完整性的品牌印象。4.2 从闪屏页与新手引导教育用户当用户首次登录DOV时,一则趣味化的小短片,也向用户表达了产品的主要核心:“虽然我们和多数人相识,却只能和少数人亲密。这里,是只属于你和密友们的新天地。”首屏小短片需要给用户营造一种热闹的气氛,视觉上会更加活泼来吸引用户的眼球,采用黄黑与黑白两种高对比对色彩去加重需要用户所悉知的内容。同样,在短片中延续整个DOV常用的四方连续的设计手法。
4.3 从功能的设定强化概念尽管人们可以借助社交网络实现“好友”遍天下,但真正的密友屈指可数,而且数量基本保持不变,结交一个新朋友往往会冷落一个老友。在DOV的密友生态环境中,我们希望杜绝泛泛之交,去限制每个用户的密友的数量,从而让用户去珍惜每一个名额。视觉样式上采用卡片的形式,强化视觉上的个人意识。通过大投影的卡片来与背景的白色拉开层次距离,让整体卡片更加立体。同样,排版上采用左齐的排版设计样式,采用大字体的标题设计,偏向于海报的排版 。DOV的首TAB页,如同一个所有好友的集居地。用户可以轻松的看到所有的好友每日状态,如果好友更新了日迹内容,好友的卡片会透传视频内容和未读数。点击好友卡片,就会以连续的小视频播放展示好友24小时内发布的生活小视频。扁平设计已经流行了好几年,但是过于扁平的设计会让整体空间层次混乱,在DOV的设计中大量运用卡片以及大投影的设计来为空间层次感增效。同样在动态的设计中,我们给2D图形赋予更多的伪3D的一种动态质感,让整体动态以及界面的视觉呈现上更加立体,让层次更加丰富细腻。5 维系与加强已有的情感基础引导用户导入了强关系链联系人之后,如何维系与加强已有的情感基础就成了主要的目的。在网络上,社交情感的维系依靠信息,特别是真正有价值的属于“你与我”的独有的信息。在交互设计中,参照实际的物理的社会互动因素,可极大提升用户界面的参与感和趣味性。5.1 实时体现社交情感温度DOV的AIO背景,从视觉层面体现实时的社交情感温度。背景颜色会随着聊天热度变化从淡漠的浅灰色逐渐升温到柔情的橙黄色,最终到达至炙热的红紫色。通过视觉的表达上的色温感受来透传好友之间的亲密热度。5.2 即时同步行为状态好友的意义,除了实际的沟通,精神上的陪伴感也非常重要。当两个用户同时出现在AIO窗口中时,会激活”小状态“功能。他或者正在写着什么给你,或者正在等待着你说话。这些依据用户行为触发趣味性动效,可以有效的拉近情感空间。即便地理位置上也许相隔甚远,但通过一块共同的屏幕,却仿佛就在眼前。合理运用迪士尼的动画设计原则,更加拟人化的动态设计可以更好地调动用户的内心情绪,夸张的表现手法也同样会赋予UI界面更多的戏剧性效果。5.3 丰富信息交换的载体
视频,能传达丰富的信息。但传统的视频聊天作为一种非常需要双人同步在线的重社交方式,存在天然痛点。比如旅行途中,我想给朋友看看我正在进行时的美景美食,朋友却在会议中,不能接收视频聊天邀请。鉴于此,DOV新设定了一个同框功能,允许只打开一方的摄像头,并且默认语音静默。这样如果用户想将现在所处的环境展示给朋友,就不需要发起一个非常重的视频聊天,而只需要打开同框功能,将摄像头内拍摄到的内容作为AIO背景同步给好友。而当时间点方便时,好友也可以选择加入同框,成为一个类似视频聊天的体验。此时,按下“记录”的按钮,就生成了一张拍立得质感的记录图片,沉淀在AIO内,成为友谊的见证。5.4 提升信息交换的效率强关系的一大特征在于有高频的信息交换诉求。在QQ,微信等诸多社交软件的培养下,异步发布自己的生活点滴和心情,分享给好友,已经成为了一种非常天然的心理诉求和用户习惯作为好朋友,常常会互相告知所在的位置,心情,所做的事情,等等。DOV在AIO内加入了一个“此时此刻”功能,可以快速拉起摄像头拍摄现在的场景,再用加上简单有趣的图像化状态说明。相比一本正经的汇报现状,更强调分享生活小点滴,以及自己的碎碎念和小心情,是一种更具有趣味性和轻量化的沟通分享能力。5.5 创建更多C2C沟通场景当朋友们在谈论社交网络中的内容时,经常说一句话是 “嘿,你看到那张照片了吗?”DOV将C2C的沟通起点,前置于观看场景。当用户看见朋友的生活小视频时,可以用消息和轻互动直接开启聊天。异步分享成为了即时沟通的社交动机,因此而开启更多的可能的沟通场景。同时,DOV的分享体系下,视频不仅仅是一种异步分享方式,而是想对着所有好友说的碎碎念。所以,相比一般发在朋友圈里的那种展现美好生活形态的记录,DOV内更多的是我的各种好的坏的大的小的当下心情与境遇。因此,朋友的即时反馈就极为重要。好友的回复,是对分享发表的直接正向鼓励,可以有效的刺激更多的内容产出。6 结语所有人的社交圈,都是分层的。有的是无话不说的兄弟或闺蜜,有的是兴趣结识的同好,有的是工作中结识的同事,有的是一起度过学校时光的同窗,有的是无法割舍的亲人,有的是萍水相逢的点头之交。面对不同的社交圈层,我们往往要扮演不同的社交形象,选择性的交流或分享。年轻一代往往希望更纯粹的社交,让沟通分享脱离功利,回归纯粹的友谊。围绕着这种年轻人的新诉求,为他们搭建舒适贴心的沟通与分享场景,一直以来,都是DOV的产品创新设计的主路径。或许将来的社交潮流,是更多的是小社交,每个人都有自己的小圈子,既有独立性,也有合作性,而且各有各自的规则,私密和公开相结合,去正式化,去平台化。社交模式创新的尝试,路阻且长。但望,流年笑掷,未来可期。最后,附上DOV二维码,欢迎大家下载体验。以下ISUX文章,你可能也感兴趣
▽DOV社交探索 | 贴纸极致设计 DOV原创驱动-短视频社交设计思考 ISUX Showreel 2018 拨动情绪的内容设计 情感化IP体验设计感谢阅读,以上内容均由腾讯ISUX团队原创设计,以及腾讯ISUX版权所有,转载请注明出处,违者必究,谢谢您的合作。注明出处格式:
文章来自公众号:腾讯ISUX(https://isux.tencent.com/articles/dov-social.html)↓点击前往 ISUX 官网
CVPR新规严禁审稿期间公开宣传论文,可发arXiv,LeCun:疯了吧!
机器之心报道编辑:蛋酱、泽南打击投机取巧行为。除了标题各种 all you need 之外,投稿之后在推特知乎上宣传一波,油管上发个介绍视频现在几乎是 AI 顶会热门研究的「标配」。在社交网络如此发达的今天,「酒香不怕巷子深」的美好世界看起来越来越难以实现了。在论文水平差距不大的情况下,PR 能力成为了不少人比拼的方向。但众所周知,自我宣传有可能影响到审稿人的判断,进而让身处大型实验室的研究者们更容易获益。最近几天,人工智能领域最热门的会议 CVPR 决定拿社交媒体开刀了。在近日召开的 CVPR 2021 PAMI TC 会议上,马普智能系统研究所所长 Michael Black 提出了一项动议:审稿期间禁止在社交媒体上宣传论文。在 arXiv 上自动发布的预印本论文除外。这项动议得到了一部分研究者认同,但同时也引来许多争议。有研究者表示,这项动议的宗旨是解决同行评审中的偏见问题,但打击社区内交流的消极后果是,同行评审中的偏见问题并不会改善。在最终通过的投票版本中,内容如下:PAMITC 当前的政策是禁止作者发布新闻稿或谈论正处于 IEEE/CVF 审查阶段的论文。这有利于双盲审查,并有助于保持公众对科研同行审查过程的信任。
随着研究者们越来越多地使用社交媒体来传播科研成果,这项政策需要更新。特别是社交媒体动态已经在很大程度上取代了传统的新闻发布。按照目前的政策,此类内容处理起来会有些模棱两可,但只要作者没有在社交媒体上说这篇论文正在接受 IEEE/CVF 会议的审查,就可能会被解释为允许这样做。这个漏洞违反了最初媒体禁令的原则。问题在于,拥有大量关注者的一部分以及在社交媒体上发起的宣传活动,让审稿人接触到这篇论文,而它所受到的关注可能会影响他们的判断。这让关注者较少或不参加这类活动的那部分人处于不利地位,并会使同行评审过程出现偏差。
同行评审是科学的支柱。这个过程能够让研究公布之前被发现错误或虚假的内容,减少了研究被撤回的风险,并增加公众对科学进步的信任。本质上,科研在资金和独立性方面都有赖于这种信任的建立,任何破坏这种信任的行为都会对基础研究产生长期的负面影响。对于人们最为关心的一些问题,CVPR 组织方也进行了解答:Q:新规定生效之后我把投 CVPR 的论文放在 arXiv 上,这能行吗?A:将论文放在 arXiv 上显然有助于计算机视觉研究信息的快速传播。arXiv 论文并不是「已发表」,而应该被理解为「预发表」的状态。这种开放的预先发表模式为研究社区提供了一种有效的查错模式,与同行评审效果类似。arXiv 论文经常会被更正和修改,这一网站的建立就是为了这样的科学修订过程。将论文放在 arXiv 上供专家进行早期分析,与在社交媒体上向广大受众进行宣传有本质不同。Q:那我在推特上转发 arXiv 上转发的 CVPR 论文投稿,这样可以吗?A:没问题。这是一个自然而然的过程,并不构成作者宣传他们的工作。arXiv 推文主要是由该领域的专家而非普通公众关注,其讨论的工作可被认为是具有完整性的,且处于预发表状态的,因此判断为可行。但这与在社交媒体上流传的论文宣传视频不同。Q:这种规定难道不会让科学研究的传播速度变慢?A:并不。实际上是该领域专家实现科学进步,而不是普通大众。对 arXiv 的豁免意味着研究社区仍然可以提前获得研究进展,并可以将其效果评估为「非同行评审」。「允许,但又不允许宣传论文」的设置看起来很合理,也为研究人员们讨论业内最新研究留下了空间。目前,这项动议已经以 370:83 的票数在 CVPR 2021 投票通过。反对者:这规定漏洞太多虽然动议都在 PAMI-TC 的会议上以很大优势通过了,但仍有一些人对于新规表达了不满。人们表示:首先就会有一种情况,如果有一部分研究达成了「共识」,代表对方在社交媒体发布内容,宣传对方的研究。原本在审查过程中就存在的一些「利益团体」,在这里依旧会出现。以及,关于研究审查过程中的禁令,很大的一个影响是阻止了众多科研进展在 arXiv 上发布。除非是像 ACL 会议一样对预印本论文发布和双盲审稿之间权衡出合理的提交方案。还有一种可能存在的情况。那些不遵守规则并找到漏洞的人,之后从宣传研究上获得收益要比现在还要大,比如采用匿名的社交媒体账户宣传就可逃避该政策。Reddit 上的机器学习版块最受非议的一点就是非实名制发帖,我们是否希望推特也变成这样?这项动议针对的是那些来自大型实验室或拥有大量粉丝的研究者,但同时也极大地阻碍到了小型实验室的人。图灵奖得主、深度学习先驱 Yann LeCun 则直接在推特上表示:这种行为会限制科学信息的交流,损害技术进步且违反道德规范。简而言之就是疯了。在今年的大会结束后,CVPR 2022 计划将于明年 6 月 21-24 日在美国的新奥尔良举行。论文提交的 Deadline 则是今年 11 月 16 日。对于这项新规,你怎么看呢?参考内容:https://amytabb.com/ts/2021-06-16-cvpr-motion-position/WAIC AI开发者论坛:后深度学习的AI时代7月8日—10日,AI 开发者论坛将通过三大核心模块:AI开发者论坛、WAIC· 开发者黑客松和WAIC· 云帆奖展示本年度人工智能领域最前沿的研究方向和技术成果。7月10日,WAIC AI开发者论坛邀请到多位业界大咖带来精彩分享,主题涵盖大规模语言智能、SysML(机器学习系统)、多模态机器学习及大规模自动生成技术、RISC-V技术及生态、AI 原生计算机系统等热门话题,满足 AI 开发者多层次的学习需求。在精彩的分享外,我们还准备了RTX 3060 显卡、HHKB键盘、Air Tag、人工智能专业书籍、桌搭鼠标垫,现场签到即可参与抽取。识别下方二维码,立即报名。
©THE END转载请联系本公众号获得授权投稿或寻求报道:[email protected]
java美食论坛系统发帖子系统美食论坛网站美食分享论坛源码
ssm开发的美食论坛系统,用户注册之后可以发布关于美食的帖子,其他人可以回帖,评论,点赞回复和评论,分为楼主,第一楼,第二楼等。可以再个人中心查看我对别人的回复,以及别人对我的回复。演示视频 https://www.bilibili.com/video/BV1zq4y1q74r/?share_source=copy_webvd_source=ed0f04fbb713154db5cc611225d92156环境:jdk8+mysql5+tomcat8.5技术:ssm(spring+springMVC+myibats)+maven+pagehlper+css+jq+js+ajax+simditor+bootstrap功能:首页最新帖子展示,最近发布时间几秒前,几分钟前,几个天前,几个月前,分页;最热帖子展示,根据回复量倒序排列,展示前30条数据;根据帖子标题搜索;发帖功能,可以发图文信息,根据图文信息自动在首页展示1张或者2到三张图片;没有图片则只展示文字标题;登录;注册;帖子详情页,回复主贴,可回复图文信息,点击用户头像即可回复用户,回复楼中楼展示,收藏帖子,点赞回复,删除回复;个人中心,编辑资料,上传头像,修改密码,修改个性签名;查看我发布的帖子,删除我发布的帖子,分页展示;查看我的收藏,取消收藏,分页展示;查看我回复了谁,删除我的回复,分页展示;查看谁回复了我,不下再现实回复信息,分页展示;退出账号;快捷按钮菜单:主页,刷新,返回上一页,发帖,回复,收藏等.管理员功能分类管理:分页,添加,编辑,删除,根据名称搜索帖子管理:分页,审核通过,审核不通过,根据标题、用户昵称、分类、审核状态搜索回复评论管理:分页,删除,根据回复内容查找用户管理:分页,禁用,启用,根据昵称、禁启用状态查找修改密码退出登录在这里插入图片描述在这里插入图片描述