注册
环信即时通讯云

环信即时通讯云

单聊、群聊、聊天室...
环信开发文档

环信开发文档

Demo体验

Demo体验

场景Demo,开箱即用
RTE开发者社区

RTE开发者社区

汇聚音视频领域技术干货,分享行业资讯
技术讨论区

技术讨论区

技术交流、答疑
资源下载

资源下载

收集了海量宝藏开发资源
iOS Library

iOS Library

不需要辛辛苦苦的去找轮子, 这里都有
Android Library

Android Library

不需要辛辛苦苦的去找轮子, 这里都有

Golang实现单机百万长连接服务 - 美图的三年优化经验

美图长连接服务简介 随着科技的飞速发展,技术的日新月异,长连接的运用场景日益增多。不仅在后端服务中被广泛运用,比较常见的有数据库的访问、服务内部状态的协调等,而且在 App 端的消息推送、聊天信息、直播弹字幕等场景长连接服务也是优选方案。长连接服务的重要性也...
继续阅读 »
美图长连接服务简介
随着科技的飞速发展,技术的日新月异,长连接的运用场景日益增多。不仅在后端服务中被广泛运用,比较常见的有数据库的访问、服务内部状态的协调等,而且在 App 端的消息推送、聊天信息、直播弹字幕等场景长连接服务也是优选方案。长连接服务的重要性也在各个场合被业界专家不断提及,与此同时也引起了更为广泛地关注和讨论,各大公司也开始构建自己的长连接服务。

6.jpg



美图公司于2016 年初开始构建长连接服务,与此同时, Go 在编程语言领域异军突起,考虑到其丰富的编程库,完善的工具链,简单高效的并发模型等优势,使我们最终选择 Go 去作为实现长连接服务的语言。在通信协议的选择上,考虑到 MQTT 协议的轻量、简单、易于实现的优点,选择了 MQTT 协议作为数据交互的载体。其整体的架构会在下文中做相应地介绍。

美图长连接服务(项目内部代号为bifrost )已经历时三年,在这三年的时间里,长连接服务经过了业务的检验,同时也经历了服务的重构,存储的升级等,长连接服务从之前支持单机二十几万连接到目前可以支撑单机百万连接。在大多数长连接服务中存在一个共性问题,那就是内存占用过高,我们经常发现单个节点几十万的长连接,内存却占用十几G 甚至更多,有哪些手段能降低内存呢?

本文将从多个角度介绍长连接服务在内存优化路上的探索,首先会先通过介绍当前服务的架构模型,Go 语言的内存管理,让大家清晰地了解我们内存优化的方向和关注的重要数据。后面会重点介绍我们在内存优化上做的一些尝试以及具体的优化手段,希望对大家有一定的借鉴意义。

架构模型

一个好的架构模型设计不仅能让系统有很好的可扩展性,同时也能在服务能力上有很好的体现。除此之外,在设计上多考虑数据的抽象、模块的划分、工具链的完善,这样不仅能让软件具有更灵活的扩展能力、服务能力更高,也提高系统的稳定性和健壮性以及可维护性。

在数据抽象层面抽象pubsub 数据集合,用于消息的分发和处理。模块划分层面我们将服务一分为三:内部通讯(grpcsrv)、外部服务(mqttsrv)、连接管理(session)。工具链的方面我们构建了自动化测试,系统 mock ,压测工具。美图长连接服务架构设计如下:图一架构图从架构图中我们可以清晰地看到由7 个模块组成,分别是:conf 、grpcsrv 、mqttsrv、session、pubsub、packet、util ,每个模块的作用如下:


1.jpg



conf :配置管理中心,负责服务配置的初始化,基本字段校验。

grpcsrv :grpc 服务,集群内部信息交互协调。

mqttsrv :mqtt 服务,接收客户端连接,同时支持单进程多端口 MQTT 服务。

session :会话模块,管理客户端状态变化,MQTT 信息的收发。

pubsub :发布订阅模块,按照 Topic 维度保存 session 并发布 Topic 通知给 session。

packet:协议解析模块,负责 MQTT 协议包解析。

util :工具包,目前集成监控、日志、grpc 客户端、调度上报四个子模块。

Go 的内存管理

众所周知,Go 是一门自带垃圾回收机制的语言,内存管理参照 tcmalloc 实现,使用连续虚拟地址,以页( 8k )为单位、多级缓存进行管理。针对小于16 byte 直接使用Go的上下文P中的mcache分配,大于 32 kb 直接在 mheap 申请,剩下的先使用当前 P 的 mcache 中对应的 size class 分配 ,如果 mcache 对应的 size class 的 span 已经没有可用的块,则向 mcentral 请求。如果 mcentral 也没有可用的块,则向 mheap 申请,并切分。如果 mheap 也没有合适的 span,则向操作系统申请。

Go 在内存统计方面做的也是相当出色,提供细粒度的内存分配、GC 回收、goroutine 管理等统计数据。在优化过程中,一些数据能帮助我们发现和分析问题,在介绍优化之前,我们先来看看哪些参数需要关注,其统计参数如下:

go_memstats_sys_bytes :进程从操作系统获得的内存的总字节数 ,其中包含 Go 运行时的堆、栈和其他内部数据结构保留的虚拟地址空间。

go_memstats_heap_inuse_bytes:在 spans 中正在使用的字节。其中不包含可能已经返回到操作系统,或者可以重用进行堆分配,或者可以将作为堆栈内存重用的字节。

go_memstats_heap_idle_bytes:在 spans 中空闲的字节。

go_memstats_stack_sys_bytes:栈内存字节,主要用于 goroutine 栈内存的分配。

在内存监控中根据Go 将堆的虚拟地址空间划分为 span ,即对内存8K或更大的连续区域进行统计。span 可能处于以下三种状态之一 :

idle 不包含对象或其他数据,空闲空间的物理内存可以释放回 OS (但虚拟地址空间永远不会释放),或者可以将其转换为使用中或栈空间;

inuse 至少包含一个堆对象,并且可能有空闲空间来分配更多的堆对象;

stack span 用于 goroutine 栈,栈不被认为是堆的一部分。span 可以在堆和堆栈内存之间更改,但它从来不会同时用于两者。

此外有一部分统计没有从堆内存中分配的运行时内部结构(通常因为它们是实现堆的一部分),与堆栈内存不同,分配给这些结构的任何内存都专用于这些结构,这些主要用于调试运行时内存开销。

虽然Go 拥有了丰富的标准库、语言层面支持并发、内置runtime,但相比C/C++ 完成相同逻辑的情况下 Go 消耗内存相对增多。在程序的运行过程中,它的 stack 内存会随着使用而自动扩容,但在 stack 内存回收采用惰性回收方式,一定程度的导致内存消耗增多,此外还有GC 机制也会带来额外内存的消耗。

Go 提供了三种内存回收机制:定时触发,按量触发,手动触发。在内存垃圾少量的情况下,Go 可以良好的运行。但是无论采用哪种触发方式,由于在海量用户服务的情况下造成的垃圾内存是巨大的,在 GC 执行过程中服务都会感觉明显的卡顿。这些也是目前长连接服务面对的难题,在下文中我将会逐一介绍我们如何减少和解决问题的产生的具体实践。

优化之路

在了解架构设计、Go 的内存管理、基础监控后,相信大家已经对当前系统有了一个大致的认识,先给大家展示一下内存优化的成果,下表一是内存优化前后的对比表,在线连接数基本相同的情况下,进程内存占用大幅度降低,其中 stack 申请内存降低约 5.9 G,其次 heap 使用内存降低 0.9 G,other 申请内存也小幅下降。那么我们是如何做到内存降低的呢?那接下来我将会把我们团队关于进行内存优化的探索和大家聊一聊。


2.jpg



在优化前随机抽取线上一台机器进行分析内存,通过监控发现当前节点进程占用虚拟内存为22.3 G,堆区使用的内存占用 5.2 G ,堆区未归还内存为 8.9 G,栈区内存为 7.25 G,其它约占用 0.9 G,连接数为 225 K。

我们简单进行换算,可以看出平均一个链接占用的内存分别为:堆:23K,栈:32K。通过对比业内长连接服务的数据可以看出单个链接占用的内存偏大,根据监控数据和内存分配原理分析主要原因在:goroutine 占用、session 状态信息、pubsub 模块占用,我们打算从业务、程序、网络模式三个方面进行优化。

业务优化

上文中提到 session 模块主要是用于处理消息的收发,在实现时考虑到在通常场景中业务的消息生产大于客户端消息的消费速度的情况,为了缓解这种状况,设计时引入消息的缓冲队列,这种做法同样也有助于做客户端消息的流控。

缓冲消息队列借助chan 实现 ,chan 大小根据经验将初始化默认配置为 128 。但在目前线上推送的场景中,我们发现,消息的生产一般小于消费的速度,128 缓冲大小明显偏大,因此我们把长度调整为 16 ,减少内存的分配。

在设计中按照topic 对客户端进行分组管理的算法中,采用空间换时间的方式,组合 map 和 list 两种数据结构对于客户端集合操作提供O(1)的删除、O(1)的添加、O(n)的遍历。数据的删除采用标记删除方式,使用辅助 slice 结构进行记录,只有到达预设阈值才会进行真正的删除。虽然标记删除提高了遍历和添加的性能,但也同样带来了内存损耗问题。

大家一定好奇什么样的场景需要提供这样的复杂度,在实际中其场景有以下两种情况:

在实际的网络场景中,客户端随时都可能由于网络的不稳定断开或者重新建联,因此集合的增加和删除需要在常数范围内。

在消息发布的流程中,采用遍历集合逐一发布通知方式,但随着单个topic 上的用户量的增加,经常会出现单个 topic 用户集合消息过热的问题,耗时太久导致消息挤压,因此针对集合的遍历当然也要求尽量快。

通过benchamrk 数据分析,在标记回收 slice 长度在 1000 时,可以提供最佳的性能,因此默认配置阈值为 1000。在线上服务中,无特殊情况都是采用默认配置。但在当前推送服务的使用中,发现标记删除和延迟回收机制好处甚微,主要是因为 topic 和客户端为 1 : 1 方式,也就是不存在客户端集合,因此调整回收阈值大小为 2,减少无效内存占用。

上述所有优化,只要简单调整配置后服务灰度上线即可,在设计实现时通过conf 模块动态配置,降低了服务的开发和维护成本。通过监控对比优化效果如下表,在优化后在线连接数比优化的在线连接更多的情况下, heap 使用内存使用数量由原来的 4.16G 下降到了 3.5G ,降低了约 0.66 G。


3.jpg



golang 代码优化

在实现上面展示的架构的时候发现在session 模块 和 mqttsrv 模块之间存在很多共享变量,目前实现方式都是采用指针或者值拷贝的,由于 session的数量和客户端数据量成正比也就导致消耗大量内存用于共享数据,这不仅仅增加 GC 压力,同样对于内存的消耗也是巨大的。就此问题思考再三,参考系统的库 context 的设计在架构中也抽象 context 包负责模块之间交互信息传递,统一分配内存。此外还参考他人减少临时变量的分配的优化方式,提高系统运行效率。主要优化角度参考如下:

在频繁申请内存的地方,使用pool 方式进行内存管理

小对象合并成结构体一次分配,减少内存分配次数

缓存区内容一次分配足够大小空间,并适当复用

slice 和 map 采 make 创建时,预估大小指定容量

调用栈避免申请较多的临时对象

减少[]byte 与 string 之间转换,尽量采用 []byte 来字符串处理

目前系统具被完备的单元测试、集成测试,因此经过一周的快速的开发重构后灰度上线监控数据对比如下表:在基本相同的连接数上,heap 使用内存约占用降低 0.27G,stack 申请内存占用降低 3.81G。为什么 stack 会大幅度降低呢?

通过设置stackDebug 重新编译程序追查程序运行过程,优化前 goroutine 栈的大多数在内存为 16K,通过减少临时变量的分配,拆分大函数处理逻辑,有效的减少触发栈的内存扩容(详细分析见参考文章),优化后 goroutine 栈内存降低到 8 K。一个连接需要启动两个 goroutine 负责数据的读和写,粗略计算一个连接减少约 16 K 的内存,23 w 连接约降低 3.68 G 内存。


4.jpg



网络模型优化

在Go 语言的网络编程中经典的实现都是采用同步处理方式,启动两个 goroutine 分别处理读和写请求,goroutine 也不像 thread ,它是轻量级的。但对于一百万连接的情况,这种设计模式至少要启动两百万的 goroutine,其中一个 goroutine 使用栈的大小在 2 KB 到 8KB, 对于资源的消耗也是极大的。在大多数场景中,只有少数连接是有数据处理,大部分 goroutine 阻塞 IO 处理中。在因此可以借鉴 C 语言的设计,在程序中使用 epoll 模型做事件分发,只有活跃连接才会启动 goroutine 处理业务,基于这种思想修改网络处理流程。

网络模型修改测试完成后开始灰度上线,通过监控数据对比如下表:在优化后比优化前的连接数多10 K的情况下,heap 使用内存降低 0.33 G,stack 申请内存降低 2.34 G,优化效果显著。


5.jpg



总结

在经过业务优化,临时内存优化,网络模型优化操作后,线上服务保证21w 长连接在线实际内存占用约为 5.1 G。简单进行压测 100w 连接只完成建立连接,不进行其他操作约占用 10 G。长连接服务内存优化已经取得阶段性的成功,但是这仅仅是我们团队的一小步,未来还有更多的工作要做:网络链路、服务能力,存储优化等,这些都是亟待探索的方向。如果大家有什么好的想法,欢迎与我们团队分享,共同探讨。

bifrost项目目前我们有开源计划,敬请大家期待。

参考文章

go tool pprof 使用介绍 :https://segmentfault.com/a/1190000016412013

Go 内存监控介绍:https://golang.org/src/runtime/mstats.go

Go 内存优化介绍:https://blog.golang.org/profiling-go-programs

高性能Go服务内存分配:https://segment.com/blog/allocation-efficiency-in-high-performance-go-services

Go stack 优化分析:https://studygolang.com/article 收起阅读 »

【源码下载】一款使用环信SDK实现的开源--社交demo

React webIm demo简介   --(集成环信SDK) webIm demo 是基于环信sdk开发的一款具有单聊、群聊、聊天室、音视频等功能的应用,为了react用户能够快速集成环信 im sdk和音视频sdk,我们特使用了re...
继续阅读 »
React webIm demo简介   --(集成环信SDK)

webIm demo 是基于环信sdk开发的一款具有单聊、群聊、聊天室、音视频等功能的应用,为了react用户能够快速集成环信 im sdk和音视频sdk,我们特使用了react全家桶,为大家提供参考。

