From 75ff22c91d7710573231ba3ed75259f3d1477cc8 Mon Sep 17 00:00:00 2001
From: tjx <t2856754968@163.com>
Date: 星期六, 11 十月 2025 16:15:48 +0800
Subject: [PATCH] 发送接口的底层调整

---
 WebApi/Gs.Sys/Services/FmController.cs |   85 +++++++++++---
 WebApi/Gs.Toolbox/InterfaceUtil.cs     |   43 ++++--
 WebApi/Gs.HostIIS/appsettings.json     |    7 
 WebApi/CLAUDE.md                       |  215 ++++++++++++++++++++++++++---------
 4 files changed, 251 insertions(+), 99 deletions(-)

diff --git a/WebApi/CLAUDE.md b/WebApi/CLAUDE.md
index 15fb77b..ecac9ef 100644
--- a/WebApi/CLAUDE.md
+++ b/WebApi/CLAUDE.md
@@ -4,92 +4,189 @@
 
 ## Project Overview
 
-This is a .NET 8 MES (Manufacturing Execution System) solution built with ASP.NET Core Web API. The system is organized as a modular monolith with domain-separated projects for different business areas.
+This is a .NET 8 MES (Manufacturing Execution System) solution built with ASP.NET Core Web API. The system uses a custom API framework with modular architecture, organizing business functionality into domain-separated projects.
 
 ## Solution Structure
 
-The solution follows a modular architecture with these main projects:
-
-- **Gs.HostIIS**: Main Web API host application with Swagger documentation
-- **Gs.Entity**: Data entities organized by business domains (BaseInfo, QC, Sys, Warehouse)
-- **Gs.Toolbox**: Core utilities, API framework, and dependency injection infrastructure
-- **Business Modules**:
-  - **Gs.BaseInfo**: Basic information management (items, customers, suppliers, staff, etc.)
-  - **Gs.Warehouse**: Inventory and warehouse management
-  - **GS.QC**: Quality control functionality
+- **Gs.HostIIS**: Main Web API host application (targets .NET 8.0)
+- **Gs.Toolbox**: Core framework with custom DI, controller discovery, and utilities (targets .NET 6.0)
+- **Gs.Entity**: Data entity models organized by domain (BaseInfo, QC, Sys, Warehouse)
+- **Business Modules** (all target .NET 8.0):
+  - **Gs.BaseInfo**: Basic information (items, customers, suppliers, staff, departments)
+  - **Gs.Warehouse**: Inventory and warehouse operations
+  - **GS.QC**: Quality control and inspection
   - **Gs.Sys**: System administration and user management
   - **Gs.Report**: Reporting functionality
   - **Gs.Wom**: Work order management
   - **Gs.Sales**: Sales management
   - **Gs.Pda**: Mobile/handheld device support
-  - **Gs.QiTaCk** / **Gs.QiTaRk**: Additional business modules (鍏跺畠鍑哄簱/鍏跺畠鍏ュ簱)
+  - **Gs.QiTaCk** / **Gs.QiTaRk**: Miscellaneous in/out operations (鍏跺畠鍑哄簱/鍏跺畠鍏ュ簱)
   - **Gs.Ww**: Outsourcing management (濮斿)
   - **Gs.JJGZ**: Piece-rate wage management (璁′欢宸ヨ祫)
 
 ## Development Commands
 
 ### Build and Run
-- Build entire solution: `dotnet build GsMesSolution.sln`
-- Run main API: `dotnet run --project Gs.HostIIS`
-- Build specific project: `dotnet build Gs.ProjectName/Gs.ProjectName.csproj`
+```bash
+# Build entire solution
+dotnet build GsMesSolution.sln
+
+# Run main API (starts on http://localhost:5263 by default)
+dotnet run --project Gs.HostIIS
+
+# Build specific module
+dotnet build Gs.BaseInfo/Gs.BaseInfo.csproj
+```
 
 ### Development Server
-- The API runs on default ports (check launchSettings.json for specific ports)
-- Swagger UI is available at `/swagger` endpoint
-- CORS is configured to allow all origins for development
+- API runs on `http://localhost:5263` (Project profile) or `http://localhost:37005` (IIS Express)
+- Swagger UI: `http://localhost:5263/swagger`
+- CORS configured to allow all origins in development
 
 ## Architecture Details
 
 ### Custom API Framework
