Commit 3eb5fde9 by Lizh

增加用户图像上传处理逻辑

parent 742a8654
...@@ -6,6 +6,7 @@ import com.jomalls.custom.app.dto.system.*; ...@@ -6,6 +6,7 @@ import com.jomalls.custom.app.dto.system.*;
import com.jomalls.custom.app.vo.system.SysDeptVO; import com.jomalls.custom.app.vo.system.SysDeptVO;
import com.jomalls.custom.app.vo.system.SysRoleVO; import com.jomalls.custom.app.vo.system.SysRoleVO;
import com.jomalls.custom.app.vo.system.SysUserVO; import com.jomalls.custom.app.vo.system.SysUserVO;
import org.springframework.web.multipart.MultipartFile;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
...@@ -45,7 +46,7 @@ public interface SysUserService { ...@@ -45,7 +46,7 @@ public interface SysUserService {
* *
* @param avatarUrl 头像URL * @param avatarUrl 头像URL
*/ */
void updateAvatar(String avatarUrl); String updateAvatar(MultipartFile file);
/** /**
* 查询用户详情(含角色列表、岗位ID) * 查询用户详情(含角色列表、岗位ID)
......
...@@ -110,7 +110,7 @@ public class SysLoginServiceImpl implements SysLoginService { ...@@ -110,7 +110,7 @@ public class SysLoginServiceImpl implements SysLoginService {
sysLogininforService.recordLogininfor(username, UserConstants.EXCEPTION, "用户名长度不在允许范围内"); sysLogininforService.recordLogininfor(username, UserConstants.EXCEPTION, "用户名长度不在允许范围内");
throw new ServiceException("用户名长度不正确"); throw new ServiceException("用户名长度不正确");
} }
// IP 黑名单校验(与 custom-back 对齐) // IP 黑名单校验
String blackIpList = sysConfigService.selectConfigByKey(UserConstants.BLACK_IP_KEY); String blackIpList = sysConfigService.selectConfigByKey(UserConstants.BLACK_IP_KEY);
if (StringUtils.hasText(blackIpList)) { if (StringUtils.hasText(blackIpList)) {
String currentIp = getCurrentIp(); String currentIp = getCurrentIp();
...@@ -180,9 +180,7 @@ public class SysLoginServiceImpl implements SysLoginService { ...@@ -180,9 +180,7 @@ public class SysLoginServiceImpl implements SysLoginService {
loginUser.setPermissions(permissions); loginUser.setPermissions(permissions);
// 7. 生成 token // 7. 生成 token
String token = tokenHandle.createToken(loginUser); return tokenHandle.createToken(loginUser);
log.info("用户[{}]登录成功", username);
return token;
} }
/** /**
...@@ -444,7 +442,7 @@ public class SysLoginServiceImpl implements SysLoginService { ...@@ -444,7 +442,7 @@ public class SysLoginServiceImpl implements SysLoginService {
} }
/** /**
* 检查 IP 是否匹配黑名单(与 custom-back IpUtils.isMatchedIp 对齐) * 检查 IP 是否匹配黑名单
* <p> * <p>
* 支持:精确匹配、通配符匹配(如 192.168.*.100) * 支持:精确匹配、通配符匹配(如 192.168.*.100)
*/ */
......
...@@ -12,8 +12,6 @@ import com.jomalls.custom.app.exception.ServiceException; ...@@ -12,8 +12,6 @@ import com.jomalls.custom.app.exception.ServiceException;
import com.jomalls.custom.app.service.system.SysRoleService; import com.jomalls.custom.app.service.system.SysRoleService;
import com.jomalls.custom.app.service.system.SysUserService; import com.jomalls.custom.app.service.system.SysUserService;
import com.jomalls.custom.app.utils.BeanMapper; import com.jomalls.custom.app.utils.BeanMapper;
import com.jomalls.custom.app.utils.CharsetKit;
import com.jomalls.custom.app.utils.UniqueId;
import com.jomalls.custom.app.vo.system.SysDeptVO; import com.jomalls.custom.app.vo.system.SysDeptVO;
import com.jomalls.custom.app.vo.system.SysRoleVO; import com.jomalls.custom.app.vo.system.SysRoleVO;
import com.jomalls.custom.app.vo.system.SysUserVO; import com.jomalls.custom.app.vo.system.SysUserVO;
...@@ -21,13 +19,17 @@ import com.jomalls.custom.dal.entity.*; ...@@ -21,13 +19,17 @@ import com.jomalls.custom.dal.entity.*;
import com.jomalls.custom.domain.service.system.*; import com.jomalls.custom.domain.service.system.*;
import com.jomalls.custom.security.LoginUser; import com.jomalls.custom.security.LoginUser;
import com.jomalls.custom.security.SecurityUtils; import com.jomalls.custom.security.SecurityUtils;
import com.jomalls.custom.utils.UniqueId;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.dromara.x.file.storage.core.FileInfo;
import org.dromara.x.file.storage.core.FileStorageService;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
...@@ -50,6 +52,7 @@ public class SysUserServiceImpl implements SysUserService { ...@@ -50,6 +52,7 @@ public class SysUserServiceImpl implements SysUserService {
private final SysDeptDomainService sysDeptDomainService; private final SysDeptDomainService sysDeptDomainService;
private final SysPostDomainService sysPostDomainService; private final SysPostDomainService sysPostDomainService;
private final PasswordEncoder passwordEncoder; private final PasswordEncoder passwordEncoder;
private final FileStorageService fileStorageService;
@Override @Override
public IPage<SysUserVO> pageList(SysUserVO user, Page<SysUserVO> page) { public IPage<SysUserVO> pageList(SysUserVO user, Page<SysUserVO> page) {
...@@ -107,9 +110,7 @@ public class SysUserServiceImpl implements SysUserService { ...@@ -107,9 +110,7 @@ public class SysUserServiceImpl implements SysUserService {
if (!StringUtils.hasText(user.getUserName())) { if (!StringUtils.hasText(user.getUserName())) {
throw new ServiceException("新增用户'" + user.getUserName() + "'失败,登录账号不能为空"); throw new ServiceException("新增用户'" + user.getUserName() + "'失败,登录账号不能为空");
} }
if (CharsetKit.containsChinese(user.getUserName())) {
throw new ServiceException("新增用户'" + user.getUserName() + "'失败,登录账号请使用英文字符");
}
// 用户名校验 // 用户名校验
long count = sysUserDomainService.count( long count = sysUserDomainService.count(
new LambdaQueryWrapper<SysUserEntity>().eq(SysUserEntity::getUserName, user.getUserName())); new LambdaQueryWrapper<SysUserEntity>().eq(SysUserEntity::getUserName, user.getUserName()));
...@@ -339,11 +340,17 @@ public class SysUserServiceImpl implements SysUserService { ...@@ -339,11 +340,17 @@ public class SysUserServiceImpl implements SysUserService {
} }
@Override @Override
public void updateAvatar(String avatarUrl) { public String updateAvatar(MultipartFile file) {
LoginUser loginUser = SecurityUtils.getLoginUser(); LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser == null) { if (loginUser == null) {
throw new ServiceException("未登录"); throw new ServiceException("未登录");
} }
// 文件为空
if(file.isEmpty()) {
throw new ServiceException("上传图片异常,请联系管理员");
}
FileInfo fileInfo = fileStorageService.of(file).setPath("avatar/").upload();
String avatarUrl = fileInfo.getUrl();
SysUserEntity user = sysUserDomainService.getById(loginUser.getUserId()); SysUserEntity user = sysUserDomainService.getById(loginUser.getUserId());
if (user == null) { if (user == null) {
throw new ServiceException("用户不存在"); throw new ServiceException("用户不存在");
...@@ -351,6 +358,7 @@ public class SysUserServiceImpl implements SysUserService { ...@@ -351,6 +358,7 @@ public class SysUserServiceImpl implements SysUserService {
user.setAvatar(avatarUrl); user.setAvatar(avatarUrl);
sysUserDomainService.updateById(user); sysUserDomainService.updateById(user);
log.info("用户头像已更新: userId={}", loginUser.getUserId()); log.info("用户头像已更新: userId={}", loginUser.getUserId());
return avatarUrl;
} }
@Override @Override
...@@ -363,7 +371,7 @@ public class SysUserServiceImpl implements SysUserService { ...@@ -363,7 +371,7 @@ public class SysUserServiceImpl implements SysUserService {
if (currentUser == null) { if (currentUser == null) {
throw new ServiceException("用户不存在"); throw new ServiceException("用户不存在");
} }
// 只更新允许的字段(nickName/email/phonenumber/sex,与 custom-back 对齐 // 只更新允许的字段(nickName/email/phonenumber/sex)
if (StringUtils.hasText(user.getNickName())) currentUser.setNickName(user.getNickName()); if (StringUtils.hasText(user.getNickName())) currentUser.setNickName(user.getNickName());
if (StringUtils.hasText(user.getEmail())) currentUser.setEmail(user.getEmail()); if (StringUtils.hasText(user.getEmail())) currentUser.setEmail(user.getEmail());
if (StringUtils.hasText(user.getPhonenumber())) currentUser.setPhonenumber(user.getPhonenumber()); if (StringUtils.hasText(user.getPhonenumber())) currentUser.setPhonenumber(user.getPhonenumber());
......
...@@ -2,6 +2,7 @@ package com.jomalls.custom.security; ...@@ -2,6 +2,7 @@ package com.jomalls.custom.security;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.jomalls.custom.utils.UniqueId;
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException; import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts; import io.jsonwebtoken.Jwts;
...@@ -239,7 +240,7 @@ public class TokenHandle { ...@@ -239,7 +240,7 @@ public class TokenHandle {
* 用户权限、部门等全量信息存储在 Redis login_tokens:{uuid} 中。 * 用户权限、部门等全量信息存储在 Redis login_tokens:{uuid} 中。
*/ */
public String createToken(LoginUser loginUser) { public String createToken(LoginUser loginUser) {
String uuid = java.util.UUID.randomUUID().toString().replace("-", ""); String uuid = UniqueId.generateUniqueId();
loginUser.setToken(uuid); loginUser.setToken(uuid);
loginUser.setLoginTime(System.currentTimeMillis()); loginUser.setLoginTime(System.currentTimeMillis());
loginUser.setExpireTime(System.currentTimeMillis() + expireTime * 60 * 1000L); loginUser.setExpireTime(System.currentTimeMillis() + expireTime * 60 * 1000L);
...@@ -268,8 +269,8 @@ public class TokenHandle { ...@@ -268,8 +269,8 @@ public class TokenHandle {
*/ */
private String buildJwtCompatibly(String uuid, LoginUser loginUser) { private String buildJwtCompatibly(String uuid, LoginUser loginUser) {
try { try {
// Base64URL 编码(与 JJWT 0.9.1 TextCodec.BASE64URL 兼容,适配custom的ts服务 使用同一格式)
Base64.Encoder encoder = Base64.getUrlEncoder().withoutPadding(); Base64.Encoder encoder = Base64.getUrlEncoder().withoutPadding();
log.info(JSON.toJSONString(loginUser));
// Header: {"alg":"HS512"} // Header: {"alg":"HS512"}
String header = "{\"alg\":\"HS512\"}"; String header = "{\"alg\":\"HS512\"}";
String headerBase64 = encoder.encodeToString( String headerBase64 = encoder.encodeToString(
...@@ -286,7 +287,12 @@ public class TokenHandle { ...@@ -286,7 +287,12 @@ public class TokenHandle {
// JWT 24 小时硬过期(与 custom-back TOKEN_TIMEOUT = 86400000L 一致) // JWT 24 小时硬过期(与 custom-back TOKEN_TIMEOUT = 86400000L 一致)
claims.put("exp", (System.currentTimeMillis() + 86400000L) / 1000); claims.put("exp", (System.currentTimeMillis() + 86400000L) / 1000);
String payload = JSON.toJSONString(claims); // 使用 Jackson 序列化 claims,非 ASCII 字符转 \\uXXXX 转义
// 前端 atob() → JSON.parse() 无法正确解码 UTF-8 多字节字符
// \\uXXXX 全为 ASCII,前端可正常 parse
String payload = new com.fasterxml.jackson.databind.ObjectMapper()
.configure(com.fasterxml.jackson.core.json.JsonWriteFeature.ESCAPE_NON_ASCII.mappedFeature(), true)
.writeValueAsString(claims);
String payloadBase64 = encoder.encodeToString( String payloadBase64 = encoder.encodeToString(
payload.getBytes(java.nio.charset.StandardCharsets.UTF_8)); payload.getBytes(java.nio.charset.StandardCharsets.UTF_8));
......
package com.jomalls.custom.app.utils; package com.jomalls.custom.utils;
import java.util.UUID; import java.util.UUID;
...@@ -18,4 +18,12 @@ public class UniqueId { ...@@ -18,4 +18,12 @@ public class UniqueId {
public static String generateUniqueId(int length) { public static String generateUniqueId(int length) {
return UUID.randomUUID().toString().replace("-", "").substring(START_INDEX, length); return UUID.randomUUID().toString().replace("-", "").substring(START_INDEX, length);
} }
/**
* 生成UUID
* @return
*/
public static String generateUniqueId() {
return UUID.randomUUID().toString().replace("-", "");
}
} }
...@@ -34,18 +34,17 @@ import java.util.concurrent.atomic.AtomicReference; ...@@ -34,18 +34,17 @@ import java.util.concurrent.atomic.AtomicReference;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class SaasAdminServiceImpl implements SaasAdminService { public class SaasAdminServiceImpl implements SaasAdminService {
/** 获取admin的分类树接口 */
private static final String GET_ALL_LIST_URL = "/api/manage/rest/baseCategoryInfo/all_list"; private static final String GET_ALL_LIST_URL = "/api/manage/rest/baseCategoryInfo/all_list";
/** 根据id获取admin的分类接口 */
private static final String GET_PROPERTY_BY_IDS_URL = "/api/manage/rest/baseProperty/getByIds"; private static final String GET_PROPERTY_BY_IDS_URL = "/api/manage/rest/baseProperty/getByIds";
/** 获取国家代码接口 */
private static final String GET_ALL_COUNTRY_CODE_URL = "/api/manage/rest/getAllCountry"; private static final String GET_ALL_COUNTRY_CODE_URL = "/api/manage/rest/getAllCountry";
private static final int DEFAULT_MAP_SIZE = 2; private static final int DEFAULT_MAP_SIZE = 2;
/** 分类列表缓存 TTL(毫秒):5 分钟 */ /** 分类数 & 国家代码缓存 默认TTL(毫秒):1 小时 */
private static final long CACHE_TTL_MS = 5 * 60 * 1000; private static final long DEFAULT_CACHE_TTL_MS = 60 * 60 * 1000;
/** 国家代码缓存 TTL(毫秒):1 小时 */
private static final long COUNTRY_CODE_CACHE_TTL_MS = 60 * 60 * 1000;
private final RemoteApiClient remoteApiClient; private final RemoteApiClient remoteApiClient;
...@@ -163,7 +162,7 @@ public class SaasAdminServiceImpl implements SaasAdminService { ...@@ -163,7 +162,7 @@ public class SaasAdminServiceImpl implements SaasAdminService {
String body = response.getBody(); String body = response.getBody();
List<CountryCodeModel> data = JSON.parseArray(body, CountryCodeModel.class); List<CountryCodeModel> data = JSON.parseArray(body, CountryCodeModel.class);
if (!data.isEmpty()) { if (!data.isEmpty()) {
countryCodeCache.set(new CacheEntry<>(data, COUNTRY_CODE_CACHE_TTL_MS)); countryCodeCache.set(new CacheEntry<>(data));
} }
return data; return data;
} }
...@@ -185,7 +184,7 @@ public class SaasAdminServiceImpl implements SaasAdminService { ...@@ -185,7 +184,7 @@ public class SaasAdminServiceImpl implements SaasAdminService {
CacheEntry(T data) { CacheEntry(T data) {
this.data = data; this.data = data;
this.expireAt = System.currentTimeMillis() + CACHE_TTL_MS; this.expireAt = System.currentTimeMillis() + DEFAULT_CACHE_TTL_MS;
} }
CacheEntry(T data, long ttlMs) { CacheEntry(T data, long ttlMs) {
......
package com.jomalls.custom.webapp.controller.system; package com.jomalls.custom.webapp.controller.system;
import com.jomalls.custom.app.constant.UserConstants;
import com.jomalls.custom.utils.UniqueId;
import com.jomalls.custom.utils.R; import com.jomalls.custom.utils.R;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
...@@ -15,10 +16,7 @@ import java.awt.*; ...@@ -15,10 +16,7 @@ import java.awt.*;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.util.Base64; import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
...@@ -48,7 +46,7 @@ public class CaptchaController { ...@@ -48,7 +46,7 @@ public class CaptchaController {
@Operation(summary = "生成验证码") @Operation(summary = "生成验证码")
public R<Object> getCode() { public R<Object> getCode() {
// 生成 UUID 标识 // 生成 UUID 标识
String uuid = UUID.randomUUID().toString().replace("-", ""); String uuid = UniqueId.generateUniqueId(UserConstants.INIT_PASSWORD_LENGTH);;
// 生成数学验证码 // 生成数学验证码
int a = new Random().nextInt(50) + 1; int a = new Random().nextInt(50) + 1;
......
...@@ -55,10 +55,7 @@ public class SysProfileController { ...@@ -55,10 +55,7 @@ public class SysProfileController {
@Operation(summary = "更新当前用户头像") @Operation(summary = "更新当前用户头像")
@PostMapping("/avatar") @PostMapping("/avatar")
public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) { public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) {
log.info("上传头像文件: {}", file.getOriginalFilename()); String avatarUrl = sysUserService.updateAvatar(file);
String avatarUrl = "/profile/avatar/" + file.getOriginalFilename();
sysUserService.updateAvatar(avatarUrl);
AjaxResult ajax = AjaxResult.success(); AjaxResult ajax = AjaxResult.success();
ajax.put("imgUrl", avatarUrl); ajax.put("imgUrl", avatarUrl);
return ajax; return ajax;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment