diff --git a/src/main/java/com/corewing/app/controller/AppUserController.java b/src/main/java/com/corewing/app/controller/AppUserController.java index 860ed57..bd42d4f 100644 --- a/src/main/java/com/corewing/app/controller/AppUserController.java +++ b/src/main/java/com/corewing/app/controller/AppUserController.java @@ -55,10 +55,10 @@ public class AppUserController { try { String token = userService.login(request.getAccount(), request.getPassword()); - // 更新登录IP + // 更新登录IP和归属地 AppUser user = userService.getByAccount(request.getAccount()); String loginIp = IpUtil.getClientIp(httpRequest); - userService.updateLoginIp(user.getId(), loginIp); + userService.updateLoginInfo(user.getId(), loginIp); Map data = new HashMap<>(); data.put("token", token); @@ -75,7 +75,7 @@ public class AppUserController { * 用户注册(需要验证码) */ @PostMapping("/register") - public Result register(@RequestBody RegisterRequest request) { + public Result register(@RequestBody RegisterRequest request, HttpServletRequest httpRequest) { try { AppUser user = new AppUser(); user.setUsername(request.getUsername()); @@ -84,7 +84,9 @@ public class AppUserController { user.setTelephone(request.getTelephone()); user.setAvatar(request.getAvatar()); - userService.register(user, request.getCode()); + // 获取注册IP + String registerIp = IpUtil.getClientIp(httpRequest); + userService.register(user, request.getCode(), registerIp); return Result.success("注册成功"); } catch (Exception e) { return Result.error(e.getMessage()); diff --git a/src/main/java/com/corewing/app/service/AppUserService.java b/src/main/java/com/corewing/app/service/AppUserService.java index c5fc245..8ab997a 100644 --- a/src/main/java/com/corewing/app/service/AppUserService.java +++ b/src/main/java/com/corewing/app/service/AppUserService.java @@ -54,9 +54,10 @@ public interface AppUserService extends IService { * * @param user 用户信息 * @param code 验证码 + * @param registerIp 注册IP * @return 是否成功 */ - boolean register(AppUser user, String code); + boolean register(AppUser user, String code, String registerIp); /** * 更新登录IP @@ -65,4 +66,12 @@ public interface AppUserService extends IService { * @param loginIp 登录IP */ void updateLoginIp(Long userId, String loginIp); + + /** + * 更新登录信息(IP和归属地) + * + * @param userId 用户ID + * @param loginIp 登录IP + */ + void updateLoginInfo(Long userId, String loginIp); } diff --git a/src/main/java/com/corewing/app/service/impl/AppUserServiceImpl.java b/src/main/java/com/corewing/app/service/impl/AppUserServiceImpl.java index 8347a7b..4d30869 100644 --- a/src/main/java/com/corewing/app/service/impl/AppUserServiceImpl.java +++ b/src/main/java/com/corewing/app/service/impl/AppUserServiceImpl.java @@ -7,6 +7,7 @@ import com.corewing.app.entity.AppUser; import com.corewing.app.mapper.AppUserMapper; import com.corewing.app.service.AppUserService; import com.corewing.app.service.VerifyCodeService; +import com.corewing.app.util.Ip2RegionUtil; import org.springframework.stereotype.Service; import org.springframework.util.DigestUtils; import org.springframework.util.StringUtils; @@ -20,9 +21,11 @@ import java.nio.charset.StandardCharsets; public class AppUserServiceImpl extends ServiceImpl implements AppUserService { private final VerifyCodeService verifyCodeService; + private final Ip2RegionUtil ip2RegionUtil; - public AppUserServiceImpl(VerifyCodeService verifyCodeService) { + public AppUserServiceImpl(VerifyCodeService verifyCodeService, Ip2RegionUtil ip2RegionUtil) { this.verifyCodeService = verifyCodeService; + this.ip2RegionUtil = ip2RegionUtil; } @Override @@ -92,7 +95,7 @@ public class AppUserServiceImpl extends ServiceImpl impl } @Override - public boolean register(AppUser user, String code) { + public boolean register(AppUser user, String code, String registerIp) { // 检查用户名是否已存在 AppUser existUser = getByUsername(user.getUsername()); if (existUser != null) { @@ -136,6 +139,12 @@ public class AppUserServiceImpl extends ServiceImpl impl user.setStatus(1); } + // 设置注册IP和归属地 + if (StringUtils.hasText(registerIp)) { + user.setRegisterIp(registerIp); + user.setRegisterRegion(ip2RegionUtil.getRegion(registerIp)); + } + // 保存用户 return this.save(user); } @@ -147,4 +156,13 @@ public class AppUserServiceImpl extends ServiceImpl impl user.setLoginIp(loginIp); this.updateById(user); } + + @Override + public void updateLoginInfo(Long userId, String loginIp) { + AppUser user = new AppUser(); + user.setId(userId); + user.setLoginIp(loginIp); + user.setLoginRegion(ip2RegionUtil.getRegion(loginIp)); + this.updateById(user); + } } diff --git a/src/main/java/com/corewing/app/util/Ip2RegionUtil.java b/src/main/java/com/corewing/app/util/Ip2RegionUtil.java new file mode 100644 index 0000000..b0a8911 --- /dev/null +++ b/src/main/java/com/corewing/app/util/Ip2RegionUtil.java @@ -0,0 +1,179 @@ +package com.corewing.app.util; + +import lombok.extern.slf4j.Slf4j; +import org.lionsoul.ip2region.xdb.Searcher; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.io.InputStream; + +/** + * IP 归属地查询工具类 + */ +@Slf4j +@Component +public class Ip2RegionUtil { + + private Searcher searcher; + private byte[] vIndex; + + /** + * 初始化 IP2Region 搜索器 + */ + @PostConstruct + public void init() { + try { + // 从 classpath 加载 xdb 文件 + ClassPathResource resource = new ClassPathResource("ipdb/ip2region.xdb"); + InputStream inputStream = resource.getInputStream(); + + // 将 InputStream 转换为 byte[] + vIndex = new byte[inputStream.available()]; + inputStream.read(vIndex); + inputStream.close(); + + // 创建 searcher 对象 + searcher = Searcher.newWithBuffer(vIndex); + log.info("IP2Region 初始化成功"); + } catch (Exception e) { + log.error("IP2Region 初始化失败: {}", e.getMessage(), e); + } + } + + /** + * 根据 IP 地址查询归属地 + * + * @param ip IP 地址 + * @return 归属地信息,格式:国家|区域|省份|城市|ISP + */ + public String getRegion(String ip) { + if (ip == null || ip.trim().isEmpty()) { + return "未知"; + } + + // 过滤本地 IP + if (isLocalIp(ip)) { + return "局域网"; + } + + try { + if (searcher == null) { + log.warn("IP2Region 搜索器未初始化"); + return "未知"; + } + + String region = searcher.search(ip); + return formatRegion(region); + } catch (Exception e) { + log.error("查询 IP 归属地失败, IP: {}, 异常: {}", ip, e.getMessage()); + return "未知"; + } + } + + /** + * 格式化归属地信息 + * 原始格式:国家|区域|省份|城市|ISP + * 格式化后:省份-城市 + * + * @param region 原始归属地信息 + * @return 格式化后的归属地 + */ + private String formatRegion(String region) { + if (region == null || region.trim().isEmpty()) { + return "未知"; + } + + try { + String[] parts = region.split("\\|"); + if (parts.length < 5) { + return region; + } + + String country = parts[0]; + String province = parts[2]; + String city = parts[3]; + + // 处理国内 IP + if ("中国".equals(country)) { + // 如果省份和城市相同,只返回省份 + if (province.equals(city)) { + return province; + } + // 如果省份或城市为 0,说明数据不完整 + if ("0".equals(province) || "0".equals(city)) { + return "中国"; + } + return province + "-" + city; + } + + // 处理国外 IP + if (!"0".equals(country) && !country.isEmpty()) { + return country; + } + + return "未知"; + } catch (Exception e) { + log.error("格式化归属地信息失败: {}", region, e); + return region; + } + } + + /** + * 判断是否为本地 IP + * + * @param ip IP 地址 + * @return 是否为本地 IP + */ + private boolean isLocalIp(String ip) { + return ip.startsWith("127.") || + ip.startsWith("192.168.") || + ip.startsWith("10.") || + ip.startsWith("172.16.") || + ip.startsWith("172.17.") || + ip.startsWith("172.18.") || + ip.startsWith("172.19.") || + ip.startsWith("172.20.") || + ip.startsWith("172.21.") || + ip.startsWith("172.22.") || + ip.startsWith("172.23.") || + ip.startsWith("172.24.") || + ip.startsWith("172.25.") || + ip.startsWith("172.26.") || + ip.startsWith("172.27.") || + ip.startsWith("172.28.") || + ip.startsWith("172.29.") || + ip.startsWith("172.30.") || + ip.startsWith("172.31.") || + "localhost".equalsIgnoreCase(ip) || + "0:0:0:0:0:0:0:1".equals(ip); + } + + /** + * 获取完整的归属地信息(用于调试) + * + * @param ip IP 地址 + * @return 完整的归属地信息 + */ + public String getFullRegion(String ip) { + if (ip == null || ip.trim().isEmpty()) { + return "未知"; + } + + if (isLocalIp(ip)) { + return "局域网"; + } + + try { + if (searcher == null) { + log.warn("IP2Region 搜索器未初始化"); + return "未知"; + } + + return searcher.search(ip); + } catch (Exception e) { + log.error("查询 IP 归属地失败, IP: {}, 异常: {}", ip, e.getMessage()); + return "未知"; + } + } +} diff --git a/src/main/resources/ipdb/ip2region.xdb b/src/main/resources/ipdb/ip2region.xdb new file mode 100644 index 0000000..6f86c7d Binary files /dev/null and b/src/main/resources/ipdb/ip2region.xdb differ