# MESCommonBase 响应输出与 SqlWebCall 默认分支详细文档

文档范围：流程图中下半部分的两块内容。

- `响应输出`
- `DataLink.SqlWebCall 默认分支`

关联入口：`MES_Manage/submit/MESCommonBase.ashx`

生成时间：2026-07-02

## 1. 相关程序清单

| 程序文件 | 关键位置 | 作用 |
| --- | --- | --- |
| `MES_Manage/submit/MESCommonBase.ashx` | `ProcessRequest` 的 `switch(type)` 和外层 `catch` | 负责把不同 Type 的结果写入 HTTP 响应 |
| `02DataLinkMesWork/DataLinkMesWork.SqlWebCall.cs` | `DataLink.SqlWebCall(int type, JsonData jsonData, jsonobj dataobj)` | 默认分支二次分发入口 |
| `02DataLinkMesWork/DataLinkMesWork.SqlWebCall.cs` | `InitSystemReg(...)` | 初始化数据库连接字符串 |
| `02DataLinkMesWork/DataLinkMesWork.SqlWebCall.cs` | `GetString_JsonData(...)` | 从新协议 JSON 读取 `Type/Name/Param/token` 等字段 |
| `02DataLinkMesWork/DataLinkMesWork.cs` | `ExePROCEDURE_Type*`、`ExecuteInsertMesWork`、`ExecuteSelectMesWork` | 具体 SQL、存储过程、登录、分页、返回格式处理 |
| `02DataLinkMesWork/P0.MES.Common/bizDataAccess/SQLCommon.cs` | `ExecuteStoredProcedure`、`ExecuteDataTable`、`ExecuteDataset`、`ExecuteInsertMesWork`、`ExecuteSelectMesWork` | 底层 ADO.NET 数据库执行封装 |
| `01BasicData/BasicData/BasicData.cs` | `jsonobj` | 旧协议请求对象 |
| `MES_Manage/Web.config` | `appSettings["ConnectionString"]` | 默认数据库连接配置来源 |

## 2. 这部分在总流程中的位置

`MESCommonBase.ashx` 先解析请求体或 `param` 参数，拿到 `type` 后进入 `switch(type)`。

流程图下半部分对应的是：

```text
switch(type)
  |
  |-- 文件下载分支 2001/2002/2003/2004/16 -> 响应输出：二进制下载
  |-- 文件上传分支 15 -> 响应输出：JSON 文本
  |-- IP 查询分支 4000 -> 响应输出：文本 IP
  |-- default -> DataLink.SqlWebCall(...) -> 响应输出：JSON 文本
```

也就是说，`响应输出` 是所有分支最终面向 HTTP 客户端的出口；`DataLink.SqlWebCall 默认分支` 是普通业务请求的二次路由器。

## 3. 响应输出：程序流程

### 3.1 默认响应头

`ProcessRequest` 进入后先执行：

```csharp
context.Response.ContentType = "application/json";
```

因此除文件下载分支外，默认都会以 `application/json` 作为响应类型。需要注意：`Type=4000` 实际写出的是纯文本 IP，但 Content-Type 没有改成 `text/plain`。

### 3.2 二进制下载响应

适用 Type：

- `2001`
- `2002`
- `2003`
- `2004`
- `16`

典型代码模式：

```csharp
HttpContext.Current.Response.ContentType = "application/octet-stream";
HttpContext.Current.Response.AddHeader(
    "Content-Disposition",
    "attachment; filename=" + HttpUtility.UrlEncode(fileName, Encoding.UTF8)
);
HttpContext.Current.Response.AddHeader(
    "Access-Control-Expose-Headers",
    "Content-Disposition"
);
HttpContext.Current.Response.BinaryWrite(bytes);
HttpContext.Current.Response.Flush();
HttpContext.Current.Response.End();
```

字段含义：

