# WebServer **Repository Path**: HxwProgrammer/WebServer ## Basic Information - **Project Name**: WebServer - **Description**: No description available - **Primary Language**: NodeJS - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2019-09-24 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 待处理事情: > 开发中遇到一些未处理的问题, 但是处理起来又稍显麻烦, 所以现在这里记录起来,一起修复. ``` 1. mysql错误处理,如:数据库被关闭, 遇到这种情况则所有需要操作mysql的都直接返回失败,并且启动定时器定时重新连接. 程序启动的时候数据库就连接失败了, 也同样处理(重连,和所有操作返回失败.) 2. 客户端同时和多个机器人聊天的情况,转发消息时要能区分出消息是发往那个服务的. ``` # 术语 ``` 客户端:指的是浏览器。 agent:是一个代理程序,每个服务器上都会有这么一个程序,负责和WxService交互以及用户状态的查询(查mysql和redis),同时也负责和web服务端交互 服务:即agent和agent所在服务器的服务程序(如:WxService和TES)。组合起来为客户端的用户提供服务。统称为一个服务。 web服务端:处理用户通过客户端(浏览器)发送过来的请求并返回结果。同时转发客户消息到agent,或者转发agent发送过来的消息到客户端。 ``` # 流程梳理 ## 1.web服务端 ### 读取配置文件 ``` 1.http端口 2.websocket绑定端口 3.数据库配置(存储服务信息) ``` ### http ``` 1.创建express实例 2.初始化好请求的路由 1. 获取首页 2. 获取服务列表 3. 服务操作。 1.增:增加一个新的服务,即:增加了一个新的机器,填写agent的信息。 2.删:删除了一个服务,如:有一个服务器不使用了,则需要把该服务的信息删除。 3.改:修改服务信息,如:redis或mysql的配置变更了。需要更新 4.查:查询某个服务的信息,不暂时可能不使用,因为一般都使用获取服务列表功能。 3.绑定并监听端口,服务启动完成。 ``` ### websocket ``` 1.创建websocket实例 2.注册好各种事件 1.响应客户端连接 1.创建客户端对象,保存客户信息 2.为客户端连接对象绑定各种事件 1.消息进入 1.根据method判断是自己处理,还是把消息转发给agent 2.连接断开 3.错误事件 2.初始化客户端数据 1.分配sessionId 2.通过分配的sessionId为key,把连接对象保存到map中。 2.错误事件 3.绑定并监听端口 ``` ### 其他 ``` 1.创建数据库对象 2.连接数据库 3.读取所有服务信息 4.遍历读取到的数据 1.尝试连接到agent 1.agent连接成功,发送agent信息,并以服务id为key把agent的连接保存到map中。 当客户端发来了消息,通过服务id找到对应的agent,并把消息发送过去。如果agent不存在则返回 失败给客户端。 2.agent连接失败,启动定时器重连,如果地址信息是不可能的则不在连接。 2.不管连接成功或者失败,最后都需要更新agent状态到数据库。 ``` ## 2.agent ### 1. 初始化 ``` 1. 读取配置文件 1.绑定的端口 2.创建websocket对象。 3.注册各种事件 4.绑定并监听端口。正式启动服务 ``` ### 2. agent信息处理 ``` 1.web服务端连接过来后,会发送agent信息(包括:WxService地址,redis地址,mysql地址等信息。),接收并保存。 2.根据信息内容,连接redis和mysql。 1.成功:初始化完成,发送成功消息给web服务端,最后更新到数据库。 2.失败:发送agent失败的消息给web服务端,web服务端会把失败信息更新到数据库,用户就能看到了。 3.如果连接失败的原因是可修复的(对端没有启动),则启动定时器重连,如果是不可修复的,什么也不做。 ``` ### 3. 转发消息 ``` 1. 客户端登录 1.根据登录包中的serviceId,选择需要连接的WxService地址,(一个agent可以连接到好多个WxService) 2.连接成功 1. 把消息包中的sessionId和连接对象做绑定。sessionId为key连接对象为value保存到map中。 2. 提取appid和uname,组装一个登录的消息包发送个WxService 3. 如果登录失败,则删除sessionId的绑定管理,并断开WxService连接 4. 如果登录成功,则把登录结果返回给客户端,并且启动定时器,定时查询用户的状态变更,当数据有变 更是,主动推送给用户。 3.连接失败 1.返回登录失败的消息给客户端。 2.反馈失败状态给Web服务端。 3.如果失败是可修复的,则启动定时器重连。 4.如果失败不可修复,则什么也不做,并且有客户端消息包的时候,直接返回失败。(或者就不能让客户端发消息。) 2. 用户说话 1.提取appid和用户内容,组装消息包发送给WxService。 2.接收WxService发送过来的机器人说的话,并转发给前端。 ``` ### 4.用户信息 ``` 1. 当有用户登录成功后,启动一个定时器,定时查询用户状态,如果状态发生变化,则把用户信息发送给客户端 2. 用户也可以主动发起查询用户信息的请求。 3. 用户可以禁用自动查询用户信息功能。 ``` ## html页面 ``` 1.首页 1. 为方便扩展,为不同的功能创建不同的组件,首页中列出功能列表。 目前功能列表中只有聊天功能,并实时显示用户状态(标签,记忆,话题) 2.待扩展功能,数据库数据展示功能。 ``` ``` 2.管理员界面 1.为方便扩展每个功能都必须放在一个模块中,如果以后要新增功能,增加新模块就可以。 2.目前需要服务管理页面。(服务的增删改查) ``` # 1. 客户端(浏览器) ## 第一阶段: 1. 获取首页 2. 页面渲染完毕后,发送http请求获取服务列表,以及websocket服务端的地址 3. 连接websocket服务端。 4. 把获取到的服务列表渲染到页面中 ## 第二阶段: 1. 用户选择服务和appid(如果有多个) 2. 填写uname后登录 3. 向后端发送登录请求(登录请求中必须携带服务id和appid) 4. 处理登录结果 1. 登录成功,允许用户输入内容,并发送。 2. 登录失败,提示错误原因,允许用户再次登录。 ## 第三阶段: 1. 登录成功后,服务端会立即返回用户的当前状态,客户端接收并渲染到页面上。 2. 禁用选择服务功能(即:只能选择一次) 3. 用户开始聊天,发送聊天内容 4. 接收机器人的聊天内容,并并显示到页面上 5. 接收服务端推送的用户信息,并渲染到页面上。 ## 结束。 # 2.web服务端 ## 1.作为http服务端 ### 第一阶段: 1. 服务启动,绑定端口,等待请求 ### 第二阶段: 1. 用户进入首页请求。 2. 用户请求获取服务列表,(查询数据库并返回。) 3. 管理员功能 1.服务列表的CURD。 ### 结束。 ## 2.作为websocket服务端。 ### 第一阶段: 1. 服务启动绑定端口 2. 从数据库中获取到服务列表。 3. 根据服务列表中的agentAddr,连接到各个agent。 4. 连接成功后,发送AgentInfo信息给agent。 5. 启动定时器,每隔30秒从数据库中读取一次服务列表, 1. 如果有新增服务,则重复第3和第4步,连接到agent。 2. 如果有服务被删除,则断开连接。通过服务id和已有的服务id对比(可以知道哪些被删除了) 3. 如果有服务的信息变更了, 1. agentAddr改变了,则需要断开原来的连接连接到新的agent. 2. 如果是wxServiceAddr变更了,则需要通知给对应的agent。 ### 第二阶段: 1. 响应客户端连接。 2. 当客户端连接成功后,为该连接分配一个唯一的sessionid。并创建session对象(保存服务id,断开连接后才能通知到。)。 3. 接收客户端发送过来的请求,加入sessionid后转发给指定的agent.(登录请求必须带上服务id,通过服务id找到agent连接。) 4. 接收agent发送过来的内容,并提取sessionid,然后把消息发送给前端。 5. 如果前端断开了连接,则需要清除session信息,并且通知agent,清除该用户信息。 ### 结束。 ## 3. 服务状态更新 1. web服务端启动的时候从数据库中读取所有服务的数据。 2. 通过读取到的agent地址连接到agent, 1.连接成功,则更新成功的状态到数据库,客户端就可以看到服务状态。 2.连接失败,则更新失败的状态以及失败原因到数据库。 3. 连接失败后,根据失败的原因决定是否重连 1.可修复状态,则启动定时器重连。如:因为agent程序没启动,而导致的连接失败,则为可修复状态。 2.不可修复状态,则不重连。如:agent地址是错误的,根本不可能连接成功,则为不可修复状态。 4.agent连接成功,但是agent连接WxService失败了,也要更新状态到数据库中。 # 3.agent ## 第一阶段: 1. 程序启动,绑定端口, 2. 接收web服务端发送过来的wxService信息。 3. 保存wxService信息。(用来模仿小程序登录用的,当用户发了登录消息后,开始连接到WxService,并发送登录请求。) ## 第二阶段: 1. 接收web服务端转发过来的客户端请求。 1. 登录, 1. 提取请求中的uname和appid,连接WxService并发送登录请求。 2. 如果登录成功,则把消息发送给web服务端,最终发送到客户端, 3. 启动定时器,每秒检测用户的状态,如果发生变更,则把状态信息发送给客户端。 2. 聊天 1. 接收web服务端转发发送过来的客户端请求,验证通过后(是否登录成功,appid是否允许),发送给WxService。 2. 接收WxService发送过来的内容,并返回给客户端 3. 登出: 1. 停止检查用户状态的定时器。 2. 断开和WxService的连接(指定用户的) 3. 清除用户信息。 4. 获取用户状态。(有是有用户想强制获取用户状态。) 2. 接收web服务端发送过来的,客户端下线消息。 1. 执行用户登出流程 3. 接收web服务端发送过来的WxService信息变更消息 1. 如果有数据变更, 1. appid则更新appid 2. wxServiceAddr变更,则需要断开原有连接,并连接到最新的WxService 3. redis配置变更,断开原来的连接连接到新的redis cluster 4. mysq配置变更,断开原来的连接连接到新地址指向的mysql 4. 如果检测到web服务端的连接断开,则清除所有的用户信息,停止状态检测定时器,并断开WxService的连接。还原到刚启动时的状态。 5. 如果和WxService的连接断开,则开启定时器重连,连接成功后,通过缓存的uname和appid重新进行登录。如果连接失败,则需要通知客户端服务出了异常。(通知一次即可。) **说明:对于web服务端和agent尽量做到能不重启就不要重启,比如配置信息变更了,程序需要能自动校正,而不是要重启**程序。 # 4. 端口定义 + http服务端端口:2019 * ws服务端端口:2020 * agent端口:2021 # 5. 页面 1.聊天页面(包括显示用户信息) 2.管理员界面,服务的管理等功能。(稍微验证下) # 6. 协议: ## 1. 首页获取 url: https://xxx.com/WebTesting/index.html ## 2. 服务的增删改查(管理员界面) ### 1. 增:增加服务。 #### 请求: ```json method: PUT url: https://xxx.com/WebTesting/Service/addServices body: [{ "redisCluster":[ {"port": 7000,"host":"192.168.1.200"}, {"port": 7001,"host":"192.168.1.200"}, {"port": 7002,"host":"192.168.1.200"} ], "mysqlAddr":"127.0.0.1", "mysqlPort":3306, "mysqlUser":"root", "mysqlPwd":"root", "mysqlDbname":"tesdb001", "wxServiceAddr":"ws://127.0.0.1:1443", "agentAddr":"ws://127.0.0.1:2021/WebTesting/Agent", "allowedAppids":["1.00002","1.00003"], "description":"医疗小程序" }] 字段说明: ``` | 字段名 | 类型 | 长度 | 必填 | 说明 | | ------------- | --------- | ---- | ---- | ------------------------------------------------ | | redisCluster | JsonArray | | 是 | redis集群地址,需要填写所有主节点ip和端口 | | mysqlAddr | string | | 是 | mysql数据库地址 | | mysqlPort | int | | 是 | mysql数据库端口 | | mysqlUser | string | | 是 | mysql数据库用户 | | mysqlPwd | string | | 是 | mysql数据库密码 | | mysqlDbname | string | | 是 | mysql数据库名 | | wxServiceAddr | string | | 是 | WxService地址(小程序服务端地址) | | agentAddr | string | | 是 | agent地址 | | allowedAppids | JsonArray | | 是 | 允许的appid | | description | string | | 是 | 服务描述,越清楚越好,客户端选择服务的时候用的。 | #### 应答: body: { "affectedRows":1, "result":1, "msg":"ok" } | 字段名 | 类型 | 长度 | 必填 | 说明 | | ------------ | ------ | ---- | ---- | ------------------------------------- | | affectedRows | int | | 否 | 插入成功的个数。 | | result | int | | 是 | 请求的处理结果,1:成功,其它:错误码 | | msg | string | | 是 | 如果请求失败了,则这里填写失败原因 | ### 2. 删:删除服务 #### 请求: method: DELETE url: https://xxx.com/WebTesting/Service/delServices body: { "serviceIds":[1,2,3] } | 字段名 | 类型 | 长度 | 必填 | 说明 | | ---------- | --------- | ---- | ---- | -------------- | | serviceIds | JsonArray | | 是 | 要删除的服务id | #### 应答: body { "affectedRows":1, "result":1, "msg":"ok" } | 字段名 | 类型 | 长度 | 必填 | 说明 | | ------------ | ------ | ---- | ---- | ------------------------------------- | | affectedRows | int | | 否 | 删除的个数 | | result | int | | 是 | 请求的处理结果,1:成功,其它:错误码 | | msg | string | | 是 | 如果请求失败了,则这里填写失败原因 | ### 3. 改:更新服务 #### 请求: ```json method: POST url: https://xxx.com/WebTesting/Service/updateServices body: [{ "serviceId":1, "redisCluster":[ {"port": 7000,"host":"192.168.1.200"}, {"port": 7001,"host":"192.168.1.200"}, {"port": 7002,"host":"192.168.1.200"} ], "mysqlAddr":"127.0.0.1", "mysqlPort":3306, "mysqlUser":"root", "mysqlPwd":"root", "mysqlDbname":"tesdb001", "wxServiceAddr":"ws://127.0.0.1:1443", "agentAddr":"ws://127.0.0.1:2021/WebTesting/Agent", "allowedAppids":["1.00002", "1.00003"], "description":"医疗小程序" }] ``` * 说明:请参考 1. 增:增加服务。 消息的字段说明。 #### 应答: ```json body: { "result":1, "msg":"ok" } ``` ### 4. 查:查询服务信息,包括(数据库地址,redis地址,agent地址等) #### 1. 请求: method: GET url: https://xxx.com/WebTesting/Service/getServices #### 2. 应答: body: { "result":1, "msg":"ok", "servicdLists":[{ "serviceId":1, "redisCluster":[ {"port": 7000,"host":"192.168.1.200"}, {"port": 7001,"host":"192.168.1.200"}, {"port": 7002,"host":"192.168.1.200"} ], "mysqlAddr":"127.0.0.1", "mysqlPort":3306, "mysqlUser":"root", "mysqlPwd":"root", "mysqlDbname":"tesdb001", "wxServiceAddr":"ws://127.0.0.1:1443", "agentAddr":"ws://127.0.0.1:2021/WebTesting/Agent", "allowedAppids":["1.00002","1.00003"], "description":"医疗小程序", "status":1, "msg":"ok" }] } ## 3. 服务列表获取 ### 1. 服务列表获取 #### 请求: method: GET url: https://xxx.com/WebTesting/Service/getServiceList #### 应答: ```json body: { "wsAddr":"wss://xxx.com/WebTesting", "serviceLists":[ { "serviceId":1, "allowedAppids":["1.00002", "1.00003"], "description":"医疗小程序", "status":1, "msg":"ok" } ], "result":1, "msg":"ok" } ``` 字段描述: | 字段 | 类型 | 长度 | 必填 | 说明 | | ------------- | --------- | ---- | ---- | ------------------------------------------------------------ | | wsAddr | string | | 是 | 服务端websocket地址,用于后续通信。 | | serviceLists | string | | 是 | 服务列表,包含所有服务的信息 | | serviceId | int | | 是 | 服务id,通过这个id最终选择指定的agent。 | | allowedAppids | JsonArray | | 是 | 当用户选择好了服务后,允许测试的appid | | description | string | | 是 | 服务的具体说明,帮助用户选择 | | status | int | | 是 | 服务状态,是否可用,1:服务正常,0:表示服务异常,这个时候客户端需要禁止用户选择。 | | msg | string | | 是 | 如果服务异常了,在这里显示异常的原因,方便问题定位,而需要去具体的服务器上看。 | ## 4.浏览器和Web服务端 ### 1. 登录 #### 请求: body { "method":"loginReq", "serviceId":1, "appid":"1.00002", "uname":"hxw002" } #### 应答: body { "method":"loginResp", "strUid":10086, "result":1, "msg":"ok" } ### 2. 登出 #### 请求: body { "method":"logoutReq", "appid":"1.00002" } #### 应答: body { "method":"logoutResp", "result":1, "msg":"ok" } ### 3.用户说话 #### 请求: body { "method":"aichatReq", "appid":"1.00002", "content":"你好" } ### 4.机器人说话 #### 应答: body { "method":"aichatResp", "content":"你好啊", "action":"", "result":1, "msg":"ok" } ### 5.用户状态信息 #### 请求: body { "method":"getUserStatusReq", "appid":"1.00002" } #### 应答: body { "method":"getUserStatusResp", "uuid":"xxx-xxx-xxx-xxx", "tag":{ "curTags":[{ "id":10000, "desc":"用户在镜头前", "beginTime":1000000, "endTime":100000000, "isInUse":true //是否已经生效 }], "deletedTags":[{ "id":10000, "desc":"用户在镜头前", "beginTime":1000000, "endTime":100000000, "isInUse":true }], "newTags":[{ "id":10000, "desc":"用户在镜头前", "beginTime":1000000, "endTime":100000000, "isInUse":true }] }, "curTopic":{ "id":10086, "desc":"闲聊话题", "prevId":10087, "prevDesc":"BB话题" }, "curTopicList":{ "curTopics":[{ "id":10000, "desc":"用户在镜头前", "endTime":100000000, }], "deletedTopics":[{ "id":10000, "desc":"用户在镜头前", "endTime":100000000, }], "newTopics":[{ "id":10000, "desc":"用户在镜头前", "endTime":100000000, }] }, "result":1, "msg":"ok" } 说明:服务端也会主动推送这个消息包,当用户状态变更的时候 ### 6. 服务端状态 #### 应答: body { "method":"servStatus", "status":1, "msg":"ok" } 说明:当agent或者WxService连接不到的时候,通知客户端,禁止发送消息。(主动推送的消息) ## 5.Web服务端和Agent ### 1.WxService信息同步 #### 请求: body { "method":"agentInfoReq", "redisCluster":[ {"port": 7000,"host": "192.168.1.200"}, {"port": 7001,"host": "192.168.1.200"}, {"port": 7002,"host": "192.168.1.200"} ], "mysqlAddr":"127.0.0.1", "mysqlPort":3306, "mysqlUser":"root", "mysqlPwd":"root", "mysqlDbname":"tesdb001", "wxServiceAddr":"ws://127.0.0.1:1443", "allowedAppids":["1.00002","1.00003"], "description":"医疗小程序" } #### 应答: body { "method":"agentInfoResp", "result":1, "msg":"ok" } ### 2.状态同步 #### 请求 ``` body { "method":"agentStatusReq", "serviceId", 1086, "status":1, "msg":"ok" } ``` 字段说明: | 字段 | 类型 | 长度 | 必填 | 说明 | | --------- | ------ | ---- | ---- | ---------------------------------- | | servicdId | int | | 是 | 服务id | | status | int | | 是 | 服务状态,1:正常,0:异常 | | msg | string | | 是 | 服务异常的时候,这里携带异常信息。 | ### 3. 登录 > 请求: ``` { "method":"loginReq", "uuid":"xxx-xxx-xxx", "uname":"hxw002", "appid":"1.00002" } ``` > 应答: ``` { "method":"loginResp", "uuid":"xxx-xxx-xxx", "strUid":"10086", "result":1, "msg":"ok" } ``` ### 4. 登出 > > 请求: > > ``` > { > "method":"logoutReq", > "uuid":"xxx-xxx-xxx", > } > ``` > > > > > 应答: > > ``` > { > "method":"logoutResp", > "uuid":"xxx-xxx-xxx", > "result": 1, > "msg":"ok" > } > ``` ### 5. 聊天 >> 请求: > >``` >{ > "method":"aichatReq", > "uuid":"xxx-xxx-xxx", > "appid":"1.00002", > "content":"xxx" >} >``` > > > >> 应答: > >``` >{ > "method":"aichatResp", > "uuid":"xxx-xxx-xxx", > "content":"xxx", > "action":"", > "result":1, > "msg":"ok" >} >``` # 7. 数据库: ## 1.字段明细 | 字段 | 类型 | 长度 | 说明 | | ------------- | --------- | ---- | ------------------------------------------------------------ | | id | int | | 数据库自增量,也是服务id | | redisCluster | JsonArray | 1024 | redis集群地址,nodejs中,需要集群的所有主机ip和端口
[
{"port": 7000,"host": "192.168.1.200"},
{"port": 7001,"host": "192.168.1.200"},
{"port": 7002,"host": "192.168.1.200"}
] | | mysqlAddr | string | 64 | mysql地址 | | mysqlPort | int | | mysql端口,默认(3306) | | mysqlUser | string | 64 | mysql数据库用户名 | | mysqlPwd | string | 64 | mysql数据库密码 | | mysqlDbname | string | 64 | mysql数据库名 | | wxServiceAddr | string | 64 | 小程序服务端地址,格式:ws://xxx.xxx.xxx:443/5.00034 | | agentAddr | agentAddr | 64 | agent地址,格式:ws://172.16.0.23:3000/WebTesting/Agent | | allowedAppids | JsonArray | 1024 | 服务允许的appid,格式:["1.00002","1.00003"] | | description | string | 1024 | 服务描述,客户端可以根据服务的描述,选择需要在哪个服务上测试。 | | status | int | | 服务状态,如:agent连接不成功,或者WxService连接失败。 | | msg | string | 1024 | 服务状态描述,服务状态异常了,在这里填写异常信息,便于发现问题。 | ## 2.建表命令 ```sql CREATE TABLE `tb_service_info` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '就是一个自增的,唯一的id而已。', `redisCluster` varchar(1024) DEFAULT NULL COMMENT 'redis集群地址,格式:[{"port": 7000,"host": "192.168.1.200"}, {"port": 7001,"host": "192.168.1.200"}, {"port": 7002,"host": "192.168.1.200"}], 格式说明,因为服务端是nodejs,nodejs连接redis集群需要这么填。', `mysqlAddr` varchar(64) DEFAULT NULL COMMENT 'mysql地址,查询表三数据,把各种id(标签,话题)转换成内容。', `mysqlPort` int(11) DEFAULT '3306', `mysqlUser` varchar(64) DEFAULT NULL, `mysqlPwd` varchar(64) DEFAULT NULL, `mysqlDbname` varchar(64) DEFAULT NULL, `wxServiceAddr` varchar(64) DEFAULT NULL COMMENT '小程序服务端地址,如:ws://xxx.xxx.xxx:443/5.00034', `agentAddr` varchar(64) DEFAULT NULL COMMENT 'agent地址。', `allowedAppids` varchar(1024) DEFAULT NULL, `description` varchar(1024) DEFAULT NULL, `status` int(11) DEFAULT '1', `msg` varchar(256) DEFAULT 'ok', PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=19 DEFAULT CHARSET=utf8 ``` # 扩展知识 ## 1. sql 更新多个内容 ```sql 1. mysql一次更新多个内容(注:不是一条语句更新多条数据,而是一个sql语句里面有不同的条件。)只是为了避免执行多个sql语句而已。 方法:使用insert 命令的(ON DUPLICATE KEY UPDATE) key存在则更新,不存在则插入的特性。 语句:INSERT INTO t_member (id, name, email) VALUES (1, 'nick', 'nick@126.com'), (4, 'angel','angel@163.com'), (7, 'brank','ba198@126.com') ON DUPLICATE KEY UPDATE name=VALUES(name), email=VALUES(email); ``` 注意:ON DUPLICATE KEY UPDATE 只是MySQL的特有语法,并不是SQL标准语法! ## 2.mysql插入多条数据(nodejs) ```javascript 1. mysql 插入多条数据,参考: http://www.mysqltutorial.org/mysql-nodejs/insert/ 可能需要翻墙。 代码如下: var post = {id: 1, title: 'Hello MySQL'}; var query = connection.query('INSERT INTO posts SET ?', post, function (error, results, fields) { if (error) throw error; }); 其中post可是一个对象数组,这样就可以插入多条数据了。 如果只有一个数据则post可以是一个对象。对象的字段必须和数据库一直。 2. 删除数据使用in。 ``` ## 3.mysql同时执行多个更新语句 > 因为mysql操作是异步的,所以如果需要执行多个更新sql语句,必须要一次执行,不能分多次。 > > 这就需要将多个sql语句打包在一次传给mysql库执行。 ```js // 1. 连接的mysql的时候,通过multipleStatements属性指定可以执行多个sql语句 var mysql = require('mysql'); var connection = mysql.createConnection({ // ... 其它参数 multipleStatements: true //update 的时候执行多条update语句。 }); // 2. 语句拼接 var strSql = ''; strSql += mysql.format(`update ${tableName} SET ? where id = ${serviceId} ;`, updateObj); // 3. 最后执行 mysql.query(strSql,(err,results)=>{}); ``` # 开发进度 ## 浏览器端(html) + 网页原型 ## 服务端(包含:http和websocket) > 当前状态: > > http服务: > > 1. 服务列表获取接口 > 2. 服务列表的增删改查接口 > > websocket服务: > > 1. 定时从数据库获取服务信息,当服务列表变更后改变程序当前状态。 进度:100% > 2. 响应浏览器的websocket连接,并保存连接信息,分配uuid。进度:100% > 3. 根据服务信息主动连接到agent,根据服务信息的变更更新Agent信息(增删改)。进度:100% > 4. 转发浏览器发送的消息到Agent。进度:10%(基本实现转发,但是还没有开始根据消息协议处理。) > > 其它: > > 1. 数据库操作 进度:80%(功能基本能用,没有处理异常,代码也还有待优化) > 2. > > ## Agent > 1. 绑定端口,并相应web服务端的连接请求 进度:100% > 2. 接收服务信息消息,根据消息内容,连接redis,mysql 进度:100% (包括异常处理。) > 3. 连接WxService并处理浏览器的登录聊天登出消息。进度:0%(还没开始) > 4. 定时获取用户当前状态,发送给浏览器 0% > 5. 服务状态通知web服务端(redis,mysql,WxService连接失败) 0% > 6. # 流程推演 > - 打开浏览器,输入网址 > 1. Web服务端返回登录首页 > > - 普通用户输入账号密码登录 > 1. Web服务端,验证用户账号密码是否正确 > 1. 验证通过:返回普通用户功能导航页。并返回cookie > 2. 验证失败:返回失败信息。 > > - 用户选择“即时通讯功能” > > 1. 跳转(切换)到即时通讯页面 > 2. 连接websocket > 3. 发送请求获取服务列表。 > > - 用户选择服务 > > 1. 获取用户的测试账号(uname) > > 2. 发送登录请求(appid,serviceId,uname) > > 3. Web服务器增加uuid信息并转发请求到Agent。 > > 4. Agent创建websocket客户端连接WxService > > 1. 连接成功后把登录消息包转换成WxService的登录消息包,发送给WxService > > 2. 发送WxService的应答到Web服务端(带上uuid)。包含:uid,result,msg. > > 3. 启动定时任务定时检查用户的状态,状态变动后发送给Web服务端。 > > 5. Web服务端接收到登录应答,提取uuid找到客户端连接,把消息转发给客户端 > > 6. 客户端接收到登录结果,根据result的值做出处理。 > > 1. result:1 表示成功 允许用户输入内容。 > 2. result:其它值 表示异常,需要显示异常给用户看。 > > 7. 结束 > > - 用户选择了另一个服务 > > 1. 退出上一次登录 > 2. Web服务端发送登出请求到Agent > 3. Agent接收到登出请求 > 1. 断开该用户的WxService连接 > 2. 定时状态检测的定时器。 > 3. 清除保存的数据。 > 4. 然后执行步骤:“用户选择服务” > > - 聊天功能 > > 1. 点击发送:获取用户输入内容 > 2. 发送用户输入内容到Web服务端,如果内容为空则提示用户内容不可以为空。 > 3. Web服务端转发聊天信息到Agent(通过登录时的serviceId找到Agent连接)(带上uuid) > 4. Agent通过uuid找到和WxService的连接,组装消息包把消息发送给WxService. > 5. Agent接收到WxService返回的机器人消息,带上uuid发送给Web服务端 > 6. Web服务端接收到机器人消息通过uuid找到客户端连接,发送个客户端。