巴青农资商城

商城服务协议技术方案.md 13KB

商城服务协议 — 技术方案

依据: 《商城服务协议功能需求.md》v1.0
关联: 《会员管理技术方案.md》v1.0.1、《商城入驻协议技术方案.md》v1.0、《首页banner设置技术方案.md》v1.0、《关联需求分析.md》v1.2
范围: 全平台 单条 买家服务协议配置;平台查看/保存;C 端只读拉取(注册/登录场景); 会员/订单表 FK。
原则: 单表单行(config_id=1);启用时正文必填;C 端 @Anonymous;与入驻协议 同构分表SP4 区分注册开放与登录是否须勾选。


1. 技术架构

选型
基础框架 RuoYi v3.9.2springboot2 分支)
数据库 MySQL 5.7.39
ORM / 权限 / 响应 MyBatis;@PreAuthorizeAjaxResult
富文本 库字段 LONGTEXTAgreementContentSupport 判空
日志 保存配置 @Log

1.1 模块落位

baqing-shop
├── com.ruoyi.web.modules.content
│   ├── controller.MallServiceAgreementController           # 平台单页配置
│   ├── controller.MallServiceAgreementAppController        # C 端只读
│   ├── domain.BizMallServiceAgreement
│   ├── mapper.BizMallServiceAgreementMapper
│   ├── service.IMallServiceAgreementService
│   ├── constant.MallServiceAgreementConstants
│   ├── facade.IMallServiceAgreementFacade
│   ├── facade.impl.MallServiceAgreementFacadeImpl
│   └── support.AgreementContentSupport                     # 与入驻协议共用
sql/
└── biz_mall_service_agreement.sql

入驻协议Banner 同属 content 包;独立表 biz_mall_service_agreement

1.2 业务链

biz_mall_service_agreement(config_id=1 · 单例)
        │
        ├── 平台 GET/PUT /agri/mallServiceAgreement
        │
        ├── C 端 GET /api/member/serviceAgreement(Anonymous)
        │       ├── registrationOpen=false → 不可完成新注册
        │       └── requireAgreementOnLogin=false → 登录页不展示勾选(SP4)
        │
        └── 会员模块 POST /api/member/register | /login
                └── Facade 校验 agreementAccepted(见 §4.4、§5)
模块 关系
会员管理 register / login 注入 IMallServiceAgreementFacadebiz_member 结构
入驻协议 表、接口路径 独立;用户须 分别勾选(SP7)
商户/店铺/订单 无依赖;改协议不级联(SP10)

1.3 跨模块契约

接口 提供方 调用方 方法
IMallServiceAgreementFacade 本模块 MemberAppController / MemberServiceImpl isRegistrationOpen() — 同 isEnabled()
isAgreementRequiredOnLogin() — 同 isEnabled()
assertAcceptedForRegister(boolean agreementAccepted)
assertAcceptedForLogin(boolean agreementAccepted)
getCheckboxLabel()
场景 Facade 行为
注册 !isRegistrationOpen() →「会员注册暂未开放」;未勾选 →「请先阅读并同意商城服务协议」
登录 !isAgreementRequiredOnLogin()跳过 协议校验;已启用且未勾选 → 同上协议文案

1.4 状态联动

事件 biz_mall_service_agreement biz_member
保存协议/改启用 更新本表 不变
会员禁用/启用 不变 协议配置

2. 数据库设计

2.1 本模块表

表名 说明
biz_mall_service_agreement 商城服务协议 单例配置(买家会员注册/登录)

2.2 字段

字段 类型 说明
config_id bigint PK 固定 1CONFIG_ID=1
agreement_title varchar(128) 协议标题;非空
version_label varchar(32) 版本号;可空
content longtext 富文本 HTML;启用时非空
enable_flag char(1) 01 是;默认 0
create_by, create_time, update_by, update_time, remark RuoYi 审计

del_flag 多版本行。

2.3 单例约定

规则 实现
仅一行 WHERE config_id = 1
首次保存 无记录 INSERT;有则 UPDATE
禁止 DELETE 不提供删除接口