| 字段 | 含义 |
| --- | --- |
| `ContentType = application/octet-stream` | 告诉浏览器按二进制文件处理 |
| `Content-Disposition = attachment` | 触发浏览器下载，而不是直接打开 |
| `filename=...` | 下载文件名，使用 UTF-8 URL 编码处理中文文件名 |
| `Access-Control-Expose-Headers` | 允许跨域前端读取 `Content-Disposition` 响应头 |
| `BinaryWrite(bytes)` | 写出文件二进制 |
| `Flush()` | 刷新响应缓冲区 |
| `End()` / `Close()` | 结束响应 |

不同文件分支的差异：

| Type | 生成文件的程序 | 文件名来源 | 结束方式 |
| --- | --- | --- | --- |
| `2001` | `ExcelWebCall.ExcelFile(...)` | `fileName + "." + fileExtension` | `Response.End()` |
| `2002` | `ExcelWebCall.ExcelFilePdf(...)` | `fileName + "." + fileExtension`，通常为 PDF | `Response.End()` |
| `2003` | `ExcelWebCall.ExcelFile(...)` | `fileName + "." + dataimg[0]` | `Response.Close()` |
| `2004` | `DataLink.ExePROCEDURE_Type2004(...)` | 数据库返回文件名和扩展名 | `Response.End()` |
| `16` | `DataLink.ExePROCEDURE_Type16(...)` | 数据库返回文件名和后缀 | `Response.End()` |

### 3.3 Type=16 的空文件处理

`Type=16` 下载前有一层空判断：

```csharp
DataLink.ExePROCEDURE_Type16(jsonData, out bytes, out fileName, out suffix);
if (bytes == null) return;
```

如果下游没有返回文件二进制，则直接 `return`，不会写 JSON 错误，也不会写文件响应。调用方看到的可能是空响应。

### 3.4 Type=15 上传文件后的 JSON 响应

`Type=15` 是上传文件分支，不返回下载流。

流程：

```text
读取 context.Request.Files
  |
  |-- 有文件：
  |     读取第一个文件 InputStream 为 byte[]
  |     从 FileName 拆 name/suffix
  |     调 DataLink.ExePROCEDURE_Type15(jsonData, name, suffix, bytes)
  |     Response.Write(responseText)
  |
  |-- 无文件：
        name=""
        suffix=""
        bytes=new byte[1]
        调同一个 ExePROCEDURE_Type15
        Response.Write(responseText)
```

`responseText` 来自下游存储过程调用，常见返回：

```json
[{"result":"1"}]
```

或：

```json
[{"result":"0"}]
```

### 3.5 Type=4000 的文本响应

`Type=4000` 返回请求 IP：

```csharp
context.Response.Write(userIP);
```

当前 IP 判断逻辑：

```csharp
if (context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"] != "")
    userIP = context.Request.ServerVariables["REMOTE_ADDR"];
else
    userIP = context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if (userIP == null || userIP == "")
    userIP = context.Request.UserHostAddress;
```

注意：这里逻辑疑似写反。通常 `HTTP_X_FORWARDED_FOR` 有值时才优先取它；当前代码在有转发头时反而取 `REMOTE_ADDR`。

### 3.6 default 分支的 JSON 响应

普通业务请求进入 default：

```csharp
responseText = DataLinkMesWork.DataLink.SqlWebCall(type, jsonData, dataobj);
context.Response.Headers.Remove("Server");
context.Response.Write(responseText);
```

特点：

- 响应体完全由 `SqlWebCall` 返回值决定。
- 尝试移除 `Server` 响应头。
- 仍使用入口处默认的 `application/json`。
- `SqlWebCall` 对未支持 Type 返回空字符串。

### 3.7 外层异常响应

`ProcessRequest` 外层包了一个总 `try/catch`：

```csharp
string responseText = "NULL";

try
{
    ...
}
catch(Exception err)
{
    context.Response.Write(responseText);
}
```

异常后的响应行为：

- 不记录异常。
- 不设置 HTTP 状态码。
- 写出当前 `responseText`。
- 如果异常发生在下游调用前，返回 `"NULL"`。
- 如果异常发生在 `responseText` 已赋值之后，可能返回旧结果。

## 4. DataLink.SqlWebCall 默认分支：入口流程

入口签名：

```csharp
public static string SqlWebCall(int type, JsonData jsonData, jsonobj dataobj)
```

