doc:添加项目文档

This commit is contained in:
tangweijie 2025-05-28 17:00:26 +08:00
parent b551b4016c
commit 77650de0f2
4 changed files with 1272 additions and 0 deletions

378
docs/API接口文档.md Normal file
View File

@ -0,0 +1,378 @@
# HospitalPay-Go Socket 服务器接口文档
## 1. 概述
HospitalPay-Go Socket 服务器是一个基于TCP协议的医院支付系统服务器提供病人入院、出院、消费记录、余额查询等功能。
### 1.1 服务器信息
- **协议**: TCP Socket
- **端口**: 配置文件中指定
- **编码**: UTF-8
- **数据格式**: JSON
### 1.2 消息格式
所有请求和响应都遵循以下格式:
```
[长度(4位)][功能码(4位)][医院编码(4位)][时间戳(19位)][JSON数据]
```
- **长度**: 4位数字表示整个消息的长度
- **功能码**: 4位数字标识请求的功能类型
- **医院编码**: 4位字符医院标识码
- **时间戳**: 19位时间戳格式为 `yyyy-MM-dd HH:mm:ss`
- **JSON数据**: 具体的请求或响应数据
### 1.3 响应格式
所有响应都包含以下基础结构:
```json
{
"ResultCode": "string", // 结果代码
"ResultData": object, // 响应数据(可选)
"ResultMsg": "string" // 结果消息(可选)
}
```
## 2. 错误码说明
| 错误码 | 说明 |
|--------|------|
| 0000 | 成功 |
| 0005 | 未找到病人信息 |
| 0006 | 入院处理失败 |
| 0007 | 病人已入院 |
| 0008 | 系统异常 |
| 0009 | 请求参数错误 |
| 1001 | 出院处理失败 |
| 1002 | 消费额度查询失败 |
| 1003 | 消费记录保存失败 |
| 1004 | 实时余额查询失败 |
| 1005 | 发票同步失败 |
## 3. 接口详情
### 3.1 入院登记
**功能码**: `0001`
**功能说明**: 病人入院时的登记处理
**请求参数**:
```json
{
"FCode": "string" // 病人编号,必填
}
```
**响应参数**:
```json
{
"ResultCode": "0000",
"ResultData": {
"FCode": "string", // 病人编号
"FName": "string", // 病人姓名
"AmountA": 100.00, // A类金额
"AmountB": 50.00, // B类金额
"AmountC": 150.00, // C类金额
"BankAccNo": "string", // 银行账号
"BankAmount": 1000.00, // 银行余额
"Fflag": 0, // 状态标志 (0:正常, 1:已入院)
"Flimitflag": 0, // 限制标志
"Flimitamt": 0.00 // 限制金额
}
}
```
**示例**:
```
请求: 0001{"FCode":"3516022343"}
响应: {"ResultCode":"0000","ResultData":{"FCode":"3516022343","FName":"测试病人","AmountA":100.00,"AmountB":50.00,"AmountC":150.00,"BankAccNo":"6222021234567890","BankAmount":1000.00,"Fflag":0,"Flimitflag":0,"Flimitamt":0}}
```
### 3.2 消费额度查询
**功能码**: `0002`
**功能说明**: 查询病人当月消费额度信息
**请求参数**:
```json
{
"FCode": "string" // 病人编号,必填
}
```
**响应参数**:
```json
{
"ResultCode": "0000",
"ResultData": {
"AmountA": 50.00, // A类消费金额
"AmountB": 30.00, // B类消费金额
"FreeAmountA": 0.00, // A类可用金额
"FreeAmountB": 0.00, // B类可用金额
"Checkflag": 0, // 检查标志
"FCode": "string", // 病人编号
"FCriminal": "string", // 病人姓名
"Flag": 0 // 状态标志
}
}
```
### 3.3 出院处理
**功能码**: `0003`
**功能说明**: 病人出院时的处理
**请求参数**:
```json
{
"FCode": "string" // 病人编号,必填
}
```
**响应参数**:
```json
{
"ResultCode": "0000",
"ResultMsg": "出院处理成功"
}
```
### 3.4 消费记录
**功能码**: `0004`
**功能说明**: 记录病人消费信息
**请求参数**:
```json
{
"FCode": "string", // 病人编号,必填
"InvoiceNo": "string", // 发票号,必填
"AmountA": 50.00, // A类金额
"AmountB": 30.00, // B类金额
"Amount": 80.00, // 总金额
"FreeAmountA": 0.00, // A类可用金额
"FreeAmountB": 0.00, // B类可用金额
"CrtDate": "2024-01-01T10:00:00Z", // 创建日期
"FCriminal": "string", // 病人姓名
"CardCode": "string", // 卡号
"OrderId": 123456 // 订单ID
}
```
**响应参数**:
```json
{
"ResultCode": "0000",
"ResultMsg": "消费记录保存成功"
}
```
### 3.5 实时余额查询
**功能码**: `0005`
**功能说明**: 查询病人实时余额信息
**请求参数**:
```json
{
"FCode": "string" // 病人编号,必填
}
```
**响应参数**: 与入院登记响应格式相同
### 3.6 发票同步
**功能码**: `0006`
**功能说明**: 同步发票信息到系统
**请求参数**:
```json
{
"InvoiceList": [
"INV001",
"INV002",
"INV003"
]
}
```
**响应参数**:
```json
{
"ResultCode": "0000",
"ResultData": [
{
"BankFlag": 2, // 银行标志
"CAmount": 100.00, // 金额
"FCode": "3516022343", // 病人编号
"Origid": "INV001", // 原始发票号
"SendDate": "2024-01-01T10:00:00Z" // 发送日期
}
]
}
```
## 4. 连接管理
### 4.1 连接限制
- 最大并发连接数:配置文件中指定
- 连接超时时间:配置文件中指定
- 读取超时时间:配置文件中指定
- 写入超时时间:配置文件中指定
### 4.2 连接流程
1. 客户端建立TCP连接
2. 发送请求消息(按照消息格式)
3. 服务器验证医院编码
4. 处理业务逻辑
5. 返回响应消息
6. 关闭连接
## 5. 安全说明
### 5.1 医院编码验证
所有请求必须包含正确的医院编码,否则返回"无效的医院编码"错误。
### 5.2 超时控制
- 连接建立后有读取超时限制
- 响应发送有写入超时限制
- 业务处理有上下文超时控制10秒
## 6. 监控指标
系统提供以下监控指标:
- 活跃连接数
- 请求计数(按功能码分类)
- 请求处理时间(按功能码分类)
- 错误计数(按功能码和错误码分类)
## 7. 示例代码
### 7.1 Go 客户端示例
```go
package main
import (
"fmt"
"net"
"time"
)
func main() {
conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
panic(err)
}
defer conn.Close()
// 构造请求消息
functionCode := "0001"
hospitalCode := "H001"
timestamp := time.Now().Format("2006-01-02 15:04:05")
data := `{"FCode":"3516022343"}`
message := fmt.Sprintf("%s%s%s%s", functionCode, hospitalCode, timestamp, data)
fullMessage := fmt.Sprintf("%04d%s", len(message), message)
// 发送请求
_, err = conn.Write([]byte(fullMessage))
if err != nil {
panic(err)
}
// 读取响应
buffer := make([]byte, 4096)
n, err := conn.Read(buffer)
if err != nil {
panic(err)
}
response := string(buffer[:n])
fmt.Printf("Response: %s\n", response)
}
```
### 7.2 Python 客户端示例
```python
import socket
import json
import time
def send_request(host, port, function_code, hospital_code, data):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
sock.connect((host, port))
# 构造消息
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
json_data = json.dumps(data)
message = f"{function_code}{hospital_code}{timestamp}{json_data}"
full_message = f"{len(message):04d}{message}"
# 发送请求
sock.send(full_message.encode('utf-8'))
# 接收响应
response = sock.recv(4096).decode('utf-8')
# 解析响应
length = int(response[:4])
json_response = response[4:4+length]
return json.loads(json_response)
finally:
sock.close()
# 使用示例
if __name__ == "__main__":
result = send_request(
"localhost", 8080,
"0001", "H001",
{"FCode": "3516022343"}
)
print(json.dumps(result, indent=2, ensure_ascii=False))
```
## 8. 常见问题
### 8.1 连接被拒绝
- 检查服务器是否启动
- 检查端口是否正确
- 检查是否达到最大连接数限制
### 8.2 医院编码错误
- 确认医院编码配置正确
- 检查请求消息格式是否正确
### 8.3 请求超时
- 检查网络连接
- 确认服务器负载情况
- 检查请求数据格式是否正确
### 8.4 数据格式错误
- 确认JSON格式正确
- 检查必填字段是否完整
- 验证数据类型是否匹配
## 9. 版本历史
| 版本 | 日期 | 说明 |
|------|------|------|
| 1.0.0 | 2024-01-01 | 初始版本支持基础的6个功能接口 |
## 10. 联系方式
如有问题请联系开发团队。