2.4 DDL

脚本:sql/biz_mall_service_agreement.sql

CREATE TABLE `biz_mall_service_agreement` (
  `config_id` bigint(20) NOT NULL COMMENT '固定为1',
  `agreement_title` varchar(128) NOT NULL DEFAULT '' COMMENT '协议标题',
  `version_label` varchar(32) DEFAULT NULL COMMENT '版本号',
  `content` longtext COMMENT '富文本正文',
  `enable_flag` char(1) NOT NULL DEFAULT '0' COMMENT '0否1是',
  `create_by` varchar(64) DEFAULT '',
  `create_time` datetime DEFAULT NULL,
  `update_by` varchar(64) DEFAULT '',
  `update_time` datetime DEFAULT NULL,
  `remark` varchar(500) DEFAULT NULL,
  PRIMARY KEY (`config_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商城服务协议配置';

INSERT INTO `biz_mall_service_agreement` (
  `config_id`, `agreement_title`, `version_label`, `content`, `enable_flag`, `create_time`
)
SELECT 1, '农资商城用户服务协议', 'v1.0', '', '0', NOW()
FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM `biz_mall_service_agreement` WHERE `config_id` = 1);

2.5 字典

dict_type 用途
sys_yes_no enable_flag 平台页展示

3. 平台端接口设计

基路径: /agri/mallServiceAgreement
权限: agri:mallServiceAgreement:query | edit

3.1 接口一览

方法 路径 权限 说明
GET / query 获取当前配置
PUT / edit 保存配置

3.2 获取配置 GET /

响应 data

{
  "configId": 1,
  "agreementTitle": "农资商城用户服务协议",
  "versionLabel": "v1.0",
  "content": "<p>协议正文...</p>",
  "enableFlag": "1",
  "enableFlagLabel": "是",
  "updateBy": "admin",
  "updateTime": "2026-05-26 10:00:00"
}
场景 行为
表无记录 configId=1;标题/内容空;enableFlag=0

3.3 保存配置 PUT /

{
  "agreementTitle": "农资商城用户服务协议",
  "versionLabel": "v1.0",
  "content": "<p>正文 HTML</p>",
  "enableFlag": "1",
  "remark": ""
}
字段 校验
agreementTitle 非空,≤128
versionLabel 可空,≤32
enableFlag 0 / 1
content enableFlag=1 时非空且 isBlankHtml=false;上限 512KB(应用层)
enableFlag=0 content 可空

3.4 错误文案(Service)

场景 msg
标题空 请输入协议标题
启用但内容空 启用状态下请填写协议内容
内容仅空白标签 协议内容不能为空
启用参数非法 是否启用参数无效

4. C 端接口设计

基路径: /api/member/serviceAgreement
鉴权: @Anonymous

4.1 获取服务协议 GET /api/member/serviceAgreement

说明
数据源 config_id=1
缓存 本期 不做 Redis

协议已启用且正文有效(isEnabled()=true):

{
  "code": 200,
  "data": {
    "enabled": true,
    "registrationOpen": true,
    "requireAgreementOnLogin": true,
    "agreementTitle": "农资商城用户服务协议",
    "versionLabel": "v1.0",
    "content": "<p>HTML...</p>",
    "checkboxLabel": "我已阅读并同意《农资商城用户服务协议》(v1.0)"
  }
}

协议未启用或正文无效(SP4):

{
  "code": 200,
  "data": {
    "enabled": false,
    "registrationOpen": false,
    "requireAgreementOnLogin": false,
    "message": "会员注册暂未开放"
  }
}
字段 含义
enabled 是否对外展示 完整协议正文+勾选(与 isEnabled() 一致)
registrationOpen 是否允许 新会员注册(本期 = isEnabled()
requireAgreementOnLogin 登录提交前是否 必须勾选(本期 = isEnabled();禁用时 false
message 未开放时提示;不返回 content

checkboxLabel 规则(与入驻协议一致):

我已阅读并同意《{agreementTitle}》
若 versionLabel 非空 → 我已阅读并同意《{agreementTitle}》({versionLabel})

4.2 开放状态 GET /api/member/serviceAgreement/status

{
  "code": 200,
  "data": {
    "registrationOpen": true,
    "requireAgreementOnLogin": true
  }
}

供 C 端注册/登录页 单独拉取开关(可与 §4.1 合并,由前端只调 §4.1)。

4.3 与入驻协议路径区分

接口 用途
GET /api/member/serviceAgreement 买家 服务协议(本模块)
GET /api/merchant/entry/agreement 商家入驻 协议

4.4 会员注册/登录对接(会员模块扩展)

在《会员管理技术方案》C 端接口上增加 Body 字段(实现期):

接口 新增字段 校验
POST /api/member/register agreementAccepted assertAcceptedForRegister(true)
POST /api/member/login agreementAccepted assertAcceptedForLogin(...) — 协议未启用时 忽略 该字段

会员 status、密码、验证码等校验 先于或并行于 协议校验,顺序以实现为准;协议失败 不写入 biz_member


5. Service 要点

getConfig()                    → 平台 VO
saveConfig(row)                → validate → insert/update(1)
getForApp()                    → 拼 enabled / registrationOpen / requireAgreementOnLogin / checkboxLabel
isEnabled()                    → enable_flag=1 && !isBlankHtml(content)
isRegistrationOpen()           → isEnabled()
isAgreementRequiredOnLogin()   → isEnabled()
assertAcceptedForRegister(b)   → !open → 会员注册暂未开放;!b → 请先阅读并同意商城服务协议
assertAcceptedForLogin(b)      → !require → return;!b → 请先阅读并同意商城服务协议

5.1 富文本与安全

实现
存储 原样存 content
空内容 AgreementContentSupport.isBlankHtml
共用 biz_merchant_entry_agreement 同一 Support 类

6. 业务规则映射

编号 实现
SP1 config_id=1 单表一行
SP2 MallServiceAgreementController + 菜单权限
SP3 assertAcceptedForRegister / assertAcceptedForLogin
SP4 requireAgreementOnLogin=falseassertAcceptedForLogin 直接 return
SP5 saveConfig 启用时校验 content
SP6 无历史表
SP7 /api/member/serviceAgreement vs /api/merchant/entry/agreement
SP8 商户/平台后台 不引用 Facade
SP9 无发布步骤
SP10 无级联 UPDATE 会员/订单等

7. 菜单权限

菜单(建议) 权限标识
商城服务协议 agri:mallServiceAgreement:query
保存 agri:mallServiceAgreement:edit

上级:农资管理 → 内容管理(与入驻协议、Banner 并列)。


8. 关联方案摘要

【平台】PUT /agri/mallServiceAgreement
    ↓
【C 端】GET /api/member/serviceAgreement
    ↓
【C 端】POST /api/member/register  + agreementAccepted
【C 端】POST /api/member/login     + agreementAccepted(协议禁用时可不传/忽略)
    ↓
【并行】已是会员 → GET /api/merchant/entry/agreement → 入驻勾选(独立)
对比 入驻协议 本模块
biz_merchant_entry_agreement biz_mall_service_agreement
平台路径 /agri/merchantEntryAgreement /agri/mallServiceAgreement
C 端路径 /api/merchant/entry/agreement /api/member/serviceAgreement
禁用后老用户 仅拦 新入驻 新注册登录不要求勾选(SP4)
Facade assertAccepted 单方法 注册/登录 分拆 两个 assert

9. 非本期实现

协议历史表、勾选存证、强制阅读时长、PDF、多语言、下单页二次协议、Redis 缓存、sys_config 存正文。


10. 文档索引

文档 版本
商城服务协议功能需求.md v1.0
商城入驻协议技术方案.md v1.0
会员管理技术方案.md v1.0.1
关联需求分析.md v1.2

文档版本:v1.0 · MySQL 5.7.39 · RuoYi v3.9.2-springboot2 · 关联《商城服务协议功能需求.md》v1.0