调用来源：

```csharp
DataLinkMesWork.DataLink.SqlWebCall(type, jsonData, dataobj)
```

调用时机：`MESCommonBase.ashx` 的 `switch(type)` 没有匹配到 `2001/2002/2003/2004/15/16/4000` 时进入。

### 4.1 初始化数据库连接

`SqlWebCall` 开始时检查静态字段 `initSystemIsOk`：

```csharp
if (!initSystemIsOk)
{
    if (!InitSystemReg(out resultReg))
    {
        return resultReg;
    }
}
```

`InitSystemReg` 当前实际逻辑：

```csharp
connectionString = ConfigurationManager.AppSettings["ConnectionString"];
initSystemIsOk = true;
return true;
```

说明：

- 第一次进入 `SqlWebCall` 时，从 `Web.config` 读取 `ConnectionString`。
- 成功后设置静态标记，后续调用复用。
- 原先注册号、序列号、加密连接串等校验逻辑已经被注释。

### 4.2 二次分发

初始化后进入 `switch(type)`：

```text
SqlWebCall(type,jsonData,dataobj)
  |
  |-- 8888 / 5001 / 5002：加密、注册/改密、登录
  |-- 1 / 2 / 5：旧协议存储过程
  |-- 11 / 111 / 12 / 13 / 21：新协议存储过程
  |-- 1001 / 1002 / 3 / 4 / 7 / 22 / 3001：SQL、建表导入、SQL 命令
```

`SqlWebCall` 自身不直接访问数据库；它只选择具体方法。真正数据库执行在：

- `DataLinkMesWork.cs`
- `SQLCommon.cs`

## 5. SqlWebCall 支持的 Type 明细

### 5.1 登录、注册、加密类

| Type | 方法 | 输入对象 | 主要流程 | 返回 |
| --- | --- | --- | --- | --- |
| `8888` | `GetEncryptStr(jsonData)` | 新协议 `JsonData` | 读取 `Name/name`，用 `AesKeyIvGenerator.Encrypt(name1, SN, SN)` 加密 | 加密字符串，失败默认 `[]` |
| `5001` | `ExePROCEDURE_Type11_AddUser(jsonData)` | 新协议 `JsonData` | 读取 `Param` 数组，找到 `Password` 后 MD5，再执行 `Name` 指定存储过程 | 存储过程首表 JSON 或 `[]` |
| `5002` | `ExePROCEDURE_Type11_Login(jsonData)` | 新协议 `JsonData` | 解密前端密码，执行 `Name` 指定存储过程查账号，再比对数据库密码，成功后写入 token | 成功返回用户表 JSON 并追加 `token`，失败返回 `result=0,msg=...` |

登录成功后会调用固定存储过程：

```text
权限管理_Token_增加数据
```

校验 token 时调用：

```text
权限管理_Token_查询数据
```

### 5.2 旧协议存储过程类

旧协议使用 `BasicData.jsonobj dataobj`，字段为：

```csharp
public string Type;
public string ModularID;
public string Name;
public string Param;
public string UserID;
public string Pagination;
public string token;
public bool HasReturn;
```

| Type | 方法 | 执行对象 | 参数格式 | 返回 |
| --- | --- | --- | --- | --- |
| `1` | `ExePROCEDURE_Type1(dataobj)` | `Name` 的第一个 `&` 前作为存储过程名 | `@p=value=type&@p2=value=type` | 首个 DataTable JSON；分页时返回带总数结构；失败 `result=0` |
| `2` | `ExePROCEDURE_Type2(dataobj)` | `Name` 指定存储过程 | 同 Type 1 | 成功 `result=1`，失败 `result=0` |
| `5` | `ExePROCEDURE_Type5(dataobj)` | `Name` 指定存储过程 | `@p&value&type|@p2&value&type` | 首个 DataTable JSON；参数错误返回中文错误字符串 |

旧协议参数解析在 `SQLCommon.GetCmdParam(...)` 中完成：

```text
多个参数：用 & 分隔
单个参数：参数名=值=类型
输出参数：参数名=值=类型=output
```

