# Web端修改商户用户信息接口文档 ## 模块概述 本模块提供商户用户信息修改功能,支持修改昵称、姓名、身份证号、账号简介、头像等个人信息。接口采用部分更新策略,只更新提供的非空字段。 --- ## 接口信息 ### 修改商户用户信息 #### 接口详情 - **接口名称**: 修改商户用户信息 - **接口路径**: `POST /merchantUser/updateMerchantUserInfo` - **请求方式**: POST - **Content-Type**: application/json - **接口描述**: 修改指定商户用户的个人信息,支持部分字段更新 - **登录验证**: ✅ 需要(使用 `@LoginRequired` 注解) --- ## 请求参数 ### 请求体(JSON格式) | 参数名 | 类型 | 必填 | 说明 | 示例值 | |--------|------|------|------|--------| | id | Integer | 是 | 用户ID | 12345 | | nickName | String | 否 | 昵称 | "小明的店铺" | | name | String | 否 | 真实姓名 | "张三" | | idCard | String | 否 | 身份证号 | "110101199001011234" | | accountBlurb | String | 否 | 账号简介 | "专业提供优质服务" | | headImg | String | 否 | 头像URL | "https://example.com/avatar.jpg" | **注意**: - `id` 字段必须提供 - 其他字段均为可选,只需提供需要修改的字段 - 未提供的字段不会被更新 --- ## 请求示例 ### 示例1: 修改昵称和头像 ```http POST /merchantUser/updateMerchantUserInfo Content-Type: application/json Authorization: Bearer YOUR_TOKEN { "id": 12345, "nickName": "小明的店铺", "headImg": "https://example.com/avatar.jpg" } ``` ```bash curl -X POST "http://localhost:8080/merchantUser/updateMerchantUserInfo" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{ "id": 12345, "nickName": "小明的店铺", "headImg": "https://example.com/avatar.jpg" }' ``` ### 示例2: 修改真实姓名和身份证号 ```json { "id": 12345, "name": "张三", "idCard": "110101199001011234" } ``` ### 示例3: 只修改账号简介 ```json { "id": 12345, "accountBlurb": "专业提供优质服务,欢迎光临" } ``` ### 示例4: 修改所有信息 ```json { "id": 12345, "nickName": "小明的店铺", "name": "张三", "idCard": "110101199001011234", "accountBlurb": "专业提供优质服务", "headImg": "https://example.com/avatar.jpg" } ``` --- ## 响应参数 ### 成功响应 ```json { "code": 200, "success": true, "data": true, "msg": "修改成功" } ``` ### 响应字段说明 | 字段名 | 类型 | 说明 | |--------|------|------| | code | Integer | 状态码, 200表示成功 | | success | Boolean | 是否成功 | | data | Boolean | 修改结果, true表示成功 | | msg | String | 提示信息 | ### 失败响应 #### 1. 用户ID为空 ```json { "code": 500, "success": false, "data": null, "msg": "修改失败:用户ID不能为空" } ``` #### 2. 修改失败(数据库更新失败) ```json { "code": 500, "success": false, "data": false, "msg": "修改失败" } ``` #### 3. 未登录或登录过期 ```json { "code": 500, "success": false, "data": null, "msg": "请先登录" } ``` #### 4. 系统异常 ```json { "code": 500, "success": false, "data": null, "msg": "修改失败:{异常信息}" } ``` --- ## 业务逻辑说明 ### 更新流程 ``` ┌─────────────────────────────────────────────┐ │ 1. 接收请求参数 │ │ StoreUser对象(包含id和待更新字段) │ └─────────────────┬───────────────────────────┘ ↓ ┌─────────────────────────────────────────────┐ │ 2. 登录验证(AOP切面) │ │ - 验证Token │ │ - 验证用户类型 │ └─────────────────┬───────────────────────────┘ ↓ ┌─────────────────────────────────────────────┐ │ 3. 参数验证 │ │ 检查用户ID是否为空 │ └─────────────────┬───────────────────────────┘ ↓ ┌─────────────────────────────────────────────┐ │ 4. 构建更新对象 │ │ 创建新的StoreUser对象 │ │ 设置id字段 │ └─────────────────┬───────────────────────────┘ ↓ ┌─────────────────────────────────────────────┐ │ 5. 部分字段更新逻辑 │ │ IF nickName != null AND nickName != "" │ │ THEN 设置nickName │ │ IF name != null AND name != "" │ │ THEN 设置name │ │ IF idCard != null AND idCard != "" │ │ THEN 设置idCard │ │ IF accountBlurb != null AND accountBlurb != ""│ │ THEN 设置accountBlurb │ │ IF headImg != null AND headImg != "" │ │ THEN 设置headImg │ └─────────────────┬───────────────────────────┘ ↓ ┌─────────────────────────────────────────────┐ │ 6. 执行数据库更新 │ │ storeUserMapper.updateById(storeUser) │ └─────────────────┬───────────────────────────┘ ↓ ┌─────────────────────────────────────────────┐ │ 7. 返回结果 │ │ updateResult > 0 → 成功 │ │ updateResult = 0 → 失败 │ └─────────────────────────────────────────────┘ ``` ### 部分更新策略 接口采用**部分更新(Partial Update)**策略: 1. **只更新提供的字段**: - 只有非空(not null 且 not empty)的字段才会被更新 - 未提供的字段保持原值不变 2. **空字符串处理**: - 空字符串 `""` 被视为无效值,不会更新数据库 - 使用 `StringUtils.isNotEmpty()` 进行判断 3. **Null值处理**: - `null` 值的字段不会被更新 - 如果需要清空某个字段,需要在业务层面另行处理 ### 字段说明 #### 1. nickName(昵称) - **用途**: 商户用户的显示名称 - **示例**: "小明的店铺"、"张三商户" - **约束**: 字符串类型,可为中英文 #### 2. name(真实姓名) - **用途**: 商户的真实姓名 - **示例**: "张三"、"李四" - **约束**: 通常用于实名认证 #### 3. idCard(身份证号) - **用途**: 商户的身份证号码 - **示例**: "110101199001011234" - **约束**: 18位身份证号 - **安全**: 敏感信息,需要加密存储和传输 #### 4. accountBlurb(账号简介) - **用途**: 商户账号的简要介绍 - **示例**: "专业提供优质服务" - **约束**: 文本类型,可较长 #### 5. headImg(头像) - **用途**: 商户用户的头像URL - **示例**: "https://example.com/avatar.jpg" - **约束**: 有效的图片URL地址 --- ## 数据库操作 ### 涉及的表 #### store_user(商户用户表) **更新SQL**: ```sql UPDATE store_user SET nick_name = ? (如果提供), name = ? (如果提供), id_card = ? (如果提供), account_blurb = ? (如果提供), head_img = ? (如果提供) WHERE id = ? ``` **说明**: - 使用 MyBatis-Plus 的 `updateById` 方法 - 只更新非空字段(动态SQL) - 自动填充更新时间(如果配置了自动填充) --- ## 业务场景 ### 场景1: 新用户完善个人信息 **背景**: 用户刚注册,需要完善个人信息 **请求**: ```json { "id": 12345, "nickName": "小明的店铺", "name": "张三", "idCard": "110101199001011234", "accountBlurb": "专业提供优质服务" } ``` **预期**: - 所有提供的字段都被成功更新 - 返回 "修改成功" --- ### 场景2: 用户修改头像 **背景**: 用户只想更换头像 **请求**: ```json { "id": 12345, "headImg": "https://example.com/new_avatar.jpg" } ``` **预期**: - 只有 `headImg` 字段被更新 - 其他字段保持不变 - 返回 "修改成功" --- ### 场景3: 用户修改昵称 **背景**: 用户想要更改显示昵称 **请求**: ```json { "id": 12345, "nickName": "新店铺名称" } ``` **预期**: - 只有 `nickName` 字段被更新 - 返回 "修改成功" --- ### 场景4: 批量修改多个字段 **背景**: 用户一次性修改多个信息 **请求**: ```json { "id": 12345, "nickName": "小明的优质店铺", "accountBlurb": "专业提供优质服务,欢迎光临", "headImg": "https://example.com/avatar.jpg" } ``` **预期**: - 三个字段都被成功更新 - 返回 "修改成功" --- ### 场景5: ID不存在 **背景**: 提供的用户ID在数据库中不存在 **请求**: ```json { "id": 999999, "nickName": "测试店铺" } ``` **预期**: - 数据库更新影响行数为0 - 返回 "修改失败" --- ### 场景6: 未提供ID **背景**: 请求中没有提供用户ID **请求**: ```json { "nickName": "测试店铺" } ``` **预期**: - 抛出异常:"用户ID不能为空" - 返回错误信息 --- ## 安全考虑 ### 1. 登录验证 接口使用 `@LoginRequired` 注解进行登录验证: - ✅ 验证JWT Token的有效性 - ✅ 验证用户类型为 `storePlatform` - ✅ 检查Token是否在Redis中存在(未过期/未注销) ### 2. 身份验证 **建议**: - 前端应验证当前登录用户只能修改自己的信息 - 可以在业务层添加用户ID匹配验证 - 管理员可能需要特殊权限修改其他用户信息 ### 3. 数据验证 **建议**: - 身份证号格式验证(18位数字) - 昵称长度限制(如1-20个字符) - 头像URL格式验证 - 防止SQL注入(MyBatis-Plus已处理) - 防止XSS攻击(前端需处理) ### 4. 敏感信息 **身份证号**: - ⚠️ 高度敏感信息 - 建议加密存储 - 日志中应脱敏显示 - HTTPS传输 --- ## 日志记录 ### 日志级别 接口记录了详细的日志信息: ```java // INFO级别 - 请求日志 log.info("MerchantUserController.updateMerchantUserInfo?storeUser={}", storeUser); // INFO级别 - 业务开始 log.info("MerchantUserServiceImpl.updateMerchantUserInfo - 开始修改商户用户信息: userId={}", userId); // DEBUG级别 - 字段更新 log.debug("MerchantUserServiceImpl.updateMerchantUserInfo - 更新昵称: {}", nickName); // INFO级别 - 业务完成 log.info("MerchantUserServiceImpl.updateMerchantUserInfo - 修改完成: userId={}, success={}", userId, success); // ERROR级别 - 异常日志 log.error("MerchantUserController.updateMerchantUserInfo ERROR: {}", e.getMessage(), e); ``` ### 日志脱敏 **建议**对敏感字段进行脱敏: ```java // 身份证号脱敏 String maskedIdCard = idCard.replaceAll("(\\d{6})\\d{8}(\\d{4})", "$1********$2"); log.debug("更新身份证号: {}", maskedIdCard); // 110101********1234 ``` --- ## 异常处理 ### 异常场景 | 异常情况 | HTTP状态码 | 返回code | 返回msg | |----------|-----------|----------|---------| | 用户ID为空 | 200 | 500 | "修改失败:用户ID不能为空" | | 数据库更新失败 | 200 | 500 | "修改失败" | | 未登录 | 200 | 500 | "请先登录" | | 系统异常 | 200 | 500 | "修改失败:{异常信息}" | --- ## 测试用例 ### 测试场景1: 正常修改单个字段 **前置条件**: - 用户已登录 - 用户ID存在 **请求**: ```json { "id": 12345, "nickName": "新昵称" } ``` **预期结果**: - HTTP 200 - 返回 `{"success": true, "msg": "修改成功"}` - 数据库中该用户的 `nick_name` 字段被更新 --- ### 测试场景2: 正常修改多个字段 **请求**: ```json { "id": 12345, "nickName": "新昵称", "name": "新姓名", "headImg": "https://example.com/new.jpg" } ``` **预期结果**: - 所有提供的字段都被更新 - 其他字段保持不变 --- ### 测试场景3: 用户ID不存在 **请求**: ```json { "id": 999999, "nickName": "测试" } ``` **预期结果**: - HTTP 200 - 返回 `{"success": false, "msg": "修改失败"}` - 数据库无变化 --- ### 测试场景4: 用户ID为空 **请求**: ```json { "nickName": "测试" } ``` **预期结果**: - HTTP 200 - 返回 `{"success": false, "msg": "修改失败:用户ID不能为空"}` --- ### 测试场景5: 未登录 **前置条件**: - 不携带Token或Token无效 **预期结果**: - HTTP 200 - 返回 `{"success": false, "msg": "请先登录"}` --- ### 测试场景6: 空字符串字段 **请求**: ```json { "id": 12345, "nickName": "", "name": "张三" } ``` **预期结果**: - `nickName` 不会被更新(空字符串被忽略) - `name` 会被更新为 "张三" - 返回 "修改成功" --- ## 性能优化 ### 1. 数据库优化 确保 `store_user` 表的主键有索引: ```sql -- 主键索引(通常自动创建) ALTER TABLE store_user ADD PRIMARY KEY (id); ``` ### 2. 更新策略 - ✅ 使用 MyBatis-Plus 的 `updateById` 方法(基于主键更新,性能高) - ✅ 动态SQL,只更新非空字段 - ✅ 避免全表扫描 ### 3. 缓存策略 如果用户信息频繁读取,建议: - 使用Redis缓存用户信息 - 更新成功后清除对应的缓存 - 缓存Key: `store:user:{userId}` ```java // 伪代码示例 @Override public boolean updateMerchantUserInfo(StoreUser storeUser) { boolean success = storeUserMapper.updateById(storeUser); if (success) { // 清除缓存 redisTemplate.delete("store:user:" + storeUser.getId()); } return success; } ``` --- ## 注意事项 ### 1. 部分更新 - ⚠️ 接口只更新提供的非空字段 - ⚠️ 未提供的字段保持原值 - ⚠️ 空字符串 `""` 不会更新数据库 ### 2. 必填字段 - ⚠️ `id` 字段必须提供 - ⚠️ 至少提供一个需要更新的字段(否则更新无意义) ### 3. 字段长度 建议在前端和后端都进行字段长度验证: - 昵称: 1-50个字符 - 姓名: 2-20个字符 - 身份证号: 18位 - 账号简介: 0-200个字符 ### 4. 敏感信息 - ⚠️ 身份证号是敏感信息,需要特别保护 - ⚠️ 建议加密存储 - ⚠️ 日志中应脱敏 - ⚠️ 使用HTTPS传输 ### 5. 并发更新 在高并发场景下,可能需要考虑: - 使用乐观锁(version字段) - 使用分布式锁 - 处理更新冲突 --- ## 迁移说明 ### 原接口(app端) - **服务**: `alien-store` - **路径**: `/alienStore/store/user/setUserInfo` - **Controller**: `StoreUserController` - **Service**: `StoreUserService.setUserInfo()` ### 新接口(web端) - **服务**: `alien-store-platform` - **路径**: `/merchantUser/updateMerchantUserInfo` - **Controller**: `MerchantUserController` - **Service**: `MerchantUserService.updateMerchantUserInfo()` ### 差异说明 | 项目 | app端 | web端 | 说明 | |------|-------|-------|------| | 接口路径 | `/setUserInfo` | `/updateMerchantUserInfo` | 更符合web端命名规范 | | 登录验证 | 无明确标注 | ✅ 有(@LoginRequired) | 增加统一登录验证 | | 参数类型 | StoreUser | StoreUser | 保持一致 | | 返回类型 | R\ | R\ | 保持一致 | | 业务逻辑 | ✅ | ✅ | 完全复用 | | 日志记录 | 基础 | 详细 | 增强日志记录 | ### 复用的核心组件 1. **Mapper**: `StoreUserMapper` 2. **Entity**: `StoreUser` 3. **Util**: `StringUtils` (Apache Commons Lang3) --- ## 扩展功能建议 ### 1. 字段验证 可以添加更详细的字段验证: ```java // 身份证号验证 if (StringUtils.isNotEmpty(idCard)) { if (!IdCardValidator.isValid(idCard)) { throw new RuntimeException("身份证号格式不正确"); } } // 昵称长度验证 if (StringUtils.isNotEmpty(nickName)) { if (nickName.length() > 50) { throw new RuntimeException("昵称长度不能超过50个字符"); } } ``` ### 2. 审计日志 记录用户信息修改的审计日志: ```java // 保存修改记录 AuditLog auditLog = new AuditLog(); auditLog.setUserId(storeUser.getId()); auditLog.setAction("UPDATE_USER_INFO"); auditLog.setDetails(JSONObject.toJSONString(storeUser)); auditLog.setOperateTime(new Date()); auditLogMapper.insert(auditLog); ``` ### 3. 变更通知 当关键信息修改时,发送通知: ```java // 如果修改了身份证号,发送通知 if (StringUtils.isNotEmpty(storeUser.getIdCard())) { notificationService.send(userId, "您的身份证号已更新"); } ``` --- ## 更新日志 ### 2025-11-17 **新增接口**: - ✅ `POST /merchantUser/updateMerchantUserInfo` - 修改商户用户信息 **核心功能**: - ✅ 支持部分字段更新 - ✅ 动态SQL,只更新非空字段 - ✅ 登录验证(@LoginRequired) - ✅ 详细日志记录 - ✅ 完善异常处理 - ✅ 参数验证(用户ID) **涉及文件**: - `MerchantUserController.java` - 更新 - `MerchantUserService.java` - 更新 - `MerchantUserServiceImpl.java` - 更新 **代码质量**: - ✅ Linter检查:无错误 - ✅ 日志记录:详细 - ✅ 异常处理:完善 - ✅ 代码注释:完整 **开发人员**: ssk --- **文档版本**: v1.0 **最后更新**: 2025-11-17 **维护人员**: ssk