同时我们也提供了[Vue版demo]  (https://github.com/easemob/webim-vue-demo)。

项目截图:


16df69ea7a91facd.jpg




16df6a2287ccb151.jpg




16df6a550a6bd226.jpg




16df6a87eaa8c068.jpg




16df6aef07a80553.jpg




16df6b33eaa4edfd.jpg



项目地址:https://github.com/easemob/webim


开发环境

完全基于React + Redux的单向数据流,引入ant-design组件库。 

响应式布局, 一套Demo同时支持PC和H5,自适应不同终端屏幕尺寸

支持所有的现代浏览器(不支持IE6-11)




初始化安装
- 在/demo目录下执行  
npm i

- 运行demo
- cd demo && npm start (requires node@>=6

http://localhost:3001
- cd demo && HTTPS=true npm start (webrtc supports HTTPS only)

https://localhost:3001

注意:只有在https的情况才支持语音视频功能
 

打包发布demo
cd demo && npm run build 
/demo/build 目录下的就是可以运行和部署的版本


可能遇见的问题:

1. 如果在npm i的过程中遇到
> phantomjs-prebuilt@2.1.14 install /Users/will/work/my-project/node_modules/phantomjs-prebuilt> node install.jsPhantomJS not found on PATHDownloading https://github.com/Medium/phantomjs/releases/download/v2.1.1/phantomjs-2.1.1-macosx.zipSaving to /var/folders/mh/2ptfthxj2qb49jscj1b0gjsm0000gn/T/phantomjs/phantomjs-2.1.1-macosx.zipReceiving...Error making request.Error: connect ETIMEDOUT 54.231.113.227:443    at Object.exports._errnoException (util.js:1018:11) at exports._exceptionWithHostPort (util.js:1041:20) at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1090:14)

FIX: 这个问题,可以尝试 PHANTOMJS_CDNURL=https://npm.taobao.org/mirrors/phantomjs/ npm install --save-dev phantomjs-prebuilt 来解决

2. 执行npm start时如果出现
> node scripts/start.js/Users/wenke/www/web-im/demo/scripts/start.js:23const { ^SyntaxError: Unexpected token { at exports.runInThisContext (vm.js:53:16) at Module._compile (module.js:373:25) at Object.Module._extensions..js (module.js:416:10)    at Module.load (module.js:343:32) at Function.Module._load (module.js:300:12) at Function.Module.runMain (module.js:441:10) at startup (node.js:139:18) at node.js:974:3

FIX: 请检查node版本是否是v6.0

项目模块

本项目包含两部分:
一部分是项目主模块,这部分主要包含了项目的业务逻辑,比如增,删好友、音视频聊天、信息修改、群设置等

另一部分是 环信sdk集成(包含音视频sdk)



16e1ba1c27a529fe.jpg



src项目结构


16e1bd9c61944e5f.jpg




更多关于环信sdk[集成文档]
http://docs-im.easemob.com/im/web/intro/start




参与贡献
如果你有什么好的想法,或者好的实现,可以通过下边的步骤参与进来,让我们一起把这个项目做得更好,欢迎参与
1.Fork本仓库
2.新建feature_xxx分支 (单独创建一个实现你自己想法的分支)
3.提交代码
4.新建Pull Request
5.等待我们的Review & Merge


最后的最后如果你有更好的建议,或者你的疑惑,请随时给我留言。

  收起阅读 »

【源码下载】一款使用环信SDK实现的开源--社交demo

React webIm demo简介   --(集成环信SDK) webIm demo 是基于环信sdk开发的一款具有单聊、群聊、聊天室、音视频等功能的应用,为了react用户能够快速集成环信 im sdk和音视频sdk,我们特使用了react全家桶,为大家...
继续阅读 »
React webIm demo简介   --(集成环信SDK)

webIm demo 是基于环信sdk开发的一款具有单聊、群聊、聊天室、音视频等功能的应用,为了react用户能够快速集成环信 im sdk和音视频sdk,我们特使用了react全家桶,为大家提供参考。
  • 同时我们也提供了[Vue版demo]  https://github.com/easemob/webim-vue-demo


项目截图:



16df69ea7a91facd.jpg




16df6a2287ccb151.jpg




16df6a550a6bd226.jpg




16df6a87eaa8c068.jpg




16df6aef07a80553.jpg




16df6b33eaa4edfd.jpg




项目地址:https://github.com/easemob/webim 

开发环境:
完全基于React + Redux的单向数据流,引入ant-design组件库。 

响应式布局, 一套Demo同时支持PC和H5,自适应不同终端屏幕尺寸

支持所有的现代浏览器(不支持IE6-11)

初始化安装:
- 在/demo目录下执行  
npm i
- 运行demo
- cd demo && npm start (requires node@>=6) http://localhost:3001注意:只有在https的情况才支持语音视频功能
- 打包发布demo
cd demo && npm run build /demo/build 目录下的就是可以运行和部署的版本
可能遇见的问题:
1. 如果在npm i的过程中遇到
> phantomjs-prebuilt@2.1.14 install /Users/will/work/my-project/node_modules/phantomjs-prebuilt> node install.jsPhantomJS not found on PATHDownloading https://github.com/Medium/phantomjs/releases/download/v2.1.1/phantomjs-2.1.1-macosx.zipSaving to /var/folders/mh/2ptfthxj2qb49jscj1b0gjsm0000gn/T/phantomjs/phantomjs-2.1.1-macosx.zipReceiving...Error making request.Error: connect ETIMEDOUT 54.231.113.227:443 at Object.exports._errnoException (util.js:1018:11) at exports._exceptionWithHostPort (util.js:1041:20) at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1090:14)FIX:
这个问题,可以尝试 PHANTOMJS_CDNURL=https://npm.taobao.org/mirrors/phantomjs/ npm install --save-dev phantomjs-prebuilt 来解决
2. 执行npm start时如果出现
> node scripts/start.js/Users/wenke/www/web-im/demo/scripts/start.js:23const { ^SyntaxError: Unexpected token { at exports.runInThisContext (vm.js:53:16) at Module._compile (module.js:373:25) at Object.Module._extensions..js (module.js:416:10) at Module.load (module.js:343:32) at Function.Module._load (module.js:300:12) at Function.Module.runMain (module.js:441:10) at startup (node.js:139:18) at node.js:974:3 FIX: 请检查node版本是否是v6.0+ 
项目模块
本项目包含两部分:一部分是项目主模块,这部分主要包含了项目的业务逻辑,比如增,删好友、音视频聊天、信息修改、群设置等
另一部分是 环信sdk集成(包含音视频sdk)



16e1ba1c27a529fe.jpg




src项目结构



16e1bd9c61944e5f.jpg




更多关于环信sdk[集成文档]
http://docs-im.easemob.com/im/web/intro/start 


参与贡献
如果你有什么好的想法,或者好的实现,可以通过下边的步骤参与进来,让我们一起把这个项目做得更好,欢迎参与
1.Fork本仓库
2.新建feature_xxx分支 (单独创建一个实现你自己想法的分支)
3.提交代码
4.新建Pull Request
5.等待我们的Review & Merge
收起阅读 »

获取好友列表总是失败呢,type=16

      明明是先登陆,后监听,再是获取好友列表。怎么会先执行第三步呢?


1.png


 


2.png


 
 
明明是先登陆,后监听,再是获取好友列表。怎么会先执行第三步呢?

webim 怎么样输出最近联系人到h5页面

webim 怎么样输出最近联系人到h5页面
webim 怎么样输出最近联系人到h5页面

客服云 查看IM用户上传推送证书

                                                进入appkey console后台查看已注册用户 上传推送证书 ****1.快速创建的关联 切换到管理员模式--渠道管理---手机APP  APP信息页面点...
继续阅读 »
                                                进入appkey console后台查看已注册用户 上传推送证书
****1.快速创建的关联
切换到管理员模式--渠道管理---手机APP 
APP信息页面点击 蓝色字体  直接登录IM关联后台,可以进入快速创建的关联后台,点击appkey后可查看IM用户和证书信息

29-1进入关联后台.png




29-2进入关联后台.png




29-3进入关联后台.png


****2.手动绑定的管理 需要单独登录IMconsole后台查看
IMconsole后台登录地址:
https://console.easemob.com/user/login  收起阅读 »

客服云 查看有哪些会话在待接入排队过

                                                        查看有哪些会话在待接入排队过   (管理员模式--会话统计--排队统计)可以查看该报表,此报表为增值功能,标准版坐席没有,需要单独联系商务经理开通...
继续阅读 »
                                                        查看有哪些会话在待接入排队过 
 (管理员模式--会话统计--排队统计)可以查看该报表,此报表为增值功能,标准版坐席没有,需要单独联系商务经理开通
文档:
http://docs.easemob.com/cs/200admin/90statistics#%E6%8E%92%E9%98%9F%E7%BB%9F%E8%AE%A1
另外该 报表中的排队次数,可以点击该数量之后弹窗显示相关会话数据


排队报表5-1.png




排队报表5-2.png


相关会话可以导出后查看具体信息
弹窗列表显示的 数据:
客服一列(如果排队的会话被客服接起这里会显示接待的客服,如果没有任何一个客服接入过直接被关闭的显示为空)
开始时间(表示客服接入或者转入会话的时间,如果没有被客服接入过,显示为空)
会话标签(如果没有打过标签,此处也为空) 收起阅读 »

IM 和 客服 并存开发指南—iOS篇

 如果觉得哪里描述的不清晰,可评论内指出,会不定期更新。  一、SDK 介绍       HelpDesk.framework 为 客服SDK(带实时音视频)       HelpDeskLite.framework 为 客服SDK(不带实时音视频)    ...
继续阅读 »
 如果觉得哪里描述的不清晰,可评论内指出,会不定期更新。

 一、SDK 介绍
      HelpDesk.framework 为 客服SDK(带实时音视频)
      HelpDeskLite.framework 为 客服SDK(不带实时音视频)
      Hyphenate.framework 为 IM SDK(带实时音视频)
      HyphenateLite.framework 为 IM SDK(不带实时音视频)
      环信客服SDK 基于 IM SDK 3.x , 如果同时集成 客服 和 IM,只需要在初始化、登录、登出操作时使用客服SDK 提供的相应API,IM 的其他API均不受影响。
      UI 部分集成需要分别导入 HelpDeskUI 和 IM demo 中的UI文件(也可以自定义UI)。 下面详细介绍IM 和 客服共存的开发步骤。

二、注意事项
      1、开发过程中,初始化、登录和登出,务必只使用客服访客端SDK的API。
      2、需要联系商务开通客服长连接。
           不开通长连接,会出现用户长时间(一天或几天)不使用app,再打开app会无法正常使用im相关功能的问题,报错信息一般是User is not login。
      3、IM SDK 和客服SDK 都包括了模拟器的CPU 架构,在上传到app store时需要剔除模拟器的CPU 架构,保留  armv7、arm64,参考文档:上传appstore以及打包ipa注意事项。 

三、资源准备
      到环信官网下载客服访客端的开源的商城Demo源码 + SDK,下载链接:http://www.easemob.com/download/cs  选  择“iOS SDK”下载(如下图)。
      

下载客服.png


      到环信官网下载IM的开源的Demo源码 + SDK ,下载链接:http://www.easemob.com/download/im 选择 iOS SDK(如下图)。
      

下载IM.png



下载的 IM SDK+Demo 和 客服SDK+Demo 中都有 IM 的 Hyphenate.framework 或 HyphenateLite.framework,为了保持版本的匹配,我们只使用 IM Demo 中的 UI, 而不使用 IM SDK 中 的 Hyphenate.framework 或 HyphenateLite.framework 文件。

四、集成步骤
      1、阅读客服访客端SDK集成文档,集成客服,地址:http://docs.easemob.com/cs/300visitoraccess/iossdk。 
      2、阅读 IM 的集成文档,地址:http://docs-im.easemob.com/im/ios/sdk/prepare 
      3、将 IM Demo 中的 UI 文件按照自己的需求分模块导入到工程中
      4、将 IM 的 UI 所依赖的第三方库集成到项目中(IM集成文档内有说明)
      5、在pch文件中引入 EMHeaders.h 
          #ifdef __OBJC__ 
            //包含实时音视频功能 
            #import  
            // 若不包含实时音视频,则替换为 
            // #import  
            #import "HelpDeskUI.h" 
            #import "EMHeaders.h" 
         #endif
      6、由于HelpDeskUI 和 IM UI 中都使用了 第三方库,如果工程中出现三方库重复的问题,可将重复文件删除,如果部分接口已经升级或弃用可自行升级、调整。

提供的兼容Demo介绍:
     1、Demo集成了初始化sdk、登录、退出登录、IM单聊、联系客服的简单功能,处理了第三方库冲突的问题。
     2、pch文件中的appkey等信息需要换成开发者自己的。
     3、Demo源码下载地址: https://pan.baidu.com/s/1v1TUl-fqJNLQrtsJfWYGzw 
         提取码: kukb  收起阅读 »

客服云配置机器人自定义菜单

                                                                   设置机器人自定义菜单 1》管理员模式---智能机器人 新建机器人 2》管理员模式----设置----会话分配规则  ...
继续阅读 »
                                                                   设置机器人自定义菜单
1》管理员模式---智能机器人 新建机器人

1-1设置机器人自定义菜单.png


2》管理员模式----设置----会话分配规则  渠道指定,添加访客发起会话的渠道(例如APP和网页),全天指定机器人

1-2设置机器人自定义菜单.png


 
     修改路由规则后,需要在管理员模式--当前会话  手动关闭旧会话,新会话才能重新调度
3》 企业版机器人:管理员模式---智能机器人  点击【机器人管理】,新页面跳转到企业版机器人管理平台  
     知识管理模块----菜单管理
    旧版机器人:管理员模式--智能机器人  点击自定义菜单页签

1-3设置机器人自定义菜单.png




1-4设置机器人自定义菜单.png




1-5设置机器人自定义菜单.png




1-6设置机器人自定义菜单.png




1-7设置机器人自定义菜单.png


 
4》机器人的自定义菜单需要设置为默认回复或者欢迎语才能触发
以下是设置为默认回复:点击蓝色 添加默认回复按钮,选择类型为菜单

1-8设置机器人自定义菜单.png




1-9设置机器人自定义菜单.png




1-10设置机器人自定义菜单.png


 
以下是配置为机器人的欢迎语

1-11设置机器人自定义菜单.png



 
点击蓝色添加按钮,同样选择为菜单类型,选择刚创建的自定义菜单,最后保存(注意设置为机器人欢迎语后:只有网页会生效显示,APP需要单独代码集成,微信和微博不支持机器人欢迎语)

1-12设置机器人自定义菜单.png




1-13设置机器人自定义菜单.png


 
注意:如果新建自定义菜单的时候只新建了菜单名,没有设置菜单项,上图界面是无法点击保存按钮的。需到自定义菜单页面给菜单添加菜单项。

1-14设置机器人自定义菜单.png




1-15设置机器人自定义菜单.png


 
以上是配置好的截图,就可以发起新会话测试了
5》测试:以下是网页测试截图 
h5链接在:管理员模式--渠道管理-网站 接入方式  页面 ,点击直接打开h5链接按钮即可

1-16设置机器人自定义菜单.png


 
 附:企业版机器人基础配置文档:http://docs-ai.easemob.com/cs/preliminary    
        客服系统旧版机器人文档:http://docs.easemob.com/cs/200admin/40robot#%E6%99%BA%E8%83%BD%E6%9C%BA%E5%99%A8%E4%BA%BA
  收起阅读 »

客服云访客发送订单或者轨迹消息后不展示

                                                  访客端发送订单或者轨迹消息后不显示 默认是发送后访客端删除本条消息,可以修改  Android ChatrowOrder 查找removeMessage ...
继续阅读 »
                                                  访客端发送订单或者轨迹消息后不显示
默认是发送后访客端删除本条消息,可以修改 
Android
ChatrowOrder 查找removeMessage

24-1访客端发送订单轨迹后不显示.png



网页修改   
管理员模式--设置--系统开关 访客端同步展示轨迹消息 开关打开

24-2访客端发送订单轨迹后不显示.png


  收起阅读 »

客服云自定义角色权限

****1.新建自定义角色 管理员模式----设置---权限管理 页面右上角 添加角色 需要填写角色名称,角色权限 分为两大类:管理员模式 和客服模式 这两种模式的展开项分别对应两种模式下的所有功能模块(按需勾选)  以下操作以新建一个角色权限为:...
继续阅读 »
****1.新建自定义角色
管理员模式----设置---权限管理 页面右上角 添加角色

30-1自定义角色权限.png



需要填写角色名称,角色权限 分为两大类:管理员模式客服模式
这两种模式的展开项分别对应两种模式下的所有功能模块(按需勾选)
 以下操作以新建一个角色权限为:客服模式下所有功能,管理员模式下只有质检功能 为例:
   **第一:客服模式根目录直接勾选

30-2自定义角色权限.png


   **第二:管理员模式  点击 展示所有功能模块,只需要勾选 质检功能的3个复选框

30-3自定义角色权限.png


       同时可以规则新角色的数据权限:租户或者客服

30-6自定义角色权限(改数据权限).png



 
   **第三:最后操作保存(tips:创建的自定义角色不能超过20个)
****2.给客服修改权限
管理员模式---成员管理--客服 
进入客服列表页面,选择要修改的客服,选项 一列点击编辑按钮

30-4自定义角色权限.png


 
弹出客服信息修改对话框,滑动到底部
修改角色,最后保存

30-5自定义角色权限.png


 
客服如果是登录状态,会自动给客服发送通知,客服需重新登录系统,新角色权限可以生效。 收起阅读 »

客服云启用坐席

                                                                                         启用坐席 1》管理员可以启用或禁用其他管理员和客服账户。一个租户下,在同一时间,最...
继续阅读 »
                                                                                         启用坐席
1》管理员可以启用或禁用其他管理员和客服账户。一个租户下,在同一时间,最大启用数即为该租户的“购买坐席数”。
注:可以进入“管理员模式 > 设置 > 企业信息”页面,查看您的租户的“购买坐席数”和“账户到期日”。 
2》如果您的租户只购买了一个坐席,就只能使用当前管理员账号,不能操作禁用当前启用其他账号,一个租户至少 得有一个管理员权限的账号
3》管理员账号也占用一个坐席,可以切换到客服模式接会话
管理员模式--成员管理--客服列表 账户启用一列 可以启用或者禁用坐席账号(见下图)

9-1启用坐席.png


  收起阅读 »

客服云 配置机器人接会话

****管理员模式--智能机器人 新建机器人,从机器人信息页面,点击蓝色字体:机器人管理,可跳转到机器人操作平台 ****管理员模式----设置----会话分配规则  渠道:添加访客发起会话的渠道,可修改为全天机器人接入 ***...
继续阅读 »
****管理员模式--智能机器人 新建机器人,从机器人信息页面,点击蓝色字体:机器人管理,可跳转到机器人操作平台

28-1配置机器人接会话.png



****管理员模式----设置----会话分配规则  渠道:添加访客发起会话的渠道,可修改为全天机器人接入

28-2配置机器人接会话.png


****跳转到机器人平台添加知识规则或自定义菜单
1》添加知识规则

28-3配置机器人接会话.png



2》添加自定义菜单

28-4配置机器人接会话.png


 
注意:添加自定义菜单后,需要将该自定义菜单设置为机器人 的默认回复才可能触发(添加默认回复,选择为菜单类型,之后选择刚新建的自定义菜单,最后保存)

28-5配置机器人_接会话.png




     更多机器人配置文档参见
http://docs-ai.easemob.com/cs/preliminary#%E5%9F%BA%E7%A1%80%E6%93%8D%E4%BD%9C
****发起会话测试
修改路由规则的配置后,新会话才能生效。可以先到管理员模式--当前会话手动关闭旧会话, 新会话测试

28-6配置机器人接会话.png


  收起阅读 »

客服云 修改语言

*****修改客服系统语言********   客服系统切换语言(支持中英文切换) 1.登录的时候 浏览器打开 https://kefu.easemob.com/ 界面右上角可以切换语言 2.如果是已登录状态,切换到客服模式 客服模式--客服信息...
继续阅读 »
*****修改客服系统语言********
  客服系统切换语言(支持中英文切换)

1.登录的时候
浏览器打开 https://kefu.easemob.com/
界面右上角可以切换语言

27-1修改语言.png



2.如果是已登录状态,切换到客服模式
客服模式--客服信息 语言一栏可以切换,最后需点击右下角保存

27-2修改语言.png


*********************************************************************
************网页集成切换到英文**************
webim 后面添加/en-US/

27-3修改语言.png



切换后界面如下

27-4修改语言.png


  收起阅读 »

客服云网页下班留言配置

                                                                              网页下班留言配置 网页渠道:如果下班时间的路由规则是指定的技能组,可以从网页插件页面配置 下班时间让访...
继续阅读 »
                                                                              网页下班留言配置
网页渠道:如果下班时间的路由规则是指定的技能组,可以从网页插件页面配置 下班时间让访客直接进入留言界面,这样下班时间访客无法发起会话,只能提交留言(注意:网页端访客暂时无法收到客服和管理员对留言的评论,需要通过电话或邮箱联系访客。 )
如需修改,客服系统 切换到管理员模式--渠道管理--网站 --- 功能设置页签,找到 下班留言配置:可修改为 展示默认消息或进入聊天窗,最后保存。访客端需重新加载页面


26-1网页下班留言配置.png


  收起阅读 »

客服云 访客发送的消息客服系统看不到排查

                              访客发送的消息客服系统看不到 1. 先到 管理员模式--搜索  点击消息按钮,输入访客发送的消息内容 查询看下,默认是搜索的近7天的消息,如果时间不在7天内,可以重新点击筛选排序,放大时间范围筛选 。...
继续阅读 »
                              访客发送的消息客服系统看不到
1. 先到 管理员模式--搜索  点击消息按钮,输入访客发送的消息内容 查询看下,默认是搜索的近7天的消息,如果时间不在7天内,可以重新点击筛选排序,放大时间范围筛选 。若可以查询到,点击会话后查看是哪一个客服接入的

22-1访客发送的消息客服系统看不到.png


如果直接无法看到是哪一个客服接入的,复制会话ID 到历史会话 ID一栏,查询出具体会话,点击进入会话详情页,看右侧记录一栏的数据,可以看出是哪一个客服接入的没有回复造成的

22-2访客发送的消息客服系统看不到.png



注:如果是该客服没有上线造成的没有回复消息。需要客服前一天退出登录前手动关闭自己进行中所有会话,这样才能保证新会话能自动调度给上线的客服
也可以通过配置 不活跃会话超时结束 或 访客超时未回复结束会话实现关闭会话(管理员模式--设置--系统开关 页面
2.排查访客是否被加入黑名单
管理员模式---客户中心--客户信息 黑名单列表可以查看。
黑名单中的客户可以再次发送消息,但系统不会为其创建会话;客户被移除黑名单后,再次发送消息时可以成功创建会话。 
如果用户是在黑名单中,可以先移除黑名单后再测试。

22-3访客发送的消息客服系统看不到.png



3. 看 是否达到了待接入上限
   待接入上限:每个租户如果坐席数量 小于等于5,允许的最大待接入会话数为1000,如果某个租户下坐席数超过5个,则该租户的最大待接入会话数为坐席数x200。
  待接入会话数超过上限后,不允许访客创建新的会话。
   管理员模式--待接入 模块可以查看排队的会话量
   

待接入.png


 
如果仍无法解决,可以工作时间登录客服系统,切到管理员模式 --右上角 技术支持--联系客服 发起会话咨询。 收起阅读 »

客服云查看会话满意度评价

                                                                          查看会话的满意度评价 两种情况:查看具体某一个会话的评分或者是查看系统对所有会话评分的统计结果 1》查看具体...
继续阅读 »
                                                                          查看会话的满意度评价
两种情况:查看具体某一个会话的评分或者是查看系统对所有会话评分的统计结果
1》查看具体某一个会话的评分
结束的会话才能到历史会话看评分。先把会话关闭掉,之后可以到管理员模式---历史会话模块,列表直接有评分一列的数据。如果列表不显示关闭的会话,可以点击右上角筛选排序,重新指定条件筛选出会话(可选择按会话结束时间筛选)

查看评价19-1.png


或者点击一个会话,进入会话详情页,看右侧指标一栏(如下图)

查看评价19-2.png



2》查看系统对会话的评价总统计结果
管理员模式--会话统计--工作质量 页面,页面顶部的满意度评分是系统的会话评价统计,页面底部是对每一个客服服务的评分统计(见下图)

查看评价19-3.png




查看评价19-4.png


  收起阅读 »

客服云 客服管理操作

1》添加客服 管理员模式--成员管理--客服 右上角 添加客服按钮 添加时可以设置 客服账号邮箱密码、昵称、最大接待人数、权限等信息。如果需要重置坐席密码也可在此页面操作。 2》修改客服状态 管理员可以修改客服或其他管理员的在线状态,支持在...
继续阅读 »
1》添加客服
管理员模式--成员管理--客服 右上角 添加客服按钮
添加时可以设置 客服账号邮箱密码、昵称、最大接待人数、权限等信息。如果需要重置坐席密码也可在此页面操作。

客服操作17-1.png


2》修改客服状态
管理员可以修改客服或其他管理员的在线状态,支持在空闲、忙碌、隐身、离开状态之间切换。在客服列表中点击“在线状态”一列的下拉按钮,修改客服的在线状态。
注:若客服离线,则不可强行修改其状态为在线。暂不支持自定义角色修改管理员或客服的在线状态。

客服操作17-2.png


 
3》设置客服最大接待人数
最大接待人数可以由管理员统一操作也可以由每一个客服自行调整自己的数量(二者只能选其一)
 ----1---统一由管理员调整
管理员模式--设置--系统开关 客服自定义最大接待人数 需要关闭。之后管理员在 成员管理--客服 页面点击编辑按钮,弹窗后可以修改客服的最大接待人数 ,最后保存

设置客服最大接待人数18-1.png



----2---每一个客服自行修改自己的最大接待人数
管理员模式--设置--系统开关 客服自定义最大接待人数 需要开启。修改后客服模式 最大接待人数为可调状态

设置客服最大接待人数18-2.png


 
客服更多配置操作参见以下文档:
http://docs.easemob.com/cs/200admin/20teammgm#%E5%AE%A2%E6%9C%8D%E7%AE%A1%E7%90%86 收起阅读 »

客服云配置系统自动结束会话

                                                                   配置系统自动结束会话 注意:修改配置后针对新创建的会话生效,旧会话还是需要手动关闭 1》机器人名下的会话自动结束配置: ...
继续阅读 »
                                                                   配置系统自动结束会话
注意:修改配置后针对新创建的会话生效,旧会话还是需要手动关闭
1》机器人名下的会话自动结束配置:
        管理员模式---智能机器人 会话超时设置页签 
可以设置超时时间和超时提示语
可以设置超时时间(1~30分钟)和1~5条超时回复。当访客超过设定时间仍没有回复消息时,机器人将随机从超时回复中选择1条回复访客,1分钟后访客仍没有回复,会话将被自动结束。 

系统自动结束会话15-1.png


 
2》人工客服名下的会话自动结束配置
        i. 会话在待接入排队的情况 
            管理员模式--设置--系统开关 待接入超时结束会话

系统自动结束会话15-2.png


 
       ii.会话由人工客服接入的情况(在人工客服的进行中会话列表)
           管理员模式--设置--系统开关 访客超时未回复自动结束会话                不活跃会话超时自动结束 两个配置项

系统自动结束会话15-3.png




系统自动结束会话15-4.png



注意:如果同时打开了“访客超时未回复自动结束会话”和“不活跃会话超时自动结束”开关,当满足任意开关的超时条件时,会话将会被自动结束。
配置文档可参考以下链接
http://docs.easemob.com/cs/200admin/50settings#%E8%AE%BF%E5%AE%A2%E8%B6%85%E6%97%B6%E6%9C%AA%E5%9B%9E%E5%A4%8D%E8%87%AA%E5%8A%A8%E7%BB%93%E6%9D%9F%E4%BC%9A%E8%AF%9D
注意:修改配置后针对新创建的会话生效,旧会话还是需要手动关闭
 
 
  收起阅读 »

客服云问候语配置

                                                             问候语的配置 (注意:问候语只有上班时间才会 自动发送,下班时间不会触发,上班时间是走的时间计划页面的配置) 客服系统可以同时配置三种...
继续阅读 »
                                                             问候语的配置
(注意:问候语只有上班时间才会 自动发送,下班时间不会触发,上班时间是走的时间计划页面的配置)
客服系统可以同时配置三种问候语:企业问候语 技能组问候语 客服问候语三种,触发条件不同

1.  企业问候语(管理员模式--设置--系统开关 企业问候语):当访客发送首条消息后,收到系统自动发送的企业问候语。

问候语配置14-1.png



2.技能组问候语(管理员模式--成员管理--在线技能组 基础设置 技能组问候语):会话调度给某个技能组后会触发该技能组的问候语

问候语配置14-2.png


3.客服问候语(客服模式--客服信息 客服问候语 每一个客服配置自己的问候语):会话成功分配或转接给当前客服/管理员时,访客会收到系统自动发送的客服问候语

问候语配置14-3.png


如果同时配置三个,可能都会触发。根据需求配置即可,一般情况只需要配置企业问候语。其他的不使用可以把对应开关关闭 收起阅读 »

客服云机器人转人工配置

                                       机器人转人工配置  1》旧版机器人:      管理员模式--智能机器人 自动回复--转人工设置。      可以规定转人工时间(全时间段允许 全时间段禁止 自定义时间<...
继续阅读 »
                                       机器人转人工配置 
1》旧版机器人
     管理员模式--智能机器人 自动回复--转人工设置。
     可以规定转人工时间(全时间段允许 全时间段禁止 自定义时间<仅上班 或者仅下班允许转接>)
     配置限制转接提示语
指定转人工技能组(如果是不指定,会话是转接到未分组。如果要实现机器人转接不同的技能组,此处选择为不指定,之后配合 会话分配规则页面的 关联指定 来实现机器人转接到不同技能组)


机器人转人工配置11-1.png




机器人转人工配置11-2.png




2》企业版机器人转人工配置
    1)管理员模式--智能机器人 点击机器人信息页面的蓝色字体 机器人管理 进入到机器人平台


机器人转人工配置11-3.png



    2)

机器人转人工配置11-4.png


上图是配置转人工时间段和指定技能组的
机器人设置模块--转人工设置 
默认情况可以把转人工指令和转人工意图开启即可


机器人转人工配置11-5.png


转人工指令可以自定义修改,默认的指令有:
中文:转人工、转坐席、转人工客服、转人工坐席、转人工服务;英文:chat with agent
   企业版机器人的转人工更多配置可以参见以下文档:
http://docs-ai.easemob.com/cs/900help#%E8%AE%BE%E7%BD%AE%E6%9C%BA%E5%99%A8%E4%BA%BA%E8%BD%AC%E4%BA%BA%E5%B7%A5 收起阅读 »

客服云在线技能组配置

                                            技能组操作 设置技能组下班提示语            管理员模式--成员管理--在线技能组 切换到基础设置页签            有技能组下班提示语 开关(需要...
继续阅读 »
                                            技能组操作
  1. 设置技能组下班提示语 

          管理员模式--成员管理--在线技能组 切换到基础设置页签 
          有技能组下班提示语 开关(需要 打开)配置下班提示语,最后保存(见下图)
          如果下班时间路由规则直接指定技能组,下班时间访客发起会话时,系统自动发送下班提示语给访客

技能组操作10-1.png


      2.配置技能组问候语
        同上界面,基础设置页签 有问候语开关 需要打开,配置问候语,最后保存(注:问候语只有上班时间才会发送下班时间不会发送。这里的上班时间是时间计划页面的配置)

       更多技能组配置可以参见以下文档:
http://docs.easemob.com/cs/200admin/20teammgm#%E6%8A%80%E8%83%BD%E7%BB%84%E7%AE%A1%E7%90%86
  收起阅读 »

客服云 客服发送知识库附件

                                                           客服发送知识库中附件给访客 发送知识时,图文消息仅包含知识标题和知识内容,不包含知识的附件。若发送知识中的附件,需打开知识详情,并点击附件右...
继续阅读 »
                                                           客服发送知识库中附件给访客
发送知识时,图文消息仅包含知识标题和知识内容,不包含知识的附件。若发送知识中的附件,需打开知识详情,并点击附件右上角的“发送”按钮。


发送知识库附件6-1.png


设置知识库附件发送个访客的方式:
管理员模式--知识库  点击发送方式设置按钮


发送知识库附件6-2.png




发送知识库附件6-3.png


  收起阅读 »

客服云待接入会话不自动分配

                                                               待接入不自动分配问题          查看待接入列表的数据(管理员模式---待接入 可以看到租户所有的待接入数据)  ...
继续阅读 »
                                                               待接入不自动分配问题
         查看待接入列表的数据(管理员模式---待接入 可以看到租户所有的待接入数据) 

待接入不自动分配4-1.png


        看截图框选 技能组和 坐席名两列的数据
 i..如果坐席名列有数据,例如截图中第一条,说明有指定客服,需要保证指定的坐席上线后系统才能自动调度给该指定的客服
ii..如果坐席名一列没有数据,看技能组一列显示的技能组名称
之后对照到 (管理员模式--成员管理--在线技能组 ) 查看该技能组内客服状态分布
需要保证技能组内有 空闲状态的客服同时该客服的进行中会话数量不能超过最大接待人数

待接入不自动分配4-2.png



例如上图,是因为技能组内没有空闲的客服存在
     解决:
让该客服调整状态为空闲
如果仍不能自动调度,查看该空闲客服的当前进行中会话是否达到最大接待量(最大接待量不能是0)

待接入不自动分配4-3.png


客服的进行中会话数量可以到(管理员模式--当前会话 右上角 指定客服筛选看数量,对比进行中和最大接待量)

待接入不自动分配4-4.png


如果是因为超过最大接待量导致的,需要该客服手动关闭部分进行中会话,使其数量小于最大接待量
注意:另外后续不想客服手动关闭,可以配置系统自动结束会话
可以到  管理员模式----设置--系统开关 页面  
   访客超时未回复自动结束会话
   不活跃会话超时自动结束。配置后针对的是新会话生效,旧会话还是需要手动结束

配置文档:
http://docs.easemob.com/cs/200admin/50settings#%E8%AE%BF%E5%AE%A2%E8%B6%85%E6%97%B6%E6%9C%AA%E5%9B%9E%E5%A4%8D%E8%87%AA%E5%8A%A8%E7%BB%93%E6%9D%9F%E4%BC%9A%E8%AF%9D



如果按照上述条件均排查都符合正常,可以看下不会自动调度的时间是非工作时间,有个系统开关控制 
(管理员模式--设置--系统开关 只有技能组的上班时间才自动分配会话)如果是开启需要关闭可解决不自动调度的问题

待接入不自动分配4-5.png


 如果是是工作时间的会话没有自动调度,确认下如果客服坐席使用的手机APP工作台登录,需要把  管理员模式---设置--系统开关页面  分配会话时检测坐席在线状态  这个配置开关要关闭

检测.png


如果仍无法解决,可以在工作时间发起会话联系在线客服咨询解决。
    需要登录客服系统,切换到管理员模式 之后点击右上角技术支持,发起会话即可。

    登录客服云地址:https://kefu.easemob.com 收起阅读 »

客服云代码指定客服

                                          代码指定客服(APP,网页渠道都可以实现) 1》注意:代码指定针对新会话生效,需要到(管理员模式--当前会话) 手动关闭旧会话,访客端发起新会话测试 指定客服说明: 指定...
继续阅读 »
                                          代码指定客服(APP,网页渠道都可以实现)
1》注意:代码指定针对新会话生效,需要到(管理员模式--当前会话) 手动关闭旧会话,访客端发起新会话测试

  • 指定客服说明:

  • 指定客服之后,若指定客服在线,不受接待量和状态的限制(无论什么状态,无论接待量是多少),都直接分配给该客服

  • 指定客服之后,若指定客服不在线,有个灰度功能可以控制客服不在线时,会话是否分配给客服

  • 开关关闭时,客服不在线,则会话在待接入中排队(开关默认为关闭状态)

  • 开关开启时,客服不在线,则会话直接分配给该坐席

  • 开关为灰度功能,客户不能自己控制,需要联系环信开通,开通功能联系商务即可


2》相关代码 安卓文档:
http://docs.easemob.com/cs/300visitoraccess/androidsdk#%E6%8C%87%E5%AE%9A%E5%AE%A2%E6%9C%8D
ios:
http://docs.easemob.com/cs/300visitoraccess/iossdk#%E6%8C%87%E5%AE%9A%E6%9F%90%E4%B8%AA%E5%AE%A2%E6%9C%8D
网页(h5链接)
http://docs.easemob.com/cs/300visitoraccess/web-widget#%E6%8C%87%E5%AE%9A%E5%AE%A2%E6%9C%8D1
网页(引入js)
http://docs.easemob.com/cs/300visitoraccess/web-widget#%E6%8C%87%E5%AE%9A%E5%AE%A2%E6%9C%8D 

     参数中的客服账号可以到(管理员模式--成员管理--客服 列表)邮箱一列数据 为指定的客服账号


代码指定客服3-1.png


  
   验证方法: 如果某一个会话没有生效,先手动关闭掉,之后到到管理员模式---历史会话  找到该会话,点击后 查看右侧记录页面的数据:确认下 是否有该坐席被指定发起会话的记录,如果有说明成功,没有的话,请检查下代码和是否是新会话测试的
   

代码指定客服3-2.png

收起阅读 »

客服云 历史会话的筛选和导出

                                                                  历史会话的筛选和导出    tips:     结束的会话才能到历史会话模块进行导出。(历史会话记录默认保存6个月,如需延长...
继续阅读 »
                                                                  历史会话的筛选和导出
   tips:
  1.     结束的会话才能到历史会话模块进行导出。(历史会话记录默认保存6个月,如需延长,请联系环信商务经理。 )
  2.     客服模式和管理员模式都可以进行导出,数据权限不同
  3.     历史会话模块默认显示的是本周数据,从周日开始
  4.     在客服模式的“历史会话”页面,客服和管理员仅可查看自己参与并结束的会话;
  5.     在管理员模式的“历史会话”页面,管理员可以查看所有客服的历史会话。

 第一:筛选
      进入历史会话模块,点击右上角筛选排序,可以重新指定时间段,根据会话标签,访客昵称,环信ID等条件筛选


历史会话2-1.png



 ​第二:导出
     直接点击界面右下角导出按钮,生成的文件需要到导出管理进行下载


历史会话2-2.png




历史会话2-3.png



     更多历史会话操作 文档地址:
http://docs.easemob.com/cs/100agentmode/30history#%E5%AF%BC%E5%87%BA%E5%8E%86%E5%8F%B2%E4%BC%9A%E8%AF%9D
收起阅读 »

客服云 代码传访客属性

代码传访客属性(例如访客资料页的:昵称,名字,公司,描述等字段) 代码传客户信息 针对自定义字段不生效 客服系统以下开关需要打开           管理员模式--设置--系统开关 允许访客端修改客户信息  开关保持开启状态,见下图 ...
继续阅读 »
代码传访客属性(例如访客资料页的:昵称,名字,公司,描述等字段)
代码传客户信息 针对自定义字段不生效
  1. 客服系统以下开关需要打开

          管理员模式--设置--系统开关 允许访客端修改客户信息  开关保持开启状态,见下图


开关1-1.png




      2.APP和网页集成都需要对应的代码传值   相关代码文档:
Android 
http://docs.easemob.com/cs/300visitoraccess/androidsdkapi#%E5%8F%91%E9%80%81%E5%B8%A6%E8%AE%BF%E5%AE%A2%E5%B1%9E%E6%80%A7%E7%9A%84%E6%B6%88%E6%81%AF
ios
http://docs.easemob.com/cs/300visitoraccess/iossdkapi#%E5%8F%91%E9%80%81%E5%B8%A6%E8%AE%BF%E5%AE%A2%E5%B1%9E%E6%80%A7%E7%9A%84%E6%B6%88%E6%81%AF
网页(引入js)
http://docs.easemob.com/cs/300visitoraccess/web-widget#%E6%98%BE%E7%A4%BA%E8%AE%BF%E5%AE%A2%E4%BF%A1%E6%81%AF

   代码传值针对新创建的会话生效,需要到管理员模式--当前会话 找到旧会话,手动关闭。(手动关闭会话见下图)


手动关闭会话1-2.png


 
  之后访客端发送新消息,要求新会话第一条消息扩展必须有访客属性
  收起阅读 »

如何用 30 天入门年薪 30 万的技术领域?

2016年3月15日,人机大战第五场在韩国首尔进行,经过长达5个小时搏杀,李世石认输,最终李世石与AlphaGo总比分定格在1比4。 值得注意的是:李世石仅在3月15日结束的第四场对战中取得了唯一的一场胜利。而支撑机器取胜的核心技术,就是深度学习。 自此之...
继续阅读 »
2016年3月15日,人机大战第五场在韩国首尔进行,经过长达5个小时搏杀,李世石认输,最终李世石与AlphaGo总比分定格在1比4。
值得注意的是:李世石仅在3月15日结束的第四场对战中取得了唯一的一场胜利。而支撑机器取胜的核心技术,就是深度学习。
自此之后,深度学习开始逐渐进入大家的视野。

01
什么是深度学习?


深度学习其实是机器学习(Machine Learning)的一个分支学科,而机器学习主要是研究数据之言的关系的,比如它可以用来分析性别、年龄、学历、职业等因素之间的数学关系。
显而易见,这些因果关系并不是一个简单的线性关系就能解决的。但是深度学习通过多非线性模型表示数据之间的关系,从而确定数据之间的相互关系是什么。

02
深度学习已经融入到了人们的日常生活中


现在汽车的自动驾驶、短信邮件的自动回复、扫地机器人,以及在围棋中击败最优秀人类选手的软件,都应用到了深度学习。甚至骚扰电话,也有很多是AI机器人打过来的,可能电话那头和你聊得正酣、声音甜美的客服妹子只是一串代码。
因此,阿里达摩院的工程师们,受不了骚扰骚扰电话的骚扰,发起了“二哈”AI,让机器人与骚扰电话自动聊天。
大家感受一下这对话画风


1.gif


 
总之,AI机器人的骚扰电话,正在用AI进行解决。

03

可能是编程领域薪资最高的岗位


而与此同时学习深度学习的人也开始变得多了起来,但由于深度学习涉及面比较广,很多学者会对此望而却步。然而如果学好深度学习,不仅仅可以给你带来高薪资,也有可能会给你带来很多的荣誉!因为在此领域还是非常缺乏人才的!
大家可以看一份数据


2.png


 
平均月薪已达到3w,年薪36w。


4.png


 
就应届生深度学习岗的薪资,都已经达到了很多其他互联网职业3年工作经验都难以企及的水平。
现在各个大厂,也都有深度学习相关的项目,而且待遇方面也是极其的诱人。
百度推出“少帅计划”,针对30岁以下的深度学习科学家,开出100万以上年薪
阿里巴巴对外宣布将通过校园招聘组建一支规模达数百人的 NASA“青年军”
华为更是开出200万年薪招聘应届毕业生,其专业均为最前沿的人工智能领域
作为一个有追求的程序员,相信你在脑海中闪过无数次这样的疑问:如何掌握深度学习?

04

如何掌握深度学习?


目前市场对于深度学习的需求,早已出现供不应求的现象,但太多工程师想入门或者转行却不知从何开始,很多初学者都会有这样的困惑:
一边要熟练掌握线性代数、矩阵计算,一边还要搞概率论
一边要去研究各种库与框架,一边学习如何在用编程语言实现算法
一边学习如何制作数据集、特征提取,一边微调参数,选择合适的算法
这样一轮下来,深度学习还没有开始就已经走上了放弃之路。
放眼网上现有的一些深度学习课程,经常会发现有些知识点覆盖不全,或者学习门槛较高,研究性的问题不多,或者说只关注面试,而忽略了底层的逻辑以及真实案例的实操。
因此,我们通上百名深度学习用户的调研,与老师花费上千小时的时间,提炼了102节精华课程。
我们希望通过“从零开始深度学习”这门课程,让你0基础入门深度学习,建立起完整的学习路径,同时通过“智能问答模型”的实战案例,将所学知识学以致用。
#购课即送王海良老师在京东原价69的《智能问答与深度学习》实体书一本#

讲师介绍
王海良:Chatopera联合创始人&CEO,微软人工智能最有价值专家,先后工作于IBM软件开发实验室和创新中心。
李卓桓:25年编程经验,曾任优酷网首席科学家、叽歪网创始人,水木清华BBS站长,紫霞BBS站长。
林旭鸣:北京邮电大学模式识别实验室研究生,任职阿里巴巴的阿里小蜜团队。
陈可心:香港大学硕士,任职经历包括:微软中国、今日头条研发中心,联想香港人工智能中心以及联合国亚太分部。
李思珍:现任职今日头条,主要工作实现人机交互系统的意图识别和关键词优化。

#课程介绍#
相信很多小伙伴,在入门深度学习的路上,会被一些数据基础搞得犯怵,搭建环境的时候也许会遇到很多坑。
因此,我们把课程分为五个模块:数学基础、Python编程语言基础、深度学习初步、深度学习深化、智能问答模型实践,带你一探深度学习的究竟,希望你通过此次学习,不仅掌握原理更能动手实操。
除了上述102节视频课程,我们还为这次课程搭配了书籍《深度学习与智能问答》,一边跟着视频实操,一边阅读书籍巩固,学习效果double~


5.jpg


 
如何保证你的学习效果?
由基础的数学学习开始,逐渐建立完整的深度学习知识体系
五大模块,由知识学习到真实案例的实践
购课送原价69元深度学习实体书,边学边读,加深记忆
建立一个互助、监督的高效学习社群,随时交流问题

购课须知
本课程包含哪些内容?
包含102节视频课(20+小时)+实体书(京东原价69)

上课形式是怎样的?
课程授课形式为:视频+群答疑

#如何报名#
限时特惠:99元(原价199元)
拼团特惠69元~
图书(含邮寄) + 100+节视频课程,每课仅需6毛钱

扫码立即抢购!
平均一天2元,就是一瓶水钱
坚持30天,换你一次进入高薪技术领域的机会
如果对课程有疑问
欢迎扫码回复“1”进课程咨询群


6.jpg


 
 
  收起阅读 »

环信助力2019 中国开源年会(COSCon'19)顺利启航!

  COSCon '19正式启动啦!时间: 2019-11-02 09:00 ~ 11-03 17:00 地址: 上海普陀区上海普陀区中山北路3663号华东师范大学(中北校区) 业界最具影响力的开源年度盛会2019中国开源年会 ( COSCon'19 )...
继续阅读 »


微信图片_20190909152905.jpg


 
COSCon '19正式启动啦!时间: 2019-11-02 09:00 ~ 11-03 17:00
地址: 上海普陀区上海普陀区中山北路3663号华东师范大学(中北校区)
业界最具影响力的开源年度盛会2019中国开源年会 ( COSCon'19 )将于 11月2-3日在华东师范大学 (上海普陀区中山北路校区)由开源社举办。
我们预期会有超过1600人现场参与这次盛会,还会有超过1万名在线的观众,热切围观。感谢许多社区伙伴、企业伙伴和志愿者,携手促使这样规模的 COSCon  诞生。
本次大会的主题是“开源无疆、携手出航”(Let’s Cross the Boundaries Together!),这也代表我们对于中国开源,走向世界,走向辉煌的殷切期望。
本次大会将持续两天,我们策划的主题包括:开源软件、开源硬件、社区运营与治理、开源教育等方向。也特别希望听到各种自由/开源相关的成功故事和酸甜苦辣的经历。我们诚挚地邀请您的参与!
 
大会亮点


活动亮点.jpg


 
活动报名链接:https://www.bagevent.com/event/5744455 
  收起阅读 »

【报名】环信&华为首届《5G音视频开发创业沙龙》大咖+干货+大奖,等你来撩!

2019年是5G商用元年,作为第五代通信技术,未来将结合云计算、人工智能、物联网等技术逐渐改变亿万用户的生活消费方式,带来万亿级的产业空间。Gartner预测到2020年,7%的全球通信服务提供商将拥有商业上可行的无线5G服务,2023年5G智能手机将占总销量...
继续阅读 »
2019年是5G商用元年,作为第五代通信技术,未来将结合云计算、人工智能、物联网等技术逐渐改变亿万用户的生活消费方式,带来万亿级的产业空间。Gartner预测到2020年,7%的全球通信服务提供商将拥有商业上可行的无线5G服务,2023年5G智能手机将占总销量的51%,谁先拥抱5G谁就能在万亿级的新产业空间里拔得头筹。
当下,短视频、互动直播等音视频应用火爆来袭,抖音、快手等已成为当红流量入口,当移动互联红利消失殆尽,各行业获客成本不断攀升的态势下,5G催生的音视频应用风口已成为兵家必争之地,让各条赛道又迎来弯道超车的好机会。
 同时,音视频应用正在加快与人工智能、5G信息显示等领域的融合,不断催生新业态和商业模式,但对创业者和开发者来说还是挑战诸多:1,5G时代如何选择一家靠谱的云基础设施资源服务厂商?音视频领域有哪些技术要点和哪些典型应用?2,5G时代音视频社交领域有哪些新玩法?3,5G创业如何实现从0—1的快速冷启动?4,产品研发上线后该如何科学运营,拉新、获客、留存,洞悉市场和用户行为实现快速增长……
 
9月21日,坐标北京,中关村创业大街,来这里听一场就够了!
 
环信联合华为举办首届《5G音视频开发创业沙龙》,给创业者们带来5G音视频方面的最新最佳技术实践,分享在5G音视频领域创业,开发、运营、安全、部署等一揽子解决方案,深入解读5G音视频火热背后的技术奥秘和新增长机会。


5G音视频长图.jpg


 


微信二维码.jpg


 
扫码进入 环信&华为@5G沙龙交流群
 
报名链接:http://hdxu.cn/sJ8fm 
  收起阅读 »

new

修改聊天界面titlebar 标题 看LoginActivity intent跳转传参  setTitleName() 这个参数就是title // 进入主页面 Intent intent = new IntentBuilder(LoginActivit...
继续阅读 »
  1. 修改聊天界面titlebar 标题

看LoginActivity intent跳转传参  setTitleName() 这个参数就是title
// 进入主页面
Intent intent = new IntentBuilder(LoginActivity.this) .setTargetClass(ChatActivity.class) .setVisitorInfo(DemoMessageHelper.createVisitorInfo()) .setServiceIMNumber(Preferences.getInstance().getCustomerAccount()) .setScheduleQueue(DemoMessageHelper.createQueueIdentity(queueName)) .setTitleName(titleName) // .setScheduleAgent(DemoMessageHelper.createAgentIdentity("ceshiok1@qq.com")) .setShowUserNick(true) .setBundle(bundle) .build();
startActivity(intent);        2. 设置显示客服头像和昵称
      (1)首先客服系统 切换到管理员模式--设置--系统开关  访客端显示客服昵称开关打开。
      (2)参考DemoHelpe类中,setEaseUIProvider方法,消息判断为接收方receive中的代码,客服发送的消息,从消息扩展可以获取到坐席信息。
   参考文档:http://docs.easemob.com/cs/300visitoraccess/extended-message-format#%E6%98%BE%E7%A4%BA%E5%AE%A2%E6%9C%8D%E5%A4%B4%E5%83%8F%E5%92%8C%E6%98%B5%E7%A7%B0
         3.设置显示访客头像:
           (1) 找到DemoHelper 类中,setEaseUIProvider 方法,消息判断为发送方send的代码,可以设置访客头像
           (2)需要修改kefueaseui 库中ChatRow类的代码,如果远程依赖无法修改,建议先使用本地依赖的方式
本地依赖下载地址http://www.easemob.com/download/cs   打开链接,页面底部找访客端SDK和Demo,Demo中找到kefueaseui库,手动导入,把之前远程依赖的注释掉,重新添加本地依赖
          (3)ChatRow 类中找到setUpBaseView  之后看对adapter的判断


chatrow.png


把框选位置条件删掉即可
     注意:kefueaseui 库,消息布局没有访客昵称控件,如果需要显示访客昵称数据,需要单独先给布局添加昵称控件。
           
  收起阅读 »

iOS SDK 日志文件的导出

一、console后台获取,如图二、xcode获取环信SDK提供2.x和3.x两个版本。初始化SDK成功,会写入日志文件到本地。 日志文件路径如下: 2.x 已停止维护            &nb...
继续阅读 »
一、console后台获取,如图


二、xcode获取
环信SDK提供2.x和3.x两个版本。初始化SDK成功,会写入日志文件到本地。

日志文件路径如下:
  • 2.x 已停止维护             沙箱/Library/EaseMobLog
  • 3.5.4之前(不含3.5.4) 沙箱Documents/HyphenateSDK/easemoblog 
  • 3.5.4之后(含3.5.4)    沙箱Library/Application Support/HyphenateSDK/easemobLog 
下面以3.5.4之后的版本为例,演示获取方法

模拟器: 
  • 打印NSHomeDirectory()      

模拟器1.png

  • 复制路径,打开Finder前往     

模拟器2.png

  • 访问到沙箱目录

模拟器3_路径.png


真机:
  • 打开Xcode连接设备,运行成功后,前往Xcode --> Window --> Devices and Simulators 

真机1.png

  • 进入Devices界面

真机2.png

  • 选择Download Container之后会下载到本地一个.xcappdata文件。选中这个文件鼠标右键显示包内容。

真机3.png

  • 访问到沙箱目录   

真机4_路径.png


注:easemobDB是存放历史消息的本地数据库
收起阅读 »

【源码下载】一款使用环信实现的开源灵魂社交APP(含服务器)

#前言 近期,环信热心开发者-穿裤衩闯天下使用环信IM开发了一款实时聊天应用,包含简单的服务器端,现在正式开源给小伙伴们。感兴趣的同学可以一起搞一下哦,详细介绍请往下看。   上代码 服务器:VMServer 客户端:VMMatch    #VM...
继续阅读 »
#前言
近期,环信热心开发者-穿裤衩闯天下使用环信IM开发了一款实时聊天应用,包含简单的服务器端,现在正式开源给小伙伴们。感兴趣的同学可以一起搞一下哦,详细介绍请往下看。


猿匹配_logo_副本.png



  上代码
服务器:VMServer
客户端:VMMatch
 
 #VMMatch
猿匹配 —— 国内首个程序猿非严肃婚恋交友应用,让我们一言不合就来场匹配吧
 
#介绍#
首先说下中文名:为什么叫这个名字呢,因为这是一个程序猿(媛)之间匹配交流的应用啊其实这是一个使用环信 IM 开发的一款开源聊天项目,涵盖了时下流行的一些聊天元素,同时已将 IM 功能封装为单独库,可以直接引用,方便使用
项目还处在初期阶段,还有许多功能需要实现,有兴趣的可以一起来
项目资源均来自于互联网,如果有侵权请联系我
 
 #下载体验
猿匹配 小米商店 审核中
猿匹配 Google Play
 
  #项目截图


1.png



2.png



3.png



4.png



5.png



6.png


  
 #开发环境
项目基本属于在最新的Android开发环境下开发,使用Java8的一些新特性,比如Lambda表达式,
然后项目已经适配Android6.x以上的动态权限适配,以及7.x的文件选择,和8.x的通知提醒等;
· Mac OS 10.14.4
· Android Studio 3.3.2
  #项目模块儿
本项目包含两部分:
一部分是项目主模块app,这部分主要包含了项目的业务逻辑,比如匹配、信息修改、设置等
另一部分是封装成library的vmim,这是为了方便大家引用到自己的项目中做的一步封装,不用再去复杂的复制代码和资源等,
只需要将vmim以module导入到自己的项目中就行了,具体使用方式参见项目app模块儿;
 
  #功能与 TODO
IM部分功能
· [x] 链接监听
· [x] 登录注册
· [x] 会话功能
      。[x] 置顶
      。[x] 标为未读
      。[x] 删除与清空
      。[x] 草稿功能
· [x] 消息功能
      。[x] 下拉加载更多
      。[x] 消息复制(仅文字类消息)
      。[x] 消息删除
      。[x] 文本+Emoji消息收发
      。[x] 大表情消息收发
      。[x] 图片消息
        ~[x] 查看大图
        ~[ ] 保存图片
      。[x] 语音消息
        ~[x] 语音录制
        ~[x] 语音播放(可暂停,波形待优化)
        ~[x] 听筒和扬声器播放切换
      。[x] 语音实时通话功能
      。[x] 视频实时通话功能
      。[x] 通话过程中的娱乐消息收发
        ~[x] 骰子
        ~[x] 石头剪刀布
        ~[x] 大表情
      。[x] 昵称头像处理(通过回调实现)
App部分功能
· [x] 登录注册(包括业务逻辑和 IM 逻辑)
· [x] 匹配
      。[x] 提交匹配信息
      。[x] 拉取匹配信息
· [x] 聊天(这里直接加载 IM 模块儿)
· [x] 我的
      。[x] 个人信息展示
      。[x] 上传头像
      。[x] 设置昵称
      。[x] 设置签名
· [x] 设置
      。[x] 个人信息设置
      。[x] 通知提醒
      。[x] 聊天
      。[ ] 隐私(随业务部分一起完善)
      。[ ] 通用(随业务部分一起完善)
      。[ ] 帮助反馈(随业务部分一起完善)
      。[x] 关于
      。[x] 退出
· [ ] 社区
      。[ ] 发布
      。[ ] 评论
      。[ ] 收藏
      。[ ] 关注
发布功能
· [x] 多渠道打包
· [x] 签名配置
· [x] 开发与线上环境配置
· [x] 敏感信息保护
 
  #配置运行
1.首先复制config.default.gradle到config.gradle
2.配置下config.gradle环信appkey以及bugly统计Id
3.正式打包需要配置下签名信息,同时将签名文件放置在项目根目录
 
  #参与贡献
如果你有什么好的想法,或者好的实现,可以通过下边的步骤参与进来,让我们一起把这个项目做得更好,欢迎参与
1.Fork本仓库
2.新建feature_xxx分支 (单独创建一个实现你自己想法的分支)
3.提交代码
4.新建Pull Request
5.等待我们的Review & Merge
 
 #关联项目
服务器端由nodejs实现,地址见这里 VMServer
 
  #VMServer
是为Android开源项目VMMatch项目(中文名猿匹配)实现的服务端
 
  #简介
这个项目包含两部分
· 根目录:服务逻辑及API接口实现
· client目录:前端界面,和服务器端代码端放置在同一仓库下(暂未实现)
 
 #使用
简单介绍下运行环境及部署方法
1.安装nodejs开发时使用的是v10.16.0版本
2.需要安装mongodb并启动,开发使用版本4.0.10
3.下载项目到服务器,可以下载压缩包,或者用git clone命令
4.复制config_default.js到config.js,可根据自己需要修改配置文件
5.安装依赖
npm install

6.全局安装pm2
npm install pm2 -g
 
7.运行 vmshell.sh
 

环信冬冬_副本.jpg


扫码备注【开源项目】邀你加入环信开源社群
 
转载自https://blog.melove.net/develop-open-source-im-match-and-server/ 
  收起阅读 »

(客服云)iOS访客端接收图文消息不展示封面图片怎么办?

将HDBubbleView.h+Article.m中的- (instancetype)initWithDictionary:(NSDictionary *)dic方法改成下面这样就可以了 - (instancetype)initWithDictionary:(...
继续阅读 »
HDBubbleView.h+Article.m中的- (instancetype)initWithDictionary:(NSDictionary *)dic方法改成下面这样就可以了
- (instancetype)initWithDictionary:(NSDictionary *)dic {
self = [super init];
if (dic != nil) {
_type = HDCellTypeSub;
_title = [dic objectForKey:@"title"];
double createTime = [[NSDate date] timeIntervalSince1970] * 1000;
if ([dic objectForKey:@"createdTime"]) {
createTime = [[dic objectForKey:@"createdTime"] doubleValue];
}
_createTime = [self timeFormatter:createTime/1000];
_digest = [dic objectForKey:@"digest"];

// //封面展示缩略图
// NSString *thumbUrl = [dic objectForKey:@"thumbUrl"];
// if (thumbUrl) {
// if (thumbUrl && [thumbUrl hasPrefix:@"http"]) {
// _imageUrl = thumbUrl;
// }else {
// _imageUrl = [NSString stringWithFormat:@"%@%@",[HDClient.sharedClient kefuRestServer], thumbUrl];
// }
// }

// 封面展示原图
NSString *picUrl = [dic objectForKey:@"picurl"];
if (picUrl) {
if (picUrl && [picUrl hasPrefix:@"http"]) {
_imageUrl = picUrl;
}else {
_imageUrl = [NSString stringWithFormat:@"%@%@",[HDClient.sharedClient kefuRestServer], picUrl];
}
}

NSString *detailUrl = [dic objectForKey:@"url"];
if (detailUrl) {
if (detailUrl && [detailUrl hasPrefix:@"http"]) {
_url = detailUrl;
}else {
_url = [NSString stringWithFormat:@"%@%@",[HDClient.sharedClient kefuRestServer], detailUrl];
}
}
}
return self;
}
收起阅读 »

(客服云)企业版机器人如何设置转人工

注意:只购买了企业版机器人,不使用客服系统的同学,可忽略第一、二步。   一、管理员模式--智能机器人--机器人设置,选择【转人工设置】,这里可以设置“转人工时间段”、“限制转人工提示语”和“转人工指定技能组”。 不指定技能组,转人工时,会按照 设置--会话分...
继续阅读 »
注意:只购买了企业版机器人,不使用客服系统的同学,可忽略第一、二步。
 
一、管理员模式--智能机器人--机器人设置,选择【转人工设置】,这里可以设置“转人工时间段”、“限制转人工提示语”和“转人工指定技能组”。
不指定技能组,转人工时,会按照 设置--会话分配规则 页面设置的“路由规则”分配给对应的技能组。

企业版机器人转人工设置1.png


 
二、还是这个页面,选择【基础设置】,然后点击【机器人管理】,跳转到机器人管理页面。
    有些同学点击后没反应,那有可能是被浏览器拦截了,手动释放即可。

企业版机器人转人工设置2.png


 
三、来到机器人管理页面,找到机器人设置--转人工设置,先【添加转人工参数】,添加时“参数名”随便写,“参数值”必须是技能组的名称,否则无法转接到此技能组。不添加转人工参数,则无法指定技能组。
然后打开【转人工指令】的开关,配置“转人工指令”以及“参数”
开关开启,但是不指定参数,转人工时,会按照客服系统中 设置--会话分配规则 页面设置的“路由规则”分配给对应的技能组。
注意:在这里开启了“转人工指令”,第1步的“转人工指定技能组”则失效,以这里为准。
         不开启,则以第1步的“转人工指定技能组”的配置为准。

企业版机器人转人工设置3.png
收起阅读 »

《拍拍二手》微信小程序之环信接入

拍拍二手闲置平台,可以将自己的闲置物品进行转让或者捐赠。想和卖家达成共识就需要涉及IM聊天。拍拍二手闲置平台目前接入的是环信IM聊天。下面我将从三个阶段带大家玩转环信IM会话。 前期 初识IM聊天 带着问题去调研 必须接入环信吗?除了环信是否可以接入其...
继续阅读 »
拍拍二手闲置平台,可以将自己的闲置物品进行转让或者捐赠。想和卖家达成共识就需要涉及IM聊天。拍拍二手闲置平台目前接入的是环信IM聊天。下面我将从三个阶段带大家玩转环信IM会话。
前期
初识IM聊天
带着问题去调研

必须接入环信吗?除了环信是否可以接入其他即时通信?
环信目前有哪些功能呢?支持微信小程序吗?
如何接入小程序呢?
调研分析

必须接入环信吗?除了环信是否可以接入其他即时通信?
现状: 微信小程序API 提供了WebSocket 方法。
扩展: 如果服务端支持scoket通信,ios\android\H5 也全都支持Im聊天了
备注:专业第三方Im有融云、环信、云之讯等,底层实现均是基于scoket 通信。明白scoket通信后也可以自己写即时通信。
环信目前有哪些功能呢?支持微信小程序吗?
错误想法: 环信就是做im聊天的,咱们上去按照接入文档,开发就能搞定!!!
这种想法是很致命的。在所有的第三方组件接入中,如果我们不能跳出来看待问题,只是为了完成任务而完成任务。那么我们永远是最底层的低级码农。
环信目前是同行业里面做的算不错的。那么他的官网、接入规范都应该有的。微信小程序也是支持的。在后面小编会带领大家一切怎么去阅读一个官网
如何接入小程序?
接入小程序是否需要申请一个账号呢?我直接运行他们的demo可以吗? 怎么去测试呢? 此时我们可以有很多的猜想。我认为在开始接入之前我们应该很好的进行一些思考,答案显而易见。
环信接入思考篇
快即时慢

在工作中,大家会经常遇到第三方组件的接入。当接收到任务后,为了尽快完成任务。上来就google,找攻略,找技巧。往往认为这样做速度是最快的。结果适得其反,做了很多无用的功。我们意识中的快,结果却变成了慢
慢即时快image逆向思维: 任何一个第三方的组件,特别是一个大点的平台,他们为了推出自己的产品,一定会有各种各样的功能支持,接入文档说明。我们放慢速度,将这些资源用上半天的时间进行简单的梳理。后期的开发进度会有很大的提升。
上图是我在接入环信Im后进行的反思。因为在接入环信之前,其他团队成员用了很长的时间联调。假如他们在接入环信聊天之前,了解环信拥有自己的后台,可以直接给用户端发送测试消息;可以直接创建用户、创建聊天室、创建群组。他们还会花费那么久的时间去联调吗?完全不用依赖服务端。不用依赖ios,依赖android。自己使用环信后台,轻轻松松完成各种测试。
环信接入
环信官网注册自己的即时通讯云,并登陆后台


1.jpg



创建自己的应用,并记录关键信息


2.jpg



以下是关键信息哦!!!


3.jpg



备注:
应用标识 应用接入时会使用
IM 用户 可以创建、删除用户、发送消息
群组 可以创建、删除群组信息、发送消息
聊天室 可以创建、删除聊天室、发送消息
tip 通过这个后台管理系统,就可以玩转环信的接入测试了。
从环信下载小程序demo,替换 appkey 进行联调测试


4.jpg



测试走起
用户测试 
在环信后台创建用户,在小程序端登录 (用户demo1 密码:123456)

5.jpg



一对一会话测试
① 在环信后台创建用户demo2
② 点击操作,查看用户好友将demo1和demo2 添加为好友。
③ 在小程序端用demo1给demo2发送测试消息。
④ 退出demo1用户,登录demo2查看是否会接收到demo1发送的会话


6.jpg



由于环信工程师们相信码农的实力,在群组测试和聊天室测试这块为大家留下了想象空间。demo 中群组测试和聊天室测试为明确写出。让我继续带大家飞

群组测试
① 创建群组记录群组id,并给群组添加成员(demo2)


7.jpg



② 环信后台给群组发送测试消息


8.jpg



③ 控制台能收到群组测试消息,怎么展示呢? 请阅读源码解析篇
聊天室测试
① 创建聊天室记录聊天室id,将demo1 设置为超级管理员,demo2设置为管理员
② 聊天室这里没有聊天室消息的发送。请阅读源码解析篇
通过以上4个简单的测试,android、ios、h5、小程序的聊天测试均可以参照以上4点进行顺利的测试。初期就此结束。下面带代价进行源码的解析
中期
看源码前期思考


10.jpg


 
核心源码阅读


11.jpg


 
以上是环信sdk 基础代码结构。 通过简单阅读会发现:环信的scoket 通信也使用了微信小程序暴露的scoket 通信 (猜想 android、ios 其他端也有对应的scoket通信)
环信的api包装在connection.js 组件中,如果某些api没有,咱们可以扩展connection 中的方法
环信核心代码阅读完成后,发现没有涉及到缓存。看来缓存的处理是在对应的业务逻辑中。
设想:
消息应该在哪里缓存
哪里进行会话链接的监听注册
环信demo 代码阅读
会话、群组
通过前面提到的方式,大家可以在小程序控制台抓取到用户收到的会话和群组消息
会话
app.js
环信scoket 注册监听代码在app.js 中
核心代码如下:
{
//调用API从本地缓存中获取数据
var that = this
var logs = wx.getStorageSync('logs') || []
logs.unshift(Date.now())
wx.setStorageSync('logs', logs)

WebIM.conn.listen({
onOpened: function (message) {//连接成功回调
// 如果isAutoLogin设置为false,那么必须手动设置上线,否则无法收消息
// 手动上线指的是调用conn.setPresence(); 如果conn初始化时已将isAutoLogin设置为true
// 则无需调用conn.setPresence();
WebIM.conn.setPresence()
},
onPresence: function (message) { //处理“广播”或“发布-订阅”消息,如联系人订阅请求、处理群组、聊天室被踢解散等消息
switch(message.type){
case "unsubscribe":
pages[0].moveFriend(message);
break;
case "subscribe":
if (message.status === '[resp:true]') {
return
} else {
pages[0].handleFriendMsg(message)
}
break;
case "joinChatRoomSuccess":
console.log('Message: ', message);
wx.showToast({
title: "JoinChatRoomSuccess",
});
break;
case "memberJoinChatRoomSuccess":
console.log('memberMessage: ', message);
wx.showToast({
title: "memberJoinChatRoomSuccess",
});
break;
case "memberLeaveChatRoomSuccess":
console.log("LeaveChatRoom");
wx.showToast({
title: "leaveChatRoomSuccess",
});
break;
}
},
onRoster: function (message) { //处理好友申请
var pages = getCurrentPages()
if (pages[0]) {
pages[0].onShow()
}
},

onVideoMessage: function(message){ //视频处理
console.log('onVideoMessage: ', message);
var page = that.getRoomPage()
if (message) {
if (page) {
page.receiveVideo(message, 'video')
} else {
var chatMsg = that.globalData.chatMsg || []
var time = WebIM.time()
var msgData = {
info: {
from: message.from,
to: message.to
},
username: message.from,
yourname: message.from,
msg: {
type: 'video',
data: message.url
},
style: '',
time: time,
mid: 'video' + message.id
}
msgData.style = ''
chatMsg = wx.getStorageSync(msgData.yourname + message.to) || []
chatMsg.push(msgData)
wx.setStorage({
key: msgData.yourname + message.to,
data: chatMsg,
success: function () {
//console.log('success')
}
})
}
}
},

onAudioMessage: function (message) { // 音频处理
console.log('onAudioMessage', message)
var page = that.getRoomPage()
console.log(page)
if (message) {
if (page) {
page.receiveMsg(message, 'audio')
} else {
var chatMsg = that.globalData.chatMsg || []
var value = WebIM.parseEmoji(message.data.replace(/\n/mg, ''))
var time = WebIM.time()
var msgData = {
info: {
from: message.from,
to: message.to
},
username: message.from,
yourname: message.from,
msg: {
type: 'audio',
data: value
},
style: '',
time: time,
mid: 'audio' + message.id
}
console.log("Audio msgData: ", msgData);
chatMsg = wx.getStorageSync(msgData.yourname + message.to) || []
chatMsg.push(msgData)
wx.setStorage({
key: msgData.yourname + message.to,
data: chatMsg,
success: function () {
//console.log('success')
}
})
}
}
},

onLocationMessage: function (message) { // 收到位置信息
console.log("Location message: ", message);
},

onTextMessage: function (message) {//收到文本消息
var page = that.getRoomPage()
console.log(page)
if (message) {
if (page) {
page.receiveMsg(message, 'txt')
} else {
var chatMsg = that.globalData.chatMsg || []
var value = WebIM.parseEmoji(message.data.replace(/\n/mg, ''))
var time = WebIM.time()
var msgData = {
info: {
from: message.from,
to: message.to
},
username: message.from,
yourname: message.from,
msg: {
type: 'txt',
data: value
},
style: '',
time: time,
mid: 'txt' + message.id
}
chatMsg = wx.getStorageSync(msgData.yourname + message.to) || []
chatMsg.push(msgData)
wx.setStorage({
key: msgData.yourname + message.to,
data: chatMsg,
success: function () {
//console.log('success')
}
})
}
}
},
onEmojiMessage: function (message) { //收到表情信息
//console.log('onEmojiMessage',message)
var page = that.getRoomPage()
//console.log(pages)
if (message) {
if (page) {
page.receiveMsg(message, 'emoji')
} else {
var chatMsg = that.globalData.chatMsg || []
var time = WebIM.time()
var msgData = {
info: {
from: message.from,
to: message.to
},
username: message.from,
yourname: message.from,
msg: {
type: 'emoji',
data: message.data
},
style: '',
time: time,
mid: 'emoji' + message.id
}
msgData.style = ''
chatMsg = wx.getStorageSync(msgData.yourname + message.to) || [] //tip 从本地缓存中获取用户的消息 发消息+来源 适用于单人会话 msgData.yourname + message.to+当前登录人 群组/聊天室

chatMsg.push(msgData)
//console.log(chatMsg)
wx.setStorage({
key: msgData.yourname + message.to,
data: chatMsg,
success: function () {
//console.log('success')
}
})
}
}
},
onPictureMessage: function (message) {//收到图片信息
//console.log('Picture',message);
var page = that.getRoomPage()
if (message) {
if (page) {
//console.log("wdawdawdawdqwd")
page.receiveImage(message, 'img')
} else {
var chatMsg = that.globalData.chatMsg || []
var time = WebIM.time()
var msgData = {
info: {
from: message.from,
to: message.to
},
username: message.from,
yourname: message.from,
msg: {
type: 'img',
data: message.url
},
style: '',
time: time,
mid: 'img' + message.id
}
msgData.style = ''
chatMsg = wx.getStorageSync(msgData.yourname + message.to) || []
chatMsg.push(msgData)
wx.setStorage({
key: msgData.yourname + message.to,
data: chatMsg,
success: function () {
//console.log('success')
}
})
}
}
},
// 各种异常
onError: function (error) {
// 16: server-side close the websocket connection
if (error.type == WebIM.statusCode.WEBIM_CONNCTION_DISCONNECTED) {
if (WebIM.conn.autoReconnectNumTotal < WebIM.conn.autoReconnectNumMax) {
return;
}

wx.showToast({
title: 'server-side close the websocket connection',
duration: 1000
});
wx.redirectTo({
url: '../login/login'
});
return;
}

// 8: offline by multi login
if (error.type == WebIM.statusCode.WEBIM_CONNCTION_SERVER_ERROR) {
wx.showToast({
title: 'offline by multi login',
duration: 1000
})
wx.redirectTo({
url: '../login/login'
})
return;
}
},
})


}
实际开发过程中,在微信中,退出小程序,重新进入时,webscoket 通信并没有重新创建链接。存在用户收到不到消息的情况。可以将以上代码封装,例如addHXLIstener(...)。当用户重新打开后,再次注册环信监听即可。
拍拍二手闲置交易平台,主要集成的是文本聊天功能。
环信登录 例如 initLoginHX();
 var uin=wx.getStorageSync('hxuin');
var pwd=wx.getStorageSync('hxpwd');
console.log('initHX:' + uin+"||"+pwd);
var options = {
apiUrl: '服务器url',
user: '用户名',// 用户名要是字符
pwd: '密码',
grant_type: 'password',
appKey: 'appkey',
success: function (res) {
console.log("环信创建连接成功")
},
error: function (res) {
console.log("环信创建连接失败")
}
};
WebIM.conn.open(options);
chat 会话
环信的会话列表存储在本地,并没有调用服务器端数据
 var that = this
var member = wx.getStorageSync('member')
var myName = wx.getStorageSync('myUsername')
var array = []
for (var i = 0; i < member.length; i++) {
if (wx.getStorageSync(member[i].name + myName) != '') {
array.push(wx.getStorageSync(member[i].name + myName)[wx.getStorageSync(member[i].name + myName).length - 1])
}
}
//console.log(array,'1')
this.setData({
arr: array
})
通过以上代码得出结论: 环信的会话是通过遍历用户id+对方id 构成的数据。
那群组和聊天室的怎么处理呢?
环信小程序demo中只提供了聊天室列表的获取接口我们可以轻松实现聊天室列表,并没有提供群组列表的获取方式。我们需要在conection中扩展调用群组列表的接口,来实现群组列表。参照聊天室列表获取即可实现。聊天室列表实现方式如下:
connection.prototype.getChatRooms = function (options) {

var conn = this,
token = options.accessToken || this.context.accessToken;

if (token) {
var apiUrl = this.apiUrl;
var appName = this.context.appName;
var orgName = this.context.orgName;

if (!appName || !orgName) {
conn.onError({
type: _code.WEBIM_CONNCTION_AUTH_ERROR
});
return;
}

var suc = function (data, xhr) {
typeof options.success === 'function' && options.success(data);
};

var error = function (res, xhr, msg) {
if (res.error && res.error_description) {
conn.onError({
type: _code.WEBIM_CONNCTION_LOAD_CHATROOM_ERROR,
msg: res.error_description,
data: res,
xhr: xhr
});
}
};

var pageInfo = {
pagenum: parseInt(options.pagenum) || 1,
pagesize: parseInt(options.pagesize) || 20
};
// 想要实现群组列表,修改对应接口即可
var opts = {
url: apiUrl + '/' + orgName + '/' + appName + '/chatrooms',
dataType: 'json',
type: 'GET',
header: {'Authorization': 'Bearer ' + token},
data: pageInfo,
success: suc || _utils.emptyfn,
fail: error || _utils.emptyfn
};
wx.request(opts);
} else {
conn.onError({
type: _code.WEBIM_CONNCTION_TOKEN_NOT_ASSIGN_ERROR
});
}
chatroom
从本地缓存中获取聊天记录,并展示
// 环信demo 发送消息

sendMessage: function () {

if (!this.data.userMessage.trim()) return;


var that = this
// //console.log(that.data.userMessage)
// //console.log(that.data.sendInfo)
var myName = wx.getStorageSync('myUsername')
var id = WebIM.conn.getUniqueId();
var msg = new WebIM.message('txt', id);
msg.set({
msg: that.data.sendInfo,
to: that.data.yourname,
roomType: false,
success: function (id, serverMsgId) {
console.log('send text message success')
}
});
// //console.log(msg)
console.log("Sending textmessage")
msg.body.chatType = 'singleChat'; // 群组聊天 groupRoom
WebIM.conn.send(msg.body);
// 消息发送完成

if (msg) {
var value = WebIM.parseEmoji(msg.value.replace(/\n/mg, '')) // 环信表情处理
var time = WebIM.time()
var msgData = {
info: {
to: msg.body.to
},
username: that.data.myName,
yourname: msg.body.to,
msg: {
type: msg.type,
data: value
},
style: 'self',
time: time,
mid: msg.id
}
that.data.chatMsg.push(msgData)
// console.log(that.data.chatMsg)

// 存储聊天记录
// 注: 单独单聊天 key 对方环信uin+自己的uin
// 注: 群组聊天 key 群组id\聊天室id+对方环信uin+自己的uin
wx.setStorage({
key: that.data.yourname + myName,
data: that.data.chatMsg,
success: function () {
//console.log('success', that.data)
that.setData({
chatMsg: that.data.chatMsg,
emojiList: [],
inputMessage: ''
})
setTimeout(function () {
that.setData({
toView: that.data.chatMsg[that.data.chatMsg.length - 1].mid
})
}, 100)
}
})
that.setData({
userMessage: ''
})
}
},

// 环信demo 收到消息
receiveMsg: function (msg, type) {
var that = this
var myName = wx.getStorageSync('myUsername')
if (msg.from == that.data.yourname || msg.to == that.data.yourname) {
if (type == 'txt') {
var value = WebIM.parseEmoji(msg.data.replace(/\n/mg, ''))
} else if (type == 'emoji') {
var value = msg.data
} else if(type == 'audio'){
// 如果是音频则请求服务器转码
console.log('Audio Audio msg: ', msg);
var token = msg.accessToken;
console.log('get token: ', token)
var options = {
url: msg.url,
header: {
'X-Requested-With': 'XMLHttpRequest',
'Accept': 'audio/mp3',
'Authorization': 'Bearer ' + token
},
success: function(res){
console.log('downloadFile success Play', res);
// wx.playVoice({
// filePath: res.tempFilePath
// })
msg.url = res.tempFilePath
var msgData = {
info: {
from: msg.from,
to: msg.to
},
username: '',
yourname: msg.from,
msg: {
type: type,
data: value,
url: msg.url
},
style: '',
time: time,
mid: msg.type + msg.id
}

if (msg.from == that.data.yourname) {
msgData.style = ''
msgData.username = msg.from
} else {
msgData.style = 'self'
msgData.username = msg.to
}

var msgArr = that.data.chatMsg;
msgArr.pop();
msgArr.push(msgData);

that.setData({
chatMsg: that.data.chatMsg,
})
console.log("New audio");
},
fail: function(e){
console.log('downloadFile failed', e);
}
};
console.log('Download');
wx.downloadFile(options);
}
//console.log(msg)
//console.log(value)
var time = WebIM.time()
var msgData = {
info: {
from: msg.from,
to: msg.to
},
username: '',
yourname: msg.from,
msg: {
type: type,
data: value,
url: msg.url
},
style: '',
time: time,
mid: msg.type + msg.id
}
console.log('Audio Audio msgData: ', msgData);
if (msg.from == that.data.yourname) {
msgData.style = ''
msgData.username = msg.from
} else {
msgData.style = 'self'
msgData.username = msg.to
}
//console.log(msgData, that.data.chatMsg, that.data)
that.data.chatMsg.push(msgData)

// 存储聊天记录
// 注: 单独单聊天 key 对方环信uin+自己的uin
// 注: 群组聊天 key 群组id\聊天室id+对方环信uin+自己的uin

wx.setStorage({
key: that.data.yourname + myName,
data: that.data.chatMsg,
success: function () {
if(type == 'audio')
return;
//console.log('success', that.data)
that.setData({
chatMsg: that.data.chatMsg,
})
setTimeout(function () {
that.setData({
toView: that.data.chatMsg[that.data.chatMsg.length - 1].mid
})
}, 100)
}
})
}
},
环信聊天页面,聊天数据全部存储在缓存当中,跟进聊天类型的不同,主要需要调整缓存的key。详情如下:
单对单聊天 对方uin+自己的uin
群组聊天(针对某个商品,不需要好友关系,只需要临时聊天) 群组id+对方uin+自己的uin
聊天室(同群组聊天)
问题大杂烩
群组聊天缓存如何存储?
答: 缓存key 设置为 群组id+对方uin+自己的uin
聊天时,如何在聊天中携带扩展信息
答: 消息内容中,ext 支持用户自定义参数传递
var option = {
msg: data.userMessage.trim(), // 消息内容
to: data.groupId, // 接收消息对象(聊天室id)
roomType: true,
chatType: 'groupRoom',
from: data.myuin,
ext: {
//todo 需要补充的字符哦
},
success: function () {
console.log('send room text success');
},
fail: function () {
console.log('failed');
}
};
```
会话列表如何实现?
答: 通过接口获取环信的群组列表,通过自己的服务器端补全对应的会话信息。
回顾
整个环信接入,整体围绕 假设-->猜想-->实践完成的。仔细阅读官网,会为大家节约很多时间
作者:贾慧斌
文章来源:简书

  收起阅读 »

重磅开源!业内首个 React Native 转微信小程序引擎

前言 Alita 是一套由京东 ARES 多端技术团队打造的 React Native 代码转换引擎工具。它对 React 语法有全新的处理方式,支持在运行时处理 React 语法,实现了 React Native 和微信小程序之间的主要组件对齐,可以用...
继续阅读 »
前言

Alita 是一套由京东 ARES 多端技术团队打造的 React Native 代码转换引擎工具。它对 React 语法有全新的处理方式,支持在运行时处理 React 语法,实现了 React Native 和微信小程序之间的主要组件对齐,可以用简洁、高效的方式把 React Native 代码转换成微信小程序代码。
Alita 不是一个新框架,不会有额外的学习成本,她只是一套转换引擎工具,可以把 React Native 扩展到微信小程序端,大大降低多终端上的业务开发成本。以后移动端开发者只需要掌握 React Native 技术栈,就可以轻松实现 Android、iOS、Windows、Web(已有开源项目支持)、微信小程序等多端渲染。
 
Alita 项目

开源地址: https://github.com/areslabs/alita
React Native 


11.gif


 
微信小程序


1.gif



Alita 具备哪些能力
Alita 的设计目标是要尽可能无损的转换 RN 应用,即使是已经存在的 RN 应用,我们也希望只做少量的修改就可以在微信小程序平台运行,所以这就要求 Alita 必须对 React 语法有足够的支持,包括 JSX 语法,React 生命周期等

JSX 语法

Alita 支持大部分 JSX 语法,这意味着什么呢?意味着你可以使用 React 自由的代码方式以及强大的组件化支持,意味着你可以延用自己的编程习惯,不需要对已有的 RN 代码进行过多修改。这主要得益于 Alita 是在运行时处理 JSX 语法,而不是现在社区上常见的编译时处理。

因此 Alita 没有诸如以下社区其他方案的限制:

JSX 只允许出现的组件的 render 方法中
不能通过 props 传递 JSX 片段或者返回 JSX 的函数
不支持在属性上传递函数

Alita 转换以下代码毫无压力:


2.jpg


生命周期

Alita 支持所有的 React 生命周期。微信小程序本身给组件提供了生命周期,但是这些生命周期在写法和调用上与 React 存在着一些的差异,另外 React 生命周期更加丰富。Alita 在支持 React 生命周期的时候,把它们分为了两类:

第一类: componentDidMount,componentDidUpdate,componentWillUnmount 这 3 个生命周期在微信小程序上有相应的触发时机,比如ready, detached,只需要在微信小程序相关回调触发的时候,调用 React 组件对应的方法即可。
另外一类,在微信小程序端没有直接对应的生命周期,对于这一类生命周期,主要是借助于 Alita 内部嵌入的 mini-react,触发相应的回调。通过这两种方式,Alita 实现了 React 生命周期的对齐。

此外,Alita 抹平了 RN 和微信小程序之间的事件及样式差异,能够无损得将 RN事件和样式传递到微信小程序中。

RN 基本组件和 API

RN 提供了很多基本的组件和 API,这些组件加上 React 开发方式,共同构成了 RN 应用。Alita 除了要对 React 语法进行处理,还必须在预先在微信小程序平台对齐出一套与 RN 等效的组件和 API。比如在 RN 端,请求网络的方式是通过 fetch 方式,但是微信小程序本身并不存在 fetch 方法,就这要求 Alita 必须基于微信小程序的网络 API,在微信小程序上实现一个 fetch 方法。 同样的以 RN 组件 FlatList 为例,当 Alita 把 RN 应用转化为微信小程序代码之后,FlatList 在微信小程序平台并不存在,需要预先在微信小程序平台实现小程序版本的 FlatList 。这个预先处理的过程,我们称之为对齐,对齐的过程包括组件,组件属性,API 等。


3.jpg


Redux

Redux 是 JavaScript 状态容器,提供可预测化的状态管理,并且易于测试,是当前 React 技术栈流行的数据层管理方案。得益于 Alita 运行阶段处理 React 逻辑的设计,Alita 支持将使用 Redux 的 RN 应用转换成微信小程序。

动画

动画是每一个 app 不可或缺的能力,RN 和微信小程序的动画实现差异很大,RN 的动画能力要强于微信小程序,想要完全把 RN 的动画转化至微信小程序的是不可能的。为此我们封装了一套动画组件库,这一套动画组件库涵盖了所有微信小程序的动画能力,所有使用此动画库开发的动画,都可以无损转化到小程序端。
React Native


5.gif


微信小程序


6.gif


Alita 原理简介

那么 Alita 是如何将 RN 转换运行在微信小程序上的呢?我们不打算在这篇文章深入剖析,简单从编译阶段和运行阶段来说明。

编译阶段:我们通过静态分析 RN 源码,将其转换为微信小程序可以识别的代码,首先我们会将 JSX 语法转换为微信小程序的 wxml 模块语法,RN 组件在这个阶段会被转换为微信小程序自定义组件,一般会产生微信小程序需要的 4 个文件 wxml, js,json 和 wxss。 此外,我们会保留一份 babel 转译之后的 RN 源码,这份代码里面所有的 JSX 都已经由 React.createElement 替换,运行阶段,会使用这个能被微信小程序的 JavaScript 运行环境识别的源码。

运行阶段:Alita 内部嵌入了一个 mini-react,这个 mini-react 在运行阶段会运行上文所说的转译后的 RN 源码,与 React 一样,递归(React Fiber 之后,不再是递给的方式)的处理组件树,调用组件的 render 方法,调用组件生命周期,计算 context 等。另外 React 在运行的过程中有一个重要的 reconciliation 算法(即 virtual-dom),mini-react 同样提供了简化版本的 reconciliation 来决定组件的销毁与复用。mini-react 执行完之后,最终会输出一个描述视图的数据结构,这份数据结构提供了微信小程序渲染所需要的所有数据。微信小程序通过桥接模块与 mini-react 通信,获取到这一份数据,通过 setData 的方式设置到微信小程序模版上,从而渲染出视图。


7.jpg


Alita 组件库

在项目开发中,仅仅使用 RN 基本组件和 API,是很难满足需要的。我们在使用 Alita 的过程中,积累了很多常用的三端组件,包括ScrollTabView,ViewPager,SegmentedControl等等,我们正在剥离和梳理这些组件,很快会发布兼容三端的 Alita 组件库。此组件库也是我们日后的工作重点之一,我们将会不断优化和扩展新组件。

除了 Alita 组件库,我们还提供了扩展方式,开发者可以很方便的把本团队的基本 UI 组件库扩展到微信小程序端,然后通过 Alita 把使用了这些组件的 RN 应用运行在微信小程序平台。


8.jpg


结语

我们将不断拓展 Alita 的能力,支持更多端能力,如:百度小程序、头条小程序等,继续完善开发者体验,提高开发者效率,帮助更多开发者。

我们也在考察 Flutter 这一新的跨端方案和微信小程序融合转化的可行性。

我们十分重视开源社区的反馈和建议,会不断从中汲取养分,让 Alita 变得更加强大。

意见反馈

如果有任何的意见或者建议,欢迎在 Github 创建 issue,感谢你的支持和贡献。
  收起阅读 »

GitHub 开源跨平台神器 Electron 实践

认识 Electron Electron是由GitHub开发,用HTML、CSS 和 JavaScript来构建跨平台桌面应用程序的一个开源库。Electron通过将Chromium和Node.js合并到同一个运行时环境中,并将其打包为Mac、Windows...
继续阅读 »
认识 Electron

Electron是由GitHub开发,用HTML、CSS 和 JavaScript来构建跨平台桌面应用程序的一个开源库。Electron通过将Chromium和Node.js合并到同一个运行时环境中,并将其打包为Mac、Windows和Linux系统下的应用。Electron于2013年作为构建GitHub上可编程的文本编辑器Atom的框架而被开发出来。

这不意味着Electron是绑定了GUI库的JavaScript。相反,Electron使用Web页面作为它的GUI,所以你能把它看作成一个被JavaScript控制的,精简版的Chromium浏览器。

Electron的版本更新很频繁,基本保持在1周发布一个小版本,每季度发布一个大版本。除了稳定版外还有Beta版和Nightly(最新功能试用版),Chromium更新时,Electron也会跟着更新。

为什么选择Electron

如今的桌面应用软件基本都需要跨平台运行,类似于MFC、Duilib等技术都无法满足需求。当今的跨平台桌面应用软件开发以使用QT,Electron较多。

QT跨平台开发

Qt是一个跨平台C++图形用户界面应用程序开发框架。它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器。作为使用C++语言开发的框架,他的优缺点十分明显。

优点:

运行效率高;

架构健壮,性能强大。

缺点:

开发周期长;

需要开发者具有C++编程能力;

QT是一款收费软件,如果不想缴费购买License,又想用QT开发商业(闭源)程序,必须遵守LGPL协议,开源使用了LGPL库的源代码。

Electron桌面软件开发

Electron最早用于开发GitHub上的可编程文本编辑器Atom,它是一个借助Node.js和Chromium, 利用HTML/CSS/JavaScript语言创建桌面应用的框架。与之类似的还有NW.js, 但是NW.js社区发展基本处于停滞状态,更新也较慢。

优点:

使用JavaScript语言作为开发语言,方便前端开发者轻松开发桌面应用,原C++/Java语言开发者,也可以很快入手开发;

方便调试,提供了浏览器的开发者工具,轻松断点调试;

丰富的Web前端UI资源,可以快速制作绚丽的界面;

快速构建,迭代开发。最复杂的底层浏览器部分Electron已经帮你搞定,你只需要负责上层界面及业务逻辑的开发。Electron还提供了热更新功能,只需加载更新模块,会自动帮你检查更新并后台下载;

崩溃日志报告。轻松收集崩溃日志,定位错误代码;

C++插件扩展;

代码开源。Electron是GitHub上的开源项目,开发者有疑问可以在GitHub社区(https://github.com/electron/electron)上直接提issue,高级开发者可以修改Electron底层代码,订制自己的Elcetron。

缺点:

打包文件太大。Electron毕竟是一个浏览器,最小的应用安装包也要几十兆大小;

无法代码加密。和Web开发类似,使用者可以在开发者工具看到应用的客户端代码,商业软件需要代码加密的可以选择重要功能在服务端实现,桌面应用请求,或使用Node文件实现;

运行耗资源。浏览器通病,Electron应用也是多进程系统,启动几个Electron应用还好,如果太多会造成机器卡顿;

不支持XP系统,Node.js并不支持XP系统。

综上,如果你想快速的开发出炫酷的桌面应用,而又对系统限制不大,建议你选择Electron,如果你是一个前端开发人员,又想制作桌面应用,建议你选择Electron。

创建一个简单的应用

环境安装

Electron应用本质上是一个Node.js应用程序,需要安装Node.js,到官网(http://Node.js.cn/download/)安装即可。安装完后,在命令行窗口中分别输入node -v和npm -v来查看Node和NPM的版本。

初始化应用
与Node.js模块相同,应用的入口为package.json 文件,该文件可以在一个文件夹下使用npm init命令,按照提示填充各项信息生成。 一个最基本的Electron 应用一般来说会有如下的目录结构:


1.png


 main.js是主进程,完成窗口的创建,url或html文件的加载。GitHub上提供了一个简单的Electron应用https://github.com/electron/electron-quick-start.git,可供学习参考。

使用C++插件扩展功能
对于复杂的业务逻辑,可以开发成C++插件Node,C++插件主要完成一些复杂的逻辑功能,供Electron调用。Electron对于C++生成的Node插件引用功能来自于Node.js,可以使用require() 函数加载到工程中,像普通的模块一样使用。JavaScript 与C++ 库之间接口使用V8引擎,如下图所示:


2.jpg


 插件开发环境

C++插件的开发需要安装node-gyp、Python 2.76,Windows下开发还要安装Visual Studio。
每个插件都有一个工程文件binging.gyp,配置了源文件、include路径及链接库,目标文件,使用的编译器等,格式如下:


3.png


 
C++与JavaScript通过V8交互执行的整体过程如下图所示:


4.jpg


 
C++可以使用Napi接口,模块的加载使用宏NODEAPIMODULE(hello, Init),导出的JavaScript接口在Init中定义,示例如下:


5.png


 
编译C++插件使用命令如下:


6.png


 
生成的C++插件为node文件,如hello.node 在JavaScript中调用C++插件直接使用require函数,代码hello.js如下:


7.png


 JavaScript就可以调用C++的接口了,执行命令node hello.js,输出"world"。

C++中调用JavaScript传递的回调函数需要使用libuv库,libuv实现了Node.js的事件循环、工作线程、以及平台所有的的异步操作的C库。 具体参考示例代码https://github.com/nodejs/node-addon-examples

Electron打包

Electron应用打包可以使用electron-builder和electron-packager,推荐使用electron-builder,打包命令为npm run builder,可以使用参数配置生成的安装包的操作系统。

环信IM桌面端

环信的IM桌面端SDK提供了JavaScript接口,并且使用Electron框架开发的示例Demo,可以让任何一个前端人员在极短时间内搭建出一款同时在Mac、Windows上运行的即时通讯软件,拥有单聊、群聊和聊天室功能,支持文字、表情、图片、音视频等消息格式,开发时间短、界面美观,可以为开发者提供方便快捷的桌面端即时通讯解决方案。

下载地址:http://www.easemob.com/download/im
集成说明:http://docs-im.easemob.com/im/pc/intro/integration

作者:李小明,现就职于环信,高级软件开发工程师,负责IM桌面端软件的研发,以C++、Node.js为开发语言,从事多年桌面软件开发经验,对行业前沿技术永远不懈追求。 收起阅读 »

收藏了~阿里巴巴程序员常用的 15 款开发者工具

从人工到自动化,从重复到创新,技术演进的历程中,伴随着开发者工具类产品的发展。 阿里巴巴将自身在各类业务场景下的技术积淀,通过开源、云上实现或工具等形式对外开放,本文将精选了一些阿里巴巴的开发者工具,希望能帮助开发者们提高开发效率、更优雅的写代码。 由于开...
继续阅读 »
从人工到自动化,从重复到创新,技术演进的历程中,伴随着开发者工具类产品的发展。

阿里巴巴将自身在各类业务场景下的技术积淀,通过开源、云上实现或工具等形式对外开放,本文将精选了一些阿里巴巴的开发者工具,希望能帮助开发者们提高开发效率、更优雅的写代码。

由于开发者涉及的技术领域众多,笔者仅从自己熟悉的领域,以后端开发者的视角盘点平时可能用得到的工具。每个工具按照以下几点进行介绍:

工具名称和简介

使用场景

使用教程

获取方式

一、Java 线上诊断工具 Arthas

Arthas 阿里巴巴 2018 年 9 月开源的一款 Java 线上诊断工具。

工具的使用场景:

这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?

我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?

遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?

线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!

是否有一个全局视角来查看系统的运行状况?

有什么办法可以监控到 JVM 的实时运行状态?

Arthas 支持 JDK 6+,支持 Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。

使用教程:

基础教程:

https://alibaba.github.io/arthas/arthas-tutorials?language=cn&amp;id=arthas-basics

进阶教程:

https://alibaba.github.io/arthas/arthas-tutorials?language=cn&amp;id=arthas-advanced

获取方式:(免费)

开源地址:

https://github.com/alibaba/arthas

二、IDE 插件 Cloud Toolkit

Cloud Toolkit是一款 IDE 插件,可以帮助开发者更高效地开发、测试、诊断并部署应用。通过 Cloud Toolkit,开发者能够方便地将本地应用一键部署到任意机器(本地或云端),并内置 Arthas 诊断、高效执行终端命令和 SQL 等,提供 IntelliJ IDEA 版,Eclipse 版,PyCharm 版和 Maven 版。

工具的使用场景:

每次修改完代码后,是否正在经历反复地打包?

在 Maven 、Git 以及其他运维脚本和工具的之间频繁切换?

采用 SCP 工具上传?使用 XShell 或 SecureCRT 登陆服务器?替换部署包?重启?

文件上传到服务器指定目录,在各种 FTP、SCP 工具之间频繁切换 ?

使用教程:

IntelliJ IDEA 版:

https://help.aliyun.com/document_detail/98762.html

Eclipse 版:

https://help.aliyun.com/document_detail/29970.html

PyCharm 版:

https://help.aliyun.com/document_detail/112740.html

Maven 版:

https://help.aliyun.com/document_detail/108682.html

获取方式:(免费) 工具地址:

https://www.aliyun.com/product/cloudtoolkit

三、混沌实验注入工具 ChaosBlade

ChaosBlade是一款遵循混沌工程实验原理,提供丰富故障场景实现,帮助分布式系统提升容错性和可恢复性的混沌工程工具,可实现底层故障的注入,提供了延迟、异常、返回特定值、修改参数值、重复调用和 try-catch 块异常等异常场景。

工具的使用场景:

微服务的容错能力不易衡量?

容器编排配置是否合理无法验证?

PaaS 层健壮性的测试工作无从入手?

使用教程:

https://github.com/chaosblade-io/chaosblade/wiki/ 新手指南

获取方式:(免费)

开源地址:

https://github.com/chaosblade-io/chaosblade/wiki/ 新手指南

四、Java 代码规约扫描插件

该插件用于检测 Java 代码中存在的不规范的位置,并给予提示。规约插件是采用 Kotlin 语言开发。

使用教程:

IDEA 插件使用文档:

https://github.com/alibaba/p3c/wiki/IDEA 插件使用文档

Eclipse 插件使用文档:

https://github.com/alibaba/p3c/wiki/Eclipse 插件使用文档

获取方式:(免费)

开源地址:

https://github.com/alibaba/p3c

五、应用实时监控工具 ARMS

ARMS是一款 APM 类的监控工具,提供前端、应用、自定义监控 3 类监控选项,可快速构建实时的应用性能和业务监控能力。

工具的使用场景:

晚上 10 点收到 37 条报警信息,你却无从下手?

当我们发现问题的时候,客户 / 业务方已经发起投诉?

每个月花几十万买服务器,却无法保障用户体验?

使用教程:

前端监控接入:

https://help.aliyun.com/documentdetail/106086.html

应用监控接入:

https://help.aliyun.com/documentdetail/63796.html

自定义监控:

https://help.aliyun.com/document_detail/47474.html

获取方式:(收费)

工具地址:

https://www.aliyun.com/product/arms

六、静态开源站点搭建工具 Docsite

Docsite一款集官网、文档、博客和社区为一体的静态开源站点的解决方案,具有简单易上手、上手不撒手的特质,同时支持 react 和静态渲染、PC 端和移动端、支持中英文国际化、SEO、markdown 文档、全局站点搜索、站点风格自定义、页面自定义等功能。

使用教程:

https://docsite.js.org/zh-cn/docs/installation.html

获取方式:(免费)

项目地址:

https://github.com/txd-team/docsite

七、Android 平台上的秒级编译方案 Freeline

Freeline 可以充分利用缓存文件,在几秒钟内迅速地对代码的改动进行编译并部署到设备上,有效地减少了日常开发中的大量重新编译与安装的耗时。Freeline 最快捷的使用方法就是直接安装 Android Studio 插件。

使用教程:

https://github.com/alibaba/freeline/blob/master/README-zh.md

获取方式:(免费)

项目地址:

https://github.com/alibaba/freeline

八、性能测试工具 PTS

PTS可以模拟大量用户访问业务的场景,任务随时发起,免去搭建和维护成本,支持 JMeter 脚本转化为 PTS 压测,同样支持原生 JMeter 引擎进行压测。

使用教程:

https://help.aliyun.com/document_detail/70290.html

获取方式:(收费)

工具地址:

https://www.aliyun.com/product/pts

九、云效开发者工具 KT

KT 可以简化在 Kubernetes 下进行联调测试的复杂度,提高基于 Kubernetes 的研发效率。

使用教程:

https://yq.aliyun.com/articles/690519

获取方式:(免费)

工具地址:

https://yq.aliyun.com/download/3393

十、架构可视化工具 AHAS

AHAS为 K8s 等容器环境提供了架构可视化的功能,同时,具有故障注入式高可用能力评测和一键流控降级等功能,可以快速低成本的提升应用可用性。

工具的使用场景:

服务化改造过程中,想精确的了解资源实例的构成和交互情况,实现架构的可视化?

想引入真实的故障场景和演练模型?

低门槛获得流控、降级功能?

使用教程:

https://help.aliyun.com/document_detail/90323.html

获取方式:(免费)

工具地址:

https://www.aliyun.com/product/ahas

十一、数据处理工具 EasyExcel

EasyExcel 是一个用来对 Java 进行解析、生成 Excel 的框架,它重写了 poi 对 07 版 Excel 的解析,原本一个 3M 的 Excel 用 POI sax 需要 100M 左右内存,EasyExcel 可降低到 KB 级别,并且再大的 excel 也不会出现内存溢出的情况。03 版依赖 POI 的 sax 模式。在上层做了模型转换的封装,让使用者更加简单方便。

使用教程:

https://github.com/alibaba/easyexcel/blob/master/quickstart.md

获取方式:(开源)

https://github.com/alibaba/easyexcel

十二、iOS 类工具 HandyJSON

HandyJSON 是一个用于 Swift 语言中的 JSON 序列化 / 反序列化库。

与其他流行的 Swift JSON 库相比,HandyJSON 的特点是,它支持纯 Swift 类,使用也简单。它反序列化时 (把 JSON 转换为 Model) 不要求 Model 从 NSObject 继承 (因为它不是基于 KVC 机制),也不要求你为 Model 定义一个 Mapping 函数。只要你定义好 Model 类,声明它服从 HandyJSON 协议,HandyJSON 就能自行以各个属性的属性名为 Key,从 JSON 串中解析值。

使用教程:

https://github.com/alibaba/HandyJSON/blob/master/README_cn.md

获取方式:(开源)

https://github.com/alibaba/HandyJSON

十三、云上资源和应用部署工具 EDAS Serverless

EDAS Serverless一款基于 Kubernetes,面向应用和微服务的 Serverless 平台。用户无需管理和维护集群与服务器,即可通过镜像、WAR 包和 JAR 包,快速创建原生支持 Kubernetes 的容器应用,同时支持 Spring Cloud 和 Dubbo 等主流微服务框架。

使用教程:

https://help.aliyun.com/document_detail/102048.html

获取方式:(公测期间免费)

https://help.aliyun.com/document_detail/97792.html

十四、数据库连接池 Druid

Druid 是 Java 语言下的数据库连接池,它能够提供强大的监控和扩展功能。

使用教程:

https://github.com/alibaba/druid/wiki/ 常见问题

获取方式:(开源)

http://central.maven.org/maven2/com/alibaba/druid/

十五、Java 工具集 Dragonwell

Alibaba Dragonwell 是阿里巴巴内部 OpenJDK 定制版 AJDK 的开源版本, AJDK 为在线电商,金融,物流做了结合业务场景的优化,运行在超大规模的,100,000+ 服务器的阿里巴巴数据中心。 Alibaba Dragonwell 与 Java SE 标准兼容,目前仅支持 Linux/x86_64 平台。

使用教程:

https://github.com/alibaba/dragonwell8/wiki/ 阿里巴巴 Dragonwell8 用户指南

获取方式:(开源)

https://github.com/alibaba/dragonwell8


上一篇: Java首度承认PK失败,愿永久服软Python!
  收起阅读 »

史上最完整的官方Oracle OCP中文文教材,快来下载吧!!

内含文件: 1、Oracle Database 11g:SQL 基础 学生指南第1 册  2、Oracle Database 11g:SQL 基础 学生指南第2 册  3、Oracle Database 11g:数据库管理- 课堂练习I 学生指南第1 册...
继续阅读 »
内含文件:
1、Oracle Database 11g:SQL 基础 学生指南第1 册 
2、Oracle Database 11g:SQL 基础 学生指南第2 册 
3、Oracle Database 11g:数据库管理- 课堂练习I 学生指南第1 册
4、Oracle Database 11g:数据库管理- 课堂练习II 学生指南第1 册 
5、Oracle Database 11g:数据库管理- 课堂练习I 学生指南第2 册 
6、Oracle Database 11g:数据库管理- 课堂练习II 学生指南第2 册 
7、Oracle Da等等 收起阅读 »

手!慢!无!价值1980的数据分析教程,终终终于免费啦!!!

对比互联网各个岗位的裁员程度可以发现,数据分析相关岗位正在不断的扩招,已经成为了这波逆流中的黑马,什么原因导致的数据分析人才如此紧缺? 因为数据分析是大势所趋,未来的发展空间会大有可为。随着5G网络即将商用,企业每天将会产生海量的数据,BAT日均数据更是达到了...
继续阅读 »
对比互联网各个岗位的裁员程度可以发现,数据分析相关岗位正在不断的扩招,已经成为了这波逆流中的黑马,什么原因导致的数据分析人才如此紧缺?
因为数据分析是大势所趋,未来的发展空间会大有可为。随着5G网络即将商用,企业每天将会产生海量的数据,BAT日均数据更是达到了PB的级别,数据分析相关岗位才会存在着巨大的需求缺口。
长此以往,企业要用尽可能少的人才,来满足尽可能多岗位的诉求,可以这么说,数据分析将会是每个程序员个人能力最重要的补充,也是BAT这类大公司急招人才的必备技能。
但是一提数据分析,很多人就觉得无从下手,知识点零散总是抓不住重点,学习起来相当吃力。这有一份廖雪峰大神历时3个月打磨出来的《数据分析必备技能》的视频学习资料,由浅入深系统化的讲解,内容详尽。基本囊括了平时学习工作中经常用到的分析方式,这份不可或缺的宝贵资料原价值1980元,现在,关注公众号cainiao_xueyuan就可以免费领取(仅限300名)。

学完这套资料可以给你将会得到哪些收获?

1. 总时长>48个小时的干货内容,每天2小时,20天掌握数据分析必备技能;
2. 对照自己掌握知识点进行查缺补漏,帮助你扫除知识盲区、重构知识体系。
具体详细的资料内容:
1 数学理论基础           
01.数据挖掘之数学基础02.数学基础之微积分
03.机器学习之线性回归
04.机器学习之逻辑回归
05.朴素贝叶斯
06.机器学习之决策树
07.机器学习之集成学习
2 必备Python基础            
01.Python语言介绍、发展、特色02.概念介绍:Python解释器
03.Python函数及高级特性
04.交互环境介绍:启动和退出交互环境
05.Python基础语法及模块
3 高效scrapy爬虫框架           
01.scrapy简介02.scrapy选择器
03.创建scrapy爬虫
04.下载器与爬虫中间件
05.突破反爬虫机制与策略
06.使用管道 pipelines        
4 Excel数据处理            
01.认识数据表的字段和记录02.使用Excel制作数据表
03.指定常用数据类型
04.Excel导入网站数据、文本数据
05.Excel数据清洗、筛选
06.Excel数据抽样和计算

5 使用SQL实现数据操作
01SQL基础语法
02.SQL表连接
03.SQL普通函数
04.SQL窗口函数
05.SQL优化 

长按扫码 添加微信,领取干货视频


微信图片_20190507095431.jpg



Ps:学习资料由"开课吧"友情提供。 收起阅读 »

打击电信诈骗保障客户安全,环信客服云6大安全机制让骗子无所遁形!

近日,环信收到海淀网警和用户的反馈,部分不法分子通过注册环信客服云获取专业客服工具在各大论坛和二手交易平台:如百度贴吧、58同城“转转”等进行电信诈骗,不法分子冒充平台的专业客服人员诱导客户先行付款进行诈骗,造成了很恶劣的社会影响。 ...
继续阅读 »
近日,环信收到海淀网警和用户的反馈,部分不法分子通过注册环信客服云获取专业客服工具在各大论坛和二手交易平台:如百度贴吧、58同城“转转”等进行电信诈骗,不法分子冒充平台的专业客服人员诱导客户先行付款进行诈骗,造成了很恶劣的社会影响。


2.jpg




环信第一时间获悉即敏锐的采取行动,一期关停450个诈骗账号信息,同时推出6大安全机制让骗子无所遁形。

环信客服云防诈骗六大安全机制:

1.注册环节:单手机号15天内只能注册一个账号,以防反复账号行骗。

2.使用环节:关停H5聊天窗口,需申请审核后才能打开。

3.聊天环节:敏感词预警,在用户聊天记录过程中系统监控到提前设置的关键词,会触发预警给到运营人员,进行账户关停。

4.账号限制:实时操作异常账号关闭,一经发现,立即关停账号

5.黑名单机制:加入黑名单的注册手机号,永久无法再注册环信

6.报警机制:第一时间反馈所有诈骗信息至公安网警等相关部门

同时,环信也开通了反诈骗投诉信箱:Antifraud@easemob.com 和 400专线:400-622-1776,我们的工作人员会第一时间解决所有用户碰到的所有潜在欺诈问题。

维护网络安全繁荣是环信义不容辞的社会责任,环信会一如既往和网警网安等部门密切合作,打击电信诈骗,保护客户安全,环信客服云一直在路上! 收起阅读 »

同一个网站,手机端跟电脑端显示不同是怎么实现的?

同一个网站,手机端跟电脑端不同是怎么实现的? 常见的方式有三种: 1,自适应网站 同一套代码,自动实现手机端和电脑端的布局自动调整。例如:openGPS.cn 网站现在大部分页面已经支持自适应展示,手机端电脑端都可以访问本站内容,正常阅读。自适应站点,往...
继续阅读 »
同一个网站,手机端跟电脑端不同是怎么实现的?

常见的方式有三种:

1,自适应网站

同一套代码,自动实现手机端和电脑端的布局自动调整。例如:openGPS.cn 网站现在大部分页面已经支持自适应展示,手机端电脑端都可以访问本站内容,正常阅读。自适应站点,往往是对CSS布局的重点考虑,本站使用的是BootStrap这个前端样式组件实现的自适应布局。

2,网站二级目录

这种是早期网站比较喜欢的做法,因为其实这是一个网站。早期网站往往是使用虚拟主机(也叫空间)发布,一个空间只能放一个网站,所以这种做法在早期特别流行。这种结构本质还是一个网站,但是针对手机电脑客户端单独做了往往对应的一套目录,例如:

电脑站点地址一般是:www.domain.com/xxxxxxx

手机站点地址往往是:www.domain.com/m/xxxxxxx

3,手机站点使用二级域名,电脑手机各一套2套站点代码


这种做法,工作量跟二级目录基本相似,严格来说代码量稍微多点。由于是2套代码,所以发布时候也得配备2套域名,不过要求两套站点连接同一个数据库来实现数据统一。例如:

电脑端网站域名是:www.domain.com

手机端网站域名是:m.domain.com




原文地址: https://www.opengps.cn/Blog/View.aspx?id=302 文章的更新编辑依此链接为准。欢迎关注源站原创文章!
  收起阅读 »

在微信小程序里实现聊天室

第一次搞小程序,老板让我实现一个聊天室功能,压力山大啊。 花了几天时间研究比较了一下方案,最后基于环信的小程序SDK 开发了一个聊天室。   准备工作 下载环信 小程序demo+sdkgit clone https://github.com/easemob/w...
继续阅读 »
第一次搞小程序,老板让我实现一个聊天室功能,压力山大啊。
花了几天时间研究比较了一下方案,最后基于环信的小程序SDK 开发了一个聊天室。
 
准备工作
  1. 下载环信 小程序demo+sdk
    git clone https://github.com/easemob/webim-weixin-xcx
  2. 创建一个文件夹,将 demo 中的文件 comps、images、sdk、utils 拷贝到新的文件,文件目录说明

    ml.png


集成
  1. 登录环信没什么可说的,这里选择的是使用 username/password 登录,和demo中的一样,文件没有进行任何更改

    login.png

  2. 在app.js 中注册的 WebIM.conn.listen, 然后在 登陆成功的回调 onOpened 设置的跳转页面,并将登陆的 username 赋给 myName,传到新的页面中使用

    tz.png

  3. 修改 roomlist.js 获取聊天室列表,是分页获取的,这里先偷个懒,获取了第一页 20 个聊天室

    getroom.png

    然后将listChatrooms() 分别在onLoad、onShow 内,更改下,将原有的 listGroups() 替换掉
  4. 然后在roomlist.wxml 修改对应的 变量绑定名称

    listui.png


    list.png

  5. demo中的group.js 中,获取到的是当前登陆账号已加入的群组,咱们做的是聊天室功能,所以需要有一个加入的操作,找roomlist.js 中找到 into_room: function (event),然后填写加入聊天室的方法, 我是直接在当前这个里面加的跳转到聊天页面,并将当前登陆的IDmyName,聊天室IDgroupID,聊天室名称your 传给新页面

    joinrom.png

    Ex:监听是否加入聊天室成功的回调是在 onPresence 中,type:memberJoinChatRoomSuccess,正常是监听这个回调跳转页面,有点麻烦就直接这样吧
  6. 到会话页面后,需要修改一下对应的消息格式,在comps/chat/suit 目录下,将里面的文件对应的 js 文件根据文档给聊天室发送消息 格式进行修改,聊天室消息和群组消息不同,所以我目前是直接将getSendToParam()、isGroupChat() 注释,改成下面这样,demo 中下面还有代码的,这里就用 …… 代替了

    send.png


    chat.png

    就这样了,简单集成聊天室功能,demo中的UI 是开源的,可以根据自己的需求更改~下面是具体实现过程。代码也放在github 上了,有需要的兄弟自取。demo下载地址:https://github.com/lizgDonkey/room-xcx

收起阅读 »

(客服云)iOS访客端集成常见问题(非报错)

1、UI上很多地方显示英文,比如聊天页面的工具栏 把客服demo中配置的国际化文件添加到您自己的工程中。拖之前要打开国际化文件,全部选中这三个,再进行拖入。   2、进入聊天页面没有加载聊天记录 这种情况一般出现在只使用了 HDM...
继续阅读 »
1、UI上很多地方显示英文,比如聊天页面的工具栏

显示英文1.png


把客服demo中配置的国际化文件添加到您自己的工程中。拖之前要打开国际化文件,全部选中这三个,再进行拖入。

显示英文2.png


 
2、进入聊天页面没有加载聊天记录
这种情况一般出现在只使用了 HDMessageViewController 没有使用 HDChatViewController 的时候
在HDMessageViewController 的 viewDidLoad 方法中, 将 [self tableViewDidTriggerHeaderRefresh]; 的注释打开,再在这句代码之前加上 self.showRefreshHeader = YES; 
 
3、发送表情却显示字符串

访客端表情符号.png


把下面这段代码添加到appdelegate中就可以了
[[HDEmotionEscape sharedInstance] setEaseEmotionEscapePattern:@"\\[[^\\[\\]]{1,3}\\]"];
[[HDEmotionEscape sharedInstance] setEaseEmotionEscapeDictionary:[HDConvertToCommonEmoticonsHelper emotionsDictionary]];
 
4、文本消息,收发双方的布局不一样,如图

文本消息布局错误1.png


参考一下截图修改即可

文本消息布局错误2.png



5、客服能收到访客的消息,访客收不到客服的消息
(1)客服和im同时使用的话,初始化sdk、登录、登出用的是im的api会出现这种情况。必须使用客服的api。
(2)IM sdk升级为客服sdk,不兼容导致的,这种情况可以线上发起会话咨询。
      
6、发送的消息,出现在聊天页面的左侧
一般是由于当前访客没有登录或者登录失败,断点仔细检查下。

7、修改聊天页面导航栏标题
修改_title的值


收起阅读 »

(客服云)iOS访客端集成常见报错(总有一款适合你)

注意:向自己工程中添加环信SDK和UI文件的时候,不要直接向xcode中拖拽添加,先把SDK和UI文件粘贴到自己工程的finder目录中,再从finder中向xcode中拖拽添加,避免出现找不到SDK或者UI文件的情况。   1、很多同学在首次“导入...
继续阅读 »
注意:向自己工程中添加环信SDK和UI文件的时候,不要直接向xcode中拖拽添加,先把SDK和UI文件粘贴到自己工程的finder目录中,再从finder中向xcode中拖拽添加,避免出现找不到SDK或者UI文件的情况。
 
1、很多同学在首次“导入SDK”或“更新SDK重新导入SDK”后,Xcode运行报以下的error:
dyld: Library not loaded: @rpath/Hyphenate.framework/Hyphenate
  Referenced from: /Users/shenchong/Library/Developer/CoreSimulator/Devices/C768FE68-6E79-40C8-8AD1-FFFC434D51A9/data/Containers/Bundle/Application/41EA9A48-4DD5-4AA4-AB3F-139CFE036532/CallBackTest.app/CallBackTest
  Reason: image not found
       这个原因是工程未加载到 framework,正确的处理方式是在TARGETS → General → Embedded Binaries 中添加HelpDesk.framework和Hyphenate.framework依赖库,且 Linked Frameworks and Libraries中依赖库的Status必须是Required。

1访客端_image_not_found.png


 
2、运行之后,自变量为nil,这就有可能是因为上面所说的依赖库的status设置为了Optional,需要改成Required。

2访客端自变量为nil.png


 
3、打包后上传到appstore报错
(1)ERROR ITMS-90535: "Unexpected CFBundleExecutable Key. The bundle at 'Payload/toy.app/HelpDeskUIResource.bundle' does not contain a bundle executable. If this bundle intentionally does not contain an executable, consider removing the CFBundleExecutable key from its Info.plist and using a CFBundlePackageType of BNDL. If this bundle is part of a third-party framework, consider contacting the developer of the framework for an update to address this issue."
方法:把HelpDeskUIResource.bundle里的Info.plist删掉就即可。

3访客端打包90535.png


(2)This bundle is invalid. The value for key CFBundleShortVersionString ‘1.2.2.1’in the Info.plist must be a period-separated list of at most three non-negative integers. 

4访客端打包90060.png


把sdk里的plist文件的版本号改成3位数即可

5访客端打包1.2_.2_.1位置_.png


(3)Invalid Mach-O Format.The Mach-O in bundle “SMYG.app/Frameworks/Hyphenate.framework” isn’t consistent with the Mach-O in the main bundle.The main bundle Mach-O contains armv7(bitcode) and arm64(bitcode),while the nested bundle Mach-O contains armv7(machine code) and arm64(machine code).Verify that all of the targets for a platform have a consistent value for the ENABLE_BITCODE build setting.”

6访客端打包90636.png


将TARGETS-Build Settings-Enable Bitcode改为NO

7访客端打包bitcode改为NO.png


(4)还有很多同学打包失败,看不出什么原因

8访客端打包需剔除.png


那么可以先看看有没有按照文档剔除x86_64 i386两个平台
文档链接:http://docs.easemob.com/cs/300visitoraccess/iossdk#%E4%B8%8A%E4%BC%A0appstore%E4%BB%A5%E5%8F%8A%E6%89%93%E5%8C%85ipa%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9
 
4、那么剔除x86_64 i386时会遇到can't open input file的错误,这是因为cd的路径错误,把“/HelpDesk.framework”删掉。是cd到framework所在的路径,不是cd到framework

9访客端剔除cd错误.png


 
5、下图中的报错,需要创建一个pch文件,并且在pch文件添加如下判断,将环信的和自己的头文件都引入到#ifdef内部,参考文档:iOS访客端sdk集成准备工作
   #ifdef __OBJC__
   #endif
(swift项目也需这样操作)

10pch加判断1.png



11pch加判断2.png



pch加判断3.png



6、集成环信HelpDeskUI的时候,由于HelpDeskUI内部使用了第三方库,如果与开发者第三方库产生冲突,可将HelpDeskUI中冲突的第三方库删除,如果第三方库中的接口有升级的部分,请酌情进行升级。

12第三方库冲突.png


 
7、集成1.2.2版本demo中的HelpDeskUI,Masonry报错:Passing ‘CGFloat’(aka ‘double’) to parameter of incompatible type ‘__strong id’
需要在pch中添加#define MAS_SHORTHAND_GLOBALS
注意:要在#import "Masonry.h"之前添加此宏定义

13访客端Masonry报错.png


 
8、Xcode11运行demo,PSTCollectionView第三方库会有如下报错

iOS13中PSTCollectionView报错.png


标明下类型就行了,如图

iOS13中PSTCollectionView报错1.png


 
9、Xcode12.3编译报错(Building for iOS, but the linked and embedded framework......)

xcode12.3报错1_.jpg
解决方案:

 或者打开xcode,左上方点击File --- Workspace Settings,按照截图修改试下(不建议)

xcode12.3报错2_.jpg


 
收起阅读 »

(客服云)iOS访客端怎么判断会话是否结束

1、联系商务开通【会话创建、接起、结束】功能 2、在 cmdMessagesDidReceive 方法中做如下判断,返回ServiceSessionClosedEvent,则是会话已结束,如图: 代码: if ([message.body isKin...
继续阅读 »
1、联系商务开通【会话创建、接起、结束】功能
2、在 cmdMessagesDidReceive 方法中做如下判断,返回ServiceSessionClosedEvent,则是会话已结束,如图:

判断会话是否结束.png


代码:
if ([message.body isKindOfClass:[EMCmdMessageBody class]]) {
EMCmdMessageBody *_bb = (EMCmdMessageBody *)message.body;
if ([_bb.action isEqualToString:@"ServiceSessionCreatedEvent"]) {
NSLog(@"hhhhh--Creat");
} else if ([_bb.action isEqualToString:@"ServiceSessionOpenedEvent"]) {
NSLog(@"hhhhh--Open");
} else if ([_bb.action isEqualToString:@"ServiceSessionClosedEvent"]) {
NSLog(@"hhhhh--Close");
}
}
  收起阅读 »

(客服云)iOS访客端点击订单消息

1、在HDMessageCell.m 的 - (void)_setupSubviewsWithType:(EMMessageBodyType)messageType                       isSender:(BOOL)isSender  ...
继续阅读 »
1、在HDMessageCell.m 的
- (void)_setupSubviewsWithType:(EMMessageBodyType)messageType
                      isSender:(BOOL)isSender
                         model:(id)model
方法中给orderBgView 添加手势

点击订单消息1.png

UITapGestureRecognizer *tapRecognizer3 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(orderImageViewTapAction:)];
[_bubbleView.orderBgView addGestureRecognizer:tapRecognizer3];
 
2、在HDMessageCell.m 中添加手势点击事件

点击订单消息2.png

- (void)orderImageViewTapAction:(UITapGestureRecognizer *)tapRecognizer
{
if ([_delegate respondsToSelector:@selector(messageCellSelected:)]) {
[_delegate messageCellSelected:_model];
}
}
 
3、在HDMessageViewController的 
   - (void)messageCellSelected:(id)model 方法中添加订单消息的判断

点击订单消息3.png

代码:
if ([HDMessageHelper getMessageExtType:model.message] == HDExtOrderMsg) {
// 订单消息携带的扩展
NSDictionary *dic = model.message.ext;
NSLog(@"点击了订单消息");
}
收起阅读 »

(客服云)IOS访客端设置访客昵称头像

1.在HDMessageViewController.h 中添加访客昵称、头像的属性 // 访客昵称 @property (nonatomic, strong) NSString *sendName; // 访客头像(url) @property (n...
继续阅读 »
1.在HDMessageViewController.h 中添加访客昵称、头像的属性

1.png

// 访客昵称
@property (nonatomic, strong) NSString *sendName;
// 访客头像(url)
@property (nonatomic, strong) NSString *sendAvatarUrl;
// 访客头像(本地图片)
@property (nonatomic, strong) UIImage *sendAvatarImage;
 
2.在HDMessageViewController.m   - (NSArray *)formatMessages:(NSArray *)messages 方法中添加判断

2.png

if (isSender) {
if (self.sendName) {
model.nickname = self.sendName;
}
// 加载网络头像
if (self.sendAvatarUrl) {
model.avatarURLPath = self.sendAvatarUrl;
}
// 加载本地头像
if (self.sendAvatarImage) {
model.avatarImage = self.sendAvatarImage;
model.avatarURLPath = nil;
}
}
 
3.在初始化聊天页面的时候,传入访客的昵称、头像即可。
(可选择url或者本地头像图片)

3.png

ctrl.sendName = @"访客昵称";
ctrl.sendAvatarImage = [UIImage imageNamed:@"测试图片"];
// chat.sendAvatarUrl = @"";
收起阅读 »