支持类型：

- `int`
- `string`
- `boolean`
- `datetime`
- 默认按字符串处理

token 行为：

- `Type=1/2/3/4/5` 都是“如果 token 非空则校验”。
- 不传 token 时通常不会拒绝执行。

### 5.3 新协议存储过程类

新协议使用 `JsonData jsonData`，字段从请求 JSON 中读取：

```json
{
  "type": "11",
  "name": "存储过程名",
  "param": "[{\"name\":\"@p\",\"value\":\"v\",\"type\":\"string\"}]",
  "token": "..."
}
```

通用字段读取由 `GetString_JsonData(...)` 完成，兼容大小写：

| 大写字段 | 小写字段 | 含义 |
| --- | --- | --- |
| `Type` | `type` | Type |
| `Name` | `name` | 存储过程名或 SQL 文本 |
| `Param` | `param` | 参数数组字符串 |
| `UserID` | `userID` | 用户 |
| `Pagination` | `pagination` | 旧分页参数 |
| `HasReturn` | `hasReturn` | 是否返回 |
| `ModularID` | `modularID` | 模块 |
| `token` | `token` | token |

#### Type=11：查询型存储过程

方法：

```csharp
ExePROCEDURE_Type11(jsonData)
```

流程：

```text
读取 Name/Param/token
  |
  |-- token 非空：CheckToken(token)，失败返回 [{"result":"3"}]
  |
  |-- Param 为空：无参数执行存储过程
  |-- Param 非空：解析 JSON 数组为 SqlParameter[]
  |
  |-- SQLCommon.ExecuteStoredProcedure(... out DataSet ...)
  |
  |-- 有结果表：
        |-- 有 pageSize/pageList：内存分页，返回分页 JSON
        |-- 无分页字段：返回首个 DataTable JSON
  |
  |-- 有 output 参数：包装为 {"result":..., "output":...}
```

#### Type=111：服务端分页存储过程

方法：

```csharp
ExePROCEDURE_Type111(jsonData)
```

它解决“通讯服务器分页”的问题。

如果请求中包含：

```json
{
  "pageSize": 20,
  "pageList": 1
}
```

程序会自动向参数数组追加 4 个参数：

| 参数名 | 值 | 说明 |
| --- | --- | --- |
| `PageCurrent` | `pageList` | 当前页 |
| `PageSize` | `pageSize` | 每页条数 |
| `PageCount` | `1111`，`type=int`，`output=1` | 输出参数 |
| `ItemCount` | `1111`，`type=int`，`output=1` | 输出参数，总记录数 |

执行后返回：

```json
{
  "rows": [],
  "total": "总记录数"
}
```

其中 `total` 来自输出参数 `ItemCount`。

#### Type=12：执行型存储过程

方法：

```csharp
ExePROCEDURE_Type12(jsonData)
```

特点：

- 执行 `Name/name` 指定存储过程。
- 不关注 DataSet。
- 成功返回：

```json
[{"result":"1"}]
```

- 失败返回：

```json
[{"result":"0"}]
```

- 如果有输出参数，返回：

```json
{
  "result": [{"result":"1"}],
  "output": [{"参数名":"参数值"}]
}
```

#### Type=13：返回 DataSet JSON

方法：

```csharp
ExePROCEDURE_Type13(jsonData)
```

特点：

- 执行 `Name/name` 指定存储过程。
- 返回整个 `DataSet` 的 JSON，而不是只返回第一张表。
- 有输出参数时包装为：

```json
{
  "result": { "DataSet序列化结果": "..." },
  "output": [{"参数名":"参数值"}]
}
```

#### Type=21：新响应结构的存储过程

方法：

```csharp
ExePROCEDURE_Type21(jsonData)
```

特点：

- 仍然执行 `Name/name` 指定存储过程。
- 返回结构改为：

```json
{
  "code": "200",
  "message": "",
  "data": {}
}
```

或：

```json
{
  "code": "500",
  "message": "错误信息",
  "data": {}
}
```

注意：这里是否成功主要看底层返回的 `result` 字符串是否为空，不完全等同于数据库是否返回数据。

