我的服务 — 技术方案(C 端)
依据: 《我的服务功能需求.md》v1.1
关联: 平台《会员管理技术方案.md》v1.3、《商户入驻审核技术方案.md》v1.2、《商城入驻协议技术方案.md》v1.0、《关联需求分析.md》v1.6;C 端《会员注册登录技术方案.md》v1.3(登录前置)
范围: C 端 个人资料、修改密码、收货地址、商家入驻申请 的后端落位、表结构、接口契约;不含 前端页面与平台审核实现细节(平台审核见入驻审核专册)。
v1.4: 已有商户管理员 仅开店铺 入驻:GET /api/merchant/entry/context;submitApply shopOnly 分支。
原则: 账号复用 sys_user(member_id = user_id);鉴权 TokenService + MemberAuthInterceptor;入驻 form_json 快照 + 平台审核链。
1. 技术架构
| 项 |
选型 |
| 基础框架 |
RuoYi v3.9.2(springboot2 分支) |
| 数据库 |
MySQL 5.7.39 |
| ORM / 响应 |
MyBatis;AjaxResult(code、msg、data) |
| 密码 |
BCrypt(SecurityUtils.encryptPassword / matchesPassword) |
| C 端鉴权 |
MemberWebConfig → MemberAuthInterceptor → TokenService.getLoginUser → MemberContext.setMemberId |
| 文件上传 |
RuoYi 通用上传(头像、证件照等);URL 写入表单字段 |
| 入驻校验 |
EntryApplyFormValidator + IMerchantEntryAgreementFacade |
1.1 模块落位
baqing-shop/src/main/java/com/ruoyi/web/modules/
├── account/ # 资料 · 地址
│ ├── controller/MemberAppController.java
│ ├── service/impl/MemberAppServiceImpl.java
│ ├── service/impl/MemberAddressServiceImpl.java
│ ├── domain/BizMember.java、BizMemberAddress.java
│ ├── vo/MemberAddressVO.java
│ ├── config/MemberWebConfig.java
│ └── support/MemberAuthInterceptor.java、MemberContext.java
├── entry/ # 商家入驻
│ ├── controller/MerchantEntryApplyAppController.java
│ ├── service/impl/MerchantEntryApplyServiceImpl.java
│ ├── dto/EntryApplySubmitDTO.java
│ ├── vo/EntryApplyMyVO.java、EntryApplyContextVO.java
│ └── support/EntryApplyFormValidator.java、EntryApplyFormMapper.java
└── content/ # 入驻协议(只读)
└── controller/MerchantEntryAgreementAppController.java
sql/
├── biz_member.sql # biz_member + biz_member_address
├── biz_merchant_entry_apply.sql
├── biz_merchant_entry_agreement.sql
└── biz_merchant_entry_form_json说明.sql # form_json 字段对照
1.2 业务链
【资料 / 密码 / 地址】
MemberAuthInterceptor
→ MemberAppController / MemberAddressService
→ sys_user(nick_name、avatar、email、sex、password…)
→ biz_member(member_id、扩展统计)
→ biz_member_address(1:N)
【商家入驻】
GET /api/merchant/entry/agreement(匿名)
GET /api/merchant/entry/context(会员 · 是否仅开店铺)
→ 新主体:填写 subject/biz/shop
→ 已有商户管理员:仅填写 shop
→ POST /api/merchant/entry/apply
→ biz_merchant_entry_apply(form_json + 冗余检索列)
→ 平台审核 / 公示 / 建档(entry 模块 · 平台 Controller)
→ biz_merchant + biz_shop + biz_merchant_account
→ biz_member_message(审核结果通知)
| 关联模块 |
协作 |
| 会员注册登录 |
登录 Token;本模块 全部须鉴权(协议接口除外) |
| 平台会员管理 |
共用表;平台 只读 资料/地址 |
| 商户入驻审核 |
C 端 写申请;平台 approve/reject/completePublicity |
| 商城入驻协议 |
assertAccepted 提交前校验 |
| 订单(待建设) |
结算页 读 biz_member_address;本模块 不写订单 |
1.3 鉴权范围
| 路径 |
鉴权 |
/api/member/register、/login、/serviceAgreement/** |
匿名(会员注册登录专册) |
/api/member/profile、/address/** |
会员 Token |
/api/member/password(建议) |
会员 Token |
/api/merchant/entry/agreement、/status |
@Anonymous |
/api/merchant/entry/context、/apply、/my |
会员 Token(MemberWebConfig → /api/merchant/entry/**,排除 agreement/status) |
MemberWebConfig 拦截范围(摘要):
.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");
/api/member/login 不在拦截器内;登录成功后客户端 带 Token 访问 /context 等接口,由 MemberAuthInterceptor 注入 MemberContext。
Header:Authorization: Bearer {token}(与平台 TokenService 一致)。
2. 数据库设计
不新建会员主表;权威 DDL 见 sql/ 目录。下列为 本模块读写 表摘要。
2.1 账号与资料(复用)
| 表 |
本模块用途 |
关键字段 |
sys_user |
资料、密码 |
user_id(= member_id)、nick_name、email、sex、avatar、password |
biz_member |
会员扩展 |
member_id(= user_id)、register_time、消费统计 |
字段映射(C 端展示):
| 产品字段 |
存储 |
可编辑 |
| 用户 ID |
member_id |
否 |
| 会员名称 |
user_name |
否(功能需求资料页未列;改名称 非本期) |
| 手机号 |
phonenumber |
否 |
| 昵称 |
nick_name |
是 |
| 头像 |
avatar |
是 |
| 邮箱 |
email |
是 |
| 性别 |
sex(0男 1女 2未知) |
是 |
| 密码 |
password |
仅 PUT /password |
更新口径: BizMemberMapper.updateAccount 按 member_id 更新 sys_user(WHERE user_id = #{memberId});不写入 biz_member。
2.2 收货地址 biz_member_address
| 字段 |
类型 |
说明 |
| address_id |
bigint PK |
自增 |
| member_id |
bigint |
FK → biz_member.member_id |
| consignee_name |
varchar(64) |
收货人 |
| mobile |
varchar(20) |
联系电话 |
| region_code |
varchar(64) |
省市区编码(区县级 code) |
| region_name |
varchar(128) |
省市区名称(省/市/区 / 拼接) |
| detail_address |
varchar(256) |
详细地址 |
| is_default |
char(1) |
0 否 1 是;同会员 至多一条 为 1 |
| del_flag |
char(1) |
0 存在 2 逻辑删除 |
| create_time / update_time |
datetime |
排序用 |
索引: idx_member (member_id, del_flag)
列表排序(已实现): ORDER BY is_default DESC, update_time DESC(默认置顶;其余按最近更新倒序)。
2.3 入驻申请 biz_merchant_entry_apply
| 字段 |
类型 |
说明 |
| apply_id |
bigint PK |
|
| apply_no |
varchar(32) UK |
业务单号 |
| apply_status |
char(1) |
0 待审 1 已完成 2 未通过 3 公示中 |
| merchant_type |
char(1) |
1 个人 2 企业 |
| member_id |
bigint |
申请人 |
| member_code |
varchar(64) |
会员名称冗余 |
| subject_label |
varchar(128) |
列表摘要(姓名/企业名) |
| contact_name / contact_phone |
varchar |
联系人 |
| id_card_no / credit_code |
varchar(32) |
唯一性校验 |
| merchant_name / shop_name |
varchar(128) |
唯一性校验 |
| form_json |
longtext |
{ subject, biz, shop, agreementAccepted, shopOnlyMode?, existingMerchantId? } 快照 |
| reject_reason |
varchar(500) |
驳回原因 |
| merchant_id / shop_id |
bigint |
公示完成回填 |
| apply_time |
datetime |
提交时间 |
| publicity_start_time / publicity_end_time |
datetime |
公示期 |
| del_flag |
char(1) |
0/2 |
索引: idx_member_status (member_id, apply_status) — 阻塞重复提交(status in 0,3)。
2.4 协作表(只读 / 下游写入)
| 表 |
关系 |
biz_merchant_entry_agreement |
C 端读协议;enable_flag=1 才开放申请 |
biz_member_message |
审核/公示/完成时 entry 模块写入 |
biz_merchant、biz_shop、biz_merchant_account |
公示完成 后由平台 Service 创建 |
2.5 ER(本模块视角)
sys_user ──1:1── biz_member ──1:N── biz_member_address
│
└──1:N── biz_merchant_entry_apply ──►(审核后)biz_merchant / biz_shop
3. 接口设计
统一响应:AjaxResult;登录接口 token 在根级(见会员注册登录专册)。
基路径: 资料/地址 /api/member;入驻 /api/merchant/entry。
3.1 个人资料
GET /api/member/profile
| 项 |
说明 |
| 鉴权 |
会员 Token |
响应 data |
建议 MemberProfileAppVO |
| 字段 |
说明 |
| memberId |
用户 ID(只读) |
| mobile |
手机号(只读) |
| memberCode |
会员名称(只读,登录账号) |
| nickName |
昵称 |
| avatar |
头像 URL |
| email |
邮箱 |
| sex |
性别 |
已实现: MemberProfileAppVO;updateAccount 写 sys_user(email/sex)。
PUT /api/member/profile
| Body |
可写字段 |
| nickName、avatar、email、sex |
更新 sys_user(user_id = member_id) |
| 不可写 |
memberId、mobile、password |
| 校验 |
说明 |
| nickName |
非空 |
| email |
格式(可空) |
Service: MemberAppServiceImpl.updateProfile → BizMemberMapper.updateAccount(sys_user:nick_name、avatar、email、sex)。
3.2 修改密码
PUT /api/member/password(建议独立接口)
| Body |
必填 |
| oldPassword |
是 |
| newPassword |
是 |
| confirmPassword |
是 |
| 流程 | |
|------|--|
| 1 | matchesPassword(oldPassword, sys_user.password) |
| 2 | newPassword === confirmPassword |
| 3 | encryptPassword(newPassword) 更新 sys_user.password |
| 失败 msg(示例) | |
|----------------|--|
| 旧密码错误 | |
| 两次输入的密码不一致 | |
现状: 密码可通过 PUT /profile 的 password 字段更新 且无旧密码校验;须迁移 至本接口以满足功能需求 MS-W1。
3.3 收货地址
| 方法 |
路径 |
说明 |
状态 |
| GET |
/address/list |
当前会员地址列表 |
已实现 |
| POST |
/address |
新增 |
已实现 |
| PUT |
/address |
修改(Body 含 addressId) |
已实现 |
| DELETE |
/address/{addressId} |
逻辑删除 |
已实现 |
| PUT |
/address/{addressId}/default |
设默认 |
已实现 |
GET /api/member/address/list
响应 data:MemberAddressVO[]
| 字段 |
说明 |
| addressId |
|
| consigneeName、mobile |
|
| regionCode、regionName、detailAddress |
|
| isDefault |
0/1 |
| fullAddress |
regionName(去 /)+ 详细地址 |
POST /api/member/address / PUT /api/member/address
| Body 字段 |
必填 |
| addressId |
PUT 必填 |
| consigneeName、mobile |
是 |
| regionCode、regionName |
是;须 成对 非空 |
| detailAddress |
是 |
| isDefault |
否;为 1 时 同事务 clearDefault |
事务规则:
isDefault = 1 → UPDATE 同 member 其他行 is_default = 0 → 写当前行
3.4 商家入驻协议(只读)
| 方法 |
路径 |
鉴权 |
说明 |
| GET |
/api/merchant/entry/agreement |
匿名 |
协议标题、版本、正文、entryOpen |
| GET |
/api/merchant/entry/status |
匿名 |
仅 entryOpen 开关 |
3.5 入驻上下文(v1.4)
GET /api/merchant/entry/context
| 项 |
说明 |
| 鉴权 |
会员 Token |
| 响应 |
EntryApplyContextVO |
| 字段 |
说明 |
| shopOnlyMode |
是否已是商户管理员 |
| canShopOnlyApply |
是否可仅填店铺提交 |
| blockReason |
不可时的原因 |
| merchantId、merchantType、subjectLabel、merchantName |
展示用 |
| subject、biz |
只读快照(BizMerchant 结构) |
canShopOnlyApply=true 条件: 商户 cert_status=正常 且 biz_complete=1。
状态: 已实现。
3.6 提交入驻申请
POST /api/merchant/entry/apply
| 项 |
说明 |
| 鉴权 |
会员 Token |
| Body |
EntryApplySubmitDTO |
{
"subject": { },
"biz": { },
"shop": { "shopName", "shopAvatar", "shopPhone", "shopDesc" },
"agreementAccepted": true
}
仅开店铺(v1.4): Body 可仅含 shop + agreementAccepted;后端 buildSubmitFromExistingMerchant 合并 DB 商户快照;form_json 写入 shopOnlyMode=true、existingMerchantId。
subject / biz 结构同 BizMerchant;字段清单见 sql/biz_merchant_entry_form_json说明.sql。
merchant_type 由 EntryApplyFormValidator.resolveMerchantType 从表单推断(1 个人 / 2 企业);仅开店铺时取自已有商户。
| 校验 |
实现 |
| 协议勾选 |
IMerchantEntryAgreementFacade.assertAccepted |
| 待审/公示中 |
countBlockingByMemberId(status in 0,3) |
| 完整字段 |
新主体:validateMobileSubmit;仅开店铺:validateShopOnlySubmit |
| 唯一性 |
证件/信用代码/merchant_name/shop_name(仅开店铺 exclude existingMerchantId) |
| 会员昵称 |
绑定经营账号路径须 nick_name 非空 |
| 商户就绪 |
仅开店铺:assertMerchantEligibleForShopOnly(正常 + biz_complete=1) |
成功 data:
{
"applyId": 1,
"applyNo": "EA202605260001",
"applyStatus": "0"
}
状态: 已实现。
3.7 我的入驻申请
GET /api/member/entry/my → 实际路径 GET /api/merchant/entry/my
| 项 |
说明 |
| 鉴权 |
会员 Token |
| 响应 |
EntryApplyMyVO[](按 apply_time 降序) |
| 字段 |
说明 |
| applyId、applyNo |
|
| applyStatus |
0/1/2/3 |
| applyTime、auditTime |
|
| rejectReason |
status=2 时有值 |
| publicityStartTime、publicityEndTime |
status=3/1 时 建议扩展 VO |
| merchantId、shopId |
status=1 回填 |
状态: 已实现(公示时间字段 可补 至 VO)。
3.8 错误码(业务 msg 示例)
| 场景 |
msg |
| 未登录 |
请先登录(401) |
| 会员不存在 |
会员不存在 |
| 地址不存在 |
地址不存在 |
| 待审申请已存在 |
常量 MSG_PENDING_EXISTS |
| 协议未勾选 |
Facade 统一文案 |
| 旧密码错误 |
旧密码错误 |
| 证件/店名重复 |
对应唯一性提示 |
| 商户经营未完善 |
商户经营信息未完善,请联系平台完善后再申请开店 |
| 商户冻结/注销 |
对应 MSG_MERCHANT_FROZEN / MSG_MERCHANT_CANCELLED |
4. Service 要点
4.1 资料
getProfile(memberId)
→ BizMemberMapper.selectById(JOIN sys_user)
→ 脱敏 password;组装 VO(含 email/sex)
updateProfile(memberId, dto)
→ 校验 nickName 等
→ updateAccount(sys_user:nick_name, avatar, email, sex)
4.2 修改密码
changePassword(memberId, old, new, confirm)
→ 校验 confirm
→ matchesPassword(old)
→ updateAccount(password=encrypt(new))
4.3 地址
addAddress / updateAddress / setDefaultAddress
@Transactional
→ validateAddress
→ 若 isDefault=1 → clearDefault(memberId)
→ insert / update / setDefault
4.4 入驻提交
submitApply(memberId, dto)
@Transactional
→ agreementFacade.assertAccepted
→ countBlockingByMemberId == 0
→ requireMember(nick_name 非空)
→ findMerchantByMemberAdmin
├── 有:validateShopOnlySubmit + buildSubmitFromExistingMerchant + assertUnique(exclude merchantId)
└── 无:validateMobileSubmit + assertUnique
→ insert biz_merchant_entry_apply(form_json + shopOnlyMode/existingMerchantId)
5. 与平台方案协作
| 平台文档 |
C 端本模块关系 |
| 《会员管理技术方案》§4 |
共用 /api/member/profile、/address/**;平台 /agri/member 只读 |
| 《商户入驻审核技术方案》§4 |
C 端 3.5~3.7 对应其 4.0~4.2;审核 4.6~4.8 仅平台 |
| 《商城入驻协议技术方案》 |
3.4 读配置;enable_flag=0 时 apply 阻断 |
| 《关联需求分析》§2.1 |
用户链账号 ≠ 经营账号;completePublicity 后 bindAccountOnMerchantCreate |
入驻状态 C 端只读流转:
0 待审 ──平台 approve──► 3 公示中 ──期满 complete──► 1 已完成
└──平台 reject──► 2 未通过(C 端展示 rejectReason,可再 submit 新单)
6. 实现状态
| 能力 |
状态 |
备注 |
鉴权 MemberAuthInterceptor + TokenService |
已实现 |
|
GET/PUT /profile(含 email/sex) |
已实现 |
MemberProfileAppVO |
PUT /password(旧密码校验) |
已实现 |
|
| 地址 CRUD + 默认互斥 |
已实现 |
|
| 入驻协议 GET |
已实现 |
|
GET /context、POST /apply shopOnly |
已实现 |
v1.4 |
POST /apply、GET /my |
已实现 |
含公示时间 |
| C 端前端 |
已实现(见《我的服务前端技术方案.md》v1.4,含 shopOnly 分步) |
|
7. 非本期
| 项 |
说明 |
| 手机号换绑 |
— |
| 资料页改会员名称 |
功能需求未列 |
| 待审申请撤销/改资料 |
驳回后重提 |
GET /api/member/message/list |
消息列表另册 |
| 入驻表单草稿保存 |
— |
8. 修订记录
| 版本 |
说明 |
| v1.4.1 |
MemberWebConfig 统一拦截 /api/merchant/entry/**;/login 不设置 MemberContext |
| v1.4 |
入驻 仅开店铺:GET /context;validateShopOnlySubmit;form_json.shopOnlyMode |
| v1.3 |
biz_member_address:province/city/district → region_code/region_name |
| v1.2 |
移除 birthday(个人资料不含出生日期) |
| v1.1 |
email/sex 存 sys_user;PUT /password;MemberProfileAppVO |
| v1.0 |
首版 |
文档版本:v1.4.1 · MySQL 5.7.39 · RuoYi v3.9.2-springboot2 · 关联《我的服务功能需求.md》v1.2