plan.md 12 KB

商家门店分管账号 Implementation Plan

功能标识022-merchant-store-subaccounts | 日期:2026-08-28 | 规格spec.md

目标:为商家主账号提供可分配多个店铺的独立分管账号,使其按授权门店操作订单、商品和退款,并将店铺通知发送给所有可接收分管账号,同时保持平台商家管理只统计主账号。

架构:分管账号复用 info_user 认证主键并使用独立 user_type=5,通过 merchant_subaccount_store 表表达多店授权。后端用集中式门店访问服务校验真实数据归属,通知通过门店接收者路由服务选择在线分管账号或主账号兜底。平台以主商家下级视图呈现分管账号,商家端新增账号管理页。

技术栈:Java 21、Spring Boot、MyBatis/MyBatis-Plus、MySQL、Redis、JUnit 5、Mockito、Vue 2、Element UI、vue-i18n。

全局约束

  • 模块依赖保持 ruoyi-admin -> ruoyi-system,系统模块不得导入 com.ruoyi.app.*
  • App Controller 使用明确 DTO、@RequestBody@RequestHeader String token 和显式查询参数,不使用 Map 入参。
  • 数据库变更只追加到 updatesql/sql.md,不直接执行。
  • 商家端和平台端新增文案同步维护简中、繁中、英文、越南语,并保持原 CRLF。
  • 保留当前工作区已有订单状态及推送调整,不覆盖无关脏文件。
  • 密码、Token、CID 和支付资料不得写入日志或响应。

技术上下文

存储info_user 增加所属主账号、主账号控制状态和最后登录时间;新增账号门店关联表。 认证:沿用商家端 JWT 与 Redis 会话,类型 5 使用商家 App/PC token 前缀。 授权:每次请求按数据库当前状态与目标实体真实 mdId 校验,不把店铺权限缓存进 Token。 推送:沿用 iOS msdstorePushEvent -> push_message,不新增 Android FCM。 兼容性:类型 1、3、4 原有业务保持;无分管账号时通知继续发主账号。

Constitution Check

  • [通过] 新功能采用独立规格、计划、数据模型、接口契约和后续任务清单。
  • [通过] 权限校验集中到服务层,Controller 仍执行显式身份和参数边界。
  • [通过] 不引入反向模块依赖,不创建第二套认证系统。
  • [通过] 支付与退款只增加授权前置校验,不改变渠道状态机。
  • [通过] SQL 仅形成迁移文档,四语种与前端换行要求进入交付检查。

项目结构与职责

后端新增

ruoyi-system/src/main/java/com/ruoyi/system/domain/MerchantSubaccountStore.java
ruoyi-system/src/main/java/com/ruoyi/system/domain/constants/MerchantAccountConstants.java
ruoyi-system/src/main/java/com/ruoyi/system/mapper/MerchantSubaccountStoreMapper.java
ruoyi-system/src/main/resources/mapper/infouser/MerchantSubaccountStoreMapper.xml
ruoyi-system/src/main/java/com/ruoyi/system/service/IMerchantSubaccountStoreService.java
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/MerchantSubaccountStoreServiceImpl.java
ruoyi-system/src/main/java/com/ruoyi/system/service/MerchantStoreAccessService.java

ruoyi-admin/src/main/java/com/ruoyi/app/user/MerchantSubaccountController.java
ruoyi-admin/src/main/java/com/ruoyi/app/user/MerchantSubaccountAdminController.java
ruoyi-admin/src/main/java/com/ruoyi/app/user/MerchantSubaccountApplicationService.java
ruoyi-admin/src/main/java/com/ruoyi/app/user/dto/MerchantSubaccountCreateRequest.java
ruoyi-admin/src/main/java/com/ruoyi/app/user/dto/MerchantSubaccountUpdateRequest.java
ruoyi-admin/src/main/java/com/ruoyi/app/user/dto/MerchantSubaccountPasswordRequest.java
ruoyi-admin/src/main/java/com/ruoyi/app/user/dto/MerchantSubaccountStatusRequest.java
ruoyi-admin/src/main/java/com/ruoyi/app/user/dto/MerchantSubaccountView.java
ruoyi-admin/src/main/java/com/ruoyi/app/order/MerchantNotificationRouter.java

MerchantStoreAccessService 只处理账号、店铺和实体归属授权;MerchantSubaccountApplicationService 负责创建、状态、密码、授权事务及 Redis 会话撤销;MerchantNotificationRouter 留在 admin 模块以使用 PayPushPushEventService