### 5.4 SQL 文本和建表导入类

| Type | 方法 | 执行对象 | 返回 |
| --- | --- | --- | --- |
| `1001` | `ExecuteInsertMesWork(jsonData)` | `Name/name` 作为 SQL 文本，参数来自 `Param` JSON 数组 | 成功 `result=1`；有输出时返回 `output` |
| `1002` | `ExecuteSelectMesWork(jsonData)` | `Name/name` 作为 SQL 查询文本，参数来自 `Param` JSON 数组 | DataTable JSON；支持 `pageSize/pageList` 内存分页 |
| `3` | `ExePROCEDURE_Type3(dataobj)` | `dataobj.Name` 作为 SQL 查询文本 | DataTable JSON |
| `4` | `ExePROCEDURE_Type4(dataobj)` | `dataobj.Name` 作为非查询 SQL 文本 | 成功 `result=1` |
| `7` | `ExePROCEDURE_Type7(jsonData)` | `Name/name` 作为表名，`Param` 作为行数据 | 删除并重建表，批量插入，成功 `result=1` |
| `22` | `ExePROCEDURE_Type22(dataobj)` | `dataobj.Name` 作为 SQL 文本 | `{ code, message, data }` |
| `3001` | `DbCallType1003_SqlCmd.SqlExec(jsonData)` | SQL 命令执行入口 | 由该方法决定 |

这类 Type 的共性：

- 执行对象来自客户端 `Name/name`。
- 参数虽然使用 `SqlParameter` 绑定，但 SQL 文本或表名本身不是固定白名单。
- 需要调用方和网络边界可信，否则风险很高。

## 6. SqlWebCall 参数处理流程

### 6.1 新协议 Param 数组字符串

常见格式：

```json
{
  "type": "11",
  "name": "存储过程名",
  "param": "[{\"name\":\"@工位号\",\"value\":\"OP10\",\"type\":\"string\"},{\"name\":\"@数量\",\"value\":\"10\",\"type\":\"int\"}]"
}
```

处理逻辑：

```text
param 字符串
  |
  |-- JsonMapper.ToObject(param)
  |
  |-- 遍历数组
        |-- name：SqlParameter 名
        |-- value：参数值；如果是数组，转为逗号字符串并做 Unicode 解码
        |-- type：存在时做类型转换
        |-- output == "1"：改为输出参数
  |
  |-- SQLCommon.ExecuteStoredProcedure / ExecuteSelectMesWork / ExecuteInsertMesWork
```

支持类型主要为：

- `int`
- `string`
- `boolean`
- `bool`
- `datetime`
- 默认字符串

### 6.2 旧协议 Param 拼接字符串

Type 1/2 的旧协议格式：

```text
@p1=value1=string&@p2=10=int&@out=0=int=output
```

`SQLCommon.GetCmdParam(...)` 处理逻辑：

```text
Param
  |
  |-- 按 & 分割参数
  |-- 过滤包含 == / null / undefined 的项
  |-- 每项按 = 分割
  |-- 长度 2：直接字符串参数
  |-- 长度 3：按类型转换
  |-- 长度 4 且第 4 项为 output：设为输出参数
```

Type 5 的旧协议格式不同：

```text
@p1&value1&String|@p2&10&Int
```

## 7. 底层数据库执行链路

### 7.1 存储过程查询

典型调用链：

```text
MESCommonBase.ashx default
  -> DataLink.SqlWebCall(...)
  -> DataLink.ExePROCEDURE_Type11/13/21(...)
  -> SQLCommon.ExecuteStoredProcedure(...)
  -> SqlConnection / SqlDataAdapter
  -> DataSet / DataTable
  -> JsonHelper / JsonConvert 序列化
  -> Response.Write(responseText)
```

`SQLCommon.ExecuteStoredProcedure(... out DataSet ...)` 的关键行为：

- 创建 `SqlConnection`。
- 创建 `SqlCommand(procedureName, conn)`。
- 设置 `CommandType = StoredProcedure`。
- 设置 `CommandTimeout = 0`，即不限制命令超时。
- 添加 `SqlParameter[]`。
- 使用 `SqlDataAdapter.Fill(ds)` 填充结果集。
- 异常时写 `ApplicationLog`，并通过 `errorMessage` 返回异常字符串。

