diff --git a/docs/接口文档-用户管理与记录查询.md b/docs/接口文档-用户管理与记录查询.md index e7a4320..33b1636 100644 --- a/docs/接口文档-用户管理与记录查询.md +++ b/docs/接口文档-用户管理与记录查询.md @@ -1,6 +1,6 @@ # 前端接口文档 —— 用户管理增强 & 机构账号查询 & 按手机号查询文件记录 -> 版本:v1.0(2026-09-14) +> 版本:v1.2(2026-10-27,新增 1.4 修改用户姓名、1.5 批量创建机构用户) > 适用后端:s-file(alita-bjy-uygur) > Swagger 分组:「用户」「用户文件」 @@ -170,6 +170,94 @@ POST /user/admin_cancel > **与既有接口的区别**:既有 `POST /user/cancel` 为机构内注销(限定当前登录机构、且要求目标账号启用中),供原「注销申请/同意」流程使用,保持不变;本接口供系统管理员在用户管理页面跨机构直接注销。 +### 1.4 修改用户姓名 + +修改用户账号的真实姓名(`real_name`)。平台超管可修改全部用户;机构管理员仅可修改本机构用户(同一账号属于多个机构时,改的是账号本身,各机构下展示同步生效)。 + +- **URL**:`POST /user/modify_real_name` +- **权限**:平台超管(全部用户)/ 机构管理员(限本机构用户) +- **Body**(application/json) + +| 字段 | 类型 | 必填 | 说明 | +|---|---|---|---| +| userId | Long | 是 | 目标用户 id | +| realName | String | 是 | 新姓名,2-30 位,仅支持中文或英文姓名 | + +- **返回**:`AlitaResult`,data 为 `true` +- **失败场景**: + - 用户不存在 + - 目标已注销(提示「已注销账号不可修改姓名」) + - 姓名格式错误(提示「姓名格式错误!(长度2-30位,仅支持中文或英文姓名)」) + - 与原姓名一致(提示「姓名与原姓名一致,无需修改!」) + - 机构管理员操作非本机构用户(提示「只能操作本机构的用户」) + +**请求示例** + +```json +POST /user/modify_real_name +{ "userId": 10086, "realName": "张三" } +``` + +**返回示例** + +```json +{ "code": "200", "success": true, "data": true } +``` + +> **与既有接口的区别**:既有 `POST /user/modify_org_user_info` 为超管编辑企业用户完整信息(账号名/姓名/启用状态一起校验更新);本接口仅更新姓名,供用户管理页与机构账号页的「修改姓名」入口使用。 + +### 1.5 批量创建机构用户 + +一次性提交多条「手机号 + 姓名」创建机构用户。**单条失败不影响其他条目**(每条独立事务),返回与入参**顺序一一对应**的逐条结果,前端据此展示失败项。 + +- **URL**:`POST /user/batch_create_org_user` +- **权限**:登录用户(机构取当前登录机构,与单条创建 `POST /user/create_org_user` 一致) +- **Body**(application/json,对象数组,单次最多 **500** 条) + +数组元素字段: + +| 字段 | 类型 | 必填 | 说明 | +|---|---|---|---| +| phone | String | 是 | 手机号 | +| realName | String | 否 | 姓名 | + +- **返回**:`AlitaResult>` + +**OrgUserCreateResultDTO 字段** + +| 字段 | 类型 | 说明 | +|---|---|---| +| phone | String | 手机号(回显入参) | +| realName | String | 姓名(回显入参) | +| success | Boolean | 是否创建成功 | +| userId | Long | 用户 id,失败时为 null | +| msg | String | 失败原因,成功时为 null | + +**请求示例** + +```json +POST /user/batch_create_org_user +[ + { "phone": "13800000001", "realName": "张三" }, + { "phone": "13800000002", "realName": "李四" } +] +``` + +**返回示例** + +```json +{ + "code": "200", + "success": true, + "data": [ + { "phone": "13800000001", "realName": "张三", "success": true, "userId": 1001, "msg": null }, + { "phone": "13800000002", "realName": "李四", "success": false, "userId": null, "msg": "手机号已存在,不能重复添加" } + ] +} +``` + +- **失败场景**(整体请求失败):列表为空;超过 500 条(提示「单次批量创建不能超过500条」)。单条业务失败(手机号格式不正确、手机号已存在等)不使请求失败,体现在对应条目的 `success/msg` 中。 + --- ## 2. 系统管理员:查看各机构下的账号信息 @@ -405,3 +493,6 @@ GET /user_file/record/page_by_phone?phone=13800000001&recordType=2&pageNo=1&page | 禁用/注销超管 | 不能禁用超级管理员账号 / 不能注销超级管理员账号 | | recordType 非法 | 记录类型不合法,仅支持1上传/2发送/3接收 | | phone 为空 | 手机号不能为空 | +| 修改姓名格式错误 | 姓名格式错误!(长度2-30位,仅支持中文或英文姓名) | +| 修改姓名与原值相同 | 姓名与原姓名一致,无需修改! | +| 机构管理员越机构操作 | 只能操作本机构的用户 | diff --git a/src/main/java/com/senmesh/usercenter/user/controller/UserController.java b/src/main/java/com/senmesh/usercenter/user/controller/UserController.java index af4e1fd..08cfcc7 100644 --- a/src/main/java/com/senmesh/usercenter/user/controller/UserController.java +++ b/src/main/java/com/senmesh/usercenter/user/controller/UserController.java @@ -115,6 +115,15 @@ public class UserController { return AlitaResult.success(userService.createOrgUser(orgUserCreateDTO)); } + @PostMapping("user/batch_create_org_user") + @ApiOperation("批量创建机构用户(单条失败不影响其他条目,返回逐条结果)") + public AlitaResult> batchCreateOrgUser( + @Validated @RequestBody List orgUserCreateDTOs) { + orgUserCreateDTOs.forEach(orgUserCreateDTO -> + orgUserCreateDTO.setOrgId(UserContextHolder.getExistLoginOrgId())); + return AlitaResult.success(userService.batchCreateOrgUser(orgUserCreateDTOs)); + } + @PutMapping("user/modify_org_user") @ApiOperation("修改机构用户") public AlitaResult modifyOrgUser(@Validated @RequestBody OrgUserModifyDTO orgUserModifyDTO) { @@ -301,4 +310,10 @@ public class UserController { userService.pageOrgAccount(queryDTO); return AlitaResult.success(queryDTO.getPageResult()); } + + @PostMapping("user/modify_real_name") + @ApiOperation("修改用户姓名(系统管理员)") + public AlitaResult modifyRealName(@Validated @RequestBody UserRealNameModifyDTO reqDTO) { + return AlitaResult.success(userService.modifyRealName(reqDTO)); + } } diff --git a/src/main/java/com/senmesh/usercenter/user/dto/OrgUserCreateResultDTO.java b/src/main/java/com/senmesh/usercenter/user/dto/OrgUserCreateResultDTO.java new file mode 100644 index 0000000..ef49d96 --- /dev/null +++ b/src/main/java/com/senmesh/usercenter/user/dto/OrgUserCreateResultDTO.java @@ -0,0 +1,38 @@ +package com.senmesh.usercenter.user.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 批量创建机构用户单条结果 + * + * @author liu on 2026-10-27 + */ +@ApiModel(description = "批量创建机构用户结果") +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OrgUserCreateResultDTO { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty("手机号") + private String phone; + + @ApiModelProperty("姓名") + private String realName; + + @ApiModelProperty("是否创建成功") + private Boolean success; + + @ApiModelProperty("用户id,失败时为null") + private Long userId; + + @ApiModelProperty("失败原因,成功时为null") + private String msg; +} diff --git a/src/main/java/com/senmesh/usercenter/user/dto/UserRealNameModifyDTO.java b/src/main/java/com/senmesh/usercenter/user/dto/UserRealNameModifyDTO.java new file mode 100644 index 0000000..de12a71 --- /dev/null +++ b/src/main/java/com/senmesh/usercenter/user/dto/UserRealNameModifyDTO.java @@ -0,0 +1,35 @@ +package com.senmesh.usercenter.user.dto; + +import com.senmesh.common.dto.BaseOperateDTO; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.*; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 修改用户姓名操作DTO + * + * @author liu on 2026-10-27 + */ +@EqualsAndHashCode(callSuper = true) +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@ApiModel(description = "修改用户姓名操作") +public class UserRealNameModifyDTO extends BaseOperateDTO { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty("用户id") + @NotNull(message = "用户id不能为空") + private Long userId; + + @ApiModelProperty("姓名") + @NotBlank(message = "姓名不能为空") + private String realName; + +} diff --git a/src/main/java/com/senmesh/usercenter/user/service/UserService.java b/src/main/java/com/senmesh/usercenter/user/service/UserService.java index 510d803..8547509 100644 --- a/src/main/java/com/senmesh/usercenter/user/service/UserService.java +++ b/src/main/java/com/senmesh/usercenter/user/service/UserService.java @@ -157,6 +157,15 @@ public interface UserService { */ Long createOrgUser(OrgUserCreateDTO orgUserCreateDTO); + /** + * 批量创建企业用户 + * 单条失败仅回滚该条,不影响其他条目,返回逐条结果 + * + * @param orgUserCreateDTOs 创建dto列表 + * @return {@link List}<{@link OrgUserCreateResultDTO}> 与入参顺序一一对应 + */ + List batchCreateOrgUser(List orgUserCreateDTOs); + /** * 修改企业用户 * @@ -366,4 +375,13 @@ public interface UserService { */ void pageOrgAccount(OrgAccountQueryDTO queryDTO); + /** + * 修改用户姓名(系统管理员) + * 平台超管可修改全部用户,机构管理员仅可修改本机构用户 + * + * @param reqDTO 操作dto + * @return {@link Boolean} + */ + Boolean modifyRealName(UserRealNameModifyDTO reqDTO); + } diff --git a/src/main/java/com/senmesh/usercenter/user/service/impl/UserServiceImpl.java b/src/main/java/com/senmesh/usercenter/user/service/impl/UserServiceImpl.java index 2234d7d..413d646 100644 --- a/src/main/java/com/senmesh/usercenter/user/service/impl/UserServiceImpl.java +++ b/src/main/java/com/senmesh/usercenter/user/service/impl/UserServiceImpl.java @@ -57,6 +57,7 @@ import com.senmesh.usercenter.user.dto.ModifyPasswordDTO; import com.senmesh.usercenter.user.dto.OrgAccountDTO; import com.senmesh.usercenter.user.dto.OrgAccountQueryDTO; import com.senmesh.usercenter.user.dto.OrgUserCreateDTO; +import com.senmesh.usercenter.user.dto.OrgUserCreateResultDTO; import com.senmesh.usercenter.user.dto.OrgUserModifyDTO; import com.senmesh.usercenter.user.dto.OrgUserOperateDTO; import com.senmesh.usercenter.user.dto.PersonUserCardCreateDTO; @@ -81,6 +82,7 @@ import com.senmesh.usercenter.user.dto.UserExtensionOperateDTO; import com.senmesh.usercenter.user.dto.UserOperateDTO; import com.senmesh.usercenter.user.dto.UserQueryByRoleDTO; import com.senmesh.usercenter.user.dto.UserQueryDTO; +import com.senmesh.usercenter.user.dto.UserRealNameModifyDTO; import com.senmesh.usercenter.user.dto.UserStatusOperateDTO; import com.senmesh.usercenter.user.entity.User; import com.senmesh.usercenter.user.login.dto.UserDetailDTO; @@ -107,6 +109,7 @@ public class UserServiceImpl implements UserService { private static final String CARD_NO_LOCK_NAME = "user:cardno"; private static final String USERNAME_LOCK_NAME = "user:username"; private static final String AUTH_LOCK_NAME = "user:auth"; + private static final int BATCH_CREATE_ORG_USER_LIMIT = 500; @Resource private UserMapper userMapper; @@ -208,6 +211,32 @@ public class UserServiceImpl implements UserService { return userId; } + @Override + public List batchCreateOrgUser(List orgUserCreateDTOs) { + Assert.isTrue(CollectionUtils.isNotEmpty(orgUserCreateDTOs), "创建列表不能为空"); + Assert.isTrue(orgUserCreateDTOs.size() <= BATCH_CREATE_ORG_USER_LIMIT, + "单次批量创建不能超过" + BATCH_CREATE_ORG_USER_LIMIT + "条"); + List resultDTOs = + Lists.newArrayListWithExpectedSize(orgUserCreateDTOs.size()); + // 不开外层事务,逐条走代理调用,单条独立事务+手机号锁,失败仅回滚该条 + UserService proxy = ((UserService) AopContext.currentProxy()); + for (OrgUserCreateDTO createDTO : orgUserCreateDTOs) { + OrgUserCreateResultDTO resultDTO = OrgUserCreateResultDTO.builder() + .phone(createDTO.getPhone()) + .realName(createDTO.getRealName()) + .build(); + try { + resultDTO.setUserId(proxy.createOrgUser(createDTO)); + resultDTO.setSuccess(Boolean.TRUE); + } catch (Exception e) { + resultDTO.setSuccess(Boolean.FALSE); + resultDTO.setMsg(e.getMessage()); + } + resultDTOs.add(resultDTO); + } + return resultDTOs; + } + @Transactional @Override public void modifyOrgUser(OrgUserModifyDTO orgUserModifyDTO) { @@ -1093,4 +1122,22 @@ public class UserServiceImpl implements UserService { Assert.isTrue(UserContextHolder.isSuperior(), "当前用户无操作权限"); userMapper.listOrgAccount(queryDTO); } + + @Override + @Transactional + public Boolean modifyRealName(UserRealNameModifyDTO reqDTO) { + // 平台超管可修改全部用户姓名,机构管理员仅可修改本机构用户 + Assert.isTrue(UserContextHolder.isSuperior() || UserContextHolder.isAdministrator(), + "当前用户无操作权限"); + this.checkOrgScopeOfOperateUser(reqDTO.getUserId()); + User user = userMapper.getById(reqDTO.getUserId()); + Assert.notNull(user, "用户不存在"); + Assert.isTrue(!UserStatusEnum.CLOSED.getStatus().equals(user.getStatus()), "已注销账号不可修改姓名"); + Assert.isTrue(ValidateUtils.checkRealName(reqDTO.getRealName()), + "姓名格式错误!(长度2-30位,仅支持中文或英文姓名)"); + Assert.isTrue(!StringUtils.equals(reqDTO.getRealName(), user.getRealName()), + "姓名与原姓名一致,无需修改!"); + User updateInfo = User.builder().id(user.getId()).realName(reqDTO.getRealName()).build(); + return userMapper.updateEntitySelective(updateInfo, UserContextHolder.getLoginUserId()) == 1; + } }