巴青农资商城

商户管理技术方案.md 20KB

商户管理 — 技术方案

依据: 《商户管理功能需求.md》v1.5
关联: 《店铺管理》《商品分类》《商品管理》功能需求 v1.3.5 / v1.3.1 / v1.3.3(doc/农资商城web/
范围: 本文以 商户模块 的数据库、接口为主;关联模块给出 表关系、Facade 契约、状态联动,不展开店铺/商品/分类/订单的完整表结构与接口。
原则: 平台 新增商户 时通过 IMerchantAccountBindService 创建首条 商户经营账号;C 端入驻经 入驻审核+公示 后由 MerchantEntryApplyService 建档并绑会员。
v1.5: 同步 sql/biz_merchant.sql 全字段 COMMENT;新增 id_card_typelegal_id_card_typereg_region_*corp_bank_account;平台企业新增仅校验法人姓名+企业名称。


1. 技术架构

选型
基础框架 RuoYi v3.9.2springboot2 分支)
数据库 MySQL 5.7.39
ORM / 权限 / 响应 MyBatis;Spring Security + @PreAuthorizeAjaxResult / TableDataInfo
文件 证照走 RuoYi CommonController 上传,库内存 URL
操作日志 增删改、改认证状态使用 @Log(RuoYi 惯例)