105
docs/README.md Normal file
View File

@ -0,0 +1,105 @@
# HospitalPay-Go 接口文档
欢迎使用 HospitalPay-Go Socket 服务器接口文档。本文档提供了完整的API接口说明、测试用例和快速参考。
## 📚 文档目录
### 1. [API接口文档](./API接口文档.md)
**完整的接口规范文档**
- 服务器概述和消息格式
- 详细的接口说明6个功能接口
- 错误码说明
- 连接管理和安全说明
- 监控指标
- 客户端示例代码Go/Python
- 常见问题解答
### 2. [测试用例](./测试用例.md)
**全面的测试用例文档**
- 基础功能测试(每个接口的正常和异常情况)
- 异常情况测试(连接、并发、超时)
- 性能测试吞吐量、内存、CPU
- 测试脚本Go/Python
- 测试报告模板
### 3. [快速参考](./快速参考.md)
**开发者快速查阅手册**
- 消息格式和功能码列表
- 错误码速查表
- 数据结构定义
- 快速示例
- 客户端代码模板
## 🚀 快速开始
### 服务器信息
- **协议**: TCP Socket
- **端口**: 配置文件中指定默认8080
- **编码**: UTF-8
- **数据格式**: JSON
### 消息格式
```
[长度(4位)][功能码(4位)][医院编码(4位)][时间戳(19位)][JSON数据]
```
### 支持的功能
| 功能码 | 功能名称 | 说明 |
|--------|----------|------|
| 0001 | 入院登记 | 病人入院时的登记处理 |
| 0002 | 消费额度查询 | 查询病人当月消费额度 |
| 0003 | 出院处理 | 病人出院时的处理 |
| 0004 | 消费记录 | 记录病人消费信息 |
| 0005 | 实时余额查询 | 查询病人实时余额 |
| 0006 | 发票同步 | 同步发票信息到系统 |
## 🔧 开发指南
### 1. 环境准备
确保服务器已启动并配置正确的端口和医院编码。
### 2. 客户端开发
参考 [API接口文档](./API接口文档.md) 中的示例代码,或使用 [快速参考](./快速参考.md) 中的代码模板。
### 3. 测试验证
使用 [测试用例](./测试用例.md) 中的测试脚本验证功能是否正常。
## 📋 示例请求
### 入院登记示例
```
请求: 00540001H0012024-01-15 10:30:00{"FCode":"3516022343"}
响应: {"ResultCode":"0000","ResultData":{"FCode":"3516022343","FName":"测试病人",...}}
```
### 消费记录示例
```
请求: 01840004H0012024-01-15 10:30:00{"FCode":"3516022343","InvoiceNo":"INV001",...}
响应: {"ResultCode":"0000","ResultMsg":"消费记录保存成功"}
```
## ⚠️ 重要提醒
1. **医院编码验证**: 所有请求必须包含正确的医院编码
2. **消息格式**: 严格按照指定格式构造消息
3. **超时控制**: 注意连接和处理超时时间
4. **错误处理**: 根据错误码进行相应的错误处理
## 🐛 问题反馈
如果在使用过程中遇到问题:
1. 首先查阅 [API接口文档](./API接口文档.md) 中的"常见问题"部分
2. 使用 [测试用例](./测试用例.md) 中的测试脚本进行验证
3. 检查服务器日志和网络连接
4. 联系开发团队获取支持
## 📖 文档版本
| 版本 | 日期 | 说明 |
|------|------|------|
| 1.0.0 | 2024-01-15 | 初始版本包含6个基础功能接口 |
---
**注意**: 本文档基于当前代码版本生成,如有更新请及时同步文档内容。

