pangu-user-platform/docs/05-模块技术方案/会员区域字段需求技术方案.md

577 lines
16 KiB
Markdown
Raw Permalink Normal View History

# 会员区域字段需求技术方案
**版本**: v1.0
**编制人**: pangu
**编制日期**: 2026-02-05
**状态**: 待审核
---
## 一、需求概述
### 1.1 需求背景
当前会员管理系统中,会员数据缺少区域归属信息。在添加教育关系和亲子关系时,需要用户从头开始选择区域,用户体验不佳。
### 1.2 需求内容
| 序号 | 需求项 | 描述 |
|------|--------|------|
| 1 | 会员增加区域字段 | 在会员数据中记录所属区域 |
| 2 | H5注册时选择区域 | 用户注册时必须选择所在区域 |
| 3 | 弹窗区域默认值 | 添加教育关系/亲子关系时,区域默认选择会员所在区域,但允许修改 |
---
## 二、现状分析
### 2.1 数据库现状
```sql
-- pg_member 表已有 region_id 字段
CREATE TABLE `pg_member` (
...
`region_id` bigint DEFAULT NULL COMMENT '区域ID',
...
);
```
**结论**: 数据库表已具备 `region_id` 字段,无需修改表结构。
### 2.2 后端实体现状
```java
// PgMember.java 中缺少 regionId 字段
public class PgMember extends BaseEntity {
private Long memberId;
private String memberCode;
private String phone;
// ... 无 regionId 字段
}
```
**结论**: 实体类需要添加 `regionId` 字段。
### 2.3 H5注册现状
当前H5注册表单字段
- 手机号码 (phone)
- 图形验证码 (imageCaptcha)
- 短信验证码 (verificationCode)
- 密码 (password)
- 同意协议 (agreed)
**结论**: 注册流程无区域选择,需要新增。
### 2.4 弹窗组件现状
**管理后台前端**:
| 组件 | 区域选择 | 默认值 |
|------|---------|--------|
| EducationDialog (教育关系) | ✅ 有 | 无默认值 |
| StudentSelectDialog (亲子关系) | ❌ 无 | - |
**H5前端**:
| 组件 | 区域选择 | 默认值 |
|------|---------|--------|
| TeacherIdentityForm (教育身份) | ✅ 有 | 无默认值 |
| ParentChildrenForm (绑定学生) | ✅ 有 | 无默认值 |
**结论**:
- 管理后台EducationDialog 已有区域选择需添加默认值逻辑StudentSelectDialog 需添加区域筛选功能
- H5前端两个弹窗均已有区域选择只需添加默认区域逻辑
---
## 三、技术方案
### 3.1 总体架构
```
┌─────────────────────────────────────────────────────────────┐
│ H5前端 │
├─────────────────┬───────────────────────────────────────────┤
│ 注册页面 │ 添加区域选择器注册时提交regionId │
└────────┬────────┴───────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 后端API │
├─────────────────┬───────────────────────────────────────────┤
│ H5AuthController│ register接口接收regionId │
│ PgMember实体 │ 新增regionId字段 │
└────────┬────────┴───────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 管理后台前端 │
├─────────────────┬───────────────────────────────────────────┤
│ EducationDialog │ open时传入会员regionId作为默认值 │
│ StudentSelectDialog │ 新增区域筛选默认使用会员regionId │
│ MemberDialog │ 向子组件传递会员regionId │
└─────────────────┴───────────────────────────────────────────┘
```
### 3.2 详细设计
#### 3.2.1 后端修改
**文件**: `PgMember.java`
```java
// 新增字段
/**
* 区域ID
*/
private Long regionId;
```
**文件**: `H5AuthServiceImpl.java` - register方法
```java
// 修改注册逻辑接收并保存regionId
public R<LoginVo> register(RegisterDto dto) {
// ... 原有验证逻辑
PgMember member = new PgMember();
member.setPhone(dto.getPhone());
member.setPassword(encryptPassword(dto.getPassword()));
member.setRegionId(dto.getRegionId()); // 新增保存区域ID
member.setRegisterSource("2"); // H5注册
member.setRegisterTime(new Date());
// ... 保存会员
}
```
**文件**: `RegisterDto.java` (如不存在需创建)
```java
// 新增区域ID字段
private Long regionId;
```
#### 3.2.2 H5前端修改
##### 3.2.2.1 注册页面新增区域选择
**文件**: `user-front/src/views/register/index.vue`
```vue
<!-- 新增区域选择器使用el-cascader与现有弹窗风格一致 -->
<el-form-item label="所在区域" prop="regionPath" required>
<el-cascader
v-model="registerForm.regionPath"
:options="regionsData"
:props="{ value: 'regionId', label: 'regionName', children: 'children' }"
placeholder="请选择省市区"
clearable
style="width: 100%"
/>
</el-form-item>
```
```javascript
// 表单数据新增
registerForm = {
phone: '',
imageCaptcha: '',
verificationCode: '',
password: '',
agreed: false,
regionPath: [] // 新增:区域路径 [provinceId, cityId, districtId]
}
// 获取区域树(复用已有逻辑)
const regionsData = ref([])
const loadRegionsData = async () => {
const res = await request.get('/h5/region/tree')
regionsData.value = res.data || []
}
// 提交注册时传递regionId取数组最后一个元素
register({
// ... 原有字段
regionId: registerForm.regionPath[registerForm.regionPath.length - 1]
})
```
**文件**: `user-front/src/api/user.js`
```javascript
// 修改register函数添加regionId参数
export function register(data) {
return request({
url: '/h5/auth/register',
method: 'post',
data: {
phone: data.consumerName,
password: data.password,
smsCode: data.verifyCode,
captchaCode: data.captchaValue,
uuid: data.captchaUuid,
regionId: data.regionId // 新增
}
})
}
```
##### 3.2.2.2 弹窗默认区域
**文件**: `user-front/src/components/TeacherIdentityForm.vue`
```javascript
// 新增props接收默认区域
props: {
// ... 原有props
defaultRegionPath: { // 新增:默认区域路径
type: Array,
default: () => []
}
}
// 组件挂载或打开时设置默认值
watch(() => props.defaultRegionPath, (val) => {
if (val && val.length > 0 && formData.regionPath.length === 0) {
formData.regionPath = [...val]
// 触发学校加载
loadSchoolsByRegion(val[val.length - 1])
}
}, { immediate: true })
```
**文件**: `user-front/src/components/ParentChildrenForm.vue`
```javascript
// 新增props接收默认区域同上
props: {
// ... 原有props
defaultRegionPath: {
type: Array,
default: () => []
}
}
```
**文件**: `user-front/src/views/userCenter/index.vue`
```javascript
// 获取会员区域信息
const memberRegionPath = ref([])
// 加载会员信息时获取区域路径
const loadMemberInfo = async () => {
// ... 原有逻辑
if (memberInfo.regionId) {
memberRegionPath.value = await getRegionPath(memberInfo.regionId)
}
}
// 向弹窗组件传递默认区域
<teacher-identity-form
:default-region-path="memberRegionPath"
// ... 其他props
/>
<parent-children-form
:default-region-path="memberRegionPath"
// ... 其他props
/>
```
#### 3.2.3 管理后台前端修改
**文件**: `MemberDialog.vue`
```javascript
// 向子组件传递会员区域ID
const memberRegionId = ref(null)
// 编辑会员时获取区域ID
const open = async (row) => {
if (row) {
// ... 加载会员数据
memberRegionId.value = row.regionId
}
}
// 打开教育关系弹窗时传递默认区域
const handleAddEducation = () => {
educationDialogRef.value?.open(memberId.value, null, null, memberRegionId.value)
}
// 打开亲子关系弹窗时传递默认区域
const handleAddStudent = () => {
studentSelectDialogRef.value?.open({
memberId: memberId.value,
defaultRegionId: memberRegionId.value
})
}
```
**文件**: `EducationDialog.vue`
```javascript
// 修改open方法签名增加默认区域参数
const open = async (mId, row, index, defaultRegionId) => {
resetForm()
memberId.value = mId
isEdit.value = !!row
localIndex.value = index ?? null
visible.value = true
await Promise.all([loadRegionTree(), loadSubjectList()])
if (row) {
// 编辑模式:使用已有数据
// ... 原有逻辑
} else if (defaultRegionId) {
// 新增模式:使用会员默认区域
regionIds.value = await getRegionPath(defaultRegionId)
form.regionId = defaultRegionId
await loadSchoolList(defaultRegionId)
}
}
```
**文件**: `StudentSelectDialog.vue`
```vue
<!-- 新增区域筛选 -->
<el-form-item label="区域">
<el-cascader
v-model="queryParams.regionIds"
:options="regionTree"
:props="{ value: 'regionId', label: 'regionName', checkStrictly: true }"
placeholder="请选择区域"
clearable
style="width: 180px"
@change="handleRegionChange"
/>
</el-form-item>
```
```javascript
// 新增区域相关变量
const regionTree = ref([])
const defaultRegionId = ref(null)
// 修改查询参数
const queryParams = reactive({
pageNum: 1,
pageSize: 10,
studentName: '',
studentNo: '',
regionIds: [], // 新增
regionId: null // 新增
})
// 修改open方法
const open = async (options = {}) => {
memberId.value = options.memberId || null
defaultRegionId.value = options.defaultRegionId || null
excludeStudentIds.value = options.excludeIds || []
resetQuery()
// 加载区域树
await loadRegionTree()
// 设置默认区域
if (defaultRegionId.value) {
queryParams.regionIds = await getRegionPath(defaultRegionId.value)
queryParams.regionId = defaultRegionId.value
}
visible.value = true
getList()
}
// 区域变更处理
const handleRegionChange = (val) => {
queryParams.regionId = val && val.length ? val[val.length - 1] : null
handleQuery()
}
// 修改getList添加regionId参数
const getList = async () => {
const params = {
// ... 原有参数
regionId: queryParams.regionId || undefined // 新增
}
// ...
}
```
---
## 四、接口变更
### 4.1 H5注册接口
**路径**: `POST /h5/auth/register`
**请求体变更**:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| phone | String | 是 | 手机号 |
| password | String | 是 | 密码 |
| smsCode | String | 是 | 短信验证码 |
| captchaCode | String | 是 | 图形验证码 |
| uuid | String | 是 | 验证码UUID |
| **regionId** | **Long** | **是** | **区域ID新增** |
### 4.2 区域树接口H5端
**路径**: `GET /h5/region/tree`
**说明**: 如不存在需新增返回区域树结构供H5选择。
**响应体**:
```json
{
"code": 200,
"data": [
{
"regionId": 42,
"regionName": "湖北省",
"children": [
{
"regionId": 4201,
"regionName": "武汉市",
"children": [...]
}
]
}
]
}
```
### 4.3 学生列表接口
**路径**: `GET /business/student/available`
**请求参数变更**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| pageNum | Integer | 否 | 页码 |
| pageSize | Integer | 否 | 每页数量 |
| studentName | String | 否 | 学生姓名 |
| studentNo | String | 否 | 学号 |
| **regionId** | **Long** | **否** | **区域ID新增** |
---
## 五、数据库变更
### 5.1 表结构
**无变更** - `pg_member` 表已有 `region_id` 字段。
### 5.2 数据迁移
**无需迁移** - 新注册用户将自动填充区域ID历史用户区域ID保持为NULL。
---
## 六、影响范围
### 6.1 后端文件清单
| 文件 | 修改类型 | 说明 |
|------|---------|------|
| PgMember.java | 修改 | 新增regionId字段 |
| H5AuthServiceImpl.java | 修改 | register方法接收regionId |
| RegisterDto.java | 新增/修改 | 新增regionId字段 |
| H5RegionController.java | 新增 | 提供区域树接口 |
| PgStudentServiceImpl.java | 修改 | available方法支持regionId筛选 |
### 6.2 H5前端文件清单
| 文件 | 修改类型 | 说明 |
|------|---------|------|
| views/register/index.vue | 修改 | 新增区域选择器 |
| api/user.js | 修改 | register函数添加regionId |
| components/TeacherIdentityForm.vue | 修改 | 新增defaultRegionPath prop |
| components/ParentChildrenForm.vue | 修改 | 新增defaultRegionPath prop |
| views/userCenter/index.vue | 修改 | 获取会员区域并传递给弹窗 |
### 6.3 管理后台前端文件清单
| 文件 | 修改类型 | 说明 |
|------|---------|------|
| MemberDialog.vue | 修改 | 传递memberRegionId给子组件 |
| EducationDialog.vue | 修改 | open方法增加defaultRegionId参数 |
| StudentSelectDialog.vue | 修改 | 新增区域筛选功能 |
---
## 七、测试要点
### 7.1 功能测试
| 测试项 | 测试步骤 | 预期结果 |
|--------|---------|---------|
| H5注册-区域必填 | 不选区域直接注册 | 提示"请选择所在区域" |
| H5注册-区域保存 | 选择区域后注册 | 会员数据包含正确的regionId |
| H5教育身份-默认区域 | 有区域的会员打开"添加教育身份"弹窗 | 区域字段自动选中会员所在区域 |
| H5教育身份-可修改 | 修改默认选中的区域 | 可正常切换到其他区域,学校列表刷新 |
| H5绑定学生-默认区域 | 有区域的会员打开"绑定学生"弹窗 | 区域字段自动选中会员所在区域 |
| H5绑定学生-可修改 | 修改默认选中的区域 | 可正常切换到其他区域,学校列表刷新 |
| 管理后台-教育关系默认区域 | 为有区域的会员添加任教信息 | 区域字段自动选中会员所在区域 |
| 管理后台-亲子关系区域筛选 | 为有区域的会员添加亲子关系 | 区域筛选默认选中会员所在区域 |
### 7.2 兼容性测试
| 测试项 | 测试步骤 | 预期结果 |
|--------|---------|---------|
| 历史会员-无区域(H5) | 无区域会员打开"添加教育身份"弹窗 | 区域字段为空,需手动选择 |
| 历史会员-无区域(管理后台) | 为无区域的会员添加任教信息 | 区域字段为空,需手动选择 |
| 历史会员-弹窗正常 | 打开历史会员的各类弹窗 | 弹窗正常显示,无报错 |
---
## 八、风险评估
| 风险项 | 风险等级 | 应对措施 |
|--------|---------|---------|
| H5注册流程变更 | 🟡 中 | 充分测试,确保注册流程顺畅 |
| 历史数据无区域 | 🟢 低 | 兼容处理,无区域时默认值为空 |
| 弹窗组件改动 | 🟢 低 | 保持向后兼容,参数可选 |
---
## 九、工作量估算
| 模块 | 工作项 | 估算 |
|------|--------|------|
| 后端 | PgMember实体修改 | 0.5h |
| 后端 | 注册接口修改 | 1h |
| 后端 | 区域树接口H5端如不存在 | 1h |
| 后端 | 学生列表接口修改 | 0.5h |
| H5前端 | 注册页面区域选择 | 1.5h |
| H5前端 | TeacherIdentityForm默认区域 | 1h |
| H5前端 | ParentChildrenForm默认区域 | 1h |
| H5前端 | userCenter传递区域 | 0.5h |
| 管理后台 | MemberDialog修改 | 0.5h |
| 管理后台 | EducationDialog修改 | 1h |
| 管理后台 | StudentSelectDialog修改 | 2h |
| 测试 | 功能测试 | 2h |
| **合计** | | **12.5h** |
---
## 十、审批记录
| 角色 | 姓名 | 意见 | 日期 |
|------|------|------|------|
| 技术负责人 | | | |
| 产品负责人 | | | |
---
*文档生成时间: 2026-02-05*