# 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找到客户端连接,发送个客户端。