# 服务角色统一申请设计 日期: 2026-07-30 状态: ✅ 已实施 ## 概述 "我的"页面增加统一的服务角色申请入口,用户可多选申请成为规划师、营养师、管家、服务商、文章管理员、活动提供商,上传身份证和资质证书,填写联系地址,提交后台审核。 现有分散的申请页面(butler/apply.vue、nutritionist/apply.vue、vendor/apply.vue、guide/register)将被此统一入口替换。 ## 角色枚举 | 角色 Key | 显示名称 | 现有后端 | 说明 | |----------|----------|----------|------| | `planner` | 规划师 | GuideApplication/Guide | 成长规划师 | | `nutritionist` | 营养师 | User.nutritionistStatus | — | | `butler` | 管家 | User.butlerStatus + ButlerProfile | — | | `vendor` | 服务商 | User.vendorStatus + vendorInfo | 供应商/服务商 | | `article_admin` | 文章管理员 | 新增 User.article_admin_status | 知识管理模块 | | `activity_provider` | 活动提供商 | 新增 User.activity_provider_status | — | ## 数据库设计 ### 新建表 `service_role_applications` ```sql CREATE TABLE `service_role_applications` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL COMMENT '申请人ID', `roles` varchar(255) NOT NULL COMMENT '申请角色,JSON数组 e.g. ["planner","nutritionist"]', `id_card` varchar(18) DEFAULT '' COMMENT '身份证号', `id_card_images` text COMMENT '身份证照片,JSON数组[正面URL,反面URL]', `certificate_images` text COMMENT '资质证书照片,JSON数组', `province` varchar(50) DEFAULT '' COMMENT '省', `city` varchar(50) DEFAULT '' COMMENT '市', `district` varchar(50) DEFAULT '' COMMENT '区', `street` varchar(50) DEFAULT '' COMMENT '街道', `address_detail` varchar(200) DEFAULT '' COMMENT '详细地址', `status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '0-pending, 1-approved, 2-rejected', `reject_reason` varchar(500) DEFAULT '' COMMENT '驳回原因', `reviewed_by` int(11) DEFAULT NULL COMMENT '审核人ID', `reviewed_at` datetime DEFAULT NULL COMMENT '审核时间', `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_user_id` (`user_id`), KEY `idx_status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='服务角色申请表'; ``` ### User 表新增字段 ```sql `article_admin_status` tinyint(4) DEFAULT NULL COMMENT '0无 1待审 2通过 3驳回', `activity_provider_status` tinyint(4) DEFAULT NULL COMMENT '0无 1待审 2通过 3驳回', ``` ## 后端设计 ### 实体 ```java @Data @TableName("service_role_applications") public class ServiceRoleApplication { @TableId(type = IdType.AUTO) private Long id; private Long userId; private String roles; // JSON Array private String idCard; private String idCardImages; // JSON Array private String certificateImages; // JSON Array private String province; private String city; private String district; private String street; private String addressDetail; private Integer status; // 0=pending, 1=approved, 2=rejected private String rejectReason; private Long reviewedBy; private LocalDateTime reviewedAt; private LocalDateTime createdAt; private LocalDateTime updatedAt; } ``` ### DTO ```java @Data public class ServiceRoleApplyDTO { @NotEmpty(message = "至少选择一个角色") private List roles; private String idCard; private List idCardImages; private List certificateImages; private String province; private String city; private String district; private String street; private String addressDetail; } ``` ### Controller: `ServiceRoleApplicationController` ``` POST /api/service-role/apply → 提交申请(含图片URL列表) POST /api/service-role/my-apply → 查询我的申请记录 POST /api/service-role/upload-image → 上传单张图片(MultipartFile, 返回图片URL) ``` 统一 `@PostMapping`,返回 `Result`。 ### Service: `ServiceRoleApplicationService` ```java public interface ServiceRoleApplicationService { Result submit(Long userId, ServiceRoleApplyDTO dto); Result getMyApplication(Long userId); Result approve(Long id, Long reviewerId); Result reject(Long id, String reason, Long reviewerId); } ``` ### 审批通过同步逻辑 ``` planner → 创建 Guide 记录 nutritionist → User.nutritionistStatus = 2 (approved) butler → User.butlerStatus = 2 + 创建 ButlerProfile vendor → User.vendorStatus = 2 article_admin → User.article_admin_status = 2 activity_provider → User.activity_provider_status = 2 ``` ## 前端设计 ### ProfileMenu 新增入口 在 `pages/profile/components/ProfileMenu.vue` 的「个人」分组新增菜单项(因 profile.vue 传 `show-services=false` 隐藏了服务分组): ```vue 📋 服务角色申请 › ``` ```js goToServiceRoleApply() { uni.navigateTo({ url: '/pages/profile/service-role-apply' }) } ``` ### 新增页面 `pages/profile/service-role-apply.vue` 页面结构: 1. **角色选择区** — 卡片式多选,6个角色网格布局,选中显示✓标记。参考 `assessment/apply.vue` 的 `:class="{selected:...}"` 模式 2. **身份证信息** — 身份证号输入框 + 正反面照片上传(两个独立上传位) 3. **资质证书** — 可添加多张证书照片,每张独立上传,显示缩略图,可删除 4. **联系地址** — 复用 `` 四级联动 + 详细地址输入框 5. **提交按钮** — 渐变样式,表单验证通过后可点击 ### 图片上传 使用 `uni.chooseImage()` + `uni.uploadFile()`,先上传到服务器获取 URL,再提交表单时传 URL 列表。 ### API ```js export const submitServiceRoleApply = (data) => post('/api/service-role/apply', data) export const getMyServiceRoleApply = (data) => post('/api/service-role/my-apply', data) export const uploadServiceRoleImage = (filePath) => ... // uni.uploadFile ``` ### 页面注册 在 `pages.json` 中 profile 子包注册: ```json { "path": "pages/profile/service-role-apply", "style": { "navigationBarTitleText": "服务角色申请" } } ``` ## Web 管理端审核 `cfc-web` 新增 `ServiceRoleApplications.vue`: - 列表展示所有 `pending` 申请 - 详情查看:申请角色、身份证照片、资质证书、地址 - 操作:批准/驳回(驳回时填写原因) ## 替换现有申请页 统一入口上线后: - `pages/butler/apply.vue` — 标记废弃,可考虑跳转到统一入口 - `pages/nutritionist/apply.vue` — 同上 - `pages/vendor/apply.vue` — 同上 - `pages/guide/register/index.vue` — 同上 ## 实施顺序 1. 数据库迁移(建表 + User 加字段) 2. 后端 Entity + DTO + Mapper 3. 后端 Service + Controller + 图片上传 4. Web 管理端审核页面 5. 前端统一申请页 6. ProfileMenu 入口 7. 替换/废弃旧申请页