后端修改

ruoyi-system/src/main/java/com/ruoyi/system/domain/InfoUser.java
ruoyi-system/src/main/resources/mapper/infouser/InfoUserMapper.xml
ruoyi-system/src/main/java/com/ruoyi/system/mapper/InfoUserMapper.java
ruoyi-admin/src/main/java/com/ruoyi/app/user/InfoUserController.java
ruoyi-admin/src/main/java/com/ruoyi/app/mendian/PosStoreController.java
ruoyi-admin/src/main/java/com/ruoyi/app/mendian/PosFoodController.java
ruoyi-admin/src/main/java/com/ruoyi/app/mendian/PosFenleiController.java
ruoyi-admin/src/main/java/com/ruoyi/app/mendian/FoodSpecController.java
ruoyi-admin/src/main/java/com/ruoyi/app/order/PosOrderShOprateController.java
ruoyi-admin/src/main/java/com/ruoyi/app/order/PosOrderController.java
ruoyi-admin/src/main/java/com/ruoyi/app/order/UserOrderController.java
ruoyi-admin/src/main/java/com/ruoyi/app/order/PosOrderQsOprateController.java
ruoyi-admin/src/main/java/com/ruoyi/app/order/DeliveryOrderNotificationService.java
ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties
ruoyi-admin/src/main/resources/i18n/messages_zh_TW.properties
ruoyi-admin/src/main/resources/i18n/messages_en_US.properties
ruoyi-admin/src/main/resources/i18n/messages_vi.properties
updatesql/sql.md

商家端

E:/QtwCode/foodie/foodie-store/src/api/subaccount.js
E:/QtwCode/foodie/foodie-store/src/views/MerchantSubaccount.vue
E:/QtwCode/foodie/foodie-store/src/router/index.js
E:/QtwCode/foodie/foodie-store/src/components/Aside.vue
E:/QtwCode/foodie/foodie-store/src/components/Header.vue
E:/QtwCode/foodie/foodie-store/src/views/AcidrollingCapacity.vue
E:/QtwCode/foodie/foodie-store/src/api/store.js
E:/QtwCode/foodie/foodie-store/src/api/food.js
E:/QtwCode/foodie/foodie-store/src/lang/zh.js
E:/QtwCode/foodie/foodie-store/src/lang/tw.js
E:/QtwCode/foodie/foodie-store/src/lang/en.js
E:/QtwCode/foodie/foodie-store/src/lang/vi.js

主账号显示分管账号菜单;类型 5 只显示订单、授权店铺和商品相关入口。店铺页面对类型 5 隐藏新增和删除,但后端仍强制校验。现有缺少 token 的店铺/商品请求补齐 isToken: true

平台端

E:/QtwCode/foodie/foodie-admin-vue/src/views/infouser/user/sjuser.vue
E:/QtwCode/foodie/foodie-admin-vue/src/api/infouser/user.js
E:/QtwCode/foodie/foodie-admin-vue/src/api/language/language.zh_CN.js
E:/QtwCode/foodie/foodie-admin-vue/src/api/language/language.zh_TW.js
E:/QtwCode/foodie/foodie-admin-vue/src/api/language/language.en_US.js
E:/QtwCode/foodie/foodie-admin-vue/src/api/language/language.vi.js

平台商家行新增“分管账号”按钮和对话框,不新增顶级菜单;只提供查看和平台启停。

实施阶段

Phase 1:账号模型与授权基础

  1. InfoUser 补齐三个字段和 MyBatis 映射,建立类型 5 常量,SQL 追加迁移语句。
  2. 建立关联实体、Mapper 和 Service,覆盖账号—店铺唯一关系与按店铺/账号查询。
  3. 实现 MerchantStoreAccessServicerequireOwnergetAccessibleStoreIdsrequireStoreAccessrequireOrderAccessrequireFoodAccess
  4. 测试主账号全店权限、分管账号多店权限、跨商家授权拒绝、停用状态和授权撤销立即生效。

Phase 2:分管账号生命周期与登录

  1. 实现商家端创建、列表、编辑、重置密码和主账号启停接口,所有写入使用事务。
  2. 实现平台下级列表和平台启停接口,严格分离两个状态字段。
  3. 扩展 shanglodeing 支持类型 5,签发 Token 前检查自身与主账号状态,更新最后登录时间。
  4. 增加商家退出接口;停用或重置密码时删除 App/PC 会话。
  5. 回归平台 userType=1 列表、导出和统计,证明类型 5 不出现、不计数、不进入审核。
  6. 扩展 @Auth 支持显式启用 Redis 会话认证;商家业务接口在认证切面统一完成 JWT 与 JTI 校验,Controller 不再调用会话服务二次认证。