-The solution uses a custom API framework built in `Gs.Toolbox`:
-- Custom dependency injection with lifecycle attributes (`ITransient`, `IScope`, `ISingleton`)
-- Custom controller convention via `CustomApplicationModelConvention`
-- API grouping for Swagger documentation via `ApiGroupAttribute`
-- Custom authorization with `ApiAuthorizeAttribute`
+
+The solution uses a custom framework in `Gs.Toolbox` that replaces standard ASP.NET Core conventions:
+
+#### Controller Discovery
+- Controllers are discovered by implementing the `IRomteService` interface (not by inheriting `ControllerBase`)
+- `CustomControllerFeatureProvider` scans assemblies and the `Services` folder for classes implementing `IRomteService`
+- Controllers are automatically registered without `[ApiController]` or `[Route]` attributes
+- Business logic classes in `Services/` folders act as controllers
+
+#### Routing Convention
+- Routes automatically generated as `/{ControllerName}/{ActionName}` by `CustomApplicationModelConvention`
+- HTTP methods specified via `[RequestMethod(RequestMethods.POST)]` attribute (not standard `[HttpPost]`)
+- Parameter binding: non-primitive types automatically bound from request body for POST/PUT/PATCH
+
+#### Dependency Injection
+- Custom DI system using lifecycle marker interfaces:
+  - `ITransient`: Transient lifetime (new instance per request)
+  - `IScope`: Scoped lifetime (one instance per request)
+  - `ISingleton`: Singleton lifetime (one instance for application)
+- Classes implementing these interfaces are auto-registered
+- Use `[Expose(typeof(IYourInterface))]` attribute to specify service interface explicitly
+- Auto-injection scans all referenced assemblies via `builder.AddCustomInject()`
+
+#### API Grouping
+- Swagger groups defined via `[ApiGroup(ApiGroupNames.BaseInfo)]` attribute on controller classes
+- Groups defined in `ApiGroupNames` enum (BaseInfo, QC, PerMission, WOM, ErpMes, etc.)
+- Each group generates separate Swagger document for organization
 
 ### Data Access
-- Uses SqlSugar ORM for database operations
-- Connection string configured in `appsettings.json`
-- Repository pattern implemented in `Gs.Toolbox/Repository.cs`
 
-### Configuration
-- Database: SQL Server (connection string in appsettings.json)
-- External services: ERP integration endpoints configured
-- File paths: Services, logs, upload, and download paths configured
-- Custom JSON serialization with Newtonsoft.Json
+#### SqlSugar ORM
+- Base repository: `Repository<T>` in `Gs.Toolbox/Repository.cs`
+- Connection string: `appsettings.json` 鈫� `ConnectionStrings` key
+- Static `SqlSugarScope Db` instance provides database context
+- Business classes inherit from `Repository<TEntity>` for data access
+- Transaction support via `UseTransaction()` method
 
-### Key Components
-- **CustomContractResolver**: Custom JSON property naming (converts to camelCase)
-- **ReturnDto<T>**: Standardized API response format with RtnCode, RtnData, RtnMsg
-- **PageList<T>** and **PageQuery**: Pagination support
-- **ExcelHelper**: Excel import/export functionality
-- **LogHelper**: Logging utilities
-- **DbHelperSQL**: Additional database utilities
+#### Pagination
+- Standard pagination using `PageQuery` (input) and `PageList<T>` (output)
+- `PageQuery` fields: `currentPage`, `everyPageSize`, `sortName`, `sortOrder`, `keyWord`, `keyWhere`, `keyType`
+- `CommonPage()` methods provide built-in pagination logic
 
-### Framework Features
-- Custom controller discovery via `CustomControllerFeatureProvider`
-- Module system with `IModule` interface for extensibility
-- Dependency injection container with custom lifecycle management
-- API grouping system for organized Swagger documentation
+### Response Format
 
-## Business Domains
+All API responses use `ReturnDto<T>` wrapper:
+```csharp
+{
+  "rtnCode": 1,        // ReturnCode enum: Success=1, Default=-100, Unauthorized=-101, Exception=-102
+  "rtnData": {...},    // Generic data payload
+  "rtnMsg": "..."      // Optional message
+}
+```
 