### 7.2 SQL 查询

典型调用链：

```text
Type 1002 / 3 / 22
  -> SQLCommon.ExecuteSelectMesWork 或 ExecuteDataTable / ExecuteDataset
  -> SqlDataAdapter(sql, conn)
  -> Fill(DataTable/DataSet)
  -> JSON
```

### 7.3 SQL 增删改

典型调用链：

```text
Type 1001 / 4
  -> SQLCommon.ExecuteInsertMesWork 或 ExecuteNonQuery
  -> SqlCommand.CommandText = SQL 文本
  -> ExecuteNonQuery / ExecuteScalar
  -> result=1 或 result=0
```

## 8. 返回格式汇总

### 8.1 成功返回首表 JSON

常见于：

- `Type=1`
- `Type=3`
- `Type=5`
- `Type=11`
- `Type=1002`

示例：

```json
[
  {
    "字段1": "值1",
    "字段2": "值2"
  }
]
```

### 8.2 成功/失败标志

常见于：

- `Type=2`
- `Type=4`
- `Type=12`
- `Type=1001`

成功：

```json
[{"result":"1"}]
```

失败：

```json
[{"result":"0"}]
```

### 8.3 token 失败

部分方法在 token 非空且校验失败时返回：

```json
[{"result":"3"}]
```

### 8.4 输出参数包装

普通输出参数：

```json
{
  "result": [{"result":"1"}],
  "output": [{"@out":"123"}]
}
```

分页输出参数：

```json
{
  "rows": [],
  "total": "123"
}
```

### 8.5 新结构响应

常见于：

- `Type=21`
- `Type=22`

```json
{
  "code": "200",
  "message": "",
  "data": {}
}
```

失败：

```json
{
  "code": "500",
  "message": "错误信息",
  "data": {}
}
```

### 8.6 空或未支持 Type

`SqlWebCall` 对未匹配 Type 没有 default 处理，`result` 初始为空字符串。

因此 `MESCommonBase.ashx` default 分支可能返回空响应体：

```text
""
```

如果进入外层异常，则可能返回：

```text
NULL
```

## 9. 鉴权与 token 逻辑

`CheckToken(token)` 逻辑：

```text
token == "" -> true
token 非空：
  调 权限管理_Token_查询数据
    |
    |-- 存储过程报错 -> false
    |-- 返回空表 -> false
    |-- 返回有数据 -> true
```

关键影响：

- 多数业务方法只有在 `token` 非空时才调用 `CheckToken`。
- 如果请求不传 token，常见路径不会拒绝。
- `SqlWebCall` 入口层没有统一鉴权。
- 因此权限控制依赖调用方是否传 token，以及下游存储过程是否自行校验。

## 10. 风险点与维护注意事项

### 10.1 响应层风险

1. `Response.End()` 位于大 try 内，可能触发 `ThreadAbortException`，随后外层 catch 写出 `responseText`。
2. 下载失败时没有统一 JSON 错误，`Type=16` 的 `bytes == null` 会直接空返回。
3. `Type=4000` 返回纯文本，但 Content-Type 仍可能是 `application/json`。
4. 外层异常不记录日志，定位问题困难。
5. 多种响应格式混用，前端必须按 Type 分别处理。

### 10.2 SqlWebCall 风险

1. 客户端可通过 `Name/name` 指定存储过程名、SQL 文本或表名。
2. `Type=3/4/22/1001/1002/3001` 存在直接 SQL 执行能力。
3. `Type=7` 可根据请求表名执行 `DROP TABLE` 和 `CREATE TABLE`。
4. token 校验不是统一强制的。
5. `CommandTimeout = 0` 可能导致长时间阻塞。
6. 失败返回经常只有 `result=0`，缺少错误原因。

### 10.3 参数层风险