167
docs/快速参考.md Normal file
View File

@ -0,0 +1,167 @@
# HospitalPay-Go Socket 接口快速参考
## 消息格式
```
[长度(4位)][功能码(4位)][医院编码(4位)][时间戳(19位)][JSON数据]
```
## 功能码列表
| 功能码 | 功能名称 | 请求参数 | 响应数据 |
|--------|----------|----------|----------|
| 0001 | 入院登记 | `{"FCode":"string"}` | CriminalInfo |
| 0002 | 消费额度查询 | `{"FCode":"string"}` | ConsumeQuota |
| 0003 | 出院处理 | `{"FCode":"string"}` | 成功消息 |
| 0004 | 消费记录 | ConsumeRecord | 成功消息 |
| 0005 | 实时余额查询 | `{"FCode":"string"}` | CriminalInfo |
| 0006 | 发票同步 | `{"InvoiceList":["string"]}` | InvoiceSyncResult[] |
## 错误码
| 错误码 | 说明 |
|--------|------|
| 0000 | 成功 |
| 0005 | 未找到病人信息 |
| 0006 | 入院处理失败 |
| 0007 | 病人已入院 |
| 0008 | 系统异常 |
| 0009 | 请求参数错误 |
## 数据结构
### CriminalInfo (病人信息)
```json
{
"FCode": "string", // 病人编号
"FName": "string", // 病人姓名
"AmountA": 100.00, // A类金额
"AmountB": 50.00, // B类金额
"AmountC": 150.00, // C类金额
"BankAccNo": "string", // 银行账号
"BankAmount": 1000.00, // 银行余额
"Fflag": 0, // 状态标志
"Flimitflag": 0, // 限制标志
"Flimitamt": 0.00 // 限制金额
}
```
### ConsumeQuota (消费额度)
```json
{
"AmountA": 50.00, // A类消费金额
"AmountB": 30.00, // B类消费金额
"FreeAmountA": 0.00, // A类可用金额
"FreeAmountB": 0.00, // B类可用金额
"Checkflag": 0, // 检查标志
"FCode": "string", // 病人编号
"FCriminal": "string", // 病人姓名
"Flag": 0 // 状态标志
}
```
### ConsumeRecord (消费记录)
```json
{
"FCode": "string", // 病人编号
"InvoiceNo": "string", // 发票号
"AmountA": 50.00, // A类金额
"AmountB": 30.00, // B类金额
"Amount": 80.00, // 总金额
"FreeAmountA": 0.00, // A类可用金额
"FreeAmountB": 0.00, // B类可用金额
"CrtDate": "2024-01-01T10:00:00Z", // 创建日期
"FCriminal": "string", // 病人姓名
"CardCode": "string", // 卡号
"OrderId": 123456 // 订单ID
}
```
### InvoiceSyncResult (发票同步结果)
```json
{
"BankFlag": 2, // 银行标志
"CAmount": 100.00, // 金额
"FCode": "3516022343", // 病人编号
"Origid": "INV001", // 原始发票号
"SendDate": "2024-01-01T10:00:00Z" // 发送日期
}
```
## 快速示例
### 入院登记
```
请求: 00540001H0012024-01-15 10:30:00{"FCode":"3516022343"}
响应: {"ResultCode":"0000","ResultData":{...}}
```
### 消费记录
```
请求: 01840004H0012024-01-15 10:30:00{"FCode":"3516022343","InvoiceNo":"INV001","AmountA":50.00,"AmountB":30.00,"Amount":80.00,"FreeAmountA":0.00,"FreeAmountB":0.00,"CrtDate":"2024-01-15T10:30:00Z","FCriminal":"测试病人","CardCode":"CARD001","OrderId":123456}
响应: {"ResultCode":"0000","ResultMsg":"消费记录保存成功"}
```
### 发票同步
```
请求: 00800006H0012024-01-15 10:30:00{"InvoiceList":["INV001","INV002"]}
响应: {"ResultCode":"0000","ResultData":[...]}
```
## 客户端代码模板
### Go 客户端
```go
func sendRequest(functionCode, hospitalCode, data string) (string, error) {
conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
return "", err
}
defer conn.Close()
timestamp := time.Now().Format("2006-01-02 15:04:05")
message := fmt.Sprintf("%s%s%s%s", functionCode, hospitalCode, timestamp, data)
fullMessage := fmt.Sprintf("%04d%s", len(message), message)
_, err = conn.Write([]byte(fullMessage))
if err != nil {
return "", err
}
buffer := make([]byte, 4096)
n, err := conn.Read(buffer)
if err != nil {
return "", err
}
return string(buffer[:n]), nil
}
```
### Python 客户端
```python
def send_request(function_code, hospital_code, data):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
sock.connect(('localhost', 8080))
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
json_data = json.dumps(data)
message = f"{function_code}{hospital_code}{timestamp}{json_data}"
full_message = f"{len(message):04d}{message}"
sock.send(full_message.encode('utf-8'))
response = sock.recv(4096).decode('utf-8')
length = int(response[:4])
json_response = response[4:4+length]
return json.loads(json_response)
finally:
sock.close()
```
## 常见问题
1. **连接被拒绝**: 检查服务器状态和端口
2. **医院编码错误**: 确认配置文件中的医院编码
3. **消息格式错误**: 检查长度计算和JSON格式
4. **超时**: 检查网络连接和服务器负载

