依据: 《商户管理功能需求.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_type、legal_id_card_type、reg_region_*、corp_bank_account;平台企业新增仅校验法人姓名+企业名称。
| 项 | 选型 |
|---|---|
| 基础框架 | RuoYi v3.9.2(springboot2 分支) |
| 数据库 | MySQL 5.7.39 |
| ORM / 权限 / 响应 | MyBatis;Spring Security + @PreAuthorize;AjaxResult / TableDataInfo |
| 文件 | 证照走 RuoYi CommonController 上传,库内存 URL |
| 操作日志 | 增删改、改认证状态使用 @Log(RuoYi 惯例) |
com.ruoyi.agri)baqing-shop(Web 入口)
├── com.ruoyi.web.controller.agri.MerchantController
ruoyi-system(或 ruoyi-agri)
├── domain / mapper / service
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) |
| 接口 | 提供方 | 调用方 | 方法 |
|---|---|---|---|
IMerchantShopFacade |
店铺 | 商户 | incrementShopCount / decrementShopCount(与店铺写同事务) |
IOrderFacade |
订单 | 商户、店铺 | hasUnfinishedOrdersByMerchant(Long merchantId) |
IMerchantFacade |
商户 | 店铺 | checkOpenShop、listSelectableMerchants |
订单未实现前,
hasUnfinishedOrdersByMerchant可桩返回false。
| 商户事件 | 店铺 | 商品 | C 端 |
|---|---|---|---|
| 冻结 / 注销 | 不可新开店;已有店 不自动 停业 | 状态 不变 | 不因商户冻结单独拦单 |
| 恢复正常 | openShopCheck 通过后可开店 |
— | — |
| 逻辑删除 | 须 shop_count=0 |
— | — |
| 表名 | 说明 |
|---|---|
biz_merchant |
主体资质 + 经营信息 + 认证状态 + shop_count |
| 需求(功能/C 端) | 库字段 | 备注 |
|---|---|---|
| 证件类型 | id_card_type / legal_id_card_type |
1大陆身份证 2来往内地通行证 |
| 注册地址省市区 | reg_region_code、reg_region_name |
企业 |
| 经营地址省市区 | biz_region_code、biz_region_name |
个人/企业 |
| 对公银行账号 | corp_bank_account |
企业法人 |
| 证件/营业「长期有效」 | *_valid_type=2 |
*_valid_end 可 NULL |
| 经营账号 | biz_merchant_account.* |
见 sql/biz_shop.sql 账号表段 |
| 分组 | 关键字段 |
|---|---|
| 主键/状态 | 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_no、credit_code 入驻后不可 UPDATE;merchant_name 非空时唯一(应用层)。
biz_complete: 见 §9.2 字段清单,全部有值且校验通过 → 1。
cert_time: 仅入驻成功、改 cert_status 时更新。
shop_count: 店铺增删时 Facade 维护;见 §9.7。
完整 DDL 及逐字段 COMMENT: 见仓库
sql/biz_merchant.sql。下文仅列结构要点,不以本文为准覆盖 SQL 文件。
-- 见 sql/biz_merchant.sql(含 id_card_type、reg_region_*、corp_bank_account 等 v1.5 字段)
| dict_type | 值 |
|---|---|
| merchant_type | 1个人 2企业 |
| merchant_cert_status | 0正常 1已冻结 2已注销 |
| valid_period_type | 1区间 2长期 |
路径: /agri/merchant
权限: agri:merchant:list|query|add|edit|remove|cert
| 方法 | 路径 | 说明 |
|---|---|---|
| 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 |
商户下店铺简要列表 |
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 全部通过 |
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} |
POST /MerchantCreateDTO = 主体字段(§9.3)+ 绑定字段(§9.8)。拒绝经营字段入库。MerchantServiceImpl.validateSubjectOnInsert):个人 仅姓名 必填;企业 仅法人姓名 + 企业名称 必填;信用代码有值才验唯一;经营信息 非必填。cert_status=0, cert_time=now(), biz_complete=0;同事务调用 IMerchantAccountBindService.bindAccountOnMerchantCreate。data.warnExpired=true, data.expiredItems=[...],仍 200 成功(绑定已执行)。GET /adminUserOptions| Query | 说明 |
|---|---|
| keyword | 可选;模糊 user_name / phonenumber |
| 响应 data[] | 说明 |
|---|---|
| userId, userName, nickName, phonenumber | 仅含 sys_user 且拥有 role_key=merchant 的用户;最多 50 条 |
权限:agri:merchant:add
GET /memberOptions| Query | 说明 |
|---|---|
| keyword | 可选;模糊 user_name(会员名称)/ phonenumber |
| 响应 data[] | 说明 |
|---|---|
| memberId, memberCode, nickName, mobile | sys_user 且拥有 角色 member;memberId=userId;mobile 脱敏;最多 50 条 |
权限:agri:merchant:add
PUT /cert_status∈{0,1};拒收 certStatus。merchantType, idCardNo, creditCode。biz_complete;bizCompleteChanged:true 时 msg 可带「已可开设店铺」。PUT /{merchantId}/certStatus两步确认协议(confirm):
| confirm | 行为 |
|---|---|
false 或未传 |
仅校验是否允许流转;不持久化;返回 code=200, data.needConfirm=true, data.confirmMessage |
true |
持久化并更新 cert_time |
状态机见 §9.1。
预检 GET /{merchantId}/deleteCheck
{
"code": 200,
"data": {
"canDelete": false,
"reasons": ["存在未完成订单", "已绑定店铺数量为 2"]
}
}
执行 DELETE /{merchantIds}
| 规则 | 说明 |
|---|---|
| 前置 | cert_status∈{0,1};shop_count=0;无未完成订单 |
| 批量 | 任一条不通过 → 整批失败,del_flag 均不变 |
| 前端 | 列表二次确认文案:「删除后不可恢复,是否继续?」 |
GET /selectList:del_flag=0 AND cert_status=0 AND biz_complete=1(不含 shop_count 上限条件)
GET /{id}/openShopCheck:allowed + reason(经营信息未完善 / 商户已冻结 / 商户已注销 / 商户已删除)
(同 v1.1:入驻 → 补全经营 → 开店 → 分类/商品;删店 → 删商户。)
| 编号 | 实现 |
|---|---|
| R1 | 不设 店铺数量上限;shop_count 仅统计;selectList / openShopCheck 不校验 店数 |
| R2 | MerchantAccountBindServiceImpl;MerchantBindMapper |
| R12 | BIND_SYS_USER:login_name=user_name,admin_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 | certStatus 仅 PUT .../certStatus |
| R6 | selectList 三条件过滤 |
| R7 | 编辑校验 merchant_name 唯一;入驻校验 credit_code 唯一 |
| R8 | deleteCheck + DELETE + OrderFacade |
| R9 | 无级联改 shop/goods |
| R10 | POST 主体白名单 |
| R11 | SQL 不更新 id_card_no、credit_code |
| 权限 | 标识 |
|---|---|
| 列表 | agri:merchant:list |
| 查询 | agri:merchant:query |
| 新增 | agri:merchant:add |
| 修改 | agri:merchant:edit |
| 删除 | agri:merchant:remove |
| 改认证 | agri:merchant:cert |
| 文档 | 版本 |
|---|---|
| 商户管理功能需求.md | v1.5 |
| 商户管理测试用例.md | v1.2 |
| 店铺管理功能需求/技术方案 | v1.3.5 / v1.2.5 |
| 商品分类功能需求/技术方案 | v1.3.1 / v1.1 |
| 商品管理功能需求/技术方案 | v1.3.3 / v1.2 |
会员注册/等级运营、OCR、三方实名、导出 Excel、商户冻结自动停业。
| 当前 | 目标 | 允许 | needConfirm | confirmMessage(示例) |
|---|---|---|---|---|
| 0 正常 | 1 冻结 | ✓ | 是 | 冻结后不可新开店铺,是否继续? |
| 0 正常 | 2 注销 | ✓ | 是 | 注销后资料不可编辑,是否继续? |
| 1 冻结 | 0 正常 | ✓ | 是 | 确认恢复为正常? |
| 1 冻结 | 2 注销 | ✓ | 是 | 注销后资料不可编辑,是否继续? |
| 2 注销 | 0 正常 | ✓ | 是 | 恢复后可编辑、可开店(须经营信息完整),是否继续? |
| 2 注销 | 1 冻结 | ✗ | — | — |
| 同态 | 同态 | ✗ | — | — |
allowedCertStatuses 由上表按当前 cert_status 过滤生成。已注销 仅 可目标 0。
pendingBizFields| 字段 key | 中文标签 | 个人 | 企业 |
|---|---|---|---|
| merchantName | 商户名称 | ✓ | ✓ |
| servicePhone | 客服电话 | ✓ | ✓ |
| bizRegion | 经营地址 | ✓ | ✓ |
| bizDetailAddress | 经营详细地址 | ✓ | ✓ |
| contactName | 联系人姓名 | ✓ | ✓ |
| contactPhone | 联系人手机 | ✓ | ✓ |
| contactEmail | 联系人邮箱 | ✓ | ✓ |
| bankName | 开户银行 | ✓ | ✓ |
| bankBranch | 支行名称 | ✓ | ✓ |
| bankAccount | 银行账号 | ✓ | ✓ |
| businessLicense | 营业执照电子版 | — | ✓ |
| accountPermit | 开户许可证 | — | ✓ |
bizRegion 判定:biz_region_code 与 biz_region_name 均有值。
详情接口:pendingBizFields = 上表未填项的中文标签数组。
个人 — 新增 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"
}
| 字段 | 规则 |
|---|---|
| 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) |
AjaxResult.msg)| 场景 | msg |
|---|---|
| 信用代码重复 | 该统一社会信用代码已入驻 |
| 证件号重复 | 该证件号码已入驻 |
| 商户名称重复 | 商户名称已存在 |
| 已注销不可编辑 | 商户已注销,不可编辑 |
| 不可改认证 | 请在查看详情中修改认证状态 |
| 删除-有店铺 | 请先删除该商户下全部店铺 |
| 删除-有订单 | 存在未完成订单,无法删除 |
| 删除-已注销 | 已注销商户不可删除 |
| 批量删除失败 | 部分商户不满足删除条件,操作已取消 |
| 状态不允许 | 不允许的认证状态变更 |
| 未选绑定 | 请选择并绑定管理员账号或会员账号 |
| 用户无 merchant 角色 | 所选用户未分配商户经营角色 |
| 登录名重复 | 经营账号登录名已存在 |
| 会员无昵称 | 会员昵称不能为空,无法作为经营账号管理员姓名 |
| 会员无名称 | 会员名称不能为空,无法作为经营账号登录名 |
MerchantCreateDTO)| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| bindType | String | ✓ | SYS_USER 或 MEMBER |
| 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
grantRoleIfAbsent(userId, merchant) → sys_user_role 追加 role_key=merchant 的角色login_name = 会员名称(user_name,必填)admin_name = 会员昵称(nick_name,必填)insert biz_merchant_account;password 复制会员 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"
}
shop_count 与批量删除店铺 Service 创建/逻辑删除店铺(同一 @Transactional)
→ insert/update biz_shop
→ IMerchantShopFacade.increment/decrementShopCount(merchantId)
shop_count。SELECT merchant_id, shop_count FROM biz_merchant 与 COUNT(*) 对账任务(低频)。DELETE /{merchantIds}:循环预检,任一失败立即返回,不执行部分删除。| 字段 | 列表/详情展示 |
|---|---|
| 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