Files
youlai-boot/src/main/java/com/youlai/boot/system/controller/UserController.java
Ray.Hao 7b87543978 feat: v4.5.0 包结构迁移、MyBatis-Plus 自动填充、Jackson 3.x 适配
- common.annotation -> framework.annotation(DataPermission/Log/RateLimit/RepeatSubmit/ValidField)
- common.base.BaseEntity -> framework.base.BaseEntity(含 createTime/updateTime 自动填充注解)
- BaseQuery/IBaseEnum 保留在 common.base(无框架依赖)
- AutoFillMetaObjectHandler 实现 createTime/updateTime/createBy/updateBy 自动填充
- 实体类(Dept/Role/Config/DictItem/Notice/SysUser)添加 createBy/updateBy @TableField(fill=...)
- Jackson 注解使用 com.fasterxml.jackson.annotation(2.x 兼容层),databind/core 使用 tools.jackson(3.x)
- 代码生成模板 entity.java.vm/form.java.vm 包路径同步更新
- 删除冗余 UserService default 方法、DataPermissionException、DictChangeEvent event 包
- 版本号 4.4.0 -> 4.5.0
2026-07-28 17:24:44 +08:00

296 lines
12 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.youlai.boot.system.controller;
import cn.idev.excel.EasyExcel;
import cn.idev.excel.ExcelWriter;
import com.youlai.boot.framework.annotation.Log;
import com.youlai.boot.framework.annotation.RepeatSubmit;
import com.youlai.boot.common.enums.ActionTypeEnum;
import com.youlai.boot.common.enums.LogModuleEnum;
import com.youlai.boot.common.enums.StatusEnum;
import com.youlai.boot.framework.security.util.SecurityUtils;
import com.youlai.boot.common.model.Option;
import com.youlai.boot.common.result.ExcelResult;
import com.youlai.boot.common.result.PageResult;
import com.youlai.boot.common.result.Result;
import com.youlai.boot.common.util.ExcelUtils;
import com.youlai.boot.system.listener.UserImportListener;
import com.youlai.boot.system.model.form.EmailUpdateForm;
import com.youlai.boot.system.model.form.MobileUpdateForm;
import com.youlai.boot.system.model.form.PasswordUpdateForm;
import com.youlai.boot.system.model.form.PasswordVerifyForm;
import com.youlai.boot.system.model.form.UserForm;
import com.youlai.boot.system.model.form.UserImportForm;
import com.youlai.boot.system.model.form.UserProfileForm;
import com.youlai.boot.system.model.vo.UserExportVO;
import com.youlai.boot.system.model.query.UserQuery;
import com.youlai.boot.system.model.vo.CurrentUserVO;
import com.youlai.boot.system.model.vo.UserPageVO;
import com.youlai.boot.system.model.vo.UserProfileVO;
import com.youlai.boot.system.service.UserService;
import com.youlai.boot.framework.security.token.TokenManager;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
* 用户控制层
*
* @author Ray.Hao
* @since 4.3.1
*/
@Tag(name = "02.用户接口")
@RestController
@RequestMapping("/api/v1/users")
@RequiredArgsConstructor
public class UserController {
private final TokenManager tokenManager;
private final UserService userService;
@Operation(summary = "用户列表")
@GetMapping
@Log(module = LogModuleEnum.USER, value = ActionTypeEnum.LIST)
public PageResult<UserPageVO> getUserList(
@Valid UserQuery queryParams
) {
return PageResult.success(userService.getUserPage(queryParams));
}
@Operation(summary = "新增用户")
@PostMapping
@PreAuthorize("@ss.hasPerm('sys:user:create')")
@RepeatSubmit
@Log(module = LogModuleEnum.USER, value = ActionTypeEnum.INSERT)
public Result<?> saveUser(
@RequestBody @Valid UserForm userForm
) {
boolean result = userService.saveUser(userForm);
return Result.judge(result);
}
@Operation(summary = "获取用户表单数据")
@GetMapping("/{userId}/form")
@PreAuthorize("@ss.hasPerm('sys:user:update')")
public Result<UserForm> getUserForm(
@Parameter(description = "用户ID") @PathVariable Long userId
) {
UserForm formData = userService.getUserFormData(userId);
return Result.success(formData);
}
@Operation(summary = "修改用户")
@PutMapping(value = "/{userId}")
@PreAuthorize("@ss.hasPerm('sys:user:update')")
@Log(module = LogModuleEnum.USER, value = ActionTypeEnum.UPDATE)
public Result<Void> updateUser(
@Parameter(description = "用户ID") @PathVariable Long userId,
@RequestBody @Valid UserForm userForm
) {
boolean result = userService.updateUser(userId, userForm);
return Result.judge(result);
}
@Operation(summary = "删除用户")
@DeleteMapping("/{ids}")
@PreAuthorize("@ss.hasPerm('sys:user:delete')")
@Log(module = LogModuleEnum.USER, value = ActionTypeEnum.DELETE)
public Result<Void> deleteUsers(
@Parameter(description = "用户ID多个以英文逗号(,)分割") @PathVariable String ids
) {
boolean result = userService.deleteUsers(ids);
return Result.judge(result);
}
@Operation(summary = "修改用户状态")
@PatchMapping(value = "/{userId}/status")
@PreAuthorize("@ss.hasPerm('sys:user:update')")
@Log(module = LogModuleEnum.USER, value = ActionTypeEnum.UPDATE)
public Result<Void> updateUserStatus(
@Parameter(description = "用户ID") @PathVariable Long userId,
@Parameter(description = "用户状态(1:启用;0:禁用)") @RequestParam Integer status
) {
boolean result = userService.updateUserStatus(userId, status);
// 用户禁用时立即失效其会话
if (result && StatusEnum.DISABLE.getValue().equals(status)) {
tokenManager.invalidateUserSessions(userId);
}
return Result.judge(result);
}
@Operation(summary = "重置指定用户密码")
@PutMapping(value = "/{userId}/password/reset")
@PreAuthorize("@ss.hasPerm('sys:user:reset-password')")
@Log(module = LogModuleEnum.USER, value = ActionTypeEnum.RESET_PASSWORD)
public Result<?> resetUserPassword(
@Parameter(description = "用户ID") @PathVariable Long userId,
@RequestParam String password
) {
boolean result = userService.resetUserPassword(userId, password);
return Result.judge(result);
}
@Operation(summary = "获取当前登录用户信息")
@GetMapping("/me")
public Result<CurrentUserVO> getCurrentUser() {
CurrentUserVO currentUserVo = userService.getCurrentUserInfo();
return Result.success(currentUserVo);
}
@Operation(summary = "用户导入模板下载")
@GetMapping("/template")
public void downloadTemplate(HttpServletResponse response) {
String fileName = "用户导入模板.xlsx";
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
String fileClassPath = "templates" + File.separator + "excel" + File.separator + fileName;
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(fileClassPath);
try (ServletOutputStream outputStream = response.getOutputStream();
ExcelWriter excelWriter = EasyExcel.write(outputStream).withTemplate(inputStream).build()) {
excelWriter.finish();
} catch (IOException e) {
throw new RuntimeException("用户导入模板下载失败", e);
}
}
@Operation(summary = "导入用户")
@PostMapping("/import")
@PreAuthorize("@ss.hasPerm('sys:user:import')")
@Log(module = LogModuleEnum.USER, value = ActionTypeEnum.IMPORT)
public Result<ExcelResult> importUsers(MultipartFile file) throws IOException {
UserImportListener listener = new UserImportListener();
ExcelUtils.importExcel(file.getInputStream(), UserImportForm.class, listener);
return Result.success(listener.getExcelResult());
}
@Operation(summary = "导出用户")
@GetMapping("/export")
@PreAuthorize("@ss.hasPerm('sys:user:export')")
@Log(module = LogModuleEnum.USER, value = ActionTypeEnum.EXPORT)
public void exportUsers(UserQuery queryParams, HttpServletResponse response) throws IOException {
String fileName = "用户列表.xlsx";
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
List<UserExportVO> exportUserList = userService.listExportUsers(queryParams);
EasyExcel.write(response.getOutputStream(), UserExportVO.class).sheet("用户列表")
.doWrite(exportUserList);
}
@Operation(summary = "获取用户下拉选项")
@GetMapping("/options")
public Result<List<Option<String>>> listUserOptions() {
List<Option<String>> list = userService.listUserOptions();
return Result.success(list);
}
@Operation(summary = "获取个人中心用户信息")
@GetMapping("/profile")
public Result<UserProfileVO> getUserProfile() {
Long userId = SecurityUtils.getUserId();
UserProfileVO userProfile = userService.getUserProfile(userId);
return Result.success(userProfile);
}
@Operation(summary = "个人中心修改用户信息")
@PutMapping("/profile")
@Log(module = LogModuleEnum.USER, value = ActionTypeEnum.UPDATE)
public Result<?> updateUserProfile(@RequestBody UserProfileForm formData) {
boolean result = userService.updateUserProfile(formData);
return Result.judge(result);
}
@Operation(summary = "当前用户修改密码")
@PutMapping(value = "/password")
@Log(module = LogModuleEnum.USER, value = ActionTypeEnum.CHANGE_PASSWORD)
public Result<?> changeCurrentUserPassword(
@RequestBody PasswordUpdateForm data
) {
Long currUserId = SecurityUtils.getUserId();
boolean result = userService.changeUserPassword(currUserId, data);
return Result.judge(result);
}
@Operation(summary = "发送短信验证码(绑定或更换手机号)")
@PostMapping(value = "/mobile/code")
public Result<?> sendMobileCode(
@Parameter(description = "手机号码", required = true) @RequestParam String mobile
) {
boolean result = userService.sendMobileCode(mobile);
return Result.judge(result);
}
@Operation(summary = "绑定或更换手机号")
@PutMapping(value = "/mobile")
public Result<?> bindOrChangeMobile(
@RequestBody @Validated MobileUpdateForm data
) {
boolean result = userService.bindOrChangeMobile(data);
return Result.judge(result);
}
@Operation(summary = "解绑手机号")
@DeleteMapping(value = "/mobile")
public Result<?> unbindMobile(
@RequestBody @Validated PasswordVerifyForm data
) {
boolean result = userService.unbindMobile(data);
return Result.judge(result);
}
@Operation(summary = "发送邮箱验证码(绑定或更换邮箱)")
@PostMapping(value = "/email/code")
public Result<Void> sendEmailCode(
@Parameter(description = "邮箱地址", required = true) @RequestParam String email
) {
userService.sendEmailCode(email);
return Result.success();
}
@Operation(summary = "绑定或更换邮箱")
@PutMapping(value = "/email")
public Result<?> bindOrChangeEmail(
@RequestBody @Validated EmailUpdateForm data
) {
boolean result = userService.bindOrChangeEmail(data);
return Result.judge(result);
}
@Operation(summary = "解绑邮箱")
@DeleteMapping(value = "/email")
public Result<?> unbindEmail(
@RequestBody @Validated PasswordVerifyForm data
) {
boolean result = userService.unbindEmail(data);
return Result.judge(result);
}
}