-The system handles typical MES functionality organized by API groups:
-- Base information (BaseInfo): Items, customers, suppliers, departments, staff
-- Quality control (QC): Inspection projects and quality management
-- Warehouse (PerMission): Inventory and warehouse operations
-- Work orders (WOM): Manufacturing execution
-- ERP integration (ErpMes): Data exchange with ERP systems
-- Reporting (Report/Rport): Various business reports
-- Mobile support (PDA): Handheld device operations
-- Additional modules: Sales (XS), Outsourcing (WW), etc.
+### JSON Serialization
+- Uses Newtonsoft.Json with custom `CustomContractResolver`
+- Property naming: Converts to camelCase (first letter lowercase)
+- Handles underscore-separated names (e.g., `USER_NAME` 鈫� `userName`)
+- Date format: `"yyyy-MM-dd HH:mm:ss"`
+- Reference loop handling: Ignore
+
+### Authorization
+- Custom `ApiAuthorizeAttribute` filter validates `token` header
+- Token format: `"token {guid}"` in request header
+- Use `[AllowAnonymous]` to bypass authorization
+- User context extraction via `UtilityHelper.GetUserGuidAndOrgGuid(IHttpContextAccessor)`
+
+### Configuration (appsettings.json)
+- `ConnectionStrings`: SQL Server connection string
+- `TestErpUrl`, `TestErpUrl2`, `ProductionErpUrl`: ERP integration endpoints
+- `ServicesPath`: Path to compiled DLLs for controller discovery (default: "Services")
+- `LogPath`: Log file directory (default: "logs")
+- `UploadPath`: File upload directory (mapped to `/upload` endpoint)
+- `DownPath`: File download directory (mapped to `/down` endpoint)
+
+### Project Output Configuration
+Business modules use custom output paths:
+- Compiled DLLs output to `Gs.HostIIS/bin/Debug/` (BaseOutputPath configured in .csproj)
+- XML documentation files generated automatically for Swagger
+- Framework scans `Services/` folder at runtime for controller DLLs
+
+## Creating New Business Modules
+
+1. **Create new class library project** targeting .NET 8.0
+2. **Reference required projects**:
+   ```xml
+   <ProjectReference Include="..\Gs.Entity\Gs.Entity.csproj" />
+   <ProjectReference Include="..\Gs.Toolbox\Gs.Toolbox.csproj" />
+   ```
+3. **Configure output path** in .csproj:
+   ```xml
+   <BaseOutputPath>..\Gs.HostIIS\bin</BaseOutputPath>
+   <OutputPath>..\Gs.HostIIS\bin\Debug\</OutputPath>
+   <GenerateDocumentationFile>True</GenerateDocumentationFile>
+   ```
+4. **Create Services folder** for controller classes
+5. **Implement controller** by inheriting `Repository<TEntity>` and `IRomteService`:
+   ```csharp
+   [ApiGroup(ApiGroupNames.YourGroup)]
+   public class YourManager : Repository<YourEntity>, IRomteService
+   {
+       [RequestMethod(RequestMethods.POST)]
+       public ReturnDto<PageList<YourEntity>> GetListPage(PageQuery query) { ... }
+   }
+   ```
+6. **Add to solution** and build (DLL auto-discovered at runtime)
+
+## Common Patterns
+
+### Creating API Endpoints
+```csharp
+[ApiGroup(ApiGroupNames.BaseInfo)]
+public class MesItemsManager : Repository<MesItems>, IRomteService
+{
+    private readonly IHttpContextAccessor _http;
+
+    public MesItemsManager(IHttpContextAccessor httpContextAccessor)
+    {
+        _http = httpContextAccessor;
+    }
+
+    [RequestMethod(RequestMethods.POST)]
+    public ReturnDto<PageList<MesItems>> GetListPage(PageQuery query)
+    {
+        // Business logic using Db property from Repository<T>
+    }
+}
+```
+
+### Using Transactions
+```csharp
+var affectedRows = UseTransaction(db => {
+    db.Insertable(entity).ExecuteCommand();
+    db.Updateable(otherEntity).ExecuteCommand();
+    return db.Ado.AffectedRows;
+});
+```
 
 ## Development Notes
 
-- All projects target .NET 8.0 (except Gs.Toolbox which targets .NET 6.0)
-- Nullable reference types are enabled
-- The system uses a Chinese interface (comments and some naming in Chinese)
-- Custom API framework provides dependency injection and controller conventions
-- Swagger documentation is automatically generated with XML comments from Services folder
-- Response format follows ReturnDto pattern with standardized error codes
-- File upload/download paths are configured and served as static files
\ No newline at end of file
+- The system uses Chinese for business domain comments and naming
+- Gs.Toolbox targets .NET 6.0 for compatibility; all other projects target .NET 8.0
+- Controllers are discovered at runtime from `Services/` folder - no need to manually register
+- Swagger automatically includes all controllers with their XML documentation
+- Static files (uploads/downloads) served from paths configured in appsettings.json
\ No newline at end of file
diff --git a/WebApi/Gs.HostIIS/appsettings.json b/WebApi/Gs.HostIIS/appsettings.json
index 5e33f18..533159d 100644
--- a/WebApi/Gs.HostIIS/appsettings.json
+++ b/WebApi/Gs.HostIIS/appsettings.json
@@ -6,11 +6,10 @@
     }
   },
   "AllowedHosts": "*",
-  /*"ConnectionStrings": "Data Source=192.168.1.146;Initial Catalog=TEST_MES;User ID=testUser;Password =qixi1qaz@WSXtest",*/
   "ConnectionStrings": "Data Source=192.168.0.51;Initial Catalog=TEST_MES;User ID=sa;Password =LanBao@2025;Encrypt=True;TrustServerCertificate=True;",
-  "TestErpUrl": "http://192.168.1.149:8066/WebService1.asmx/MesToErpinfoTest",
-  "TestErpUrl2": "http://192.168.1.149:8066/WebService1.asmx/MesToErpUpdateFlag",
-  "ProductionErpUrl": "http://192.168.1.149:8066/WebService1.asmx/mesToErpinfoFormal",
+  "TestErpUrl": "http://192.168.0.52:8055/",
+  "TestErpUrl2": "http://192.168.0.52:8055/",
+  "ProductionErpUrl": "http://192.168.0.52:8055/",
   "ServicesPath": "Services",
   "LogPath": "logs",
   "UploadPath": "upload",
diff --git a/WebApi/Gs.Sys/Services/FmController.cs b/WebApi/Gs.Sys/Services/FmController.cs
index 974f493..3f4c8bf 100644
--- a/WebApi/Gs.Sys/Services/FmController.cs
+++ b/WebApi/Gs.Sys/Services/FmController.cs
@@ -28,25 +28,29 @@
                 GetUserGuidAndOrgGuid(_http);
         }
 