622
docs/测试用例.md Normal file
View File

@ -0,0 +1,622 @@
# HospitalPay-Go Socket 服务器测试用例
## 1. 测试环境准备
### 1.1 服务器配置
- 服务器地址localhost
- 服务器端口8080根据配置文件
- 医院编码H001根据配置文件
### 1.2 测试工具
- TCP客户端工具如telnet、nc、自定义客户端
- 网络抓包工具如Wireshark
## 2. 基础功能测试
### 2.1 入院登记测试功能码0001
#### 测试用例 1.1:正常入院登记
**测试目的**:验证正常病人入院登记功能
**请求消息**
```
00540001H0012024-01-15 10:30:00{"FCode":"3516022343"}
```
**期望响应**
```json
{
"ResultCode": "0000",
"ResultData": {
"FCode": "3516022343",
"FName": "测试病人",
"AmountA": 100.00,
"AmountB": 50.00,
"AmountC": 150.00,
"BankAccNo": "6222021234567890",
"BankAmount": 1000.00,
"Fflag": 0,
"Flimitflag": 0,
"Flimitamt": 0
}
}
```
#### 测试用例 1.2:病人不存在
**测试目的**:验证查询不存在的病人
**请求消息**
```
00540001H0012024-01-15 10:30:00{"FCode":"9999999999"}
```
**期望响应**
```json
{
"ResultCode": "0005",
"ResultMsg": "未找到病人信息"
}
```
#### 测试用例 1.3:病人已入院
**测试目的**:验证重复入院的处理
**请求消息**
```
00540001H0012024-01-15 10:30:00{"FCode":"3516022344"}
```
**期望响应**
```json
{
"ResultCode": "0007",
"ResultMsg": "病人已入院"
}
```
#### 测试用例 1.4:请求参数错误
**测试目的**验证错误的JSON格式处理
**请求消息**
```
00540001H0012024-01-15 10:30:00{"FCode":}
```
**期望响应**
```json
{
"ResultCode": "0009",
"ResultMsg": "请求数据格式错误"
}
```
### 2.2 消费额度查询测试功能码0002
#### 测试用例 2.1:正常消费额度查询
**测试目的**:验证正常的消费额度查询
**请求消息**
```
00540002H0012024-01-15 10:30:00{"FCode":"3516022343"}
```
**期望响应**
```json
{
"ResultCode": "0000",
"ResultData": {
"AmountA": 50.00,
"AmountB": 30.00,
"FreeAmountA": 0,
"FreeAmountB": 0,
"Checkflag": 0,
"FCode": "3516022343",
"FCriminal": "测试病人",
"Flag": 0
}
}
```
#### 测试用例 2.2:病人不存在的消费额度查询
**请求消息**
```
00540002H0012024-01-15 10:30:00{"FCode":"9999999999"}
```
**期望响应**
```json
{
"ResultCode": "0005",
"ResultMsg": "未找到病人信息"
}
```
### 2.3 出院处理测试功能码0003
#### 测试用例 3.1:正常出院处理
**请求消息**
```
00540003H0012024-01-15 10:30:00{"FCode":"3516022343"}
```
**期望响应**
```json
{
"ResultCode": "0000",
"ResultMsg": "出院处理成功"
}
```
#### 测试用例 3.2:病人不存在的出院处理
**请求消息**
```
00540003H0012024-01-15 10:30:00{"FCode":"9999999999"}
```
**期望响应**
```json
{
"ResultCode": "0005",
"ResultMsg": "未找到病人信息"
}
```
### 2.4 消费记录测试功能码0004
#### 测试用例 4.1:正常消费记录
**请求消息**
```
01840004H0012024-01-15 10:30:00{"FCode":"3516022343","InvoiceNo":"INV20240115001","AmountA":50.00,"AmountB":30.00,"Amount":80.00,"FreeAmountA":0.00,"FreeAmountB":0.00,"CrtDate":"2024-01-15T10:30:00Z","FCriminal":"测试病人","CardCode":"CARD001","OrderId":123456}
```
**期望响应**
```json
{
"ResultCode": "0000",
"ResultMsg": "消费记录保存成功"
}
```
#### 测试用例 4.2:缺少必填字段的消费记录
**请求消息**
```
00540004H0012024-01-15 10:30:00{"FCode":"3516022343"}
```
**期望响应**
```json
{
"ResultCode": "0009",
"ResultMsg": "请求数据格式错误"
}
```
### 2.5 实时余额查询测试功能码0005
#### 测试用例 5.1:正常实时余额查询
**请求消息**
```
00540005H0012024-01-15 10:30:00{"FCode":"3516022343"}
```
**期望响应**:与入院登记响应格式相同
### 2.6 发票同步测试功能码0006
#### 测试用例 6.1:正常发票同步
**请求消息**
```
00800006H0012024-01-15 10:30:00{"InvoiceList":["INV001","INV002","INV003"]}
```
**期望响应**
```json
{
"ResultCode": "0000",
"ResultData": [
{
"BankFlag": 2,
"CAmount": 100.00,
"FCode": "3516022343",
"Origid": "INV001",
"SendDate": "2024-01-15T10:30:00Z"
},
{
"BankFlag": 2,
"CAmount": 100.00,
"FCode": "3516022343",
"Origid": "INV002",
"SendDate": "2024-01-15T10:30:00Z"
},
{
"BankFlag": 2,
"CAmount": 100.00,
"FCode": "3516022343",
"Origid": "INV003",
"SendDate": "2024-01-15T10:30:00Z"
}
]
}
```
#### 测试用例 6.2:空发票列表同步
**请求消息**
```
00540006H0012024-01-15 10:30:00{"InvoiceList":[]}
```
**期望响应**
```json
{
"ResultCode": "0000",
"ResultData": []
}
```
## 3. 异常情况测试
### 3.1 连接测试
#### 测试用例 7.1:无效的医院编码
**请求消息**
```
00540001XXXX2024-01-15 10:30:00{"FCode":"3516022343"}
```
**期望响应**
```json
{
"ResultCode": "0009",
"ResultMsg": "无效的医院编码"
}
```
#### 测试用例 7.2:无效的功能码
**请求消息**
```
00549999H0012024-01-15 10:30:00{"FCode":"3516022343"}
```
**期望响应**
```json
{
"ResultCode": "0008",
"ResultMsg": "未知的功能码"
}
```
#### 测试用例 7.3:消息长度不足
**请求消息**
```
0001H001
```
**期望行为**:连接被关闭,服务器记录错误日志
#### 测试用例 7.4:消息格式错误
**请求消息**
```
INVALID_MESSAGE_FORMAT
```
**期望行为**:连接被关闭,服务器记录错误日志
### 3.2 并发测试
#### 测试用例 8.1:最大连接数测试
**测试目的**:验证服务器的最大连接数限制
**测试步骤**
1. 同时建立超过配置的最大连接数的连接
2. 验证超出限制的连接被拒绝
3. 检查服务器日志记录
**期望结果**
- 在限制内的连接正常处理
- 超出限制的连接被拒绝
- 服务器记录相应的警告日志
#### 测试用例 8.2:并发请求测试
**测试目的**:验证服务器处理并发请求的能力
**测试步骤**
1. 同时发送多个不同功能码的请求
2. 验证所有请求都得到正确响应
3. 检查响应时间
**期望结果**
- 所有请求都得到正确处理
- 响应时间在可接受范围内
- 无数据竞争或死锁
### 3.3 超时测试
#### 测试用例 9.1:读取超时测试
**测试步骤**
1. 建立连接但不发送数据
2. 等待超过配置的读取超时时间
**期望结果**
- 连接被服务器主动关闭
- 服务器记录超时日志
#### 测试用例 9.2:写入超时测试
**测试步骤**
1. 发送请求后立即关闭客户端的接收
2. 服务器尝试发送响应
**期望结果**
- 服务器检测到写入超时
- 连接被正确清理
## 4. 性能测试
### 4.1 吞吐量测试
#### 测试用例 10.1:单连接吞吐量
**测试目的**:测试单个连接的处理能力
**测试方法**
- 使用单个连接连续发送1000个请求
- 记录总处理时间和平均响应时间
**性能指标**
- 每秒处理请求数TPS
- 平均响应时间
- 95%响应时间
#### 测试用例 10.2:多连接吞吐量
**测试目的**:测试多连接并发处理能力
**测试方法**
- 使用10个并发连接每个连接发送100个请求
- 记录总处理时间和响应时间分布
### 4.2 内存和CPU使用率测试
#### 测试用例 11.1:长时间运行测试
**测试目的**:验证服务器长时间运行的稳定性
**测试方法**
- 持续发送请求24小时
- 监控内存使用率、CPU使用率
- 检查是否有内存泄漏
## 5. 测试脚本
### 5.1 Go 测试脚本
```go
package main
import (
"fmt"
"net"
"time"
"sync"
"log"
)
func testConnection(testCase string, message string, expectedCode string) {
conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
log.Printf("测试用例 %s 失败: 连接错误 %v", testCase, err)
return
}
defer conn.Close()
// 发送请求
_, err = conn.Write([]byte(message))
if err != nil {
log.Printf("测试用例 %s 失败: 发送错误 %v", testCase, err)
return
}
// 读取响应
buffer := make([]byte, 4096)
conn.SetReadDeadline(time.Now().Add(5 * time.Second))
n, err := conn.Read(buffer)
if err != nil {
log.Printf("测试用例 %s 失败: 读取错误 %v", testCase, err)
return
}
response := string(buffer[:n])
log.Printf("测试用例 %s: 响应 %s", testCase, response)
// 简单验证响应码
if len(response) > 4 {
responseData := response[4:]
if expectedCode != "" && !contains(responseData, expectedCode) {
log.Printf("测试用例 %s 失败: 期望响应码 %s", testCase, expectedCode)
} else {
log.Printf("测试用例 %s 成功", testCase)
}
}
}
func contains(s, substr string) bool {
return len(s) >= len(substr) && s[:len(substr)] == substr
}
func main() {
// 测试用例
testCases := []struct {
name string
message string
expected string
}{
{
"1.1-正常入院登记",
"00540001H0012024-01-15 10:30:00{\"FCode\":\"3516022343\"}",
"\"ResultCode\":\"0000\"",
},
{
"1.2-病人不存在",
"00540001H0012024-01-15 10:30:00{\"FCode\":\"9999999999\"}",
"\"ResultCode\":\"0005\"",
},
{
"7.1-无效医院编码",
"00540001XXXX2024-01-15 10:30:00{\"FCode\":\"3516022343\"}",
"\"ResultCode\":\"0009\"",
},
{
"7.2-无效功能码",
"00549999H0012024-01-15 10:30:00{\"FCode\":\"3516022343\"}",
"\"ResultCode\":\"0008\"",
},
}
for _, tc := range testCases {
testConnection(tc.name, tc.message, tc.expected)
time.Sleep(100 * time.Millisecond) // 避免过快请求
}
// 并发测试
log.Println("开始并发测试...")
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
testConnection(
fmt.Sprintf("并发测试-%d", id),
"00540001H0012024-01-15 10:30:00{\"FCode\":\"3516022343\"}",
"\"ResultCode\":\"0000\"",
)
}(i)
}
wg.Wait()
log.Println("并发测试完成")
}
```
### 5.2 Python 测试脚本
```python
import socket
import json
import time
import threading
from concurrent.futures import ThreadPoolExecutor
def send_request(test_name, message, expected_code=None):
"""发送测试请求"""
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5)
sock.connect(('localhost', 8080))
# 发送请求
sock.send(message.encode('utf-8'))
# 接收响应
response = sock.recv(4096).decode('utf-8')
print(f"测试用例 {test_name}: 响应 {response}")
# 验证响应码
if expected_code and len(response) > 4:
response_data = response[4:]
if expected_code in response_data:
print(f"测试用例 {test_name} 成功")
else:
print(f"测试用例 {test_name} 失败: 期望响应码 {expected_code}")
sock.close()
return True
except Exception as e:
print(f"测试用例 {test_name} 失败: {e}")
return False
def run_basic_tests():
"""运行基础功能测试"""
test_cases = [
{
'name': '1.1-正常入院登记',
'message': '00540001H0012024-01-15 10:30:00{"FCode":"3516022343"}',
'expected': '"ResultCode":"0000"'
},
{
'name': '1.2-病人不存在',
'message': '00540001H0012024-01-15 10:30:00{"FCode":"9999999999"}',
'expected': '"ResultCode":"0005"'
},
{
'name': '2.1-正常消费额度查询',
'message': '00540002H0012024-01-15 10:30:00{"FCode":"3516022343"}',
'expected': '"ResultCode":"0000"'
},
{
'name': '7.1-无效医院编码',
'message': '00540001XXXX2024-01-15 10:30:00{"FCode":"3516022343"}',
'expected': '"ResultCode":"0009"'
},
]
for test_case in test_cases:
send_request(test_case['name'], test_case['message'], test_case['expected'])
time.sleep(0.1) # 避免过快请求
def run_concurrent_tests():
"""运行并发测试"""
print("开始并发测试...")
def concurrent_request(thread_id):
return send_request(
f'并发测试-{thread_id}',
'00540001H0012024-01-15 10:30:00{"FCode":"3516022343"}',
'"ResultCode":"0000"'
)
with ThreadPoolExecutor(max_workers=10) as executor:
futures = [executor.submit(concurrent_request, i) for i in range(10)]
results = [future.result() for future in futures]
success_count = sum(results)
print(f"并发测试完成: {success_count}/10 成功")
if __name__ == "__main__":
print("开始基础功能测试...")
run_basic_tests()
print("\n开始并发测试...")
run_concurrent_tests()
print("\n所有测试完成")
```
## 6. 测试报告模板
### 6.1 测试执行记录
| 测试用例 | 执行时间 | 执行结果 | 响应时间 | 备注 |
|----------|----------|----------|----------|------|
| 1.1-正常入院登记 | 2024-01-15 10:30:00 | 通过 | 50ms | - |
| 1.2-病人不存在 | 2024-01-15 10:30:05 | 通过 | 45ms | - |
| 7.1-无效医院编码 | 2024-01-15 10:30:10 | 通过 | 30ms | - |
### 6.2 性能测试结果
| 测试项目 | 测试结果 | 标准值 | 是否通过 |
|----------|----------|--------|----------|
| 单连接TPS | 1000 req/s | >500 req/s | 通过 |
| 平均响应时间 | 50ms | <100ms | 通过 |
| 95%响应时间 | 80ms | <200ms | 通过 |
| 最大并发连接 | 100 | 100 | 通过 |
### 6.3 问题记录
| 问题编号 | 问题描述 | 严重程度 | 状态 | 备注 |
|----------|----------|----------|------|------|
| BUG-001 | 某种情况下响应超时 | 中 | 已修复 | - |
## 7. 测试环境清理
测试完成后需要:
1. 清理测试数据
2. 重置数据库状态
3. 检查服务器日志
4. 验证系统资源使用情况