Files
2026-10-29 16:34:25 +08:00

204 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Store.Shops 迁移说明
统一由 `Controllers/ShopController.cs` 暴露47个业务Action:微信39个、拼多多与微店8个。原 `bxx_shop` 独立控制器及重复的vdian别名已移除,原 `ShopController` 已重命名合并。
旧逻辑来自 `KJD_WebService/BLL/BXX/BXXShopManage.cs`、`BLL/WXXD/WXShopManage.cs` 和 `BLL/WXXD/WXExpressOptimizeManage.cs`。
微信店铺沿用现有实体、续费查询和被 Store.Users 引用的命名空间,补充旧表已有的 `never_expires` 字段。
下文先介绍拼多多/微店接口,微信完整映射和部署要求见末节。
模块内部类型、文件及方法统一不带平台前缀,后续平台通过模块区分。核心服务为 `ShopService`;扩展查询、详情响应和映射分别为 `ShopQueryRequest`、`ShopDetailsInfo`、`ShopDetailsMapper`。`PddLoginShopEntity` 表示导入的拼多多登录记录,`PddShopEntity` 保留 bxx 店铺映射。
原带平台标记的 Action 统一为 `SavePddShop`、`UpdatePddShopLogin`、`DeletePddShop`、`GetPddShops`、`GetTokens`、`GetShopToken`,客户端需同步更新路由。数据库映射、上游协议及已部署的 `Shops:E57WxAccounts` 配置键保持兼容。
## 仓储查询入口
当前仓储不使用内存缓存或 Redis。单店到期时间经 `ShopQueryService → IShopRepository.GetExpireTime` 读取;列表到期信息经 `ShopService → IShopRepository.GetExpireTimes` 一次批量查询数据库,不逐店查询。
空集合返回空结果,列表缺失到期记录按 0 处理,永久有效店铺由 Service 覆盖到期时间。列表的数据范围和跨月额度重置仍在 Service。续费沿用原有事务内 ORM 读写,不包含缓存失效管道。
本次只集中上述公共查询,没有将模块内全部 SQL 迁入 Repository。后续确需缓存时,再按查询的实时性要求实现,并覆盖事务提交后的失效路径。
## 调用方式
所有接口使用当前宿主认证,路由为 `POST /api/v1/shop/{Action}`,Action采用PascalCase业务命名。
不再注册 `bxx_shop`、`wxxd_shop` 路由或旧下划线Action别名,客户端需按下表更新URL。
合并仅调整HTTP入口:读取bxx库的授权资料、店铺列表和采购备注仍使用原业务服务、权限及表映射;微店业务一并保留。
请求为 JSON DTO,字段使用 camelCase;不再发送旧版 `time/code/password/obj` 参数,也不再接受旧 GET 调用。
这次重写不是旧 ASMX 的无改动替换,客户端需迁移认证、URL、JSON 参数和返回值解析。
集合参数改为 JSON 数组,不再拼接逗号或 SQL 引号。
| 映射中的旧 action | 当前 action | 请求 |
|---|---|---|
| get_pdd_client_info | GetPddClients | 无业务参数 |
| get_shop_client_info | GetPddShopClients | ShopClientSearchRequest |
| e57_get_pdd_shop_token | GetPddShopToken | PddTokenRequest |
| admin_get_shop_list | GetAllPddShops | ShopSearchRequest |
| add_weidian_shop、add_vdian | AddWeidianShop | WeidianSaveRequest |
| mdy_weidian_shop、mdy_vdian | UpdateWeidianShop | WeidianSaveRequest |
| mdy_weidian_shop_cookie、mdy_vdian_cookie | UpdateWeidianShopLogin | WeidianCookieRequest |
| del_weidian_shop | DeleteWeidianShop | ShopIdRequest |
`vdian` 的三个历史别名统一使用上述微店Action,不再单独暴露重复接口。
`GetAllPddShops` 查询bxx.shop;`GetPddShops` 查询wxxd.pdd_shop,两套数据不混用。
业务成功与失败由现有 ApiResult 管道包装,HTTP 200;无数据成功返回 `data: null`。
列表位于 `data.items`,总数位于 `data.total`。
e57 响应仅提取上游成功响应的 `data`,使用本系统的 code,不嵌套上游结果封套。
认证、路由等 Action 外的错误继续使用宿主现有处理机制。
新增或重复导入微店示例:
```json
{
"loginName": "店铺登录名",
"shopId": "店铺ID",
"shopAccount": "店铺名称",
"notes": "备注"
}
```
更新时 `shopId` 定位原店铺,`newShopId` 修改店铺 ID;
可选字段的 null/省略表示保留原值,空串表示清空。
相同账号、登录名及店铺 ID 的重复新增转为更新。未指定店铺 ID 且登录名匹配多店时拒绝操作,防止批量误改。
Cookie 更新须提供 Cookie 或登录状态;不允许仅凭 wdToken 更新。
## 权限与行为
- 管理列表要求当前账户的数据库角色为 SuperAdmin,保留原查询条件、稳定分页和每月 300 条额度重置。
- 凭据查询仅可读取本人或明确分配给本人的店铺;管理员角色本身不扩大凭据范围。
- 微店创建归属当前账号;本人或分配账号可更新,只有所有者能删除、修改分配账号。
- `sub_user_name` 按逗号分隔的完整账号匹配,避免 owner 匹配 owner2。旧库若使用其他分隔格式,需先转换为该格式;账号之间不要带空格。
- 管理列表不返回登录密码、Cookie、令牌。专用应用配置、店铺授权及令牌接口按其用途返回凭据,均禁止 HTTP 缓存。
- 应用配置接口沿用已登录用户获取公共客户端配置的用途;不可把只允许服务端使用的其他应用密钥填入该配置。
- 分配账号不能更改店铺分配关系。不存在或无权访问的资源统一报业务失败。
- 空店铺 ID 的导入记录不创建共享额度;同一 shop_id 的登录记录共享额度。
## 部署
实体分别映射现有 `bxx.shop`、`bxx.shop_api_auth`、`bxx.shop_add_prod_count`、`weidian.shop`。
不自动创建或修改生产表,不迁移数据。使用现有 IFreeSql 连接,部署账号需要访问上述库。
额度和导入操作使用同一个 FreeSql 工作单元及 Serializable 隔离,失败整体回滚。
生产表应为 InnoDB;`shop_add_prod_count.shop_id` 应有唯一约束(旧实现的 ON DUPLICATE KEY UPDATE 已依赖该约束)。
建议确认微店 `(user_name, login_name, shop_id)` 与店铺 ID 查询索引,减少范围锁扫描。
按数据库原字段保留月份额度语义;跨年同月识别需要另行升级字段,未在本次改变。
通过部署配置提供 `Shops:PddClients` 数组;不要把真实 ClientSecret 提交到源码。
可使用环境变量:
```text
Shops__PddClients__0__Describe
Shops__PddClients__0__ClientId
Shops__PddClients__0__ClientSecret
Shops__PddClients__0__MarketId
Shops__PddClients__0__RedirectUri
Shops__PddClients__0__IsCurrent
```
没有完整应用配置时接口返回明确业务错误。旧源码中的硬编码密钥未复制。
e57 使用旧实现的固定 HTTPS 地址,正常验证证书、编码 mallId,失败最多重试一次,每次超时 30 秒;
请求取消直接传递。未发送真实外部请求。
模块服务沿用 ITransientDependency;Startup 注册共享 PddTokenClient。API 通过既有程序集发现加载控制器。
## 验证
```powershell
dotnet test Tests/Store.Shops.Tests/Store.Shops.Tests.csproj
dotnet test Tests/Store.Users.Tests/Store.Users.Tests.csproj
dotnet build Store.API/Store.API.csproj
dotnet run --project Store.API/Store.API.csproj
```
店铺测试使用独立临时 SQLite 库和模拟 HTTP 上游,覆盖归属/分配权限、精确账号匹配、分页、重复导入、部分更新、Cookie、删除、额度重置与回滚、上游重试,以及真实 MVC 的路由、参数绑定、统一返回和匿名拒绝。
SQLite 不验证 MySQL 范围锁、并发死锁或生产表实际结构;上线前仍需针对真实 MySQL 和 e57 联调。
可继续使用代码审查检查迁移边界和客户端适配。
## 微信业务Action映射
微信39项业务已合入ShopController,使用 `POST /api/v1/shop/{Action}`;下表保留旧名字仅用于客户端迁移对照。
旧 GET/ASMX 参数、签名和密码认证不再使用;客户端使用当前宿主令牌与 JSON DTO,字段 camelCase,批量编号传数组。
无数据操作统一 ApiResult;分页结果为 `data.items/data.total`,支持 NotCount 的请求返回 total=-1 表示未统计。
所有响应禁用缓存。通用列表和登录名查询不输出密码、Cookie;专用 Cookie/令牌接口检查资源范围后返回必要凭据。
| 旧Action | 当前Action | 请求DTO | 业务 |
|---|---|---|---|
| `get_shop_list` | `GetShops` | `ShopQueryRequest` | 查询本人或直属主账号分配的微信店铺 |
| `get_shop_erp_merchant` | `GetErpMerchants` | `ErpRequest` | 查询授权店铺的ERP商户余额 |
| `get_one_login_name_shop` | `GetShopsByLoginName` | `LoginRequest` | 查询登录名下的授权店铺 |
| `get_one_shop_cookie_by_app_id` | `GetShopCookies` | `ShopIdsRequest` | 批量读取授权店铺Cookie |
| `add_shop` | `AddShop` | `ShopSaveRequest` | 主账号新增微信店铺,重复记录拒绝创建 |
| `del_shop` | `DeleteShop` | `LoginRequest` | 删除本人所属的店铺记录 |
| `mdy_shop` | `UpdateShop` | `ShopSaveRequest` | 部分更新授权微信店铺 |
| `mdy_shop_cookie_and_login_status` | `UpdateShopLogin` | `CookieRequest` | 按登录名刷新授权店铺Cookie与状态 |
| `admin_get_shop_list` | `GetAllShops` | `ShopQueryRequest` | 管理员分页查询全部店铺 |
| `sub_sa_get_select_prod_shop` | `GetSelectionShops` | `ShopQueryRequest` | 管理员按选品操作员查询店铺并刷新月度额度 |
| `update_add_prod_remaining` | `UpdateShopQuota` | `QuotaRequest` | 管理员批量设置店铺上传剩余额度 |
| `admin_mdy_shop` | `AdminUpdateShop` | `ShopSaveRequest` | 管理员修改指定账号的店铺 |
| `admin_add_shop` | `AdminAddShop` | `ShopSaveRequest` | 管理员为指定账号新增店铺 |
| `admin_del_shop` | `AdminDeleteShop` | `LoginRequest` | 管理员删除指定账号店铺 |
| `set_shop_sub_sa` | `AssignShopOperator` | `AssignRequest` | 管理员批量分配或解除选品操作员 |
| `add_waybill` | `AddWaybill` | `WaybillAddRequest` | 向当前账号导入未使用运单 |
| `del_waybill` | `DeleteWaybills` | `WaybillDeleteRequest` | 删除本人尚未预占的运单 |
| `get_waybill` | `GetWaybills` | `WaybillSearchRequest` | 分页查询本人运单 |
| `get_waybill_no_user_name` | `GetAllWaybills` | `WaybillSearchRequest` | 管理员按订单跨账号查询运单 |
| `select_and_set_waybill` | `SelectAndUseWaybill` | `WaybillSelectRequest` | 在同一事务中选择并确认使用运单 |
| `select_waybill` | `ReserveWaybill` | `WaybillSelectRequest` | 为授权订单预占运单,优先匹配绑定订单 |
| `update_used_waybill` | `UpdateWaybillUsage` | `WaybillUseRequest` | 确认使用或释放本人预占运单 |
| `add_mdy_express_optimize_rst` | `SaveExpressOptimizeResult` | `OptimizeResultRequest` | 保存授权订单物流优化结果 |
| `get_express_optimize_rst` | `GetExpressOptimizeResults` | `OrderSearchRequest` | 查询授权店铺物流优化结果 |
| `add_mdy_express_optimize_remark` | `SaveExpressOptimizeRemark` | `OptimizeRemarkRequest` | 保存或清空授权订单物流备注 |
| `get_express_optimize_remark` | `GetExpressOptimizeRemarks` | `OrderSearchRequest` | 查询授权店铺物流备注 |
| `wx_add_mdy_shop_default_address` | `SaveShopDefaultAddress` | `AddressRequest` | 维护本人的店铺默认售后地址 |
| `wx_get_shop_default_address_list` | `GetShopDefaultAddresses` | `AddressSearchRequest` | 查询本人的店铺默认地址 |
| `wx_del_shop_default_address` | `DeleteShopDefaultAddresses` | `ShopIdsRequest` | 批量删除本人的默认地址 |
| `wx_add_mdy_order_address` | `SaveOrderAddress` | `OrderAddressRequest` | 保存授权店铺订单解密地址 |
| `wx_get_order_address` | `GetOrderAddresses` | `OrderSearchRequest` | 查询授权店铺订单解密地址 |
| `wx_add_mdy_buy_url_notes` | `SaveBuyUrlNote` | `NoteRequest` | 维护当前主账号共享采购备注 |
| `wx_get_buy_url_notes` | `GetBuyUrlNotes` | `NoteSearchRequest` | 查询当前主账号共享采购备注 |
| `wx_add_mdy_29wx_pdd_shop` | `SavePddShop` | `PddSaveRequest` | 导入或更新微信业务下的拼多多店铺资料 |
| `wx_mdy_29wx_pdd_cookie_and_login_status` | `UpdatePddShopLogin` | `CookieRequest` | 刷新微信侧拼多多店铺Cookie与状态 |
| `wx_del_29wx_pdd_shop` | `DeletePddShop` | `LoginRequest` | 删除本人微信侧拼多多店铺 |
| `wx_get_29wx_pdd_shop` | `GetPddShops` | `ShopSearchRequest` | 查询授权微信侧拼多多店铺 |
| `e57_get_wx_tokens` | `GetTokens` | `TokenRequest` | 管理员读取共享微信令牌列表缓存 |
| `e57_get_wx_shop_token` | `GetShopToken` | `ShopIdRequest` | 获取授权微信店铺的e57令牌 |
### 权限与兼容性说明
- 普通主账号查看自己的微信店铺;子账号必须同时满足父账号归属和完整账号分配匹配。账号列表用英文逗号分隔、不含空格。
- AdminAddShop、AdminUpdateShop、AdminDeleteShop、GetAllShops、选品店铺查询、设置额度和操作员、跨账号运单查询、共享令牌列表要求数据库 SuperAdmin 角色。
- 管理员的普通店铺、地址、单店令牌入口仍按本人的店铺范围处理;管理操作显式使用对应管理Action。
- 管理员新增/修改/删除须传 shopUserName;普通新增仅允许主账号。微信重复新增报错;微信侧拼多多重复导入更新资料,并保留已有 Cookie。
- 微信更新不传 shopId 时更新该登录名下授权记录;null 保留原值、空串清空。只有主账号或管理员可调整分配关系,分配对象须为直属子账号。
- 同登录名 Cookie 更新始终限制账号范围,修复旧版跨账号同名更新问题。GetShopCookies 改传 shopIds 数组,包含无权访问的店铺时整次拒绝。
- 默认地址仍按当前用户分别保存;订单解密地址和物流结果先限制可见店铺;采购备注由同一主账号及直属子账号共享。物流结果/备注保存必须传 shopId,不能凭订单号跨店覆盖。
- ERP 商户只能通过授权店铺的 ERP 关联读取;公司账号展开其管理账号。结果按余额降序和记录 ID 稳定分页,total 使用真实数量,替代旧版特殊负数占位。
- 普通店铺列表补充积分到期信息,never_expires 保留旧版有效期展示值 1893427200 且不判过期。isShowExpire 保留为兼容字段,旧实现同样未用于裁剪结果。
- 选品模式查询两张选品表,保留每月300条共享额度重置;普通列表不触发额度重置。月份仍沿用旧字段,尚不区分跨年同月。
### 运单状态与事务
- status=0 未使用、1 预占、2 已正式使用;只允许删除本人 status=0 的记录。
- 导入要求物流公司编码、名称、运单号,且揽收时间与绑定订单至少提供一个;运单号全库去重。
- ReserveWaybill 优先选取绑定当前订单的运单,否则仅从订单日期次日揽收、未绑定的库存随机选取;缺少订单时间时不会随意占用普通库存。
- SelectAndUseWaybill 在同一事务内分配并使用;普通库存不要求揽收时间匹配,但不会抢占绑定其他订单的运单。
- 分配时立即保存 shop_id,重复请求复用本人同订单已预占/使用记录;同订单不能绑定其他店铺。确认使用是幂等操作,释放只允许 status=1,不允许释放已使用运单。
- 事务、行锁和条件更新共同保护状态变更;取消或失败释放工作单元并回滚。保留旧业务的数据库处理范围,旧版 TODO 中未实现的 e57 发货提交不被伪装为已调用。
### 微信部署依赖与令牌配置
除现有 wxxd.shop、wxxd.user_points_shop_expire 外,读取/写入以下旧表,不自动创建或同步结构:
- wxxd.shop_add_prod_count、product_select、third_product_select、pdd_shop、wx_token。
- wxxd.wx_shop_default_address、order_decrypt_address、express_optimize、express_optimize_rst、express_optimize_remark。
- bxx.product_buy_url_notes;erp.o_shop、o_shop_group、o_user_account。
相关库需由现有 IFreeSql 连接访问。写表应使用 InnoDB;核对 shop_id、user_name/login_name/shop_id、waybill_id、user_name/order_id/status 及业务键查询索引,避免 Serializable 范围锁扩大扫描。
保留旧表已有的唯一约束;本次不执行任何生产 DDL。旧的无 shop_id 物流结果记录需要补齐真实归属后才会出现在普通用户查询中。
共享微信令牌缓存每60分钟刷新一次,e57账号由部署配置提供,例如环境变量 `Shops__E57WxAccounts__1` 对应 dbId=1;多个分组分别配置各自 ID。
不再硬编码旧测试账号。单店令牌复用 e57 HTTPS info?appId= 接口;共享列表使用 list?userName=,只接受 code=0 的结果。
缓存写入使用实际 dbId,同时更新 mdy_time;有效缓存可直接读取,过期或损坏缓存刷新失败时返回业务错误。
现有 PddTokenClient 扩展为共享 e57 客户端,保留拼多多入口签名和已有注册方式,不新增网络依赖。
### 本次验证边界
按用户要求,本次合并不新增、不运行测试;仅同步已有HTTP测试的路由、Action和服务注册,进行模块/API构建、Action命名及diff检查。
以前执行的店铺测试不能作为此次路由变更后的测试结论。
未连接真实 MySQL 或 e57;数据库结构、范围锁并发以及客户端调用仍需部署环境联调。可继续进行代码审查。