-        #region 鐗堥潰
+        #region 甯冨眬閰嶇疆
         /// <summary>
-        ///     澧炲姞
+        ///     淇濆瓨鎴栨竻绌鸿〃鍗曞竷灞�
         /// </summary>
         /// <param name="model"></param>
         /// <returns></returns>
+        /// <remarks>Saves layouts: intType 1=standard save, 2=personal save, 3=clear standard, 4=clear personal.</remarks>
         [RequestMethod(RequestMethods.POST)]
         public ReturnDto<ExpandoObject> EditModel([FromBody] dynamic model)
         {
             string applyUserGuid = "";
             string formPath = model.formPath;
             int intType = model.intType;
+            // intType: 1 = save standard layout, 2 = save personal layout, 3 = clear standard layout, 4 = clear personal layout.
             dynamic m = new ExpandoObject();
             m.outMsg = "";
+            // Gather batched SQL statements so they can be executed transactionally when saving layouts.
             Hashtable SQLStringList = new Hashtable();
             string _groupGuid = Guid.NewGuid().ToString();
             //鍙湁瓒呯骇绠$悊鍛樻潈闄�
             if (intType == 1 || intType == 3)
             {
+                // Validate the current user has administrator rights when touching standard layouts.
                 int? isAdmin = 0;
                 try
                 {
@@ -65,6 +69,7 @@
             //淇濆瓨鏍囧噯鐗�
             if (intType == 1)
             {
+                // Persist a new standard layout definition shared by all users.
                 applyUserGuid = null;
                 Gs.Toolbox.DbHelperSQL.ExecuteSql("delete from [FM_LAYOUT] where groupGuid<>'" + _groupGuid + "' and [formPath]=@formPath and applyUserGuid is null", new SqlParameter[] { new SqlParameter("@formPath", formPath) });
                 // SQLStringList.Add("delete from [FM_LAYOUT] where groupGuid<>'" + _groupGuid + "' and [formPath]=@formPath and applyUserGuid is null", new SqlParameter[] { new SqlParameter("@formPath", formPath) });
@@ -72,6 +77,7 @@
             //淇濆瓨涓汉鐗堟湰
             if (intType == 2)
             {
+                // Persist the caller's personal layout copy scoped to their user GUID.
                 applyUserGuid = _userGuid;
                 Gs.Toolbox.DbHelperSQL.ExecuteSql("delete from [FM_LAYOUT] where  groupGuid<>'" + _groupGuid + "' and [formPath]=@formPath and applyUserGuid =@applyUserGuid", new SqlParameter[] { new SqlParameter("@formPath", formPath), new SqlParameter("@applyUserGuid", applyUserGuid) });
                 //SQLStringList.Add("delete from [FM_LAYOUT] where  groupGuid<>'" + _groupGuid + "' and [formPath]=@formPath and applyUserGuid =@applyUserGuid", new SqlParameter[] { new SqlParameter("@formPath", formPath), new SqlParameter("@applyUserGuid", applyUserGuid) });
@@ -79,6 +85,7 @@
             //娓呯┖鏍囧噯鐗堟湰
             if (intType == 3)
             {
+                // Administrators can wipe the shared standard layout entirely.
                 applyUserGuid = null;
                 SQLStringList.Add("delete from [FM_LAYOUT] where [formPath]=@formPath and applyUserGuid is null", new SqlParameter[] { new SqlParameter("@formPath", formPath) });
                 Gs.Toolbox.DbHelperSQL.ExecuteSqlTranRtn(SQLStringList);
@@ -88,6 +95,7 @@
             //娓呯┖涓汉鐗堟湰
             if (intType == 4)
             {
+                // Remove the caller's personal layout while keeping the shared standard version intact.
                 applyUserGuid = _userGuid;
                 SQLStringList.Add("delete from [FM_LAYOUT] where [formPath]=@formPath and applyUserGuid =@applyUserGuid", new SqlParameter[] { new SqlParameter("@formPath", formPath), new SqlParameter("@applyUserGuid", applyUserGuid) });
                 Gs.Toolbox.DbHelperSQL.ExecuteSqlTranRtn(SQLStringList);
@@ -103,6 +111,7 @@
                     JObject job = (JObject)jsonitem;
                     if (job["idName"] != null)
                     {
+                        // Compose an insert statement for each UI control (grid, layout panel, splitter, etc.).
                         string idName = job["idName"].ToString();
                         string idXml = job["idXml"].ToString();
                         string idType = job["idType"].ToString();
@@ -127,6 +136,7 @@
             }
             catch (Exception ex)
             {
+                // 鎹曡幏淇濆瓨鏌ヨ閰嶇疆鏃剁殑寮傚父锛屽苟灏嗕俊鎭繑鍥炵粰鍓嶇銆�
                 m.outMsg = "鎿嶄綔澶辫触:" + ex.Message;
                 Gs.Toolbox.LogHelper.Debug(this.ToString(), "EditModel error锛�" + ex.Message);
             }
@@ -140,6 +150,7 @@
         /// </summary>
         /// <param name="guid"></param>
         /// <returns></returns>
+        /// <remarks>Combines the shared layout (list) with the current user's override (list2).</remarks>
         [RequestMethod(RequestMethods.POST)]
         public ReturnDto<ExpandoObject> GetModel([FromBody] dynamic model)
         {
@@ -155,10 +166,12 @@
             var dset = new DataSet();
             try
             {
+                // Stored procedure returns both standard layout data and any personal override for the current user.
                 dset = DbHelperSQL.RunProcedure("[fm_get_layout]", parameters, "0");
                 if (dset != null && dset.Tables.Count > 0
                  )
                 {
+                    // Table[0] represents the standard definition; table[1] holds the user's personal layout snapshot.
                     var _tb = dset.Tables[0].TableToDynamicList();
                     m.list = _tb;
                     var _tb2 = dset.Tables[1].TableToDynamicList();
@@ -167,6 +180,7 @@
             }
             catch (Exception ex)
             {
+                // Log retrieval failure but continue returning default result to caller.
                 LogHelper.Debug(ToString(), ex.Message);
             }
             if (m != null)
@@ -182,6 +196,7 @@
         /// </summary>
         /// <param name="guid"></param>
         /// <returns></returns>
+        /// <remarks>Retrieves the serialized layout string for the latest saved version (standard or personal).</remarks>
         [RequestMethod(RequestMethods.POST)]
         public ReturnDto<string> GetModelByVersion([FromBody] dynamic model)
         {
@@ -195,6 +210,7 @@
             var dset = new DataSet();
             try
             {
+                // Stored procedure exposes the latest serialized layout snapshot based on formPath and user scope.
                 dset = DbHelperSQL.RunProcedure("[fm_get_layout_ver]", parameters, "0");
                 if (dset != null && dset.Tables.Count > 0
                  )
@@ -204,6 +220,7 @@
             }
             catch (Exception ex)
             {
+                // Capture context when reading layout versions fails to help diagnose environment-specific issues.
                 LogHelper.Debug(ToString(), ex.Message+ ",formPath:"+ formPath+ ",_userGuid:"+ _userGuid);
 
             }
@@ -217,6 +234,7 @@
         {
             int? isAdmin = 0;
             System.Text.StringBuilder _sb = new System.Text.StringBuilder();
+            // Uses SYS_USER.IS_SYS flag to decide if the caller has elevated privileges.
             _sb.Append("select count(1) from [dbo].[SYS_USER] where GUID='" + _userGuid + "' and  IS_SYS=1");
             object _obj = Gs.Toolbox.DbHelperSQL.GetSingle(_sb.ToString());
             if (_obj == null)
@@ -235,6 +253,7 @@
         /// </summary>
         /// <param name="model">keyType:1瀹℃牳锛�0鍙嶅鏍�</param>
         /// <returns></returns>
+        /// <remarks>Packages MES data into ERP payloads and posts them according to the requested operation.</remarks>
         [RequestMethod(RequestMethods.POST)]
         public string SendErp([FromBody] dynamic model)
         {
@@ -245,6 +264,7 @@
             //string keyChild = model.keyChild;浠诲姟瀛愯妭鐐瑰悕
             //string keyMeth = model.keyMeth;鏂规硶鍚�
             //string keyNo = model.keyNo;鍗曟嵁缂栧彿
+            //string keyUrl = model.keyUrl;鎺ュ彛鍦板潃
             int _rtnInt = 0;
             string _rtnStr = "";
             try
@@ -256,13 +276,21 @@
                 string keyGuid = model.keyGuid;
                 string keyNo = model.keyNo;
                 string idtype = model.idtype;//杩欎釜浠呬粎鏄洿鏂板伐鍗曠姸鎬佺殑鏃跺�欐湁
+                string keyUrl = model.keyUrl;
                 if (string.IsNullOrEmpty(idtype))
-                    (_rtnInt, _rtnStr) = InterfaceUtil.HttpPostErp(_erpJson, keyUserGuid, keyGuid, keyNo);
+                {
+                    // 甯歌鎺ュ彛锛氭寜鎿嶄綔绫诲瀷鎺ㄩ�佸崟鏉′笟鍔℃暟鎹��
+                    (_rtnInt, _rtnStr) = InterfaceUtil.HttpPostErp(_erpJson, keyUserGuid, keyGuid, keyNo,0,keyUrl);
+                }
                 else
-                    (_rtnInt, _rtnStr) = InterfaceUtil.HttpPostErp(_erpJson, keyUserGuid, keyGuid, keyNo, 2);
+                {
+                    // 甯� idtype 鐨勮姹傜敤浜庣壒娈婃祦绋嬶紙濡傚叧闂�佸弽鍏抽棴锛夛紝ERP 闇�瑕侀澶栫殑鐘舵�佹爣璁般��
+                    (_rtnInt, _rtnStr) = InterfaceUtil.HttpPostErp(_erpJson, keyUserGuid, keyGuid, keyNo, 2,keyUrl);
+                }
             }
             catch (Exception ex)
             {
+                // 璁板綍 ERP 鏁版嵁杞崲寮傚父锛屼究浜庡畾浣嶅瓨鍌ㄨ繃绋嬫垨搴忓垪鍖栭棶棰樸��
                 Gs.Toolbox.LogHelper.Debug(this.ToString(), "Fm SendErp:" + ex.Message);
                 return "鍙戦�乪rp澶辫触:" + ex.Message;
             }
@@ -289,6 +317,7 @@
             string keyNo = model.keyNo;
             string idtype = model.idtype;//杩欎釜浠呬粎鏄洿鏂板伐鍗曠姸鎬佺殑鏃跺�欐湁
             if (keyMeth.ToUpper() == "delete".ToUpper())
+                // 鍒犻櫎鎿嶄綔鏃犻渶鍚� ERP 鎺ㄩ�佹暟鎹紝鍙渶杩斿洖绌轰覆銆�
                 return "";
             try
             {
@@ -299,6 +328,7 @@
                        new("@inEdtUserGuid", keyUserGuid),
                        new("@keyMeth", keyMeth.ToLower()),
                    };
+                // 璋冪敤涓氬姟瀹氫箟鐨勫瓨鍌ㄨ繃绋嬶紝灏� MES 鏁版嵁鎵撳寘缁� ERP銆�
                 dset = DbHelperSQL.RunProcedure(keyProduce, parameters, "0");
                 if (dset == null)
                     return "";
@@ -309,10 +339,12 @@
                 //杩欐槸鏅�氱殑鎺ュ彛
                 if (string.IsNullOrEmpty(idtype))
                 {
+                    // 甯歌鍑哄弬锛氱涓�寮犺〃鏄富鏁版嵁锛岀浜屽紶琛紙鑻ュ瓨鍦級鏄瓙琛ㄩ泦鍚堛��
                     string _mesGuid = dset.Tables[0].Rows[0][0].ToString();
                     dynamic _datajson = new ExpandoObject();
                     if (dset.Tables.Count > 1)
                     {
+                        // 澶氳〃杩斿洖鏃讹紝闇�瑕佹妸瀛愯〃闆嗗悎鎸傚埌 datajson 涓��
                         //杩欐槸杩欐槸鏅�氱殑鎺ュ彛閲岀殑缁撴锛岀粨鏋勫拰鍏跺畠涓嶄竴鏍�
                         if (keyMeth.ToLower() == "toclose".ToLower() || keyMeth.ToLower() == "closure".ToLower() || keyMeth.ToLower() == "unfinish")
                         {
@@ -325,30 +357,33 @@
                             ((IDictionary<string, object>)_datajson)[keyChild] = _lst;
                         }
                     }
-                    var _obj = new
-                    {
-                        mesid = _mesGuid,
-                        taskname = keyTaskName,
-                        optype = keyMeth,
-                        datajson = JsonConvert.SerializeObject(_datajson),
-                    };
-                    return JsonConvert.SerializeObject(_obj);
+                    // var _obj = new
+                    // {
+                    //     mesid = _mesGuid,
+                    //     taskname = keyTaskName,
+                    //     optype = keyMeth,
+                    //     datajson = JsonConvert.SerializeObject(_datajson),
+                    // };
+                    // return JsonConvert.SerializeObject(_obj);
+                    return JsonConvert.SerializeObject(_datajson);
                 }
                 //杩欐槸璁㈠崟鍥炰紶鏍囪瘑
                 List<dynamic> _datajson22 = new List<dynamic>();
                 dynamic _ob = new ExpandoObject();
                 _ob.ENTRY = dset.Tables[0].TableToDynamicList();
                 _datajson22.Add(_ob);
-                var _obj22 = new
-                {
-                    taskname = keyTaskName,
-                    idtype = idtype,
-                    datajson = JsonConvert.SerializeObject(_datajson22),
-                };
-                return JsonConvert.SerializeObject(_obj22);
+                // var _obj22 = new
+                // {
+                //     taskname = keyTaskName,
+                //     idtype = idtype,
+                //     datajson = JsonConvert.SerializeObject(_datajson22),
+                // };
+                // return JsonConvert.SerializeObject(_obj22);
+                return JsonConvert.SerializeObject(_datajson22);
             }
             catch (Exception ex)
             {
+                // 璁板綍 ERP 鏁版嵁杞崲寮傚父锛屼究浜庡畾浣嶅瓨鍌ㄨ繃绋嬫垨搴忓垪鍖栭棶棰樸��
                 Gs.Toolbox.LogHelper.Debug(this.ToString(), ex.Message);
                 throw ex;
             }
@@ -386,6 +421,7 @@
             }
             ;
             var lst = new List<dynamic>();
+            // 灏嗗垪鍚嶅拰鏄剧ず鏍囬鎷兼垚 "~" 鍒嗛殧鐨勫弬鏁帮紝浼犵粰瀛樺偍杩囩▼鐢熸垚鏌ヨ閰嶇疆銆�
             SqlParameter[] parameters =
             {
                 new("@formPath", formPath),
@@ -394,6 +430,7 @@
             var dset = new DataSet();
             try
             {
+                // fm_set_query 浼氳繑鍥炴煡璇㈡潯浠躲�佺粨鏋滃瓧娈点�佹帓搴忕瓑澶氬紶閰嶇疆琛ㄣ��
                 dset = DbHelperSQL.RunProcedure("[fm_set_query]", parameters, "0");
                 if (dset != null && dset.Tables.Count > 0)
                 {
@@ -405,6 +442,7 @@
             }
             catch (Exception ex)
             {
+                // 璁板綍 ERP 鏁版嵁杞崲寮傚父锛屼究浜庡畾浣嶅瓨鍌ㄨ繃绋嬫垨搴忓垪鍖栭棶棰樸��
                 LogHelper.Debug(ToString(), ex.Message);
             }
             return ReturnDto<dynamic>.QuickReturn(m, ReturnCode.Success, "璇诲彇鎴愬姛锛�");
@@ -429,16 +467,19 @@
                 isAdmin = chkAdmin();
                 if (isAdmin <= 0)
                 {
+                    // Query configuration is restricted to administrators to protect shared metadata.
                     m.outMsg = "浣犱笉鏄鐞嗗憳锛屾搷浣滃け璐ワ紒";
                     return ReturnDto<dynamic>.QuickReturn(m, ReturnCode.Default, "鎿嶄綔鎴愬姛锛�");
                 }
             }
             catch (Exception ex)
             {
+                // 璁板綍 ERP 鏁版嵁杞崲寮傚父锛屼究浜庡畾浣嶅瓨鍌ㄨ繃绋嬫垨搴忓垪鍖栭棶棰樸��
                 Gs.Toolbox.LogHelper.Debug(this.ToString(), "EditModel isAdmin error锛�" + ex.Message);
             }
             try
             {
+                // 鍏堟竻绌哄師鏈夋煡璇㈡潵婧愯〃锛屽啀鎵归噺鎻掑叆鏈�鏂伴厤缃��
                 Gs.Toolbox.DbHelperSQL.ExecuteSql("delete from [FM_QUERY_TABLE] where formPath=@formPath ", new SqlParameter[] { new SqlParameter("@formPath", formPath) });
                 foreach (var _obj in model.list)
                 {
@@ -451,6 +492,7 @@
             }
             catch (Exception ex)
             {
+                // 鎹曡幏淇濆瓨鏌ヨ閰嶇疆鏃剁殑寮傚父锛屽苟灏嗕俊鎭繑鍥炵粰鍓嶇銆�
                 m.outMsg = ex.Message;
                 return ReturnDto<dynamic>.QuickReturn(m, ReturnCode.Default, ex.Message);
             }
@@ -473,15 +515,18 @@
                 isAdmin = chkAdmin();
                 if (isAdmin <= 0)
                 {
+                    // 鍒犻櫎鏌ヨ閰嶇疆鍚屾牱闇�瑕佺鐞嗗憳鏉冮檺銆�
                     return ReturnDto<int>.QuickReturn(rtnInt, ReturnCode.Default, "浣犱笉鏄鐞嗗憳锛屾搷浣滃け璐ワ紒");
                 }
             }
             catch (Exception ex)
             {
+                // 璁板綍 ERP 鏁版嵁杞崲寮傚父锛屼究浜庡畾浣嶅瓨鍌ㄨ繃绋嬫垨搴忓垪鍖栭棶棰樸��
                 Gs.Toolbox.LogHelper.Debug(this.ToString(), "EditModel isAdmin error锛�" + ex.Message);
             }
             Guid? guid = model.guid;
             System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder();
+            // 閲囩敤 GUID 绮剧‘鍒犻櫎鎸囧畾鐨勬煡璇㈡暟鎹簮璁板綍銆�
             stringBuilder.Append("delete from FM_QUERY_TABLE where guid='" + guid + "'");
             rtnInt = Gs.Toolbox.DbHelperSQL.ExecuteSql(stringBuilder.ToString());
             if (rtnInt <= 0)
@@ -504,11 +549,13 @@
                 isAdmin = chkAdmin();
                 if (isAdmin <= 0)
                 {
+                    // 鍙湁绠$悊鍛樻墠鑳借皟鏁存煡璇㈠垪瀛楁鏄犲皠銆�
                     return ReturnDto<int>.QuickReturn(rtnInt, ReturnCode.Default, "浣犱笉鏄鐞嗗憳锛屾搷浣滃け璐ワ紒");
                 }
             }
             catch (Exception ex)
             {
+                // 璁板綍 ERP 鏁版嵁杞崲寮傚父锛屼究浜庡畾浣嶅瓨鍌ㄨ繃绋嬫垨搴忓垪鍖栭棶棰樸��
                 Gs.Toolbox.LogHelper.Debug(this.ToString(), "EditModel isAdmin error锛�" + ex.Message);
             }
             Guid? guid = model.guid;
diff --git a/WebApi/Gs.Toolbox/InterfaceUtil.cs b/WebApi/Gs.Toolbox/InterfaceUtil.cs
index c40aa81..f82cd7d 100644
--- a/WebApi/Gs.Toolbox/InterfaceUtil.cs
+++ b/WebApi/Gs.Toolbox/InterfaceUtil.cs
@@ -18,16 +18,18 @@
     /// <param name="hNo"></param>
     /// <param name="urlType">濡傛灉涓�2锛屽垯鏄洿鏂板伐鍗曠姸鎬�</param>
     /// <returns>濡傛灉鎴愬姛杩斿洖鏃ュ織guid,鍚﹀垯杩斿洖涓�</returns>
-    public static (int, string) HttpPostErp(string param, string edtUserGuid = "", string abtGuid = "", string hNo = "", int urlType = 0)
+    public static (int, string) HttpPostErp(string param,
+        string edtUserGuid = "", string abtGuid = "", string hNo = "",
+        int urlType = 0, string keyUrl = "")
     {
         int _rtn = 0;
         //鏃ュ織璇︾粏锛屽彂閫佺殑鏃跺�欙紝璁板綍鏃ュ織锛屽瓨鍌ㄨ繃绋嬭皟鐢ㄧ殑鏃跺�欙紝鍐嶇疮鍔犱笂mes涓氬姟鐨勬搷浣滅粨鏋�
         System.Text.StringBuilder sbLog = new System.Text.StringBuilder();
         sbLog.Append(DateTime.Now.ToString() + "寮�濮嬪彂閫�");
         string strLogGuid = Guid.NewGuid().ToString();
-        string url = AppSettingsHelper.getValueByKey("TestErpUrl");
+        string url = AppSettingsHelper.getValueByKey("TestErpUrl") + keyUrl;
         if (urlType == 2)
-            url = AppSettingsHelper.getValueByKey("TestErpUrl2");
+            url = AppSettingsHelper.getValueByKey("TestErpUrl2") + keyUrl;
         HttpWebRequest request = null;
         StreamWriter requestStream = null;
         WebResponse response = null;
@@ -41,7 +43,9 @@
             request.Timeout = 150000;
             request.AllowAutoRedirect = false;
             request.ServicePoint.Expect100Continue = false;
-            HttpRequestCachePolicy noCachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
+            HttpRequestCachePolicy noCachePolicy =
+                new HttpRequestCachePolicy(HttpRequestCacheLevel
+                    .NoCacheNoStore);
             request.CachePolicy = noCachePolicy;
             requestStream = new StreamWriter(request.GetRequestStream());
             requestStream.Write(param);
@@ -57,7 +61,8 @@
         }
         catch (Exception ex)
         {
-            LogHelper.Debug(url, "HttpPostErp response:" + param + "锛宔x:" + ex.Message);
+            LogHelper.Debug(url,
+                "HttpPostErp response:" + param + "锛宔x:" + ex.Message);
             responseStr = ex.Message;
             _rtn = -1;
         }
@@ -67,12 +72,14 @@
             requestStream = null;
             response = null;
         }
+
         if (_rtn != -1)
         {
             Result _result = JsonConvert.DeserializeObject<Result>(responseStr);
             if ("200".Equals(_result.state))
                 _rtn = 1;
         }
+
         sbLog.Append("," + DateTime.Now.ToString() + "缁撴潫鍙戦��");
         if (_rtn > 0)
             sbLog.Append(",鍙戦�佹垚鍔�");
@@ -81,24 +88,25 @@
         try
         {
             SqlParameter[] parameters =
-    {
-            new("@edtUserGuid", edtUserGuid),
-            new("@abtGuid", abtGuid),
-            new("@abtTable", ""),
-            new("@detail", sbLog.ToString()),
-            new("@hNo", hNo),
-            new("@RtnLogGuid", strLogGuid),
-            new("@SendJson", param),
-            new("@RtnJson", responseStr),
-            new("@isSuccess", (_rtn>0?1:0)),
-            new("@isErp", 1),
-        };
+            {
+                new("@edtUserGuid", edtUserGuid),
+                new("@abtGuid", abtGuid),
+                new("@abtTable", ""),
+                new("@detail", sbLog.ToString()),
+                new("@hNo", hNo),
+                new("@RtnLogGuid", strLogGuid),
+                new("@SendJson", param),
+                new("@RtnJson", responseStr),
+                new("@isSuccess", (_rtn > 0 ? 1 : 0)),
+                new("@isErp", 1),
+            };
             DbHelperSQL.RunProcedure("[prc_log_create]", parameters);
         }
         catch (Exception ex)
         {
             LogHelper.Debug(url, "HttpPostErp 鍐欏叆鏃ュ織琛�" + ex.Message);
         }
+
         return (_rtn, (_rtn > 0 ? strLogGuid : responseStr));
     }
 }
@@ -109,6 +117,7 @@
     /// 200鎴愬姛锛屽惁鍒欏け璐�
     /// </summary>
     public string? state { get; set; }
+
     public string? msg { get; set; }
 
     public string? status { get; set; }

--
Gitblit v1.9.3