1.1 农资商城模块划分(包建议 com.ruoyi.agri

baqing-shop(Web 入口)
├── com.ruoyi.web.controller.agri.MerchantController
ruoyi-system(或 ruoyi-agri)
├── domain / mapper / service

1.2 业务链与数据归属

biz_merchant(商户)
    └── biz_shop(店铺,merchant_id)── shop_count 回写商户
            ├── biz_merchant_account(经营账号,同商户多店同登录名;店铺模块维护)
            ├── biz_goods_category(分类,shop_id)
            └── biz_goods(商品,shop_id + category_id)
                    └── biz_order(订单,shop_id;删商户/店时校验)
模块 核心表(关联设计用) 与商户关系
商户管理 biz_merchant
店铺管理 biz_shop merchant_id;维护 shop_count
经营账号 biz_merchant_account merchant_id入驻时商户模块插入首条;店铺模块 CRUD 其余
商品分类 biz_goods_category shop_id 间接
商品管理 biz_goods shop_id 间接;商户冻结 改商品状态
订单 biz_order 删商户/店校验 未完成订单(《订单管理技术方案》v1.0 · O10)

1.3 跨模块 Service 契约(Java Facade)

接口 提供方 调用方 方法
IMerchantShopFacade 店铺 商户 incrementShopCount / decrementShopCount与店铺写同事务
IOrderFacade 订单 商户、店铺 hasUnfinishedOrdersByMerchant(Long merchantId)
IMerchantFacade 商户 店铺 checkOpenShoplistSelectableMerchants

订单未实现前,hasUnfinishedOrdersByMerchant 可桩返回 false

1.4 状态联动(技术侧不落库自动任务)

商户事件 店铺 商品 C 端
冻结 / 注销 不可新开店;已有店 不自动 停业 状态 不变 不因商户冻结单独拦单
恢复正常 openShopCheck 通过后可开店
逻辑删除 shop_count=0

2. 数据库设计(商户模块)

2.1 本模块表

表名 说明
biz_merchant 主体资质 + 经营信息 + 认证状态 + shop_count

2.2 需求字段与库字段对照

需求(功能/C 端) 库字段 备注
证件类型 id_card_type / legal_id_card_type 1大陆身份证 2来往内地通行证
注册地址省市区 reg_region_codereg_region_name 企业
经营地址省市区 biz_region_codebiz_region_name 个人/企业
对公银行账号 corp_bank_account 企业法人
证件/营业「长期有效」 *_valid_type=2 *_valid_end 可 NULL
经营账号 biz_merchant_account.* sql/biz_shop.sql 账号表段

2.3 字段摘要

分组 关键字段
主键/状态 merchant_id, merchant_type, cert_status, cert_time, biz_complete, shop_count
个人主体 person_name, id_card_type, id_card_no, id_valid_*, id_card_front/back
企业法人 legal_*, corp_bank_account, account_permit
企业信息 company_name, credit_code, reg_region_*, license_valid_*
经营/结算 merchant_name, service_phone, biz_region_*, contact_*, bank_*, business_license

id_card_nocredit_code 入驻后不可 UPDATE;merchant_name 非空时唯一(应用层)。

2.4 计算与维护规则

biz_complete 见 §9.2 字段清单,全部有值且校验通过 → 1

cert_time 仅入驻成功、改 cert_status 时更新。

shop_count 店铺增删时 Facade 维护;见 §9.7。

2.5 DDL

完整 DDL 及逐字段 COMMENT: 见仓库 sql/biz_merchant.sql。下文仅列结构要点,不以本文为准覆盖 SQL 文件

-- 见 sql/biz_merchant.sql(含 id_card_type、reg_region_*、corp_bank_account 等 v1.5 字段)

2.6 字典(sys_dict)

dict_type
merchant_type 1个人 2企业
merchant_cert_status 0正常 1已冻结 2已注销
valid_period_type 1区间 2长期

3. 接口设计(商户模块)

路径: /agri/merchant
权限: agri:merchant:list|query|add|edit|remove|cert

3.1 接口一览

方法 路径 说明
GET /list 分页列表
GET /{merchantId} 详情
POST / 新增(主体资质 + 绑定账号,MerchantCreateDTO
GET /adminUserOptions 入驻可选平台管理员(role_key=merchant)
GET /memberOptions 入驻可选会员
PUT / 编辑
PUT /{merchantId}/certStatus 改认证状态(支持两步 confirm)
GET /{merchantId}/deleteCheck 删除预检
DELETE /{merchantIds} 逻辑删除(批量,任一条失败则整批失败
GET /selectList 开店选商户
GET /{merchantId}/openShopCheck 是否可开店
GET /{merchantId}/shops 商户下店铺简要列表

3.2 列表 GET /list

Query 说明
merchantName 模糊 merchant_name / person_name / company_name
certStatus 0/1/2
响应 rows 说明
merchantId, merchantType
unitName, merchantName 名称空→前端「待完善」
contactName, contactPhone 未完善为 null,前端显示「—」
shopCount, certStatus, certTime
canEdit cert_status∈{0,1}
canDelete 同 deleteCheck 全部通过

3.3 详情 GET /{merchantId}

字段 说明
全表字段 主体 + 经营
bizComplete 0/1
pendingBizFields 未完整时的 中文标签列表(§9.2)
allowedCertStatuses [{value,label,needConfirm,confirmMessage}](§9.1)
canEdit, canDelete
shopCount, shops [{shopId,shopName,shopStatus}]
shopListUrl /agri/shop/list?merchantName={merchantName或unitName}

3.4 新增 POST /

  • Body:MerchantCreateDTO = 主体字段(§9.3)+ 绑定字段(§9.8)。拒绝经营字段入库。
  • 平台主体校验MerchantServiceImpl.validateSubjectOnInsert):个人 仅姓名 必填;企业 仅法人姓名 + 企业名称 必填;信用代码有值才验唯一;经营信息 非必填
  • 成功:cert_status=0, cert_time=now(), biz_complete=0;同事务调用 IMerchantAccountBindService.bindAccountOnMerchantCreate
  • 过期:data.warnExpired=true, data.expiredItems=[...]仍 200 成功(绑定已执行)。
  • 响应 msg:「请尽快在编辑中完善商户经营信息后再开设店铺」。

3.4.1 管理员选项 GET /adminUserOptions

Query 说明
keyword 可选;模糊 user_name / phonenumber
响应 data[] 说明
userId, userName, nickName, phonenumber 仅含 sys_user 且拥有 role_key=merchant 的用户;最多 50 条

权限:agri:merchant:add

3.4.2 会员选项 GET /memberOptions

Query 说明
keyword 可选;模糊 user_name(会员名称)/ phonenumber
响应 data[] 说明
memberId, memberCode, nickName, mobile sys_user 且拥有 角色 membermemberId=userId;mobile 脱敏;最多 50 条

权限:agri:merchant:add

3.5 编辑 PUT /

  • 前置:cert_status∈{0,1};拒收 certStatus
  • 不可改:merchantType, idCardNo, creditCode
  • 重算 biz_completebizCompleteChanged:true 时 msg 可带「已可开设店铺」。

3.6 认证状态 PUT /{merchantId}/certStatus

两步确认协议(confirm):

confirm 行为
false 或未传 仅校验是否允许流转;不持久化;返回 code=200, data.needConfirm=true, data.confirmMessage
true 持久化并更新 cert_time

状态机见 §9.1

3.7 删除

预检 GET /{merchantId}/deleteCheck

{
  "code": 200,
  "data": {
    "canDelete": false,
    "reasons": ["存在未完成订单", "已绑定店铺数量为 2"]
  }
}

执行 DELETE /{merchantIds}

规则 说明
前置 cert_status∈{0,1};shop_count=0;无未完成订单
批量 任一条不通过 → 整批失败del_flag 均不变
前端 列表二次确认文案:「删除后不可恢复,是否继续?」

3.8 协作接口

GET /selectListdel_flag=0 AND cert_status=0 AND biz_complete=1不含 shop_count 上限条件)

GET /{id}/openShopCheckallowed + reason(经营信息未完善 / 商户已冻结 / 商户已注销 / 商户已删除)


4. 端到端关联流程

(同 v1.1:入驻 → 补全经营 → 开店 → 分类/商品;删店 → 删商户。)


5. 业务规则映射

编号 实现
R1 不设 店铺数量上限;shop_count 仅统计;selectList / openShopCheck 不校验 店数
R2 MerchantAccountBindServiceImplMerchantBindMapper
R12 BIND_SYS_USERlogin_name=user_nameadmin_name=nick_name
R13 BIND_MEMBER:追加 role_key=merchant + biz_merchant_account;见 §9.8
R3 insert cert_status=0 + cert_time
R4 update 忽略 merchantType
R5 certStatusPUT .../certStatus
R6 selectList 三条件过滤
R7 编辑校验 merchant_name 唯一;入驻校验 credit_code 唯一
R8 deleteCheck + DELETE + OrderFacade
R9 无级联改 shop/goods
R10 POST 主体白名单
R11 SQL 不更新 id_card_nocredit_code

6. 菜单权限

权限 标识
列表 agri:merchant:list
查询 agri:merchant:query
新增 agri:merchant:add
修改 agri:merchant:edit
删除 agri:merchant:remove
改认证 agri:merchant:cert

7. 文档索引

文档 版本
商户管理功能需求.md v1.5
商户管理测试用例.md v1.2
店铺管理功能需求/技术方案 v1.3.5 / v1.2.5
商品分类功能需求/技术方案 v1.3.1 / v1.1
商品管理功能需求/技术方案 v1.3.3 / v1.2

8. 非本期实现

会员注册/等级运营、OCR、三方实名、导出 Excel、商户冻结自动停业。


9. 实现补充(联调契约)

9.1 认证状态机

当前 目标 允许 needConfirm confirmMessage(示例)
0 正常 1 冻结 冻结后不可新开店铺,是否继续?
0 正常 2 注销 注销后资料不可编辑,是否继续?
1 冻结 0 正常 确认恢复为正常?
1 冻结 2 注销 注销后资料不可编辑,是否继续?
2 注销 0 正常 恢复后可编辑、可开店(须经营信息完整),是否继续?
2 注销 1 冻结
同态 同态

allowedCertStatuses 由上表按当前 cert_status 过滤生成。已注销 可目标 0

9.2 经营信息完整度与 pendingBizFields

字段 key 中文标签 个人 企业
merchantName 商户名称
servicePhone 客服电话
bizRegion 经营地址
bizDetailAddress 经营详细地址
contactName 联系人姓名
contactPhone 联系人手机
contactEmail 联系人邮箱
bankName 开户银行
bankBranch 支行名称
bankAccount 银行账号
businessLicense 营业执照电子版
accountPermit 开户许可证

bizRegion 判定:biz_region_codebiz_region_name 均有值。
详情接口:pendingBizFields = 上表未填项的中文标签数组。

9.3 请求 JSON 样例

个人 — 新增 POST /(仅主体)

{
  "merchantType": "1",
  "personName": "张三",
  "idCardNo": "110101199001011234",
  "birthDate": "1990-01-01",
  "idValidType": "1",
  "idValidStart": "2020-01-01",
  "idValidEnd": "2030-01-01",
  "residenceAddress": "北京市朝阳区xxx",
  "gender": "0",
  "idCardFront": "/profile/upload/2025/01/a.jpg",
  "idCardBack": "/profile/upload/2025/01/b.jpg"
}

企业 — 新增 POST /

{
  "merchantType": "2",
  "legalName": "李四",
  "legalIdCardNo": "110101198001011234",
  "legalBirthDate": "1980-01-01",
  "legalIdValidType": "2",
  "legalIdValidStart": "2015-01-01",
  "legalIdValidEnd": null,
  "legalResidence": "北京市海淀区xxx",
  "legalGender": "0",
  "legalIdCardFront": "/profile/upload/legal_f.jpg",
  "legalIdCardBack": "/profile/upload/legal_b.jpg",
  "legalExtraPhoto": null,
  "companyName": "某某农资有限公司",
  "creditCode": "91110000MA01234567",
  "regAddress": "北京市西城区xxx",
  "companyDetailAddress": "xx大厦10层",
  "businessScope": "农资销售",
  "licenseValidType": "1",
  "licenseValidStart": "2020-01-01",
  "licenseValidEnd": "2030-12-31"
}

编辑 PUT /(个人,含经营信息片段)

{
  "merchantId": 1,
  "personName": "张三",
  "idCardNo": "110101199001011234",
  "merchantName": "张三农资店",
  "servicePhone": "400-800-1234",
  "bizRegionCode": "110105",
  "bizRegionName": "北京市/朝阳区",
  "bizDetailAddress": "xx路1号",
  "contactName": "张三",
  "contactPhone": "13800138000",
  "contactEmail": "zhang@example.com",
  "bankName": "中国工商银行",
  "bankBranch": "朝阳支行",
  "bankAccount": "622202xxxxxxxxxxxx"
}

9.4 字段校验规则(Service / JSR303)

字段 规则
idCardNo / legalIdCardNo 18 位身份证格式(含 X)
creditCode 18 位统一社会信用代码
contactPhone, servicePhone 手机或固话(项目统一正则)
contactEmail 邮箱格式
bankAccount 数字 8~30 位
idValidType=1 start、end 必填,end≥start
idValidType=2 end 可空;start 建议必填
图片 URL 非空;后缀 jpg/png/jpeg;单张 ≤5MB(与 RuoYi 上传配置一致)
主体必填 按 merchantType 分组校验(入驻/编辑共用 Validator)

9.5 业务错误文案(AjaxResult.msg

场景 msg
信用代码重复 该统一社会信用代码已入驻
证件号重复 该证件号码已入驻
商户名称重复 商户名称已存在
已注销不可编辑 商户已注销,不可编辑
不可改认证 请在查看详情中修改认证状态
删除-有店铺 请先删除该商户下全部店铺
删除-有订单 存在未完成订单,无法删除
删除-已注销 已注销商户不可删除
批量删除失败 部分商户不满足删除条件,操作已取消
状态不允许 不允许的认证状态变更
未选绑定 请选择并绑定管理员账号或会员账号
用户无 merchant 角色 所选用户未分配商户经营角色
登录名重复 经营账号登录名已存在
会员无昵称 会员昵称不能为空,无法作为经营账号管理员姓名
会员无名称 会员名称不能为空,无法作为经营账号登录名

9.8 入驻绑定请求字段(MerchantCreateDTO

字段 类型 必填 说明
bindType String SYS_USERMEMBER
bindUserId Long SYS_USER 时 ✓ sys_user.user_id(role_key=merchant)
bindMemberId Long MEMBER 时 ✓ member_id = sys_user.user_id(角色 member)

BIND_SYS_USER 落库 biz_merchant_account

login_name sys_user.user_name
admin_name sys_user.nick_name(空则 user_name
password 复制 sys_user.password 或加密初始密码

BIND_MEMBER

  1. grantRoleIfAbsent(userId, merchant)sys_user_role 追加 role_key=merchant 的角色
  2. login_name = 会员名称(user_name,必填)
  3. admin_name = 会员昵称(nick_name,必填)
  4. insert biz_merchant_accountpassword 复制会员 sys_user.password

实现类:com.ruoyi.web.modules.merchant.service.impl.MerchantAccountBindServiceImpl
SQL:mapper/merchant/MerchantBindMapper.xml

个人 — 新增 POST /(含绑定管理员)

{
  "merchantType": "1",
  "personName": "张三",
  "idCardNo": "110101199001011234",
  "birthDate": "1990-01-01",
  "idValidType": "1",
  "idValidStart": "2020-01-01",
  "idValidEnd": "2030-01-01",
  "residenceAddress": "北京市朝阳区xxx",
  "gender": "0",
  "idCardFront": "/profile/upload/2025/01/a.jpg",
  "idCardBack": "/profile/upload/2025/01/b.jpg",
  "bindType": "SYS_USER",
  "bindUserId": 10
}

个人 — 新增 POST /(含绑定会员)

{
  "merchantType": "1",
  "personName": "李四",
  "idCardNo": "110101199002021234",
  "birthDate": "1990-02-02",
  "idValidType": "1",
  "idValidStart": "2020-01-01",
  "idValidEnd": "2030-01-01",
  "residenceAddress": "北京市海淀区xxx",
  "gender": "0",
  "idCardFront": "/profile/upload/f.jpg",
  "idCardBack": "/profile/upload/b.jpg",
  "bindType": "MEMBER",
  "bindMemberId": 5,
  "sysUserInitPassword": "123456"
}

9.6 shop_count 与批量删除

店铺 Service 创建/逻辑删除店铺(同一 @Transactional)
    → insert/update biz_shop
    → IMerchantShopFacade.increment/decrementShopCount(merchantId)
  • 禁止在商户编辑接口修改 shop_count
  • 可选运维:SELECT merchant_id, shop_count FROM biz_merchantCOUNT(*) 对账任务(低频)。
  • DELETE /{merchantIds}:循环预检,任一失败立即返回,不执行部分删除。

9.7 展示脱敏(建议)

字段 列表/详情展示
idCardNo, legalIdCardNo 前3后4掩码
bankAccount 后4位
contactPhone 中间4位掩码

存储仍明文;可使用 RuoYi @Sensitive 或 VO 转换。


文档版本:v1.5 · MySQL 5.7.39 · RuoYi v3.9.2-springboot2 · 关联《商户管理功能需求.md》v1.5、《商户管理测试用例.md》v1.2