diff --git a/pom.xml b/pom.xml
index adfeb28..6f0b05d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,7 +29,7 @@
2.13.4.2
2.13.4
- 1.18.24
+ 1.18.30
3.12.0
5.7.20
31.0.1-jre
@@ -40,6 +40,8 @@
5.2.3
5.2.3
+ 8.0.33
+ 3.5.5
@@ -181,6 +183,19 @@
${poi-ooxml.version}
+
+
+ mysql
+ mysql-connector-java
+ ${mysql-connector-java.version}
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+ ${mybatis-plus-boot-starter.version}
+
+
@@ -271,6 +286,7 @@
org.projectlombok
lombok
true
+ provided
org.apache.commons
@@ -303,6 +319,16 @@
poi-ooxml
+
+ mysql
+ mysql-connector-java
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+
+
diff --git a/src/main/java/com/knowledge/base/application/filter/AuthFilter.java b/src/main/java/com/knowledge/base/application/filter/AuthFilter.java
index af0a2c1..56fb95c 100644
--- a/src/main/java/com/knowledge/base/application/filter/AuthFilter.java
+++ b/src/main/java/com/knowledge/base/application/filter/AuthFilter.java
@@ -1,6 +1,8 @@
package com.knowledge.base.application.filter;
+import com.knowledge.base.domain.user.service.UserService;
import com.knowledge.base.infrastructure.north.controller.UserController;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.*;
@@ -15,6 +17,9 @@ import java.io.IOException;
@Component
public class AuthFilter implements Filter {
+ @Autowired
+ private UserService userService;
+
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
@@ -27,7 +32,7 @@ public class AuthFilter implements Filter {
// 需要鉴权的路径前缀
if (path.startsWith("/search")) {
String token = request.getHeader("Authorization");
- if (!UserController.isValidToken(token)) {
+ if (!userService.isValidToken(token)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Invalid or expired token");
return;
diff --git a/src/main/java/com/knowledge/base/domain/enums/DocTypeEnum.java b/src/main/java/com/knowledge/base/domain/common/enums/DocTypeEnum.java
similarity index 81%
rename from src/main/java/com/knowledge/base/domain/enums/DocTypeEnum.java
rename to src/main/java/com/knowledge/base/domain/common/enums/DocTypeEnum.java
index 5dc5006..0890d7f 100644
--- a/src/main/java/com/knowledge/base/domain/enums/DocTypeEnum.java
+++ b/src/main/java/com/knowledge/base/domain/common/enums/DocTypeEnum.java
@@ -1,4 +1,4 @@
-package com.knowledge.base.domain.enums;
+package com.knowledge.base.domain.common.enums;
/**
* @author Luke.ye
@@ -7,7 +7,7 @@ package com.knowledge.base.domain.enums;
public enum DocTypeEnum {
MARKDOWN("markdown", "markdown"),
- HTML("html", "html"),
+ HTML("xlsx", "excel"),
PDF("pdf", "pdf"),
WORD("word", "word"),
diff --git a/src/main/java/com/knowledge/base/domain/iface/DocumentImporter.java b/src/main/java/com/knowledge/base/domain/doc/iface/DocumentImporter.java
similarity index 67%
rename from src/main/java/com/knowledge/base/domain/iface/DocumentImporter.java
rename to src/main/java/com/knowledge/base/domain/doc/iface/DocumentImporter.java
index c87618b..f13c0b9 100644
--- a/src/main/java/com/knowledge/base/domain/iface/DocumentImporter.java
+++ b/src/main/java/com/knowledge/base/domain/doc/iface/DocumentImporter.java
@@ -1,6 +1,6 @@
-package com.knowledge.base.domain.iface;
+package com.knowledge.base.domain.doc.iface;
-import com.knowledge.base.domain.enums.DocTypeEnum;
+import com.knowledge.base.domain.common.enums.DocTypeEnum;
/**
* @author Luke.ye
diff --git a/src/main/java/com/knowledge/base/domain/impl/AbstractBaseFileImporter.java b/src/main/java/com/knowledge/base/domain/doc/impl/AbstractBaseFileImporter.java
similarity index 98%
rename from src/main/java/com/knowledge/base/domain/impl/AbstractBaseFileImporter.java
rename to src/main/java/com/knowledge/base/domain/doc/impl/AbstractBaseFileImporter.java
index 0472e11..b0ba2bf 100644
--- a/src/main/java/com/knowledge/base/domain/impl/AbstractBaseFileImporter.java
+++ b/src/main/java/com/knowledge/base/domain/doc/impl/AbstractBaseFileImporter.java
@@ -1,6 +1,6 @@
-package com.knowledge.base.domain.impl;
+package com.knowledge.base.domain.doc.impl;
-import com.knowledge.base.domain.iface.DocumentImporter;
+import com.knowledge.base.domain.doc.iface.DocumentImporter;
import com.knowledge.base.infrastructure.util.LocalCacheUtil;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
diff --git a/src/main/java/com/knowledge/base/domain/impl/ExcelImporter.java b/src/main/java/com/knowledge/base/domain/doc/impl/ExcelImporter.java
similarity index 97%
rename from src/main/java/com/knowledge/base/domain/impl/ExcelImporter.java
rename to src/main/java/com/knowledge/base/domain/doc/impl/ExcelImporter.java
index c374b56..25a4f3b 100644
--- a/src/main/java/com/knowledge/base/domain/impl/ExcelImporter.java
+++ b/src/main/java/com/knowledge/base/domain/doc/impl/ExcelImporter.java
@@ -1,4 +1,4 @@
-package com.knowledge.base.domain.impl;
+package com.knowledge.base.domain.doc.impl;
/**
* @author Luke.ye
diff --git a/src/main/java/com/knowledge/base/domain/impl/MarkdownImporter.java b/src/main/java/com/knowledge/base/domain/doc/impl/MarkdownImporter.java
similarity index 88%
rename from src/main/java/com/knowledge/base/domain/impl/MarkdownImporter.java
rename to src/main/java/com/knowledge/base/domain/doc/impl/MarkdownImporter.java
index f9b83c9..eadfa42 100644
--- a/src/main/java/com/knowledge/base/domain/impl/MarkdownImporter.java
+++ b/src/main/java/com/knowledge/base/domain/doc/impl/MarkdownImporter.java
@@ -1,6 +1,6 @@
-package com.knowledge.base.domain.impl;
+package com.knowledge.base.domain.doc.impl;
-import com.knowledge.base.domain.enums.DocTypeEnum;
+import com.knowledge.base.domain.common.enums.DocTypeEnum;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
diff --git a/src/main/java/com/knowledge/base/domain/impl/PdfImporter.java b/src/main/java/com/knowledge/base/domain/doc/impl/PdfImporter.java
similarity index 90%
rename from src/main/java/com/knowledge/base/domain/impl/PdfImporter.java
rename to src/main/java/com/knowledge/base/domain/doc/impl/PdfImporter.java
index b24df09..6fb9ec8 100644
--- a/src/main/java/com/knowledge/base/domain/impl/PdfImporter.java
+++ b/src/main/java/com/knowledge/base/domain/doc/impl/PdfImporter.java
@@ -1,6 +1,6 @@
-package com.knowledge.base.domain.impl;
+package com.knowledge.base.domain.doc.impl;
-import com.knowledge.base.domain.enums.DocTypeEnum;
+import com.knowledge.base.domain.common.enums.DocTypeEnum;
import org.springframework.stereotype.Component;
import org.apache.pdfbox.pdmodel.PDDocument;
diff --git a/src/main/java/com/knowledge/base/domain/impl/WordImporter.java b/src/main/java/com/knowledge/base/domain/doc/impl/WordImporter.java
similarity index 92%
rename from src/main/java/com/knowledge/base/domain/impl/WordImporter.java
rename to src/main/java/com/knowledge/base/domain/doc/impl/WordImporter.java
index 418e11f..ba26920 100644
--- a/src/main/java/com/knowledge/base/domain/impl/WordImporter.java
+++ b/src/main/java/com/knowledge/base/domain/doc/impl/WordImporter.java
@@ -1,6 +1,6 @@
-package com.knowledge.base.domain.impl;
+package com.knowledge.base.domain.doc.impl;
-import com.knowledge.base.domain.enums.DocTypeEnum;
+import com.knowledge.base.domain.common.enums.DocTypeEnum;
import org.springframework.stereotype.Component;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
diff --git a/src/main/java/com/knowledge/base/domain/schedule/ImportScheduler.java b/src/main/java/com/knowledge/base/domain/schedule/ImportScheduler.java
index c253cd1..cba5dd8 100644
--- a/src/main/java/com/knowledge/base/domain/schedule/ImportScheduler.java
+++ b/src/main/java/com/knowledge/base/domain/schedule/ImportScheduler.java
@@ -1,6 +1,6 @@
package com.knowledge.base.domain.schedule;
-import com.knowledge.base.domain.iface.DocumentImporter;
+import com.knowledge.base.domain.doc.iface.DocumentImporter;
import com.knowledge.base.infrastructure.config.DynamicConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/src/main/java/com/knowledge/base/domain/user/repository/iface/UserRepository.java b/src/main/java/com/knowledge/base/domain/user/repository/iface/UserRepository.java
new file mode 100644
index 0000000..71b97bc
--- /dev/null
+++ b/src/main/java/com/knowledge/base/domain/user/repository/iface/UserRepository.java
@@ -0,0 +1,18 @@
+package com.knowledge.base.domain.user.repository.iface;
+
+import com.knowledge.base.domain.user.repository.po.User;
+
+import java.util.Optional;
+
+/**
+ * @author Luke.ye
+ * @date 2025/6/6 12:20
+ */
+
+
+public interface UserRepository {
+ Optional findByUsername(String username);
+ void save(User user);
+
+ Optional findById(Long id);
+}
diff --git a/src/main/java/com/knowledge/base/domain/user/repository/iface/UserTokenRepository.java b/src/main/java/com/knowledge/base/domain/user/repository/iface/UserTokenRepository.java
new file mode 100644
index 0000000..b1aa25b
--- /dev/null
+++ b/src/main/java/com/knowledge/base/domain/user/repository/iface/UserTokenRepository.java
@@ -0,0 +1,18 @@
+package com.knowledge.base.domain.user.repository.iface;
+
+import com.knowledge.base.domain.user.repository.po.UserToken;
+
+import java.util.Optional;
+
+/**
+ * @author Luke.ye
+ * @date 2025/6/6 12:20
+ */
+
+public interface UserTokenRepository {
+ void save(UserToken token);
+ Optional findByToken(String token);
+ void deleteByToken(String token);
+ void deleteByUserId(Long userId);
+}
+
diff --git a/src/main/java/com/knowledge/base/domain/user/repository/po/User.java b/src/main/java/com/knowledge/base/domain/user/repository/po/User.java
new file mode 100644
index 0000000..f373489
--- /dev/null
+++ b/src/main/java/com/knowledge/base/domain/user/repository/po/User.java
@@ -0,0 +1,27 @@
+package com.knowledge.base.domain.user.repository.po;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import java.time.LocalDateTime;
+
+/**
+ * @author Luke.ye
+ * @date 2025/6/6 12:16
+ */
+@Data
+@TableName("user")
+public class User {
+
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ private String username;
+
+ private String password;
+
+ @TableField(fill = FieldFill.INSERT)
+ private LocalDateTime addTime;
+
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private LocalDateTime updateTime;
+}
+
diff --git a/src/main/java/com/knowledge/base/domain/user/repository/po/UserToken.java b/src/main/java/com/knowledge/base/domain/user/repository/po/UserToken.java
new file mode 100644
index 0000000..219cf62
--- /dev/null
+++ b/src/main/java/com/knowledge/base/domain/user/repository/po/UserToken.java
@@ -0,0 +1,28 @@
+package com.knowledge.base.domain.user.repository.po;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@TableName("user_token")
+public class UserToken {
+
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ private Long userId;
+
+ private String token;
+
+ private Long loginAt;
+
+ private Long expiredAt;
+
+ @TableField(fill = FieldFill.INSERT)
+ private LocalDateTime addTime;
+
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private LocalDateTime updateTime;
+}
diff --git a/src/main/java/com/knowledge/base/domain/user/service/UserService.java b/src/main/java/com/knowledge/base/domain/user/service/UserService.java
new file mode 100644
index 0000000..bf51882
--- /dev/null
+++ b/src/main/java/com/knowledge/base/domain/user/service/UserService.java
@@ -0,0 +1,20 @@
+package com.knowledge.base.domain.user.service;
+
+import com.knowledge.base.domain.user.repository.po.User;
+import com.knowledge.base.domain.user.repository.po.UserToken;
+
+import java.util.Optional;
+
+public interface UserService {
+ Optional findByUsername(String username);
+ boolean verifyPassword(User user, String plainText);
+ boolean registerUser(String username, String plainPassword);
+ UserToken createToken(Long userId, long expireMs);
+ Optional findToken(String token);
+ void removeToken(String token);
+ boolean isValidToken(String token);
+
+ Optional findById(Long id);
+
+
+}
diff --git a/src/main/java/com/knowledge/base/domain/user/service/UserServiceImpl.java b/src/main/java/com/knowledge/base/domain/user/service/UserServiceImpl.java
new file mode 100644
index 0000000..232af6f
--- /dev/null
+++ b/src/main/java/com/knowledge/base/domain/user/service/UserServiceImpl.java
@@ -0,0 +1,78 @@
+package com.knowledge.base.domain.user.service;
+
+import cn.hutool.crypto.digest.SM3;
+import com.knowledge.base.domain.user.repository.iface.UserRepository;
+import com.knowledge.base.domain.user.repository.iface.UserTokenRepository;
+import com.knowledge.base.domain.user.repository.po.User;
+import com.knowledge.base.domain.user.repository.po.UserToken;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.util.Optional;
+import java.util.UUID;
+
+@Service
+@RequiredArgsConstructor
+public class UserServiceImpl implements UserService {
+
+ private final UserRepository userRepository;
+ private final UserTokenRepository tokenRepository;
+
+ @Override
+ public Optional findByUsername(String username) {
+ return userRepository.findByUsername(username);
+ }
+
+ @Override
+ public boolean verifyPassword(User user, String plainText) {
+ return user.getPassword().equalsIgnoreCase(new SM3().digestHex(plainText));
+ }
+
+ @Override
+ public boolean registerUser(String username, String plainPassword) {
+ if (userRepository.findByUsername(username).isPresent()) {
+ return false;
+ }
+ User user = new User();
+ user.setUsername(username);
+ user.setPassword(new SM3().digestHex(plainPassword));
+ userRepository.save(user);
+ return true;
+ }
+
+ @Override
+ public UserToken createToken(Long userId, long expireMs) {
+ String token = UUID.randomUUID().toString().replace("-", "");
+ long now = System.currentTimeMillis();
+ UserToken userToken = new UserToken();
+ userToken.setUserId(userId);
+ userToken.setToken(token);
+ userToken.setLoginAt(now);
+ userToken.setExpiredAt(now + expireMs);
+ tokenRepository.save(userToken);
+ return userToken;
+ }
+
+ @Override
+ public Optional findToken(String token) {
+ return tokenRepository.findByToken(token);
+ }
+
+ @Override
+ public void removeToken(String token) {
+ tokenRepository.deleteByToken(token);
+ }
+
+ @Override
+ public boolean isValidToken(String token) {
+ return tokenRepository.findByToken(token)
+ .filter(t -> System.currentTimeMillis() < t.getExpiredAt())
+ .isPresent();
+ }
+
+ @Override
+ public Optional findById(Long id) {
+ return userRepository.findById(id);
+ }
+
+}
diff --git a/src/main/java/com/knowledge/base/infrastructure/north/controller/ImportController.java b/src/main/java/com/knowledge/base/infrastructure/north/controller/ImportController.java
index 23a0eef..1d09f3a 100644
--- a/src/main/java/com/knowledge/base/infrastructure/north/controller/ImportController.java
+++ b/src/main/java/com/knowledge/base/infrastructure/north/controller/ImportController.java
@@ -1,6 +1,6 @@
package com.knowledge.base.infrastructure.north.controller;
-import com.knowledge.base.domain.iface.DocumentImporter;
+import com.knowledge.base.domain.doc.iface.DocumentImporter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
diff --git a/src/main/java/com/knowledge/base/infrastructure/north/controller/UserController.java b/src/main/java/com/knowledge/base/infrastructure/north/controller/UserController.java
index c9ffea4..7890282 100644
--- a/src/main/java/com/knowledge/base/infrastructure/north/controller/UserController.java
+++ b/src/main/java/com/knowledge/base/infrastructure/north/controller/UserController.java
@@ -1,62 +1,51 @@
package com.knowledge.base.infrastructure.north.controller;
-import cn.hutool.extra.spring.SpringUtil;
+import com.knowledge.base.domain.user.repository.po.User;
+import com.knowledge.base.domain.user.repository.po.UserToken;
+import com.knowledge.base.domain.user.service.UserService;
import com.knowledge.base.infrastructure.config.ConstantConfig;
import com.knowledge.base.infrastructure.config.DynamicConfig;
-import org.springframework.beans.factory.annotation.Autowired;
+import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.Map;
+import java.util.Optional;
-/**
- * @author Luke.ye
- * @date 2025/5/25 10:02
- */
@RestController
@RequestMapping("/api/v1/user")
+@RequiredArgsConstructor
public class UserController {
- @Autowired
- private DynamicConfig dynamicConfig;
-
- private static final Map USER_DB = Map.of(
- "admin", "Admin@654321",
- "lukeye", "lukeye"
- );
-
- // token -> timestamp
- private static final Map tokenStore = new ConcurrentHashMap<>();
-
- // token -> username
- private static final Map userMap = new ConcurrentHashMap<>();
+ private final UserService userService;
+ private final DynamicConfig dynamicConfig;
@PostMapping("/login")
public ResponseEntity> login(@RequestBody Map body, HttpServletResponse response) {
String username = body.getOrDefault("username", "").trim();
String password = body.getOrDefault("password", "").trim();
- if (!USER_DB.containsKey(username) || !USER_DB.get(username).equals(password)) {
+ Optional userOpt = userService.findByUsername(username);
+ if (userOpt.isEmpty() || !userService.verifyPassword(userOpt.get(), password)) {
return ResponseEntity.status(401).body(Map.of("message", "用户名或密码错误"));
}
- String token = UUID.randomUUID().toString().replace("-", "");
- tokenStore.put(token, System.currentTimeMillis());
- userMap.put(token, username);
+ User user = userOpt.get();
+ long expireMs = dynamicConfig.getTokenExpireTime();
+ UserToken token = userService.createToken(user.getId(), expireMs);
- Cookie cookie = new Cookie( ConstantConfig.COOKIE_KEY, token);
+ Cookie cookie = new Cookie(ConstantConfig.COOKIE_KEY, token.getToken());
cookie.setPath("/");
cookie.setDomain(dynamicConfig.getCookieDomainName());
- cookie.setHttpOnly(true); // 建议生产环境为 true,这里为兼容前端 JS 读写可设为 false
- cookie.setMaxAge((int)(dynamicConfig.getTokenExpireTime() / 1000));
+ cookie.setHttpOnly(true);
+ cookie.setMaxAge((int) (expireMs / 1000));
response.addCookie(cookie);
return ResponseEntity.ok(Map.of(
- "token", token,
- "username", username
+ "token", token.getToken(),
+ "username", user.getUsername()
));
}
@@ -67,57 +56,46 @@ public class UserController {
HttpServletResponse response
) {
String token = headerToken != null ? headerToken : cookieToken;
- if (token != null && tokenStore.containsKey(token)) {
- tokenStore.remove(token);
- userMap.remove(token);
+ if (token != null) {
+ userService.removeToken(token);
}
- // 删除 Cookie
- Cookie cookie = new Cookie( ConstantConfig.COOKIE_KEY, "");
+ Cookie cookie = new Cookie(ConstantConfig.COOKIE_KEY, "");
cookie.setPath("/");
cookie.setDomain(dynamicConfig.getCookieDomainName());
- cookie.setMaxAge(0); // 立即失效
cookie.setHttpOnly(true);
+ cookie.setMaxAge(0);
response.addCookie(cookie);
return ResponseEntity.ok(Map.of("msg", "已登出"));
}
-
@GetMapping("/check")
public ResponseEntity> check(
- @CookieValue(value = "auth_token", required = false) String cookieToken,
+ @CookieValue(value = ConstantConfig.COOKIE_KEY, required = false) String cookieToken,
@RequestHeader(value = "Authorization", required = false) String headerToken
) {
String token = headerToken != null ? headerToken : cookieToken;
- if (isValidToken(token)) {
- Map result = new HashMap<>();
- result.put("code", 0);
- result.put("msg", "校验成功");
- return ResponseEntity.ok(result);
+ if (userService.isValidToken(token)) {
+ return ResponseEntity.ok(Map.of("code", 0, "msg", "校验成功"));
}
- Map result = new HashMap<>();
- result.put("code", 401);
- result.put("msg", "未授权");
- return ResponseEntity.status(401).body(result);
+ return ResponseEntity.status(401).body(Map.of("code", 401, "msg", "未授权"));
}
-
- public static boolean isValidToken(String token) {
- if (token == null || !tokenStore.containsKey(token)) return false;
- long loginAt = tokenStore.get(token);
- long now = System.currentTimeMillis();
- long expireMs = SpringUtil.getBean(DynamicConfig.class).getTokenExpireTime();
- if (now - loginAt > expireMs) {
- tokenStore.remove(token);
- userMap.remove(token);
- return false;
+ @GetMapping("/username")
+ public ResponseEntity> getUsernameByToken(
+ @CookieValue(value = ConstantConfig.COOKIE_KEY, required = false) String cookieToken,
+ @RequestHeader(value = "Authorization", required = false) String headerToken
+ ) {
+ String token = headerToken != null ? headerToken : cookieToken;
+ Optional userTokenOpt = userService.findToken(token);
+ if (userTokenOpt.isPresent()) {
+ Long userId = userTokenOpt.get().getUserId();
+ Optional userOpt = userService.findById(userId);
+ if (userOpt.isPresent()) {
+ return ResponseEntity.ok(Map.of("username", userOpt.get().getUsername()));
+ }
}
- return true;
- }
-
- public static Optional getUsername(String token) {
- return Optional.ofNullable(userMap.get(token));
+ return ResponseEntity.status(401).body(Map.of("msg", "无效token"));
}
}
-
diff --git a/src/main/java/com/knowledge/base/infrastructure/repository/mapper/UserMapper.java b/src/main/java/com/knowledge/base/infrastructure/repository/mapper/UserMapper.java
new file mode 100644
index 0000000..a0b803d
--- /dev/null
+++ b/src/main/java/com/knowledge/base/infrastructure/repository/mapper/UserMapper.java
@@ -0,0 +1,9 @@
+package com.knowledge.base.infrastructure.repository.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.knowledge.base.domain.user.repository.po.User;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface UserMapper extends BaseMapper {
+}
diff --git a/src/main/java/com/knowledge/base/infrastructure/repository/mapper/UserTokenMapper.java b/src/main/java/com/knowledge/base/infrastructure/repository/mapper/UserTokenMapper.java
new file mode 100644
index 0000000..5373c7e
--- /dev/null
+++ b/src/main/java/com/knowledge/base/infrastructure/repository/mapper/UserTokenMapper.java
@@ -0,0 +1,9 @@
+package com.knowledge.base.infrastructure.repository.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.knowledge.base.domain.user.repository.po.UserToken;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface UserTokenMapper extends BaseMapper {
+}
diff --git a/src/main/java/com/knowledge/base/infrastructure/repository/persistence/UserRepositoryImpl.java b/src/main/java/com/knowledge/base/infrastructure/repository/persistence/UserRepositoryImpl.java
new file mode 100644
index 0000000..4a759a9
--- /dev/null
+++ b/src/main/java/com/knowledge/base/infrastructure/repository/persistence/UserRepositoryImpl.java
@@ -0,0 +1,43 @@
+package com.knowledge.base.infrastructure.repository.persistence;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.knowledge.base.domain.user.repository.iface.UserRepository;
+import com.knowledge.base.domain.user.repository.po.User;
+import com.knowledge.base.infrastructure.repository.mapper.UserMapper;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Repository;
+
+import java.util.Optional;
+
+/**
+ * @author Luke.ye
+ * @date 2025/6/6 12:21
+ */
+@Repository
+@RequiredArgsConstructor
+public class UserRepositoryImpl implements UserRepository {
+
+ private final UserMapper userMapper;
+
+ @Override
+ public Optional findByUsername(String username) {
+ return Optional.ofNullable(
+ userMapper.selectOne(
+ new QueryWrapper()
+ .eq("username", username)
+ )
+ );
+ }
+
+ @Override
+ public void save(User user) {
+ userMapper.insert(user);
+ }
+
+ @Override
+ public Optional findById(Long id) {
+ return Optional.ofNullable(userMapper.selectById(id));
+ }
+
+}
+
diff --git a/src/main/java/com/knowledge/base/infrastructure/repository/persistence/UserTokenRepositoryImpl.java b/src/main/java/com/knowledge/base/infrastructure/repository/persistence/UserTokenRepositoryImpl.java
new file mode 100644
index 0000000..e81f1f3
--- /dev/null
+++ b/src/main/java/com/knowledge/base/infrastructure/repository/persistence/UserTokenRepositoryImpl.java
@@ -0,0 +1,50 @@
+package com.knowledge.base.infrastructure.repository.persistence;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.knowledge.base.domain.user.repository.iface.UserTokenRepository;
+import com.knowledge.base.domain.user.repository.po.UserToken;
+import com.knowledge.base.infrastructure.repository.mapper.UserTokenMapper;
+import org.springframework.stereotype.Repository;
+
+import java.util.Optional;
+
+@Repository
+public class UserTokenRepositoryImpl implements UserTokenRepository {
+
+ private final UserTokenMapper tokenMapper;
+
+ public UserTokenRepositoryImpl(UserTokenMapper tokenMapper) {
+ this.tokenMapper = tokenMapper;
+ }
+
+ @Override
+ public void save(UserToken token) {
+ tokenMapper.insert(token);
+ }
+
+ @Override
+ public Optional findByToken(String token) {
+ return Optional.ofNullable(
+ tokenMapper.selectOne(
+ new QueryWrapper()
+ .eq("token", token)
+ )
+ );
+ }
+
+ @Override
+ public void deleteByToken(String token) {
+ tokenMapper.delete(
+ new QueryWrapper()
+ .eq("token", token)
+ );
+ }
+
+ @Override
+ public void deleteByUserId(Long userId) {
+ tokenMapper.delete(
+ new QueryWrapper()
+ .eq("user_id", userId)
+ );
+ }
+}
diff --git a/src/main/resources/application-dev-mac.properties b/src/main/resources/application-dev-mac.properties
index 9e7d173..cec4bfe 100644
--- a/src/main/resources/application-dev-mac.properties
+++ b/src/main/resources/application-dev-mac.properties
@@ -27,3 +27,13 @@ markdown.path=/Users/admin/Desktop/ahnx-share-src-public/public
pdf.path=/Users/admin/Desktop/ahnx-share-src-public/public
word.path=/Users/admin/Desktop/ahnx-share-src-public/public
excel.path=/Users/admin/Desktop/ahnx-share-src-public/public
+
+# mysql
+spring.datasource.url=jdbc:mysql://localhost:3306/kbase?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
+spring.datasource.username=root
+spring.datasource.password=lukeye@6
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+# mybatis-plus
+mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
+mybatis-plus.global-config.db-config.logic-delete-field=deleted
+mybatis-plus.global-config.db-config.id-type=auto
diff --git a/src/main/resources/application-dev-windows.properties b/src/main/resources/application-dev-windows.properties
index eaa12ff..ebb0366 100644
--- a/src/main/resources/application-dev-windows.properties
+++ b/src/main/resources/application-dev-windows.properties
@@ -26,4 +26,15 @@ exclude.file.path.prefix=D:/02-documents/01-ahnx-share-src-public/public
markdown.path=D:/02-documents/01-ahnx-share-src-public/public
pdf.path=D:/02-documents/01-ahnx-share-src-public/public
word.path=D:/02-documents/01-ahnx-share-src-public/public
-excel.path=D:/02-documents/01-ahnx-share-src-public/public
\ No newline at end of file
+excel.path=D:/02-documents/01-ahnx-share-src-public/public
+
+
+# mysql
+spring.datasource.url=jdbc:mysql://localhost:3306/kbase?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
+spring.datasource.username=root
+spring.datasource.password=lukeye
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+# mybatis-plus
+mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
+mybatis-plus.global-config.db-config.logic-delete-field=deleted
+mybatis-plus.global-config.db-config.id-type=auto
\ No newline at end of file
diff --git a/src/main/resources/application-docker.properties b/src/main/resources/application-docker.properties
index 55debc7..a55c0c4 100644
--- a/src/main/resources/application-docker.properties
+++ b/src/main/resources/application-docker.properties
@@ -28,4 +28,14 @@ word.path=/app/import-data
excel.path=/app/import-data
# 相对路径裁剪
-exclude.file.path.prefix=/app/import-data
\ No newline at end of file
+exclude.file.path.prefix=/app/import-data
+
+# mysql
+spring.datasource.url=jdbc:mysql://host.docker.internal:3306/kbase?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
+spring.datasource.username=root
+spring.datasource.password=lukeye
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+# mybatis-plus
+mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
+mybatis-plus.global-config.db-config.logic-delete-field=deleted
+mybatis-plus.global-config.db-config.id-type=auto
\ No newline at end of file