Phase 3:店铺、商品和订单数据权限

  1. getmystorelist 对主账号按所有权查询,对分管账号按授权集合查询。
  2. addmendian 增加 token 并强制主账号;店铺详情、修改、营业状态和营业时间校验实际店铺。
  3. 商品、分类和规格的商家端查询与写入补齐 token;修改和删除先读取目标实体的实际 mdId 再授权。
  4. 商家订单列表按可访问 mdId 集合查询;详情、接单后操作、取消和退款在状态流转前执行订单门店授权。
  5. 对收款配置、提现、结算、跨店财务及分管账号管理入口执行主账号校验;类型 5 默认拒绝未明确开放的商家能力。

Phase 4:通知路由

  1. 实现门店通知接收者查询:有效授权、双重启用、主账号正常、App 会话有效、CID 非空。
  2. 每个账号分别发布 PushEvent;CID 去重外部推送,单账号异常隔离。
  3. 替换当前有效商家推送调用点:支付成功/订单开放、骑手接单、订单取消等均传入 mdId
  4. 无分管账号、无人具有 App 推送资格或门店异常时按规格向主账号兜底。

Phase 5:两个前端

  1. 商家端新增分管账号管理页,支持多店选择、创建、编辑、密码重置和主账号启停。
  2. 根据类型 1/5 调整菜单和店铺页面按钮,分管账号不显示新增/删除店铺及敏感模块。
  3. 平台商家列表新增下属账号对话框,显示账号、状态、在线/最近登录和负责店铺,只开放平台启停。
  4. 两端补齐四语种文案并保持 CRLF。

Phase 6:验证与交付

  1. 运行定向权限、登录、平台隔离、订单操作和通知路由测试。
  2. 使用 JDK 21 构建 ruoyi-admin 及依赖模块。
  3. 构建 foodie-storefoodie-admin-vue
  4. quickstart.md 执行越权、双重停用、通知全发和主账号兜底场景。
  5. 检查三个仓库的暂存范围、换行和差异;SQL 只保留在迁移文档。
  6. 增加认证切面定向测试,覆盖有效会话放行、Redis 会话缺失拒绝、未启用会话选项时保持现有非商家接口行为,并回归商家店铺及订单入口。

测试文件规划

ruoyi-system/src/test/java/com/ruoyi/system/service/MerchantStoreAccessServiceTest.java
ruoyi-system/src/test/java/com/ruoyi/system/mapper/MerchantSubaccountStoreMapperXmlTest.java
ruoyi-admin/src/test/java/com/ruoyi/app/user/MerchantSubaccountControllerTest.java
ruoyi-admin/src/test/java/com/ruoyi/app/user/MerchantSubaccountAdminControllerTest.java
ruoyi-admin/src/test/java/com/ruoyi/app/order/MerchantNotificationRouterTest.java
ruoyi-admin/src/test/java/com/ruoyi/app/mendian/PosFoodControllerAccessTest.java

并扩展现有:

ruoyi-admin/src/test/java/com/ruoyi/app/user/InfoUserControllerTest.java
ruoyi-admin/src/test/java/com/ruoyi/app/mendian/PosStoreControllerTest.java
ruoyi-admin/src/test/java/com/ruoyi/app/order/PosOrderShOprateControllerTest.java
ruoyi-admin/src/test/java/com/ruoyi/app/order/DeliveryOrderNotificationServiceTest.java

复杂度说明

复杂度 必要性 未采用的简单方案
info_user 类型 5 + 关联表 同时复用认证并隔离平台商家统计,支持账号与店铺多对多 类型 1 加角色容易污染所有商家查询;单独账号表复制认证体系
双重状态 平台强制停用不能被主账号覆盖 共用一个状态无法区分操作者和恢复权限
集中权限服务 多个旧 Controller 的归属规则不同,必须统一防止 IDOR 仅前端隐藏或各 Controller 手写条件容易漏改
通知路由服务 同店多接收者、消息逐账号归属、主账号兜底需要统一行为 在每个订单入口复制循环会产生规则漂移
@Auth 可选会话认证 在不改变骑手、普通用户认证行为的前提下,把商家 JWT 与 Redis 会话校验集中到 Controller 之前 全局强制 Redis 校验会扩大兼容性影响;Controller 手工调用会产生重复认证和遗漏风险