From 0b10f87268bd511c7d8dddad30060abefa49aab2 Mon Sep 17 00:00:00 2001
From: zj <1772600164@qq.com>
Date: Tue, 19 Aug 2025 20:25:16 +0800
Subject: [PATCH] 1
---
ruoyi-admin/src/main/java/com/ruoyi/im/util/SymmetricCryptoUtil.java | 116 +++++++
ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyMetaObjectHandler.java | 37 ++
ruoyi-admin/pom.xml | 20 +
ruoyi-system/pom.xml | 25 +
ruoyi-system/src/main/java/com/ruoyi/system/domain/UserAccount.java | 81 +++++
ruoyi-admin/src/main/java/com/ruoyi/im/comm/Result.java | 2
pom.xml | 1
ruoyi-admin/src/main/resources/application.yml | 26 +
ruoyi-admin/src/main/java/com/ruoyi/im/service/impl/ImApiServcieImpl.java | 180 ++++++++++++
ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java | 36 ++
/dev/null | 69 ----
ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java | 1
ruoyi-admin/src/main/java/com/ruoyi/im/service/ImApiServcie.java | 8
ruoyi-admin/src/main/java/com/ruoyi/im/ImApiController.java | 35 ++
ruoyi-admin/src/main/java/com/ruoyi/im/comm/ImConstant.java | 15 +
ruoyi-admin/src/main/java/com/ruoyi/im/config/AppAuthConfig.java | 39 ++
ruoyi-admin/src/main/java/com/ruoyi/im/comm/ImBusinessException.java | 11
ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java | 106 ------
ruoyi-admin/src/main/java/com/ruoyi/im/dto/RegisterDto.java | 4
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java | 1
ruoyi-system/src/main/java/com/ruoyi/system/service/UserAccountService.java | 7
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserAccountServiceImpl.java | 16 +
ruoyi-system/src/main/java/com/ruoyi/system/mapper/UserAccountMapper.java | 14
23 files changed, 677 insertions(+), 173 deletions(-)
diff --git a/pom.xml b/pom.xml
index dab4a91..57062bd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -217,7 +217,6 @@
<artifactId>ruoyi-common</artifactId>
<version>${ruoyi.version}</version>
</dependency>
-
</dependencies>
</dependencyManagement>
diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml
index c9e3ba3..bef9bb1 100644
--- a/ruoyi-admin/pom.xml
+++ b/ruoyi-admin/pom.xml
@@ -64,6 +64,26 @@
<artifactId>ruoyi-generator</artifactId>
</dependency>
+ <dependency>
+ <groupId>com.netease.nim</groupId>
+ <artifactId>yunxin-server-sdk</artifactId>
+ <version>1.1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>com.baomidou</groupId>
+ <artifactId>mybatis-plus-boot-starter</artifactId>
+ <version>3.5.3.1</version> <!-- 使用最新版本 -->
+ </dependency>
+ <dependency>
+ <groupId>com.baomidou</groupId>
+ <artifactId>mybatis-plus</artifactId>
+ <version>3.5.3.1</version>
+ </dependency>
+ <dependency>
+ <groupId>cn.hutool</groupId>
+ <artifactId>hutool-all</artifactId>
+ <version>5.7.12</version>
+ </dependency>
</dependencies>
<build>
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java
index 32eb6f1..d37e153 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java
@@ -1,5 +1,6 @@
package com.ruoyi;
+import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/im/ImApiController.java b/ruoyi-admin/src/main/java/com/ruoyi/im/ImApiController.java
new file mode 100644
index 0000000..6b12c7b
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/ImApiController.java
@@ -0,0 +1,35 @@
+package com.ruoyi.im;
+
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.im.comm.Result;
+import com.ruoyi.im.service.ImApiServcie;
+import com.ruoyi.im.dto.RegisterDto;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/im/api")
+@Slf4j
+public class ImApiController {
+
+ @Autowired
+ private ImApiServcie imApiServcie;
+
+ /**
+ * 获取参数配置列表
+ */
+ @PostMapping("/register")
+ public Result register(@Validated RegisterDto dto){
+ try {
+ return imApiServcie.register(dto);
+ }catch (Exception e){
+ log.error("注册报错:",e);
+ return Result.error("注册失败,请稍后再试!");
+ }
+ }
+}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/im/comm/ImBusinessException.java b/ruoyi-admin/src/main/java/com/ruoyi/im/comm/ImBusinessException.java
new file mode 100644
index 0000000..587bec3
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/comm/ImBusinessException.java
@@ -0,0 +1,11 @@
+package com.ruoyi.im.comm;
+
+public class ImBusinessException extends RuntimeException {
+ public ImBusinessException(String message) {
+ super(message);
+ }
+
+ public ImBusinessException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/im/comm/ImConstant.java b/ruoyi-admin/src/main/java/com/ruoyi/im/comm/ImConstant.java
new file mode 100644
index 0000000..eff9c7d
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/comm/ImConstant.java
@@ -0,0 +1,15 @@
+package com.ruoyi.im.comm;
+
+public class ImConstant {
+ // 云信API路径
+ public static final String YUNXIN_CREATE_USER_PATH = "/user/create.action";
+
+ // 云信响应码
+ public static final int YUNXIN_SUCCESS_CODE = 200;
+ public static final int YUNXIN_ACCOUNT_EXISTS_CODE = 414;
+
+ // 业务常量
+ public static final int MAX_GENERATE_ACCOUNT_ATTEMPTS = 10;
+ public static final int MIN_GENERATE_DELAY_MS = 5;
+ public static final int MAX_GENERATE_DELAY_MS = 30;
+}
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/comm/Result.java b/ruoyi-admin/src/main/java/com/ruoyi/im/comm/Result.java
similarity index 98%
rename from ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/comm/Result.java
rename to ruoyi-admin/src/main/java/com/ruoyi/im/comm/Result.java
index e4f72bf..b23ad45 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/comm/Result.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/comm/Result.java
@@ -1,4 +1,4 @@
-package com.ruoyi.web.controller.api.comm;
+package com.ruoyi.im.comm;
import java.io.Serializable;
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/im/config/AppAuthConfig.java b/ruoyi-admin/src/main/java/com/ruoyi/im/config/AppAuthConfig.java
new file mode 100644
index 0000000..e9b5674
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/config/AppAuthConfig.java
@@ -0,0 +1,39 @@
+package com.ruoyi.im.config;
+
+public enum AppAuthConfig {
+
+ /**
+ * 应用认证配置
+ */
+ DEFAULT_CONFIG("cd693c66bb6992a7cc2b13379e981d48", "720c0b98f7a3");
+
+ private final String appKey;
+ private final String appSecret;
+
+ AppAuthConfig(String appKey, String appSecret) {
+ this.appKey = appKey;
+ this.appSecret = appSecret;
+ }
+
+ public String getAppKey() {
+ return appKey;
+ }
+
+ public String getAppSecret() {
+ return appSecret;
+ }
+
+ /**
+ * 根据key查找配置
+ * @param key 应用key
+ * @return 对应的配置,找不到返回null
+ */
+ public static AppAuthConfig getByKey(String key) {
+ for (AppAuthConfig config : values()) {
+ if (config.getAppKey().equals(key)) {
+ return config;
+ }
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/dto/RegisterDto.java b/ruoyi-admin/src/main/java/com/ruoyi/im/dto/RegisterDto.java
similarity index 85%
rename from ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/dto/RegisterDto.java
rename to ruoyi-admin/src/main/java/com/ruoyi/im/dto/RegisterDto.java
index c97b94c..bf0ce54 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/dto/RegisterDto.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/dto/RegisterDto.java
@@ -1,4 +1,4 @@
-package com.ruoyi.web.controller.api.dto;
+package com.ruoyi.im.dto;
import lombok.Data;
@@ -9,7 +9,7 @@
public class RegisterDto {
@NotNull(message = "账号不能为空")
- private Integer account; // 账号
+ private String account; // 账号
@NotEmpty(message = "密码不能为空")
private String password; // 密码
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/im/service/ImApiServcie.java b/ruoyi-admin/src/main/java/com/ruoyi/im/service/ImApiServcie.java
new file mode 100644
index 0000000..c658f6e
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/service/ImApiServcie.java
@@ -0,0 +1,8 @@
+package com.ruoyi.im.service;
+
+import com.ruoyi.im.comm.Result;
+import com.ruoyi.im.dto.RegisterDto;
+
+public interface ImApiServcie {
+ Result register(RegisterDto dto);
+}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/im/service/impl/ImApiServcieImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/im/service/impl/ImApiServcieImpl.java
new file mode 100644
index 0000000..6a260c1
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/service/impl/ImApiServcieImpl.java
@@ -0,0 +1,180 @@
+package com.ruoyi.im.service.impl;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.netease.nim.server.sdk.core.BizName;
+import com.netease.nim.server.sdk.core.YunxinApiHttpClient;
+import com.netease.nim.server.sdk.core.YunxinApiResponse;
+import com.netease.nim.server.sdk.core.exception.YunxinSdkException;
+import com.ruoyi.im.comm.Result;
+import com.ruoyi.im.config.AppAuthConfig;
+import com.ruoyi.system.domain.UserAccount;
+import com.ruoyi.im.service.ImApiServcie;
+import com.ruoyi.im.dto.RegisterDto;
+import com.ruoyi.system.service.UserAccountService;
+import com.ruoyi.im.util.SymmetricCryptoUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+import javax.annotation.PostConstruct;
+import java.time.Instant;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicLong;
+
+@Service
+public class ImApiServcieImpl implements ImApiServcie {
+ @Autowired
+ private UserAccountService userAccountService;
+
+ // 使用单例客户端,避免重复初始化
+ private YunxinApiHttpClient yunxinClient;
+
+ // 使用并发安全的集合存储正在生成的账号,防止重复
+ private final Set<Long> generatingAccounts = Collections.newSetFromMap(new ConcurrentHashMap<>());
+
+ // 使用原子计数器优化账号生成
+ private final AtomicLong lastTimestamp = new AtomicLong(0);
+ private final AtomicLong sequence = new AtomicLong(0);
+
+ @PostConstruct
+ public void init() {
+ // 初始化云信客户端,只执行一次
+ String appkey = AppAuthConfig.DEFAULT_CONFIG.getAppKey();
+ String appsecret = AppAuthConfig.DEFAULT_CONFIG.getAppSecret();
+ int timeoutMillis = 5000;
+
+ this.yunxinClient = new YunxinApiHttpClient.Builder(BizName.IM, appkey, appsecret)
+ .timeoutMillis(timeoutMillis)
+ .build();
+ }
+
+ @Override
+ public Result register(RegisterDto dto) {
+ // 短信验证 TODO
+
+ // 手机号是否存在 - 添加分布式锁或数据库唯一索引确保并发安全
+ List<UserAccount> accounts = userAccountService.list(
+ new LambdaQueryWrapper<>(UserAccount.class)
+ .eq(UserAccount::getAccount, dto.getAccount())
+ );
+
+ if(!CollectionUtils.isEmpty(accounts)){
+ return Result.error("手机号已注册!");
+ }
+
+// // 生成云信账号
+// long cloudMessageAccount;
+// try {
+// cloudMessageAccount = generateUniqueCloudMessageAccount();
+// } catch (RuntimeException e) {
+// return Result.error("系统繁忙,请稍后重试");
+// }
+
+ // 注册云信
+ String path = "/user/create.action";
+ Map<String, String> paramMap = new HashMap<>();
+ paramMap.put("accid", String.valueOf(dto.getAccount()));
+ paramMap.put("token", dto.getPassword());
+
+ YunxinApiResponse response;
+ try {
+ response = yunxinClient.executeV1Api(path, paramMap);
+ } catch (YunxinSdkException e) {
+ System.err.println("register error, traceId = " + e.getTraceId());
+ return Result.error("注册失败,系统异常");
+ }
+
+ // 获取结果
+ String data = response.getData();
+ JSONObject json = JSONObject.parseObject(data);
+ int code = json.getIntValue("code");
+
+ if (code == 200) {
+ // 注册成功
+ UserAccount userAccount = new UserAccount();
+ userAccount.setAccount(dto.getAccount());
+ userAccount.setPhoneNumber(dto.getAccount());
+ userAccount.setCloudMessageAccount(dto.getAccount());
+ userAccount.setPassword(SymmetricCryptoUtil.encryptPassword(dto.getPassword()));
+
+ try {
+ userAccountService.save(userAccount);
+ return Result.success("注册成功");
+ } catch (Exception e) {
+ // 处理数据库插入异常,可能需要回滚云信账号
+ return Result.error("注册失败,请重试");
+ }
+ } else if (code == 414) {
+ return Result.error("账号已被注册!");
+ } else {
+ System.err.println("register fail, response = " + data + ", traceId=" + response.getTraceId());
+ return Result.error("注册失败,错误码: " + code);
+ }
+ }
+
+ /**
+ * 优化的账号生成方法,使用雪花算法变体提高并发性能
+ */
+ public long generateUniqueCloudMessageAccount() {
+ final int maxAttempts = 10; // 减少尝试次数,提高效率
+
+ for (int attempt = 0; attempt < maxAttempts; attempt++) {
+ long account = generateSnowflakeLikeId();
+
+ // 使用并发集合检查是否正在生成相同账号
+ if (generatingAccounts.add(account)) {
+ try {
+ // 检查数据库是否存在
+ boolean exists = userAccountService.lambdaQuery()
+ .eq(UserAccount::getCloudMessageAccount, account)
+ .exists();
+
+ if (!exists) {
+ return account;
+ }
+ } finally {
+ // 无论成功与否,都从集合中移除
+ generatingAccounts.remove(account);
+ }
+ }
+
+ // 短暂等待后重试
+ try {
+ Thread.sleep(ThreadLocalRandom.current().nextInt(10, 50));
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException("生成账号被中断", e);
+ }
+ }
+
+ throw new RuntimeException("无法在" + maxAttempts + "次尝试内生成唯一账号");
+ }
+
+ /**
+ * 基于雪花算法的ID生成器,提高并发性能
+ */
+ private long generateSnowflakeLikeId() {
+ long currentTime = System.currentTimeMillis();
+ long timestamp = currentTime % 100_000_000L; // 取时间戳的后8位
+
+ // 使用原子操作确保序列号递增
+ long seq;
+ long lastTime;
+ do {
+ lastTime = lastTimestamp.get();
+ if (currentTime == lastTime) {
+ seq = sequence.incrementAndGet() % 100;
+ } else {
+ sequence.set(0);
+ lastTimestamp.set(currentTime);
+ seq = 0;
+ }
+ } while (!lastTimestamp.compareAndSet(lastTime, currentTime));
+
+ // 组合时间戳和序列号,生成9位ID
+ return timestamp * 100 + seq;
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/im/util/SymmetricCryptoUtil.java b/ruoyi-admin/src/main/java/com/ruoyi/im/util/SymmetricCryptoUtil.java
new file mode 100644
index 0000000..46fe42e
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/im/util/SymmetricCryptoUtil.java
@@ -0,0 +1,116 @@
+package com.ruoyi.im.util;
+
+import cn.hutool.crypto.Mode;
+import cn.hutool.crypto.Padding;
+import cn.hutool.crypto.symmetric.AES;
+
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * @ClassName : SymmetricCryptoUtil //类名
+ * @Description : 对称加密工具类 //描述
+ * @Author : tf //作者
+ * @Date: 2022/10/23 23:31
+ */
+public class SymmetricCryptoUtil {
+
+ /**
+ * 16字节的密钥(128位)
+ */
+ private static final String ENCODE_KEY = "03SsJLwB03SsJLwB"; // 改为16字节
+
+ /**
+ * 16字节的初始化向量(128位)
+ */
+ private static final String IV_KEY = "0000000000000000"; // 改为16个0
+
+ public static void main(String[] args) {
+ String originalText = "zdm321123.";
+
+ String encryptData = encryptFromString(originalText, Mode.CBC, Padding.ZeroPadding);
+ System.out.println("加密:" + encryptData);
+
+ String decryptData = decryptFromString(encryptData, Mode.CBC, Padding.ZeroPadding);
+ System.out.println("解密:" + decryptData);
+
+ // 验证加解密是否一致
+ System.out.println("验证结果:" + originalText.equals(decryptData));
+ }
+
+ /**
+ * 加密字符串
+ */
+ public static String encryptFromString(String data, Mode mode, Padding padding) {
+ AES aes;
+ if (Mode.CBC == mode) {
+ aes = new AES(mode, padding,
+ new SecretKeySpec(ENCODE_KEY.getBytes(StandardCharsets.UTF_8), "AES"),
+ new IvParameterSpec(IV_KEY.getBytes(StandardCharsets.UTF_8)));
+ } else {
+ aes = new AES(mode, padding,
+ new SecretKeySpec(ENCODE_KEY.getBytes(StandardCharsets.UTF_8), "AES"));
+ }
+ return aes.encryptBase64(data, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * 解密字符串
+ */
+ public static String decryptFromString(String data, Mode mode, Padding padding) {
+ AES aes;
+ if (Mode.CBC == mode) {
+ aes = new AES(mode, padding,
+ new SecretKeySpec(ENCODE_KEY.getBytes(StandardCharsets.UTF_8), "AES"),
+ new IvParameterSpec(IV_KEY.getBytes(StandardCharsets.UTF_8)));
+ } else {
+ aes = new AES(mode, padding,
+ new SecretKeySpec(ENCODE_KEY.getBytes(StandardCharsets.UTF_8), "AES"));
+ }
+ byte[] decryptDataBase64 = aes.decrypt(data);
+ return new String(decryptDataBase64, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * @Description: 加密密码(使用CBC模式和ZeroPadding)
+ * @Param: password 明文密码
+ * @return: 加密后的Base64字符串
+ * @Author: tf
+ * @Date: 2022/10/23
+ */
+ public static String encryptPassword(String password) {
+ return encryptFromString(password, Mode.CBC, Padding.ZeroPadding);
+ }
+
+ /**
+ * @Description: 解密密码
+ * @Param: encryptedPassword 加密后的密码字符串
+ * @return: 解密后的明文密码
+ * @Author: tf
+ * @Date: 2022/10/23
+ */
+ public static String decryptPassword(String encryptedPassword) {
+ return decryptFromString(encryptedPassword, Mode.CBC, Padding.ZeroPadding);
+ }
+
+ /**
+ * 生成随机的16字节密钥(用于生产环境)
+ */
+ public static String generateRandomKey() {
+ java.security.SecureRandom random = new java.security.SecureRandom();
+ byte[] key = new byte[16];
+ random.nextBytes(key);
+ return new String(key, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * 生成随机的16字节IV(用于生产环境)
+ */
+ public static String generateRandomIV() {
+ java.security.SecureRandom random = new java.security.SecureRandom();
+ byte[] iv = new byte[16];
+ random.nextBytes(iv);
+ return new String(iv, StandardCharsets.UTF_8);
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/ImApiController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/ImApiController.java
deleted file mode 100644
index 7119fef..0000000
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/ImApiController.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.ruoyi.web.controller.api;
-
-import com.ruoyi.common.core.page.TableDataInfo;
-import com.ruoyi.system.domain.SysConfig;
-import com.ruoyi.web.controller.api.comm.Result;
-import com.ruoyi.web.controller.api.dto.RegisterDto;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import java.util.List;
-
-@RestController
-@RequestMapping("api/")
-public class ImApiController {
-
- /**
- * 获取参数配置列表
- */
- @GetMapping("/register")
- public Result register(RegisterDto dto){
- if (!dto.getPassword().equals(dto.getConfirmPassword())) {
- throw new IllegalArgumentException("两次输入的密码不一致");
- }
-
- return Result.success();
- }
-
-
-}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/entity/User.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/entity/User.java
deleted file mode 100644
index 0fcaef0..0000000
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/entity/User.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package com.ruoyi.web.controller.api.entity;
-
-import com.fasterxml.jackson.annotation.JsonFormat;
-import io.swagger.models.auth.In;
-import lombok.Data;
-
-import java.math.BigDecimal;
-import java.time.LocalDateTime;
-import java.util.Date;
-
-@Data
-public class User {
-
- // 自增ID主键
- private Integer id;
-
- // 账号(唯一)
- private String account;
-
- // 手机号(唯一)
- private String phoneNumber;
-
- // 密码
- private String password;
-
- // 账号类型: 0:真实 1:虚拟
- private Integer accountType;
-
- // 云信账号
- private String cloudMessageAccount;
-
- // 昵称
- private String nickname;
-
- // 账户余额
- private BigDecimal balance;
-
- // 账号状态: 0:正常 1:禁用
- private String status;
-
- // 最近登录时间
- private LocalDateTime loginTime;
-
- // 最近登录IP
- private String loginIp;
-
- // 注册时间
- private LocalDateTime registerTime;
-
- // 个性签名
- private String signature;
-
- // 是否支持昵称搜索
- private Boolean supportNicknameSearch;
-
- // 是否支持手机搜索
- private Boolean supportPhoneSearch;
-
- // 是否支持ID搜索
- private Boolean supportIdSearch;
-
- // 是否支持用户名搜索
- private Boolean supportUsernameSearch;
-
- // 是否申请删除账号
- private Boolean requestDelete;
-
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- private Date createTime;
-
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- private Date updateTime;
-
-}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/util/MD5Util.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/util/MD5Util.java
deleted file mode 100644
index 4f43713..0000000
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/util/MD5Util.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package com.ruoyi.web.controller.api.util;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.security.MessageDigest;
-
-public class MD5Util {
-
- private static final char[] HEX_CHARS = "0123456789abcdef".toCharArray();
-
- /**
- * 计算字符串MD5
- */
- public static String md5(String input) {
- if (input == null) return null;
- try {
- MessageDigest md = MessageDigest.getInstance("MD5");
- byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8));
- return bytesToHex(digest);
- } catch (Exception e) {
- throw new RuntimeException("MD5计算失败", e);
- }
- }
-
- /**
- * 计算文件MD5
- */
- public static String md5(File file) {
- if (file == null || !file.exists()) return null;
-
- try (FileInputStream fis = new FileInputStream(file)) {
- MessageDigest md = MessageDigest.getInstance("MD5");
- byte[] buffer = new byte[8192];
- int length;
- while ((length = fis.read(buffer)) != -1) {
- md.update(buffer, 0, length);
- }
- return bytesToHex(md.digest());
- } catch (Exception e) {
- throw new RuntimeException("文件MD5计算失败", e);
- }
- }
-
- /**
- * 字节数组转十六进制字符串
- */
- private static String bytesToHex(byte[] bytes) {
- if (bytes == null) return null;
- StringBuilder sb = new StringBuilder(bytes.length * 2);
- for (byte b : bytes) {
- sb.append(HEX_CHARS[(b >> 4) & 0x0F]);
- sb.append(HEX_CHARS[b & 0x0F]);
- }
- return sb.toString();
- }
-
- /**
- * 验证MD5
- */
- public static boolean verify(String input, String md5) {
- return md5(input).equalsIgnoreCase(md5);
- }
-
- public static boolean verify(File file, String md5) {
- return md5(file).equalsIgnoreCase(md5);
- }
-}
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index 7089c0f..db8c314 100644
--- a/ruoyi-admin/src/main/resources/application.yml
+++ b/ruoyi-admin/src/main/resources/application.yml
@@ -96,16 +96,30 @@
secret: abcdefghijklmnopqrstuvwxyz
# 令牌有效期(默认30分钟)
expireTime: 30
-
-# MyBatis配置
+# 原有 MyBatis 配置保持不变
mybatis:
- # 搜索指定包别名
typeAliasesPackage: com.ruoyi.**.domain
- # 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
- # 加载全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml
+# 新增 MyBatis-Plus 配置
+mybatis-plus:
+ # 可以指向新的目录,避免与原有XML冲突
+ mapper-locations: classpath*:mapper/**/*Mapper.xml
+ type-aliases-package: com.ruoyi.**.domain
+ global-config:
+ db-config:
+ id-type: AUTO
+ logic-delete-value: 2 # 若依默认逻辑删除值是2
+ logic-not-delete-value: 0
+ # 字段策略:0:忽略判断,1:非NULL判断,2:非空判断
+ insert-strategy: not_null
+ update-strategy: not_null
+ configuration:
+ # 保持与原有配置一致
+ map-underscore-to-camel-case: true
+ # 开启SQL日志
+ log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# PageHelper分页插件
pagehelper:
helperDialect: mysql
@@ -126,4 +140,4 @@
# 排除链接(多个用逗号分隔)
excludes: /system/notice
# 匹配链接
- urlPatterns: /system/*,/monitor/*,/tool/*
+ urlPatterns: /system/*,/monitor/*,/tool/*
\ No newline at end of file
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java
index 057c941..a85bd77 100644
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java
@@ -6,6 +6,8 @@
import java.util.HashSet;
import java.util.List;
import javax.sql.DataSource;
+
+import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.io.VFS;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
@@ -32,101 +34,19 @@
@Configuration
public class MyBatisConfig
{
- @Autowired
- private Environment env;
-
- static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
-
- public static String setTypeAliasesPackage(String typeAliasesPackage)
- {
- ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();
- MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
- List<String> allResult = new ArrayList<String>();
- try
- {
- for (String aliasesPackage : typeAliasesPackage.split(","))
- {
- List<String> result = new ArrayList<String>();
- aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
- + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN;
- Resource[] resources = resolver.getResources(aliasesPackage);
- if (resources != null && resources.length > 0)
- {
- MetadataReader metadataReader = null;
- for (Resource resource : resources)
- {
- if (resource.isReadable())
- {
- metadataReader = metadataReaderFactory.getMetadataReader(resource);
- try
- {
- result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName());
- }
- catch (ClassNotFoundException e)
- {
- e.printStackTrace();
- }
- }
- }
- }
- if (result.size() > 0)
- {
- HashSet<String> hashResult = new HashSet<String>(result);
- allResult.addAll(hashResult);
- }
- }
- if (allResult.size() > 0)
- {
- typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0]));
- }
- else
- {
- throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包");
- }
- }
- catch (IOException e)
- {
- e.printStackTrace();
- }
- return typeAliasesPackage;
- }
-
- public Resource[] resolveMapperLocations(String[] mapperLocations)
- {
- ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
- List<Resource> resources = new ArrayList<Resource>();
- if (mapperLocations != null)
- {
- for (String mapperLocation : mapperLocations)
- {
- try
- {
- Resource[] mappers = resourceResolver.getResources(mapperLocation);
- resources.addAll(Arrays.asList(mappers));
- }
- catch (IOException e)
- {
- // ignore
- }
- }
- }
- return resources.toArray(new Resource[resources.size()]);
- }
-
@Bean
- public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception
- {
- String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");
- String mapperLocations = env.getProperty("mybatis.mapperLocations");
- String configLocation = env.getProperty("mybatis.configLocation");
- typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
- VFS.addImplClass(SpringBootVFS.class);
-
- final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
+ public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
+ // 使用 MyBatis-Plus 的 SqlSessionFactory
+ MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
- sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
- sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));
- sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
+
+ // 设置mapper.xml文件的位置
+ sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
+ .getResources("classpath*:mapper/**/*Mapper.xml"));
+
+ // 设置类型别名包
+ sessionFactory.setTypeAliasesPackage("com.ruoyi.**.domain");
+
return sessionFactory.getObject();
}
}
\ No newline at end of file
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyMetaObjectHandler.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyMetaObjectHandler.java
new file mode 100644
index 0000000..f59008c
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyMetaObjectHandler.java
@@ -0,0 +1,37 @@
+package com.ruoyi.framework.config;
+
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import org.apache.ibatis.reflection.MetaObject;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+
+@Component
+public class MyMetaObjectHandler implements MetaObjectHandler {
+
+ @Override
+ public void insertFill(MetaObject metaObject) {
+ this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
+ this.strictInsertFill(metaObject, "updateTime", Date.class, new Date());
+ // 若依特有的字段
+ this.strictInsertFill(metaObject, "createBy", String.class, getUsername());
+ this.strictInsertFill(metaObject, "updateBy", String.class, getUsername());
+ }
+
+ @Override
+ public void updateFill(MetaObject metaObject) {
+ this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
+ this.strictUpdateFill(metaObject, "updateBy", String.class, getUsername());
+ }
+
+ private String getUsername() {
+ // 从安全上下文中获取当前用户名
+ try {
+ // 若依原有的获取用户名方式
+ // return SecurityUtils.getUsername();
+ return "admin"; // 临时写死,实际中需要动态获取
+ } catch (Exception e) {
+ return "system";
+ }
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java
new file mode 100644
index 0000000..575f9dc
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java
@@ -0,0 +1,36 @@
+package com.ruoyi.framework.config;
+
+import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class MybatisPlusConfig {
+
+ /**
+ * 添加插件
+ */
+ @Bean
+ public MybatisPlusInterceptor mybatisPlusInterceptor() {
+ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+
+ // 分页插件
+ interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
+
+ // 乐观锁插件(如果需要)
+ // interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
+
+ return interceptor;
+ }
+
+ /**
+ * 自动填充处理器(用于创建时间、更新时间等)
+ */
+ @Bean
+ public MyMetaObjectHandler metaObjectHandler() {
+ return new MyMetaObjectHandler();
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
index 511842b..222ba3f 100644
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
@@ -115,6 +115,7 @@
// 静态资源,可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
+ .antMatchers("/im/api/**").permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();
})
diff --git a/ruoyi-system/pom.xml b/ruoyi-system/pom.xml
index f514d3d..ec01f08 100644
--- a/ruoyi-system/pom.xml
+++ b/ruoyi-system/pom.xml
@@ -22,7 +22,30 @@
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId>
</dependency>
-
+ <dependency>
+ <groupId>com.netease.nim</groupId>
+ <artifactId>yunxin-server-sdk</artifactId>
+ <version>1.1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>com.baomidou</groupId>
+ <artifactId>mybatis-plus-boot-starter</artifactId>
+ <version>3.5.3.1</version> <!-- 使用最新版本 -->
+ </dependency>
+ <dependency>
+ <groupId>com.baomidou</groupId>
+ <artifactId>mybatis-plus</artifactId>
+ <version>3.5.3.1</version>
+ </dependency>
+ <dependency>
+ <groupId>cn.hutool</groupId>
+ <artifactId>hutool-all</artifactId>
+ <version>5.7.12</version>
+ </dependency>
+ <dependency>
+ <groupId>org.projectlombok</groupId>
+ <artifactId>lombok</artifactId>
+ </dependency>
</dependencies>
</project>
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/UserAccount.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/UserAccount.java
new file mode 100644
index 0000000..73bb259
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/UserAccount.java
@@ -0,0 +1,81 @@
+package com.ruoyi.system.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+@TableName("user_account") // 指定表名
+public class UserAccount {
+
+ // 自增ID主键
+ @TableId(type = IdType.AUTO) // 主键自增
+ private Integer id;
+
+ // 账号(唯一)
+ private String account;
+
+ // 手机号(唯一)
+ private String phoneNumber;
+
+ // 密码
+ private String password;
+
+ // 账号类型: 0:真实 1:虚拟
+ private Integer accountType = 0;
+
+ // 云信账号
+ private String cloudMessageAccount;
+
+ // 昵称
+ private String nickname;
+
+ // 账户余额
+ private BigDecimal balance = BigDecimal.ZERO;
+
+ // 账号状态: 0:正常 1:禁用
+ private Integer status = 0;
+
+ // 最近登录时间
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date loginTime;
+
+ // 最近登录IP
+ private String loginIp;
+
+ // 注册时间
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private Date registerTime;
+
+ // 个性签名
+ private String signature;
+
+ // 是否支持昵称搜索
+ private Boolean supportNicknameSearch = true;
+
+ // 是否支持手机搜索
+ private Boolean supportPhoneSearch = true;
+
+ // 是否支持ID搜索
+ private Boolean supportIdSearch = true;
+
+ // 是否支持用户名搜索
+ private Boolean supportUsernameSearch = true;
+
+ // 是否申请删除账号
+ private Boolean requestDelete = true;
+
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @TableField(fill = FieldFill.INSERT) // 自动填充
+ private Date createTime;
+
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private Date updateTime;
+
+ private Boolean deleted = true;
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/UserAccountMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/UserAccountMapper.java
new file mode 100644
index 0000000..929e174
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/UserAccountMapper.java
@@ -0,0 +1,14 @@
+package com.ruoyi.system.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.system.domain.UserAccount;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @program: ruoyiim
+ * @description:
+ * @create: 2025-08-19 17:02
+ **/
+@Mapper
+public interface UserAccountMapper extends BaseMapper<UserAccount> {
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/UserAccountService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/UserAccountService.java
new file mode 100644
index 0000000..d469d22
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/UserAccountService.java
@@ -0,0 +1,7 @@
+package com.ruoyi.system.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.system.domain.UserAccount;
+
+public interface UserAccountService extends IService<UserAccount> {
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserAccountServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserAccountServiceImpl.java
new file mode 100644
index 0000000..8ba90d0
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserAccountServiceImpl.java
@@ -0,0 +1,16 @@
+package com.ruoyi.system.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.system.domain.UserAccount;
+import com.ruoyi.system.mapper.UserAccountMapper;
+import com.ruoyi.system.service.UserAccountService;
+import org.springframework.stereotype.Service;
+
+/**
+ * @program: ruoyiim
+ * @description:
+ * @create: 2025-08-19 16:41
+ **/
+@Service
+public class UserAccountServiceImpl extends ServiceImpl<UserAccountMapper, UserAccount> implements UserAccountService {
+}
--
Gitblit v1.9.3