1. 新协议 `Param` 是 JSON 字符串，不是 JSON 对象；调用方需要二次转义。
2. 输出参数类型转换依赖 `type` 和 `output` 字段，格式错误会进入 catch 并返回 `result=0`。
3. 数组参数被拼成逗号字符串，数组元素里如果本身包含逗号，语义会丢失。
4. 旧协议分隔符为 `&`、`=`、`|`，参数值包含这些字符时容易解析错误。

## 11. 建议改进方案

### 11.1 低成本修复

- 在 `MESCommonBase.ashx` default 分支前统一校验 token。
- 未支持 Type 返回明确错误：

```json
{"code":"400","message":"Unsupported Type","data":null}
```

- 下载分支改为 `HttpContext.Current.ApplicationInstance.CompleteRequest()`，避免 `Response.End()` 的线程中止异常。
- `Type=4000` 设置 `ContentType = "text/plain"`，并修正代理 IP 判断逻辑。
- 外层 catch 记录异常：Type、Name、IP、异常堆栈。

### 11.2 中期改造

- 建立 `Type + Name` 白名单，禁止任意存储过程名和 SQL 文本。
- 将 `Param` 从“JSON 字符串”改为真正的 JSON 数组字段。
- 统一 JSON 返回结构：

```json
{
  "code": "200",
  "message": "",
  "data": {}
}
```

- 将 SQL 执行类 Type 与普通业务接口分离。

### 11.3 长期改造

- 按业务模块拆分接口，减少万能网关。
- 数据库账号按能力拆分权限。
- 移除明文连接字符串，使用部署环境密钥。
- 对文件下载、上传、SQL 执行加入审计日志和限流。

## 12. 典型流程示例

### 12.1 Type=11 查询型存储过程

请求：

```json
{
  "type": "11",
  "name": "MES_工位_查询",
  "param": "[{\"name\":\"@工位号\",\"value\":\"OP10\",\"type\":\"string\"}]",
  "token": "token值"
}
```

流程：

```text
MESCommonBase.ashx default
  -> SqlWebCall(11,jsonData,dataobj)
  -> InitSystemReg 读取连接字符串
  -> ExePROCEDURE_Type11
  -> GetString_JsonData 读取 name/param/token
  -> token 非空则 CheckToken
  -> Param 转 SqlParameter[]
  -> SQLCommon.ExecuteStoredProcedure
  -> JsonHelper.DataTableToJson
  -> Response.Write(JSON)
```

### 12.2 Type=111 分页型存储过程

请求：

```json
{
  "type": "111",
  "name": "MES_工位_分页查询",
  "param": "[{\"name\":\"@关键字\",\"value\":\"OP\",\"type\":\"string\"}]",
  "pageSize": 20,
  "pageList": 1
}
```

内部自动追加分页参数：

```text
PageCurrent = 1
PageSize = 20
PageCount = 输出参数
ItemCount = 输出参数
```

响应：

```json
{
  "rows": [],
  "total": "100"
}
```

### 12.3 Type=1002 SQL 查询

请求：

```json
{
  "type": "1002",
  "name": "select * from Test where f1=@f1",
  "param": "[{\"name\":\"@f1\",\"value\":\"T002\",\"type\":\"string\"}]"
}
```

流程：

```text
SqlWebCall(1002)
  -> ExecuteSelectMesWork(jsonData)
  -> SQLCommon.ExecuteSelectMesWork(sql, params, connectionString, out dt)
  -> DataTableToJson
```

说明：参数值通过 `SqlParameter` 绑定，但 SQL 文本本身来自请求。

## 13. 结论

流程图中的 `响应输出` 是整个 `MESCommonBase.ashx` 的 HTTP 出口，负责二进制下载、JSON 文本、IP 文本和异常兜底输出。`DataLink.SqlWebCall 默认分支` 是普通业务请求的二次分发中心，它按 Type 调用不同数据库执行方法，覆盖登录、存储过程、SQL 查询、SQL 增删改、分页和新结构返回。

这部分程序的核心问题不是流程复杂，而是执行权过于通用：客户端可通过 `type + name + param` 控制大量数据库行为。维护时应优先关注统一鉴权、Type/Name 白名单、异常日志和响应格式统一。
