# KJD WebService 迁移文档(.NET Framework 4.6.2 → .NET 10) > 生成日期:2026-08-14 > 适用范围:旧项目 `D:\Works\KJD Cef138\KJDService` → 新项目 `D:\Works\StoreHub\StoreHub.KJDWebService` --- ## 1. 迁移背景与目标 | 项目 | 说明 | | --- | --- | | 旧项目 | `D:\Works\KJD Cef138\KJDService`,ASP.NET WebService(*.asmx),.NET Framework 4.6.2,MySQL(MySql.Data) | | 新项目 | `D:\Works\StoreHub\StoreHub.KJDWebService`,.NET 10 + ASP.NET Core,宿主为 `D:\Works\StoreHub\StoreHub`(Web 项目,已引用 KJDWebService) | | 迁移目标 | 功能等价迁移到 .NET 10;不优化代码;模型照抄;Manage 方法内逻辑不修改;执行语句改为参数方式;对外路由保持一致 | 新项目仓库结构(解决方案 `D:\Works\StoreHub\StoreHub.slnx`): ``` D:\Works\StoreHub\ ├─ StoreHub\ # Web 宿主(Program.cs,引用 KJDWebService) ├─ StoreHub.Framework\ # 公共框架(IOC、FreeSql、ApiResult、SqlRepository) ├─ StoreHub.KJDWebService\ # 本次迁移目标项目(业务代码) ├─ StoreHub.Services\ # 其他服务 └─ Core\ ``` --- ## 2. 新项目目录结构规范(必须严格遵循) ``` StoreHub.KJDWebService/ ├─ Commons/ # 公共工具(旧 Utils 照搬) ├─ Controllers/ # API(旧 *.asmx 的每个 WebMethod → Action) ├─ Dtos/ # DTO 目录 ├─ Models/ # 模型目录(旧 Model 照抄) ├─ Services/ # 服务目录(旧 BLL/*Manage → *Service) ├─ Repositorys/ # SqlRepository(框架提供) └─ Startup.cs # IStartup 入口 ``` --- ## 3. 核心编码规范 ### 3.1 Service 规范(强制) - 所有 Service 都要注入 `SqlRepository sqlRepository`; - 用 `_sqlRepository.Ado.ExecuteDataTable(...)` 执行 SQL 语句; - Service 需实现 `ITransientDependency` / `IScopedDependency` 标记接口,由框架自动注册,方便 Controller 构造注入调用。 参考 `Services/DemoService.cs`(已存在的示例): ```csharp using MAX.Core.DependencyInjection; using StoreHub.Framework.Repositorys; namespace StoreHub.KJDWebService.Services { public class DemoService : ITransientDependency { private readonly SqlRepository _sqlRepository; public DemoService(SqlRepository sqlRepository) { _sqlRepository = sqlRepository; } public bool Test() { _sqlRepository.Ado.ExecuteDataTable("select * from user"); return true; } public bool Test2() { return true; } } } ``` `SqlRepository`(`StoreHub.Framework/Repositorys/SqlRepository.cs`): ```csharp public class SqlRepository : IScopedDependency { public IAdo Ado { get; } public SqlRepository([FromKeyedServices("KJDDB")] IFreeSql fsql) { Ado = fsql.Ado; } } ``` ### 3.2 服务注册机制 框架(`StoreHub.Framework/DependencyInjection/ServiceCollectionExtensions.cs`)自动扫描并注册所有实现以下标记接口的类型: | 标记接口 | 生命周期 | | --- | --- | | `ISingletonDependency` | Singleton | | `IScopedDependency` | Scoped | | `ITransientDependency` | Transient | 因此:**Manage 对应的 Service 只需实现上述任一标记接口即可被自动注册,无需手写 DI 代码**。Controller 构造函数直接注入 Service 即可。 ### 3.3 Controller 规范 参考现有 `Controllers/Login.cs`: ```csharp [ApiController] [ApiVersion("1.0")] [Route("api/v{version:apiVersion}/[controller]/[action]")] public class Login : ControllerBase { private readonly DemoService _userService; public Login(DemoService userService) { _userService = userService; } [HttpGet] public ApiResult Loinout() { _userService.Login(); return ApiResult.Success(true); } } ``` > ⚠️ 迁移注意:旧接口路由是 `/{前缀}/{方法名}`(如 `/user/user_login`),为保持路由一致,**每个业务 Controller 应使用旧前缀路由**(见第 5.3 节),而不是统一 `api/v1/[controller]/[action]`。是否保留 `api/v{version}` 前缀需与调用方确认。 --- ## 4. 旧项目 → 新项目映射规则 | 旧项目 | 新项目 | 说明 | | --- | --- | --- | | `*.asmx` / `*.asmx.cs`(20 个) | `Controllers/*Controller.cs` | asmx 的每个 `[WebMethod]` → Controller 的一个 Action | | `BLL/*/*Manage.cs`(23 个) | `Services/*Service.cs` | Manage 类 → Service 类,注入 SqlRepository | | `Global.asax.cs` 中的 URL 重写规则 | Controller 的 Route 特性 | 路由保持与旧地址一致 | | `Model/*.cs`(47 个) | `Models/` | **照抄,不做任何改动** | | `Utils/*.cs`(含 Extension、Helper) | `Commons/` | 照搬(公共工具) | | `Web.config` 连接串/业务配置 | `appsettings.json` | `ConnKJD` → `KJDConnection`(keyed `KJDDB`)等 | | `MysqlUtils`(MySql.Data) | `_sqlRepository.Ado`(FreeSql IAdo) | SQL 执行方式改为参数化(见第 7 节) | | `NaiBa.Util.*`(Extension/Helper 命名空间) | 迁移后统一到新命名空间 | 注意 `using` 同步调整 | --- ## 5. 详细迁移清单 ### 5.1 asmx 服务 → Controller 映射(共 20 个服务 / 356 个 WebMethod) | # | asmx 服务(旧) | WebMethod 数 | 依赖的 Manage | 建议 Controller(新) | | --- | --- | --- | --- | --- | | 1 | UserService.asmx | 18 | UserManage、UpdateManage(代码中为 `UpadteManage`,见风险 9.2) | UserController | | 2 | ShopService.asmx | 18 | ShopManage | ShopController | | 3 | OrderService.asmx | 8 | OrderManage | OrderController | | 4 | ProductService.asmx | 24 | ProductManage | ProductController | | 5 | ProductThirdService.asmx | 18 | ProductThirdManage | ProductThirdController | | 6 | PriceService.asmx | 8 | PriceManage | PriceController | | 7 | UpdateService.asmx | 4 | UpdateManage | UpdateController | | 8 | ToolService.asmx | 2 | ToolManage | ToolController | | 9 | BXXUserService.asmx | 18 | BXXUserManage | BXXUserController | | 10 | BXXShopService.asmx | 24 | BXXShopManage、WXShopManage | BXXShopController | | 11 | BXXProductService.asmx | 44 | BXXProductManage、WXShopManage | BXXProductController | | 12 | BXXProductThirdService.asmx | 14 | BXXProductThirdManage | BXXProductThirdController | | 13 | BXXPriceService.asmx | 12 | BXXPriceManage | BXXPriceController | | 14 | DYUserService.asmx | 11 | DYUserManage | DYUserController | | 15 | DYShopService.asmx | 13 | DYShopManage | DYShopController | | 16 | WXUserService.asmx | 22 | WXUserManage、WXOemManage | WXUserController | | 17 | WXShopService.asmx | 38 | WXShopManage、WXExpressOptimizeManage | WXShopController | | 18 | WXProductService.asmx | 34 | WXProductManage、BXXProductManage | WXProductController | | 19 | WXOrderService.asmx | 25 | WXOrderManage | WXOrderController | | 20 | WXShopRevenueStatistics.asmx | 1 | WXShopRevenueStatisticsManage | WXShopRevenueStatisticsController | > `Global.asax.cs` 中还引用了 `DYProductService`、`DYProductThirdService`、`DYPriceService`、`WXProductThirdService`、`WXPriceService` 的重写目标,但**旧项目中不存在这些 asmx 文件**(见 9.1)。 ### 5.2 Manage → Service 映射(23 个) | BLL 目录 | Manage(旧) | Service(新) | | --- | --- | --- | | BLL/KJD | UserManage.cs | UserService.cs | | BLL/KJD | ShopManage.cs | ShopService.cs | | BLL/KJD | OrderManage.cs | OrderService.cs | | BLL/KJD | ProductManage.cs | ProductService.cs | | BLL/KJD | ProductThirdManage.cs | ProductThirdService.cs | | BLL/KJD | PriceManage.cs | PriceService.cs | | BLL/KJD | UpdateManage.cs | UpdateService.cs | | BLL/KJD | ToolManage.cs | ToolService.cs | | BLL/BXX | BXXUserManage.cs | BXXUserService.cs | | BLL/BXX | BXXShopManage.cs | BXXShopService.cs | | BLL/BXX | BXXProductManage.cs | BXXProductService.cs | | BLL/BXX | BXXProductThirdManage.cs | BXXProductThirdService.cs | | BLL/BXX | BXXPriceManage.cs | BXXPriceService.cs | | BLL/DYXD | DYUserManage.cs | DYUserService.cs | | BLL/DYXD | DYShopManage.cs | DYShopService.cs | | BLL/WXXD | WXUserManage.cs | WXUserService.cs | | BLL/WXXD | WXShopManage.cs | WXShopService.cs | | BLL/WXXD | WXProductManage.cs | WXProductService.cs | | BLL/WXXD | WXOrderManage.cs | WXOrderService.cs | | BLL/WXXD | WXShopRevenueStatisticsManage.cs | WXShopRevenueStatisticsService.cs | | BLL/WXXD | WXOemManage.cs | WXOemService.cs | | BLL/WXXD | WXExpressOptimizeManage.cs | WXExpressOptimizeService.cs | | (备注) | `ProductThridManage`、`UpadteManage` | 旧代码中仅注释/疑似笔误,见 9.2 | ### 5.3 Global.asax 路由映射表(路由必须保持一致) `Global.asax.cs` 的 `Application_BeginRequest` 中,把不带 `.asmx` 的友好 URL 重写到对应的 asmx。迁移后需在 Controller 上配置等价路由: | 旧 URL 前缀 | 重写到(旧) | 建议新路由(保持一致) | | --- | --- | --- | | `/user/{method}` | UserService.asmx/{method} | `[Route("user/[action]")]` UserController | | `/shop/{method}` | ShopService.asmx/{method} | `[Route("shop/[action]")]` ShopController | | `/order/{method}` | OrderService.asmx/{method} | `[Route("order/[action]")]` OrderController | | `/product/{method}` | ProductService.asmx/{method} | `[Route("product/[action]")]` ProductController | | `/product_third/{method}` | ProductThirdService.asmx/{method} | `[Route("product_third/[action]")]` ProductThirdController | | `/price/{method}` | PriceService.asmx/{method} | `[Route("price/[action]")]` PriceController | | `/update/{method}` | UpdateService.asmx/{method} | `[Route("update/[action]")]` UpdateController | | `/bxx_user/{method}` | BXXUserService.asmx/{method} | `[Route("bxx_user/[action]")]` BXXUserController | | `/bxx_shop/{method}` | BXXShopService.asmx/{method} | `[Route("bxx_shop/[action]")]` BXXShopController | | `/bxx_product/{method}` | BXXProductService.asmx/{method} | `[Route("bxx_product/[action]")]` BXXProductController | | `/bxx_product_third/{method}` | BXXProductThirdService.asmx/{method} | `[Route("bxx_product_third/[action]")]` BXXProductThirdController | | `/bxx_price/{method}` | BXXPriceService.asmx/{method} | `[Route("bxx_price/[action]")]` BXXPriceController | | `/dyxd_user/{method}` | DYUserService.asmx/{method} | `[Route("dyxd_user/[action]")]` DYUserController | | `/dyxd_shop/{method}` | DYShopService.asmx/{method} | `[Route("dyxd_shop/[action]")]` DYShopController | | `/dyxd_product/{method}` | DYProductService.asmx(**旧项目不存在**) | 待确认 | | `/dyxd_product_third/{method}` | DYProductThirdService.asmx(**旧项目不存在**) | 待确认 | | `/dyxd_price/{method}` | DYPriceService.asmx(**旧项目不存在**) | 待确认 | | `/wxxd_user/{method}` | WXUserService.asmx/{method} | `[Route("wxxd_user/[action]")]` WXUserController | | `/wxxd_shop/{method}` | WXShopService.asmx/{method} | `[Route("wxxd_shop/[action]")]` WXShopController | | `/wxxd_product/{method}` | WXProductService.asmx/{method} | `[Route("wxxd_product/[action]")]` WXProductController | | `/wxxd_product_third/{method}` | WXProductThirdService.asmx(**旧项目不存在**) | 待确认 | | `/wxxd_price/{method}` | WXPriceService.asmx(**旧项目不存在**) | 待确认 | | `/wxxd_order/{method}` | WXOrderService.asmx/{method} | `[Route("wxxd_order/[action]")]` WXOrderController | | `/wxxd_shop_revenue_statistics/{method}` | WXShopRevenueStatistics.asmx/{method} | `[Route("wxxd_shop_revenue_statistics/[action]")]` WXShopRevenueStatisticsController | | `/ToolService.asmx/{method}`(无前缀) | 直接访问 | `[Route("ToolService/[action]")]` ToolController | > 说明:旧 URL 没有 `api/v1` 前缀。若新项目仍使用 `api/v{version}/[controller]/[action]` 全局模板,则旧调用方 URL 会变化,属于**不保持路由一致**。默认方案:给各 Controller 单独设置与旧前缀一致的路由。 ### 5.4 Model 清单(47 个,照抄) `Model/` 根目录(14 个):CommentParam、DBPriceResultParam、DBProdDbIdMapProductIdResultParam、DBProductResultParam、JSTDownProdSkuInfo、OrderParam、ProductPurchasePriceResultParam、ProductSelectResultParam、Result、SkuImgResult、TemuDBOrderInfo、UserLoginResultParam、WXAddressInfo、WxProdSecretSpuIdShopIdMapParam、WXShopDefaultAddressInfo `Model/ErpModel/`(1 个):ErpMerchantInfo `Model/PddModel/`(7 个):BuyerLoginInfo、PDDMaterialInfo、PDDProductDecorationInfo、PDDProductDetailInfo、PDDProductInfo、PDDProductPropertyInfo、PDDSizeSpecInfo `Model/ShopModel/`(4 个):JitCardCodeParam、JSTListProductSkuInfo、ShopParam、sku_img_replace `Model/WxModel/`(20 个):oem、oms_wei_external_procurement_after_sale_info、oms_wei_external_procurement_log、oms_wei_order、oms_wei_order_item、oms_wei_refund、order_item_ext、order_pdd_buyer_after_sale、order_pdd_buyer_order、user_payment_record_info、user_points_record_info、WXBuyUrlNotesInfo、WXClientProductInfoLite、WXGetEditProductInfoLite、WxOptimizeExpressInfo、WXOrderAmountInfo、WXOrderDecryptAddressInfo、wx_qualification_image_info > 规则:**所有 model 照抄**,字段、属性、类名、命名空间规则均不修改(仅按需调整命名空间以适配新项目编译)。 ### 5.5 Utils → Commons 清单(照搬) | 旧 Utils 文件 | 新位置 | 说明 | | --- | --- | --- | | EncrypUtil.cs | Commons/ | 加密工具 | | HtmlUtils.cs | Commons/ | HTML/URL 工具 | | HttpUtils.cs | Commons/ | HTTP 工具 | | JsonUtils.cs | Commons/ | JSON 工具 | | RandomUtils.cs | Commons/ | 随机数工具 | | RegexUtils.cs | Commons/ | 正则解析(Manage 大量依赖) | | TimeUtils.cs | Commons/ | 时间工具 | | Extension/(Convert、Enum、Exception、Linq、Validate) | Commons/Extension/ | 扩展方法(Manage 依赖 `NaiBa.Util.Extension`) | | Helper/(HtmlHelper、JsonHelper、ReflectionHelper、RegexHelper) | Commons/Helper/ | 辅助类 | | ApiRequestLogger.cs | (保留在旧项目或移入新项目) | 请求录制,用于迁移后回放校验 | --- ## 6. 迁移实施步骤 1. **确认新项目骨架**:`D:\Works\StoreHub\StoreHub.KJDWebService` 已存在,`Commons`、`Controllers`、`Dtos`、`Models`、`Services` 目录已建,`DemoService`、`SqlRepository`、`Startup.cs`、`Login.cs` 示例已就位。 2. **Models 迁移**:把旧 `Model/` 下 47 个文件照抄到新项目 `Models/`(保留子目录结构或平铺均可,类定义一字不改)。 3. **Commons 迁移**:把旧 `Utils/`(含 Extension、Helper)照搬进 `Commons/`,调整命名空间与 `using` 以适配 .NET 10 编译。 4. **Services 迁移**:按 5.2 表,把每个 `*Manage.cs` 转换为 `*Service.cs`: - 类实现 `ITransientDependency`; - 构造函数注入 `SqlRepository _sqlRepository`; - 方法逻辑**原样保留**(不做优化、不改算法); - 把 `new MysqlUtils(conn)` + `mySql.XXX(sql)` 的调用改为 `_sqlRepository.Ado.XXX(sql, 参数)`(见第 7 节)。 5. **Controllers 迁移**:按 5.1 表,把每个 `*.asmx.cs` 的 `[WebMethod]` 转为 Controller Action,并保留: - 请求体读取逻辑(等价于旧 `GetRequestJsonString()`:body JSON 为空时回退 QueryString); - 响应格式(旧接口 `Context.Response.Write(result)` 输出原始 JSON 字符串 `{code,msg,...}`,新接口建议 `return Content(result, "text/html;charset=utf-8")` 保持调用方兼容)。 6. **路由配置**:按 5.3 表给 Controller 配置与旧前缀一致的路由。 7. **配置迁移**:把旧 `Web.config` 的连接串与业务配置迁到宿主 `appsettings.json`(见第 8 节)。 8. **编译与验证**:编译通过后,用旧项目 `App_Data/ApiLogs/` 下录制的请求做回放校验(`ApiRequestLogger` 录制了请求与响应),逐接口比对返回结果。 --- ## 7. SQL 执行方式改造规范(改为参数方式) 旧代码示例(字符串拼接 + 手工转义): ```csharp string conn = System.Configuration.ConfigurationManager.ConnectionStrings["ConnKJD"].ConnectionString; MysqlUtils mySql = new MysqlUtils(conn); string sql = "select * from user where name = '" + name.Replace("'", "\\'") + "'"; var dt = mySql.ExecuteDataTable(sql); mySql.Close(); ``` 新代码示例(参数化,逻辑不变): ```csharp string sql = "select * from user where name = @name"; var dt = _sqlRepository.Ado.ExecuteDataTable(sql, new { name = name }); ``` MysqlUtils 方法 → FreeSql `IAdo` 对照: | 旧(MysqlUtils) | 新(_sqlRepository.Ado) | | --- | --- | | ExecuteDataTable(sql) | ExecuteDataTable(sql, 参数) | | ExecuteDataTableRow(sql) | ExecuteDataTable(sql, 参数) 取第一行 | | ExecuteRowCount(sql) | ExecuteDataTable(sql, 参数).Rows.Count 或 ExecuteScalar | | ExecuteFirst / ExecuteFirstInt / ExecuteFirstDecimal | ExecuteScalar / ExecuteDataTable | | ExecuteAffectedRows / ExecuteNonQuery | ExecuteNonQuery | | ExecuteInsertId | ExecuteInsertId / ExecuteScalar(select last_insert_id()) | | ExecuteExists | ExecuteDataTable 判断行数 | | 事务(IsUseTrans / Commit / RollBack) | IAdo 事务 API(BeginTransaction / Commit / Rollback) | | Close() | 连接池自动管理,无需手动 Close | > 规则:**只把拼接 SQL 改为参数方式,不改变 SQL 语义与业务逻辑**。 --- ## 8. 配置迁移(Web.config → appsettings.json) 旧 `Web.config` 关键配置: | 旧配置(Web.config) | 新配置(appsettings.json) | 说明 | | --- | --- | --- | | `connectionStrings: ConnKJD` | `ConnectionStrings: KJDConnection` | 框架已用 `KJDConnection` 注册 keyed `IFreeSql("KJDDB")`(见 `StoreHub.Framework/Startup.cs`) | | `connectionStrings: ConnKJDBAK` | `ConnectionStrings: KJDBAKConnection`(建议) | 备份库连接串,业务是否使用需确认 | | `connectionStrings: UpdateVersion / UpdateFileDir / UpdateNewUrl / UpdateExe / UpdateExeChk / LowVersion / LimitVersion` 等 | `appsettings.json` 自定义配置节 | 供 UpdateService 使用 | | `connectionStrings: GetReturnPackage / GetWXRecommendHotProd / ...` | 同上 | 业务开关类配置 | | `connectionStrings: IsKeepBase64UploadImgFile / UpdateWxFindOrderFileDir / UpdateWxFindOrderUrl / WxFindOrderLimitVersion / WxFindOrderLowVersion` | 同上 | 业务配置 | > 当前 `appsettings.json` 中的 `KJDConnection` 是占位值(`Server=localhost;...`),**迁移时必须替换为旧 `ConnKJD` 的真实连接串**。 --- ## 9. 风险与待确认项 1. **Global.asax 中存在指向不存在 asmx 的路由**:`/dyxd_product/`、`/dyxd_product_third/`、`/dyxd_price/`、`/wxxd_product_third/`、`/wxxd_price/` 重写目标文件(DYProductService、DYProductThirdService、DYPriceService、WXProductThirdService、WXPriceService)在旧项目中不存在。迁移时按原样保留路由定义,但需与业务确认这些前缀是否仍被调用方使用。 2. **`UserService.asmx.cs` 的 `GetUpdateInfo` 引用 `new UpadteManage()`**:旧项目中不存在 `UpadteManage` 类(疑似 `UpdateManage` 笔误),迁移时需修正为 `UpdateManage` 或与业务确认。 3. **响应格式兼容**:旧接口返回的 `{code,msg,...}` 是原始 JSON 字符串,字段名/大小写/结构必须原样保留;不要直接替换成新框架 `ApiResult` 的序列化格式,除非调用方同步升级。 4. **请求读取方式**:旧 `GetRequestJsonString()` 先读 body,body 为空时拼 QueryString;Controller 需保留等价实现。 5. **多库支持**:旧代码可能动态切换 `ConnKJD` / `ConnKJDBAK`;新框架目前只注册了 `KJDDB` 与 `UserDB` 两个 keyed IFreeSql,若业务用到其他库需在 `StoreHub.Framework/Startup.cs` 中补充注册。 6. **上传/文件接口**:`UpdateService.DownloadFile`(返回 byte[])、`Upload/` 目录文件读写等,迁移时注意 IFormFile / 静态文件映射的等价实现。 7. **`MachineKey` / 会话**:旧配置中有 `machineKey`,若接口依赖加密校验(如 `EncrypUtil.Encryption` 的 `time`/`code` 校验),仅为应用内算法,不依赖 ASP.NET 会话,可直接沿用 `EncrypUtil`。 8. **Base64 图片**:`IsKeepBase64UploadImgFile` 等业务开关需迁移,保证行为一致。 --- ## 10. 验收标准 - [ ] 新项目可编译通过(.NET 10); - [ ] 20 个 asmx 服务全部有对应 Controller,356 个 WebMethod 全部有对应 Action; - [ ] 23 个 Manage 全部有对应 Service,且均注入 SqlRepository、使用 `_sqlRepository.Ado.ExecuteDataTable`; - [ ] 所有 Model 照抄未改动; - [ ] Manage 方法内业务逻辑未做任何优化/修改; - [ ] SQL 语句全部参数化,无字符串拼接注入风险; - [ ] 所有 Service 均已注册(实现 ITransientDependency / IScopedDependency),Controller 可正常注入; - [ ] 路由与旧 URL 保持一致(含 Global.asax 全部重写规则); - [ ] 使用 `App_Data/ApiLogs/` 录制的请求做回放,新旧响应一致; - [ ] 连接串/业务配置迁移完成(KJDConnection 为真实连接串)。 --- *本文档依据旧项目源码现状与目标项目骨架生成,供迁移实施与验收使用。*