diff --git a/backend/pangu-modules/pangu-business/sql/reset_wuhan_password.sql b/backend/pangu-modules/pangu-business/sql/reset_wuhan_password.sql new file mode 100644 index 0000000..ca54d6a --- /dev/null +++ b/backend/pangu-modules/pangu-business/sql/reset_wuhan_password.sql @@ -0,0 +1,13 @@ +-- ============================================================ +-- 重置分公司用户 wuhan 的登录密码为 admin123 +-- 使用方式:在对应库中执行本脚本后,用 wuhan / admin123 登录 +-- ============================================================ + +USE `pguser-db`; + +-- 与 init 脚本中相同的 BCrypt 哈希(admin123) +UPDATE sys_user +SET password = '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2' +WHERE user_name = 'wuhan'; + +-- 若上面影响行数为 0,说明 wuhan 用户不存在,请先执行 system_init_data.sql 初始化 diff --git a/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/h5/domain/dto/H5MemberUpdateDto.java b/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/h5/domain/dto/H5MemberUpdateDto.java index 446612d..0f1b1e5 100644 --- a/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/h5/domain/dto/H5MemberUpdateDto.java +++ b/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/h5/domain/dto/H5MemberUpdateDto.java @@ -22,4 +22,7 @@ public class H5MemberUpdateDto { @Schema(description = "生日", example = "1990-01-15") private Date birthday; + + @Schema(description = "所在区域ID", example = "420102") + private Long regionId; } diff --git a/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/h5/domain/dto/H5RegisterDto.java b/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/h5/domain/dto/H5RegisterDto.java index 3edfd71..3d52699 100644 --- a/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/h5/domain/dto/H5RegisterDto.java +++ b/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/h5/domain/dto/H5RegisterDto.java @@ -36,4 +36,7 @@ public class H5RegisterDto { @NotBlank(message = "密码不能为空") @Size(min = 6, message = "密码至少6位") private String password; + + @Schema(description = "所在区域ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "420102") + private Long regionId; } diff --git a/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/h5/domain/vo/H5MemberInfoVo.java b/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/h5/domain/vo/H5MemberInfoVo.java index 7ca3de2..acd1cbf 100644 --- a/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/h5/domain/vo/H5MemberInfoVo.java +++ b/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/h5/domain/vo/H5MemberInfoVo.java @@ -39,6 +39,9 @@ public class H5MemberInfoVo { @Schema(description = "注册时间", example = "2024-01-01 12:00:00") private Date registerTime; + @Schema(description = "所在区域ID", example = "420102") + private Long regionId; + @Schema(description = "教育身份列表") private List educations; diff --git a/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/h5/service/impl/H5AuthServiceImpl.java b/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/h5/service/impl/H5AuthServiceImpl.java index 9b606a2..66542f3 100644 --- a/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/h5/service/impl/H5AuthServiceImpl.java +++ b/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/h5/service/impl/H5AuthServiceImpl.java @@ -316,6 +316,7 @@ public class H5AuthServiceImpl implements H5AuthService { member.setPassword(BCrypt.hashpw(dto.getPassword())); member.setMemberCode(generateMemberCode()); member.setNickname("user_" + dto.getPhone().substring(7)); + member.setRegionId(dto.getRegionId()); // 保存区域ID member.setRegisterSource("2"); // H5注册 member.setRegisterTime(new Date()); member.setStatus("0"); diff --git a/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/h5/service/impl/H5MemberServiceImpl.java b/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/h5/service/impl/H5MemberServiceImpl.java index 0887bed..23cc948 100644 --- a/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/h5/service/impl/H5MemberServiceImpl.java +++ b/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/h5/service/impl/H5MemberServiceImpl.java @@ -89,6 +89,7 @@ public class H5MemberServiceImpl implements H5MemberService { vo.setGender(member.getGender()); vo.setBirthday(member.getBirthday()); vo.setRegisterTime(member.getRegisterTime()); + vo.setRegionId(member.getRegionId()); // 获取教育身份列表 vo.setEducations(getEducations()); @@ -107,7 +108,7 @@ public class H5MemberServiceImpl implements H5MemberService { throw new ServiceException("会员不存在"); } - // 只允许修改昵称、性别、生日 + // 只允许修改昵称、性别、生日、区域 if (StringUtils.isNotBlank(dto.getNickname())) { member.setNickname(dto.getNickname()); } @@ -117,6 +118,9 @@ public class H5MemberServiceImpl implements H5MemberService { if (dto.getBirthday() != null) { member.setBirthday(dto.getBirthday()); } + if (dto.getRegionId() != null) { + member.setRegionId(dto.getRegionId()); + } memberMapper.updateById(member); log.info("H5会员信息修改: memberId={}", memberId); diff --git a/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/member/domain/PgMember.java b/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/member/domain/PgMember.java index 6e84e59..51399a7 100644 --- a/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/member/domain/PgMember.java +++ b/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/member/domain/PgMember.java @@ -61,6 +61,11 @@ public class PgMember extends BaseEntity { private String tenantId; + /** + * 区域ID + */ + private Long regionId; + @TableLogic private String delFlag; diff --git a/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/member/domain/dto/MemberSaveDto.java b/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/member/domain/dto/MemberSaveDto.java index c4f65db..ad44b73 100644 --- a/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/member/domain/dto/MemberSaveDto.java +++ b/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/member/domain/dto/MemberSaveDto.java @@ -37,6 +37,9 @@ public class MemberSaveDto { @Schema(description = "状态(0正常 1停用)") private String status; + @Schema(description = "所在区域ID") + private Long regionId; + @Schema(description = "教育身份列表") private List educations; diff --git a/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/member/service/impl/PgMemberServiceImpl.java b/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/member/service/impl/PgMemberServiceImpl.java index fc983d6..32c8047 100644 --- a/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/member/service/impl/PgMemberServiceImpl.java +++ b/backend/pangu-modules/pangu-business/src/main/java/org/dromara/pangu/member/service/impl/PgMemberServiceImpl.java @@ -114,6 +114,7 @@ public class PgMemberServiceImpl implements IPgMemberService { member.setGender(dto.getGender()); member.setBirthday(dto.getBirthday()); member.setStatus(dto.getStatus()); + member.setRegionId(dto.getRegionId()); insert(member); Long memberId = member.getMemberId(); @@ -148,6 +149,7 @@ public class PgMemberServiceImpl implements IPgMemberService { member.setGender(dto.getGender()); member.setBirthday(dto.getBirthday()); member.setStatus(dto.getStatus()); + member.setRegionId(dto.getRegionId()); update(member); // 注意:编辑时教育身份通过单独的接口管理,这里不处理 diff --git a/docs/05-模块技术方案/会员区域字段需求技术方案.md b/docs/05-模块技术方案/会员区域字段需求技术方案.md new file mode 100644 index 0000000..2d829de --- /dev/null +++ b/docs/05-模块技术方案/会员区域字段需求技术方案.md @@ -0,0 +1,576 @@ +# 会员区域字段需求技术方案 + +**版本**: 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 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 + + + + +``` + +```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) + } +} + +// 向弹窗组件传递默认区域 + + + +``` + +#### 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 + + + + +``` + +```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* diff --git a/docs/业务功能按钮权限统一修复方案.md b/docs/业务功能按钮权限统一修复方案.md new file mode 100644 index 0000000..0389c8f --- /dev/null +++ b/docs/业务功能按钮权限统一修复方案.md @@ -0,0 +1,112 @@ +# 业务功能按钮权限统一修复方案 + +**问题**:角色管理中已取消「新增/修改/删除」等菜单权限后,学生管理、会员管理等自建业务页面上,对应按钮仍显示且可操作,与权限配置不一致。 + +**原因**:后端接口已使用 `@SaCheckPermission` 做权限校验(无权限会 403),但前端列表/表格上的「新增」「编辑」「删除」「导入」等按钮**未做权限控制**,未使用项目已有的 `v-hasPermi` 指令,导致仅靠菜单可见性无法隐藏这些操作按钮。 + +**修复思路**:在**前端**为所有业务模块的增删改等操作按钮增加 `v-hasPermi="['权限字符']"`,与后端 `@SaCheckPermission` 使用的权限字符一致。不修改后端(后端已校验),不修改菜单/角色配置方式。 + +--- + +## 一、涉及范围 + +以下为**自建业务**页面,需统一加按钮权限控制(系统管理下用户/角色/部门等已有 `v-hasPermi`,不在此次范围): + +| 模块 | 前端路径 | 后端权限前缀 | +|------------|----------|--------------| +| 学生管理 | `views/business/student/index.vue` | `business:student:*` | +| 会员管理 | `views/business/member/index.vue` | `business:member:*` | +| 学校管理 | `views/business/school/index.vue` | `business:school:*` | +| 应用管理 | `views/business/application/index.vue` | `business:application:*` | +| 接口字典 | `views/business/apiDict/index.vue` | `business:apiDict:*` | +| 基础数据-年级 | `views/business/base/grade/index.vue` | `business:grade:*` | +| 基础数据-班级 | `views/business/base/class/index.vue` | `business:class:*` | +| 基础数据-学科 | `views/business/base/subject/index.vue` | `business:subject:*` | +| 基础数据-区域 | `views/business/base/region/index.vue` | `business:region:*` | + +--- + +## 二、各页面具体修改 + +### 1. 学生管理 `student/index.vue` + +- 工具栏「新增」:`v-hasPermi="['business:student:add']"` +- 工具栏「导入」:`v-hasPermi="['business:student:import']"` +- 表格操作「编辑」:`v-hasPermi="['business:student:edit']"` +- 表格操作「删除」:`v-hasPermi="['business:student:remove']"` + +### 2. 会员管理 `member/index.vue` + +- 工具栏「新增」:`v-hasPermi="['business:member:add']"` +- 表格操作「编辑」:`v-hasPermi="['business:member:edit']"` +- 表格操作「重置密码」:`v-hasPermi="['business:member:resetPwd']"` +- 表格操作「删除」:`v-hasPermi="['business:member:remove']"` + +### 3. 学校管理 `school/index.vue` + +- 工具栏「新增」:`v-hasPermi="['business:school:add']"` +- 表格内「编辑」:`v-hasPermi="['business:school:edit']"` +- 表格内「新增年级」:`v-hasPermi="['business:school:edit']"` +- 表格内「新增班级」:`v-hasPermi="['business:school:edit']"` +- 表格内「删除」(学校/年级/班级):`v-hasPermi="['business:school:remove']"` + +### 4. 应用管理 `application/index.vue` + +- 工具栏「新增」:`v-hasPermi="['business:application:add']"` +- 表格操作「编辑」:`v-hasPermi="['business:application:edit']"` +- 表格操作「重置密钥」:`v-hasPermi="['business:application:edit']"` +- 表格操作「删除」:`v-hasPermi="['business:application:remove']"` + +### 5. 接口字典 `apiDict/index.vue` + +- 工具栏「新增」:`v-hasPermi="['business:apiDict:add']"` +- 工具栏「批量删除」:`v-hasPermi="['business:apiDict:remove']"` +- 表格操作「编辑」:`v-hasPermi="['business:apiDict:edit']"` +- 表格操作「删除」:`v-hasPermi="['business:apiDict:remove']"` + +### 6. 基础数据 - 年级 `base/grade/index.vue` + +- 工具栏「新增」:`v-hasPermi="['business:grade:add']"` +- 表格操作「编辑」:`v-hasPermi="['business:grade:edit']"` +- 表格操作「删除」:`v-hasPermi="['business:grade:remove']"` + +### 7. 基础数据 - 班级 `base/class/index.vue` + +- 工具栏「新增」:`v-hasPermi="['business:class:add']"` +- 表格操作「编辑」:`v-hasPermi="['business:class:edit']"` +- 表格操作「删除」:`v-hasPermi="['business:class:remove']"` + +### 8. 基础数据 - 学科 `base/subject/index.vue` + +- 工具栏「新增」:`v-hasPermi="['business:subject:add']"` +- 表格操作「编辑」:`v-hasPermi="['business:subject:edit']"` +- 表格操作「删除」:`v-hasPermi="['business:subject:remove']"` + +### 9. 基础数据 - 区域 `base/region/index.vue` + +- 工具栏「新增」:`v-hasPermi="['business:region:add']"` +- 表格操作「新增下级」:`v-hasPermi="['business:region:add']"` +- 表格操作「编辑」:`v-hasPermi="['business:region:edit']"` +- 表格操作「删除」:`v-hasPermi="['business:region:remove']"` + +--- + +## 三、技术说明 + +- **指令**:使用现有 `v-hasPermi`(`directive/permission/hasPermi.js`),无权限时移除 DOM,与系统管理页面用法一致。 +- **权限字符**:与后端 `@SaCheckPermission` 完全一致,保证「角色未勾选该权限 → 前端不显示按钮、后端接口 403」。 +- **后端**:不改动,已具备接口级校验。 +- **菜单/角色**:不改动,仅依赖现有「菜单权限」配置(角色管理里勾选的子权限如「学生新增」「学生修改」等)。 + +--- + +## 四、验收方式 + +1. 使用**分公司用户**(如 wuhan)登录,在角色管理中仅保留「学生管理」下的「学生查询」,去掉「学生新增」「学生修改」「学生删除」「学生导入」。 +2. 进入学生管理页:不应出现「新增」「导入」按钮,表格操作列不应出现「编辑」「删除」。 +3. 对会员、学校、应用、接口字典、年级/班级/学科/区域做同类配置,验证对应按钮按权限显隐。 +4. 超级管理员或拥有全部权限的角色,所有按钮仍正常显示。 + +--- + +**请确认是否按此方案执行修改,同意后再进行代码改动。** diff --git a/docs/发布计划_2026-02-05.md b/docs/发布计划_2026-02-05.md new file mode 100644 index 0000000..fa022c7 --- /dev/null +++ b/docs/发布计划_2026-02-05.md @@ -0,0 +1,290 @@ +# 增量发布计划 + +**发布日期**: 2026-02-05 +**目标服务器**: 192.168.71.56 +**发布类型**: 增量发布 +**编制人**: pangu + +--- + +## 一、发布概览 + +| 项目 | 本地最新版本 | 服务器当前版本 | 待发布提交数 | +|------|-------------|---------------|-------------| +| pangu-user-platform (后端+管理后台) | add00c9 | 2026-02-03 22:29 部署 | 5 个 | +| user_authentication_center_front (H5前端) | 70fc1ad | 2026-02-03 22:04 部署 | 2 个 | + +--- + +## 二、pangu-user-platform 发布内容 + +### 2.1 待发布提交清单 + +| 序号 | Commit ID | 提交说明 | 类型 | +|------|----------|---------|------| +| 1 | add00c9 | 新增学校自动添加年级 + 修复区域层级查询 + 清理区域数据 | feat | +| 2 | 72cb666 | 年级管理增加学段字段(小学/初中/高中/中专/大学) | feat | +| 3 | 80dd406 | 新增OpenApi基础数据接口 + 学生完整数据接口 + UI文案优化 | feat | +| 4 | 1a0b75e | 同步需求与技术方案文档 | docs | +| 5 | 6027a8c | 修改后端欢迎语为盘古后台管理系统 | refactor | + +### 2.2 功能变更摘要 + +1. **学校管理优化** + - 新增学校时自动添加对应学段的年级 + - 修复选择省/市时无法显示学校的Bug(支持区域层级查询) + - 区域树默认只展开湖北省,平行显示市级 + +2. **年级管理增强** + - 新增学段字段(小学/初中/高中/中专/大学) + - 支持按学段筛选年级 + +3. **OpenAPI接口扩展** + - 新增学校/年级/班级基础数据查询接口 + - 新增学生完整数据接口(不脱敏,需特殊授权) + +4. **UI文案优化** + - "教育身份" 改为 "任教信息" + - 后端欢迎语改为 "盘古后台管理系统" + +### 2.3 数据库增量脚本 + +| 序号 | 脚本文件 | 说明 | 影响范围 | 风险等级 | +|------|---------|------|---------|---------| +| 1 | V1.0.3__open_api_dict.sql | OpenAPI接口字典数据 | pg_api_dict表 | 🟢 低 | +| 2 | V1.0.4__grade_add_stage.sql | 年级表增加学段字段 | pg_grade表结构 | 🟡 中 | +| 3 | V1.0.5__clean_region_data.sql | 清理非湖北省区域数据 | pg_region表 | 🔴 高 | + +#### 脚本详细说明 + +**V1.0.3__open_api_dict.sql** +- 新增11条API字典记录 +- 使用 `ON DUPLICATE KEY UPDATE` 幂等设计 +- 可重复执行,无副作用 + +**V1.0.4__grade_add_stage.sql** +- 为 `pg_grade` 表新增 `stage` 字段 +- 自动根据年级名称初始化学段数据 +- ⚠️ 注意:执行前需确认 `pg_grade` 表无 `stage` 字段 + +**V1.0.5__clean_region_data.sql** +- ⚠️ **高风险脚本 - 物理删除操作** +- 删除所有非湖北省的区域数据(约3309条) +- 仅保留湖北省及其下级区域(约120条) +- ⚠️ **执行前必须备份 `pg_region` 表** + +### 2.4 服务器部署路径 + +| 组件 | 路径 | +|------|------| +| 后端 JAR | /opt/pangu-user-platform/backend/pangu-admin.jar | +| 管理后台前端 | /opt/pangu-user-platform/frontend/admin-manage/ | +| 启动脚本 | /opt/pangu-user-platform/scripts/service.sh | +| 配置文件 | /opt/pangu-user-platform/backend/application-test.yml | +| 日志目录 | /opt/pangu-user-platform/backend/logs/ | + +--- + +## 三、user_authentication_center_front 发布内容 + +### 3.1 待发布提交清单 + +| 序号 | Commit ID | 提交说明 | 类型 | +|------|----------|---------|------| +| 1 | 70fc1ad | 优化登录注册页面交互和弹窗内容 | feat | +| 2 | 842d64f | 允许通过IP地址访问H5前端 | fix | + +### 3.2 功能变更摘要 + +1. **登录注册优化** + - 优化登录注册页面交互体验 + - 改进弹窗内容展示 + +2. **访问限制修复** + - 允许通过IP地址直接访问H5前端 + +### 3.3 服务器部署路径 + +| 组件 | 路径 | +|------|------| +| H5前端 | /opt/pangu-user-platform/frontend/user-front/ | + +--- + +## 四、发布步骤 + +### 4.1 发布前准备 + +```bash +# 1. 备份数据库 +ssh root@192.168.71.56 +mysqldump -h 8.148.25.55 -uroot -p pguser-db pg_region > /opt/backup/pg_region_$(date +%Y%m%d_%H%M%S).sql +mysqldump -h 8.148.25.55 -uroot -p pguser-db pg_grade > /opt/backup/pg_grade_$(date +%Y%m%d_%H%M%S).sql +mysqldump -h 8.148.25.55 -uroot -p pguser-db pg_api_dict > /opt/backup/pg_api_dict_$(date +%Y%m%d_%H%M%S).sql + +# 2. 备份当前部署版本 +cp -r /opt/pangu-user-platform/backend/pangu-admin.jar /opt/backup/pangu-admin.jar.bak +cp -r /opt/pangu-user-platform/frontend/admin-manage /opt/backup/admin-manage.bak +cp -r /opt/pangu-user-platform/frontend/user-front /opt/backup/user-front.bak +``` + +### 4.2 本地构建 + +```bash +# 1. 构建后端 +cd /Users/felix/pgWorkSpace/pangu-user-platform/backend +./build.sh -q -f + +# 2. 构建管理后台前端 +cd /Users/felix/pgWorkSpace/pangu-user-platform/frontend +npm run build + +# 3. 构建H5前端 +cd /Users/felix/pgWorkSpace/user_authentication_center_front/user-front +npm run build +``` + +### 4.3 上传部署包 + +```bash +# 1. 上传后端JAR +scp /Users/felix/pgWorkSpace/pangu-user-platform/backend/pangu-admin/target/pangu-admin.jar root@192.168.71.56:/opt/pangu-user-platform/backend/ + +# 2. 上传管理后台前端 +scp -r /Users/felix/pgWorkSpace/pangu-user-platform/frontend/dist/* root@192.168.71.56:/opt/pangu-user-platform/frontend/admin-manage/ + +# 3. 上传H5前端 +scp -r /Users/felix/pgWorkSpace/user_authentication_center_front/user-front/dist/* root@192.168.71.56:/opt/pangu-user-platform/frontend/user-front/ + +# 4. 上传SQL脚本 +scp /Users/felix/pgWorkSpace/pangu-user-platform/scripts/sql/V1.0.*.sql root@192.168.71.56:/opt/pangu-user-platform/scripts/ +``` + +### 4.4 执行数据库脚本 + +```bash +ssh root@192.168.71.56 + +# 按顺序执行SQL脚本 +mysql -h 8.148.25.55 -uroot -paly2024A pguser-db < /opt/pangu-user-platform/scripts/V1.0.3__open_api_dict.sql +mysql -h 8.148.25.55 -uroot -paly2024A pguser-db < /opt/pangu-user-platform/scripts/V1.0.4__grade_add_stage.sql + +# ⚠️ V1.0.5 需要单独确认后执行(高风险) +# mysql -h 8.148.25.55 -uroot -paly2024A pguser-db < /opt/pangu-user-platform/scripts/V1.0.5__clean_region_data.sql +``` + +### 4.5 重启服务 + +```bash +ssh root@192.168.71.56 + +# 重启后端服务 +cd /opt/pangu-user-platform/scripts +./service.sh restart + +# 检查服务状态 +./service.sh status + +# 查看启动日志 +./service.sh logs +``` + +### 4.6 验证测试 + +| 验证项 | 验证方法 | 预期结果 | +|--------|---------|---------| +| 后端服务 | `curl http://localhost:9083/actuator/health` | {"status":"UP"} | +| 管理后台登录 | 浏览器访问管理后台 | 正常登录 | +| 学校管理-区域树 | 查看区域树展开状态 | 默认展开湖北省 | +| 学校管理-层级查询 | 选择武汉市查看学校 | 显示武汉市下所有学校 | +| 年级管理-学段 | 查看年级列表 | 显示学段列 | +| OpenAPI接口 | 调用新增的接口 | 正常返回数据 | +| H5前端登录 | 浏览器访问H5 | 正常登录 | + +--- + +## 五、回滚方案 + +### 5.1 后端回滚 + +```bash +ssh root@192.168.71.56 +cd /opt/pangu-user-platform/scripts + +# 停止服务 +./service.sh stop + +# 恢复JAR包 +cp /opt/backup/pangu-admin.jar.bak /opt/pangu-user-platform/backend/pangu-admin.jar + +# 启动服务 +./service.sh start +``` + +### 5.2 前端回滚 + +```bash +# 恢复管理后台前端 +rm -rf /opt/pangu-user-platform/frontend/admin-manage/* +cp -r /opt/backup/admin-manage.bak/* /opt/pangu-user-platform/frontend/admin-manage/ + +# 恢复H5前端 +rm -rf /opt/pangu-user-platform/frontend/user-front/* +cp -r /opt/backup/user-front.bak/* /opt/pangu-user-platform/frontend/user-front/ +``` + +### 5.3 数据库回滚 + +```bash +# 恢复pg_grade表(如果执行了V1.0.4) +mysql -h 8.148.25.55 -uroot -paly2024A pguser-db -e "ALTER TABLE pg_grade DROP COLUMN stage;" + +# 恢复pg_region表(如果执行了V1.0.5) +mysql -h 8.148.25.55 -uroot -paly2024A pguser-db < /opt/backup/pg_region_YYYYMMDD_HHMMSS.sql +``` + +--- + +## 六、风险评估 + +| 风险项 | 风险等级 | 应对措施 | +|--------|---------|---------| +| 数据库结构变更 | 🟡 中 | 执行前备份相关表 | +| 区域数据物理删除 | 🔴 高 | 必须备份,可单独安排执行 | +| 后端服务中断 | 🟢 低 | 使用热部署,中断时间<30秒 | +| 前端静态资源更新 | 🟢 低 | nginx无需重启,刷新即生效 | + +--- + +## 七、审批确认 + +| 角色 | 姓名 | 确认签字 | 日期 | +|------|------|---------|------| +| 开发负责人 | | | | +| 测试负责人 | | | | +| 运维负责人 | | | | +| 项目经理 | | | | + +--- + +## 八、特别说明 + +### 8.1 关于 V1.0.5__clean_region_data.sql + +此脚本将**物理删除**所有非湖北省的区域数据(约3309条),仅保留湖北省及其下级区域(约120条)。 + +**建议处理方式**: +1. 此脚本可在本次发布中暂不执行 +2. 单独安排时间,在业务低峰期执行 +3. 执行前必须完成 `pg_region` 表的完整备份 +4. 执行后需验证学校管理功能正常 + +### 8.2 注意事项 + +1. 发布前确保本地代码已全部提交并推送 +2. 建议选择业务低峰期(如晚间或周末)发布 +3. 发布后持续关注服务日志,确保无异常 + +--- + +*文档生成时间: 2026-02-05* diff --git a/frontend/src/views/business/member/components/EducationDialog.vue b/frontend/src/views/business/member/components/EducationDialog.vue index 97c50cf..86d4441 100644 --- a/frontend/src/views/business/member/components/EducationDialog.vue +++ b/frontend/src/views/business/member/components/EducationDialog.vue @@ -63,11 +63,19 @@