依据: 平台《会员管理功能需求.md》v1.1 §2.1、《商城服务协议功能需求.md》v1.0
关联: 平台《会员管理技术方案.md》v1.3(表结构/平台端)、《商城服务协议技术方案.md》v1.0、《商品详情内页功能需求.md》v1.1(GD3 登录引导)、《关联需求分析.md》v1.6
范围: C 端 注册、登录 及注册/登录页依赖的/api/member/serviceAgreement/**;资料/地址 见同模块MemberAppController另节或「我的」专册。
原则: 无短信验证码;手机号 + 密码 + 确认密码 注册;手机号或会员名称 + 密码 登录;账号复用sys_user(member_id = user_id);Token 与平台 Token 隔离。
| 项 | 选型 |
|---|---|
| 基础框架 | RuoYi v3.9.2(springboot2 分支) |
| 数据库 | MySQL 5.7.39 |
| ORM / 响应 | MyBatis;AjaxResult(code、msg、data) |
| 密码 | BCrypt(SecurityUtils.encryptPassword);存 sys_user.password |
| C 端鉴权 | MemberWebConfig + MemberAuthInterceptor;登录成功后框架 TokenService#createToken(LoginUser)(与 SysLoginController 一致,JWT + Redis) |
| 服务协议 | IMallServiceAgreementFacade 注册/登录勾选校验 |
| 产品用语 | 技术字段 | 存储 |
|---|---|---|
| 会员名称 / 会员 ID(登录账号) | memberCode |
sys_user.user_name |
| 手机号(登录账号) | mobile |
sys_user.phonenumber |
| 系统主键(非 登录账号) | memberId |
sys_user.user_id = biz_member.member_id |
说明: 本文及 C 端 UI 所称 「会员 ID」= 会员名称(
user_name),不是 数字型member_id。
baqing-shop/src/main/java/com/ruoyi/web/modules/account/
├── controller/MemberAppController.java # POST /register、/login
├── service/impl/MemberAppServiceImpl.java
├── support/MemberRegistrationSupport.java # 注册事务
├── dto/MemberRegisterDTO、MemberLoginDTO
└── mapper/BizMemberMapper.xml # selectByMobile、selectByMemberCode
content 模块:
└── controller/MallServiceAgreementAppController.java # GET /api/member/serviceAgreement
【注册 · 无验证码】
用户输入 mobile + password + confirmPassword [+ memberCode 可选]
→ 协议勾选(若启用)
→ 校验两次密码一致、手机号未注册
→ insertUser(role=member;user_name=memberCode)+ insert biz_member
→ 返回 memberId(系统主键)
【登录】
用户输入 account(手机号 或 会员名称)+ password
→ 协议勾选(协议启用时)
→ 解析 account → selectByMobile / selectByMemberCode
→ status=0、密码匹配
→ createLoginUser + TokenService.createToken(同 SysLoginController)
→ 返回根级 token + memberId
| 关联模块 | 协作 |
|---|---|
| 商城服务协议 | 注册/登录页拉协议;agreementAccepted |
| 商品详情 / 购物车 | 未登录加购 → 跳转本模块注册/登录 |
| 平台会员管理 | 平台禁用 → C 端 login 失败 |
复用平台会员表,不新建表。权威 DDL:sql/biz_member.sql。
| 表 | 注册/登录相关字段 |
|---|---|
sys_user |
user_name(会员名称,登录账号)、phonenumber(手机号)、password、status |
biz_member |
member_id(系统主键,非 C 端登录账号)、register_time |
sys_user_role |
注册时绑定 role_key=member |
| 规则 | 说明 |
|---|---|
| M2 | 手机号 del_flag=0 唯一 |
| 会员名称 | user_name 唯一(checkUserNameUnique) |
| M4 | status=1 禁止登录 |
基路径: /api/member
| 路径 | 鉴权 |
|---|---|
POST /register、POST /login |
@Anonymous;不 经过 MemberAuthInterceptor,不 写入 MemberContext |
GET /serviceAgreement、/serviceAgreement/status |
@Anonymous |
/profile、/address/** |
须 Token(非本文) |
MemberContext 约定(v1.4):
POST /login → 仅返回 token + memberId;不在本请求内 MemberContext.setMemberId
后续请求 → MemberAuthInterceptor 解析 Token → MemberContext.setMemberId(userId)
请求结束 → afterCompletion → MemberContext.clear()
MemberWebConfig(与实现对齐):
registry.addInterceptor(memberAuthInterceptor)
.addPathPatterns("/api/member/**", "/api/merchant/entry/**",
"/api/shop/*/follow", "/api/cart/**", "/api/checkout/**", "/api/order/**")
.excludePathPatterns("/api/member/register", "/api/member/login", "/api/member/sms/send",
"/api/member/serviceAgreement", "/api/member/serviceAgreement/**",
"/api/merchant/entry/agreement", "/api/merchant/entry/status");
| 方法 | 路径 | 说明 | 状态 |
|---|---|---|---|
| POST | /register |
手机号 + 密码 + 确认密码注册 | 已实现 |
| POST | /login |
手机号或 会员名称 + 密码登录 | 已实现 |
| GET | /serviceAgreement |
买家服务协议 | 已实现 |
| GET | /serviceAgreement/status |
注册/登录开放状态 | 已实现 |
| 项 | 说明 |
|---|---|
POST /sms/send |
非本期;注册 不依赖 短信验证码 |
POST /api/member/register| Body 字段 | 必填 | 说明 |
|---|---|---|
| mobile | 是 | 11 位手机号 |
| password | 是 | 登录密码 |
| confirmPassword | 是 | 确认密码;须与 password 一致 |
| memberCode | 否 | 会员名称(user_name);未填则注册逻辑 自动生成 唯一名称 |
| nickName | 否 | 昵称 |
| agreementAccepted | 是* | 协议 启用 时须 true |
流程:
validateRegister(mobile、password、confirmPassword 非空;两次密码一致)
→ assertAcceptedForRegister
→ countByMobile(已注册 → 失败)
→ MemberRegistrationSupport.createMember(无 verifyCode)
| 场景 | msg |
|---|---|
| 缺手机号 | 请输入手机号 |
| 缺密码 | 请输入密码 |
| 缺确认密码 | 请输入确认密码 |
| 两次密码不一致 | 两次输入的密码不一致 |
| 手机号已注册 | 手机号已注册 |
| 协议未勾选 | Facade 统一文案 |
| 注册关闭 | 会员注册暂未开放 |
成功 data: memberId(long,系统主键;登录请使用 手机号 或注册的 会员名称)。
请求示例:
{
"mobile": "13800138000",
"password": "Pass1234",
"confirmPassword": "Pass1234",
"memberCode": "农资买家001",
"agreementAccepted": true
}
POST /api/member/login| Body 字段 | 必填 | 说明 |
|---|---|---|
| account | 是 | 手机号 或 会员名称(user_name / memberCode) |
| password | 是 | 登录密码 |
| agreementAccepted | 条件 | 协议 启用 时须 true;未启用 忽略(SP4) |
兼容: 若请求仍传
mobile且account为空,Service 等同account=mobile(旧客户端过渡)。
account 解析规则:
trim(account)
→ 若匹配大陆手机号格式(11 位、1 开头)→ selectByMobile
→ 否则 → selectByMemberCode(精确匹配 sys_user.user_name)
→ 未命中 → 统一提示「账号或密码错误」
流程:
校验 account、password 非空
→ assertAcceptedForLogin
→ resolveMemberByAccount
→ status=0;matchesPassword
→ createToken
→ 返回根级 token + memberId(不写 MemberContext;见 §3.1)
成功响应(与平台 /login 一致,token 在根级):
{
"code": 200,
"msg": "操作成功",
"token": "eyJhbGciOiJIUzUxMiJ9...",
"memberId": 1001
}
memberId为系统主键;后续请求 Header:Authorization: Bearer {token}(token.header配置,默认 30 分钟有效,不足 20 分钟自动续期)。
| 场景 | msg |
|---|---|
| 缺账号/密码 | 请输入账号和密码 |
| 账号不存在或密码错 | 账号或密码错误 |
| 禁用 | 账号已禁用,请联系客服 |
请求示例(手机号):
{
"account": "13800138000",
"password": "Pass1234",
"agreementAccepted": true
}
请求示例(会员名称 / 产品所称会员 ID):
{
"account": "农资买家001",
"password": "Pass1234"
}
详述见《商城服务协议技术方案.md》§4。
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /api/member/serviceAgreement |
协议正文 + registrationOpen / requireAgreementOnLogin |
| GET | /api/member/serviceAgreement/status |
仅拉开关 |
MemberAppServiceImpl.register
→ 校验 password === confirmPassword
→ 不调用 IMemberSmsService.verifyCode
MemberAppServiceImpl.login
→ resolveMemberByAccount(account)
→ SysUser + UserDetailsServiceImpl.createLoginUser
→ TokenService.createToken(loginUser)
| 编号 | 规则 |
|---|---|
| RL1 | 注册 仅需手机号 + 密码 + 确认密码;无 短信验证码 |
| RL2 | 确认密码与密码 必须一致 |
| RL3 | 登录账号 = 手机号 或 会员名称(user_name);产品 「会员 ID」即会员名称 |
| RL4 | 登录失败 统一 提示「账号或密码错误」 |
| RL5 | 禁用会员 不可 登录 |
| RL6 | 协议启用时注册/登录须 勾选 agreementAccepted |
| RL7 | 与商户经营账号 分表,互不影响(M7) |
| RL8 | 响应中的 memberId 为 系统主键;不可 当作登录账号 |
| 场景 | 调用 |
|---|---|
| 注册/登录页初始化 | GET /api/member/serviceAgreement |
| 注册提交 | POST /register(mobile、password、confirmPassword、agreementAccepted) |
| 登录提交 | POST /login(account、password) |
| 登录成功 | 存根级 token;Header Authorization: Bearer {token} |
| 跳转资料/地址 | 登录后调 /profile、/address/* |
注册页表单:
手机号 → 密码 → 确认密码 → [会员名称 可选] → [协议勾选] → 注册
(无「获取验证码」步骤)
登录页表单:
账号(手机号或会员名称/会员ID)→ 密码 → [协议勾选] → 登录
| 项 | 状态 |
|---|---|
POST /register(无验证码 + confirmPassword) |
已实现 |
POST /login(account:手机号/会员名称) |
已实现 |
BizMemberMapper.selectByMemberCode |
已实现 |
POST /sms/send |
保留代码、非本期 |
| C 端注册/登录 前端 | 已实现(见《会员注册登录前端技术方案.md》v1.1,含根级 token 解析) |
| 项 | 说明 |
|---|---|
| 短信验证码注册/登录 | 明确不做 |
以数字 member_id 作为登录账号 |
不做 |
| 微信/支付宝 OAuth | — |
| 忘记密码 / 换绑手机 | — |
| 文档 | 关系 |
|---|---|
| 平台《会员管理技术方案》 | 表结构、平台端、Facade |
| 《商城服务协议技术方案》 | 协议接口 |
| 版本 | 说明 |
|---|---|
| v1.0 | 首版(含短信验证码) |
| v1.1 | 取消短信验证码;注册 confirmPassword;登录 account |
| v1.2 | 会员 ID = 会员名称(user_name);登录 selectByMemberCode;非 数字 member_id |
| v1.3 | Token 与平台一致:TokenService + LoginUser;响应根级 token |
| v1.4 | 明确:/login 不 设置 MemberContext;MemberWebConfig 拦截 /api/merchant/entry/**(排除 agreement/status) |
文档版本:v1.4 · MySQL 5.7.39 · RuoYi v3.9.2-springboot2