diff --git a/pom.xml b/pom.xml
index a3c336d..5b58885 100644
--- a/pom.xml
+++ b/pom.xml
@@ -104,11 +104,25 @@
3.6.0
-
-
-
-
-
+
+
+ org.dromara.x-file-storage
+ x-file-storage-spring
+ 2.3.0
+
+
+
+ software.amazon.awssdk
+ s3
+ 2.29.29
+
+
+
+ com.mailgun
+ mailgun-java
+ 2.1.1
+
+
io.jsonwebtoken
jjwt-api
diff --git a/src/main/java/com/yolo/keyborad/MyApplication.java b/src/main/java/com/yolo/keyborad/MyApplication.java
index ee5dcbb..70c49ac 100644
--- a/src/main/java/com/yolo/keyborad/MyApplication.java
+++ b/src/main/java/com/yolo/keyborad/MyApplication.java
@@ -1,19 +1,28 @@
package com.yolo.keyborad;
+import com.yolo.keyborad.common.xfile.ByteFileWrapperAdapter;
import com.yolo.keyborad.config.AppleAppStoreProperties;
import lombok.extern.slf4j.Slf4j;
+import org.dromara.x.file.storage.core.tika.ContentTypeDetect;
+import org.dromara.x.file.storage.spring.EnableFileStorage;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
@Slf4j
@SpringBootApplication
@EnableConfigurationProperties(AppleAppStoreProperties.class)
+@EnableFileStorage
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
+ @Bean
+ public ByteFileWrapperAdapter byteFileWrapperAdapter(ContentTypeDetect contentTypeDetect) {
+ return new ByteFileWrapperAdapter(contentTypeDetect);
+ }
}
diff --git a/src/main/java/com/yolo/keyborad/common/ErrorCode.java b/src/main/java/com/yolo/keyborad/common/ErrorCode.java
index 416f4ab..94f9452 100644
--- a/src/main/java/com/yolo/keyborad/common/ErrorCode.java
+++ b/src/main/java/com/yolo/keyborad/common/ErrorCode.java
@@ -17,8 +17,17 @@ public enum ErrorCode {
NOT_FOUND_ERROR(40400, "请求数据不存在"),
FORBIDDEN_ERROR(40300, "禁止访问"),
SYSTEM_ERROR(50000, "系统内部异常"),
- OPERATION_ERROR(50001, "操作失败");
-
+ OPERATION_ERROR(50001, "操作失败"),
+ APPLE_LOGIN_ERROR(40003, "Apple登录失败"),
+ FILE_IS_EMPTY(40001, "上传文件为空"),
+ TOKEN_NOT_FOUND(40102, "未能读取到有效用户令牌"),
+ TOKEN_INVALID(40103, "令牌无效"),
+ TOKEN_TIMEOUT(40104, "令牌已过期"),
+ TOKEN_BE_REPLACED(40105, "令牌已被顶下线"),
+ TOKEN_KICK_OUT(40107, "令牌已被踢下线"),
+ TOKEN_FREEZE(40108, "令牌已被冻结"),
+ TOKEN_NO_PREFIX(40109, "未按照指定前缀提交令牌"),
+ FILE_NAME_ERROR(40002, "文件名错误");
/**
* 状态码
*/
diff --git a/src/main/java/com/yolo/keyborad/common/xfile/ByteFileWrapper.java b/src/main/java/com/yolo/keyborad/common/xfile/ByteFileWrapper.java
new file mode 100644
index 0000000..a8ff36f
--- /dev/null
+++ b/src/main/java/com/yolo/keyborad/common/xfile/ByteFileWrapper.java
@@ -0,0 +1,35 @@
+package com.yolo.keyborad.common.xfile;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.dromara.x.file.storage.core.file.FileWrapper;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class ByteFileWrapper implements FileWrapper {
+ private byte[] bytes;
+ private String name;
+ private String contentType;
+ private InputStream inputStream;
+ private Long size;
+
+ public ByteFileWrapper(byte[] bytes,String name,String contentType,Long size) {
+ this.bytes = bytes;
+ this.name = name;
+ this.contentType = contentType;
+ this.size = size;
+ }
+
+ @Override
+ public InputStream getInputStream() {
+ if (inputStream == null) {
+ inputStream = new ByteArrayInputStream(bytes);
+ }
+ return inputStream;
+ }
+}
diff --git a/src/main/java/com/yolo/keyborad/common/xfile/ByteFileWrapperAdapter.java b/src/main/java/com/yolo/keyborad/common/xfile/ByteFileWrapperAdapter.java
new file mode 100644
index 0000000..bda9c61
--- /dev/null
+++ b/src/main/java/com/yolo/keyborad/common/xfile/ByteFileWrapperAdapter.java
@@ -0,0 +1,41 @@
+package com.yolo.keyborad.common.xfile;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.dromara.x.file.storage.core.file.FileWrapper;
+import org.dromara.x.file.storage.core.file.FileWrapperAdapter;
+import org.dromara.x.file.storage.core.tika.ContentTypeDetect;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+public class ByteFileWrapperAdapter implements FileWrapperAdapter {
+ private ContentTypeDetect contentTypeDetect;
+
+ /**
+ * 是否支持此资源文件
+ */
+ @Override
+ public boolean isSupport(Object source) {
+ return source instanceof byte[] || source instanceof ByteFileWrapper;
+ }
+
+ /**
+ * 对资源文件进行包装
+ */
+ @Override
+ public FileWrapper getFileWrapper(Object source, String name, String contentType, Long size) {
+ if (source instanceof ByteFileWrapper) {
+ return updateFileWrapper((ByteFileWrapper) source,name,contentType,size);
+ } else {
+ byte[] bytes = (byte[]) source;
+ if (name == null) name = "";
+ if (contentType == null) contentType = contentTypeDetect.detect(bytes,name);
+ if (size == null) size = (long) bytes.length;
+ return new ByteFileWrapper(bytes,name,contentType,size);
+ }
+ }
+}
diff --git a/src/main/java/com/yolo/keyborad/config/SaTokenConfigure.java b/src/main/java/com/yolo/keyborad/config/SaTokenConfigure.java
index ed33748..14250d9 100644
--- a/src/main/java/com/yolo/keyborad/config/SaTokenConfigure.java
+++ b/src/main/java/com/yolo/keyborad/config/SaTokenConfigure.java
@@ -40,7 +40,8 @@ public class SaTokenConfigure implements WebMvcConfigurer {
"/demo/embed",
"/demo/testSaveEmbed",
"/demo/testSearch",
- "/demo/tsetSearchText"
+ "/demo/tsetSearchText",
+ "/file/upload"
};
}
@Bean
diff --git a/src/main/java/com/yolo/keyborad/controller/FileController.java b/src/main/java/com/yolo/keyborad/controller/FileController.java
new file mode 100644
index 0000000..894256a
--- /dev/null
+++ b/src/main/java/com/yolo/keyborad/controller/FileController.java
@@ -0,0 +1,38 @@
+package com.yolo.keyborad.controller;
+
+import com.yolo.keyborad.common.BaseResponse;
+import com.yolo.keyborad.common.ResultUtils;
+import com.yolo.keyborad.model.dto.AppleLoginReq;
+import com.yolo.keyborad.service.FileService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+
+/*
+ * @author: ziin
+ * @date: 2025/12/2 15:46
+ */
+@RestController
+@Slf4j
+@RequestMapping("/file")
+@Tag(name = "文件")
+public class FileController {
+
+ @Resource
+ private FileService fileService;
+
+
+ @PostMapping("/upload")
+ @Operation(summary = "上传文件", description = "上传文件接口")
+ @Parameter(name = "file",required = true,description = "上传的文件")
+ public BaseResponse upload(@RequestParam("file") MultipartFile file) throws Exception {
+ String fileUrl = fileService.upload(file);
+ return ResultUtils.success(fileUrl);
+ }
+}
diff --git a/src/main/java/com/yolo/keyborad/controller/UserController.java b/src/main/java/com/yolo/keyborad/controller/UserController.java
index b9de52b..b93d085 100644
--- a/src/main/java/com/yolo/keyborad/controller/UserController.java
+++ b/src/main/java/com/yolo/keyborad/controller/UserController.java
@@ -3,6 +3,7 @@ package com.yolo.keyborad.controller;
import com.yolo.keyborad.common.BaseResponse;
import com.yolo.keyborad.common.ResultUtils;
import com.yolo.keyborad.model.dto.AppleLoginReq;
+import com.yolo.keyborad.model.vo.user.KeyboardUserRespVO;
import com.yolo.keyborad.service.IAppleService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -22,7 +23,6 @@ import org.springframework.web.bind.annotation.*;
@Slf4j
@RequestMapping("/user")
@Tag(name = "用户")
-@AllArgsConstructor
public class UserController {
@Resource
@@ -36,8 +36,9 @@ public class UserController {
@PostMapping("/appleLogin")
@Operation(summary = "苹果登录", description = "苹果登录接口")
@Parameter(name = "code",required = true,description = "苹果登录凭证",example = "123456")
- public BaseResponse appleLogin(@RequestBody AppleLoginReq appleLoginReq) throws Exception {
- String subjectId = appleService.login(appleLoginReq.getIdentityToken());
- return ResultUtils.success(subjectId);
+ public BaseResponse appleLogin(@RequestBody AppleLoginReq appleLoginReq) throws Exception {
+ return ResultUtils.success(appleService.login(appleLoginReq.getIdentityToken()));
}
+
+
}
diff --git a/src/main/java/com/yolo/keyborad/exception/GlobalExceptionHandler.java b/src/main/java/com/yolo/keyborad/exception/GlobalExceptionHandler.java
index 346ea02..8a66f3a 100644
--- a/src/main/java/com/yolo/keyborad/exception/GlobalExceptionHandler.java
+++ b/src/main/java/com/yolo/keyborad/exception/GlobalExceptionHandler.java
@@ -1,5 +1,6 @@
package com.yolo.keyborad.exception;
+import cn.dev33.satoken.exception.NotLoginException;
import com.yolo.keyborad.common.BaseResponse;
import com.yolo.keyborad.common.ErrorCode;
import com.yolo.keyborad.common.ResultUtils;
@@ -59,4 +60,49 @@ public class GlobalExceptionHandler {
return ResultUtils.error(ErrorCode.SYSTEM_ERROR.getCode(), errorMessage);
}
+
+ // 全局异常拦截(拦截项目中的NotLoginException异常)
+ @ExceptionHandler(NotLoginException.class)
+ public BaseResponse> handlerNotLoginException(NotLoginException nle)
+ throws Exception {
+
+ // 打印堆栈,以供调试
+ log.error("handlerNotLoginException", nle);
+ // 判断场景值,定制化异常信息
+ String message = "";
+ if(nle.getType().equals(NotLoginException.NOT_TOKEN)) {
+ message = "未能读取到有效用户令牌";
+ return ResultUtils.error(ErrorCode.TOKEN_NOT_FOUND);
+ }
+ else if(nle.getType().equals(NotLoginException.INVALID_TOKEN)) {
+ message = "令牌无效";
+ return ResultUtils.error(ErrorCode.TOKEN_INVALID);
+ }
+ else if(nle.getType().equals(NotLoginException.TOKEN_TIMEOUT)) {
+ message = "令牌已过期";
+ return ResultUtils.error(ErrorCode.TOKEN_TIMEOUT);
+ }
+ else if(nle.getType().equals(NotLoginException.BE_REPLACED)) {
+ message = "令牌已被顶下线";
+ return ResultUtils.error(ErrorCode.TOKEN_BE_REPLACED);
+ }
+ else if(nle.getType().equals(NotLoginException.KICK_OUT)) {
+ message = "令牌已被踢下线";
+ return ResultUtils.error(ErrorCode.TOKEN_KICK_OUT);
+ }
+ else if(nle.getType().equals(NotLoginException.TOKEN_FREEZE)) {
+ message = "令牌已被冻结";
+ return ResultUtils.error(ErrorCode.TOKEN_FREEZE);
+ }
+ else if(nle.getType().equals(NotLoginException.NO_PREFIX)) {
+ message = "未按照指定前缀提交令牌";
+ return ResultUtils.error(ErrorCode.TOKEN_NO_PREFIX);
+ }
+ else {
+ message = "当前会话未登录";
+ return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR);
+ }
+
+ }
+
}
\ No newline at end of file
diff --git a/src/main/java/com/yolo/keyborad/mapper/I18nMessageMapper.java b/src/main/java/com/yolo/keyborad/mapper/I18nMessageMapper.java
index 2841a7d..28b6e5c 100644
--- a/src/main/java/com/yolo/keyborad/mapper/I18nMessageMapper.java
+++ b/src/main/java/com/yolo/keyborad/mapper/I18nMessageMapper.java
@@ -5,15 +5,16 @@ package com.yolo.keyborad.mapper;
* @date: 2025/12/1 20:40
*/
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yolo.keyborad.model.entity.I18nMessage;
import org.apache.ibatis.annotations.Param;
-import org.apache.ibatis.annotations.Select;
+
import java.util.List;
-public interface I18nMessageMapper {
+public interface I18nMessageMapper extends BaseMapper {
List selectByCodeAndLocale(@Param("code") String code, @Param("locale") String locale);
}
\ No newline at end of file
diff --git a/src/main/java/com/yolo/keyborad/mapper/KeyboardUserMapper.java b/src/main/java/com/yolo/keyborad/mapper/KeyboardUserMapper.java
new file mode 100644
index 0000000..71f2608
--- /dev/null
+++ b/src/main/java/com/yolo/keyborad/mapper/KeyboardUserMapper.java
@@ -0,0 +1,12 @@
+package com.yolo.keyborad.mapper;
+
+/*
+ * @author: ziin
+ * @date: 2025/12/2 18:10
+ */
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.yolo.keyborad.model.entity.KeyboardUser;
+
+public interface KeyboardUserMapper extends BaseMapper {
+}
\ No newline at end of file
diff --git a/src/main/java/com/yolo/keyborad/model/entity/KeyboardUser.java b/src/main/java/com/yolo/keyborad/model/entity/KeyboardUser.java
new file mode 100644
index 0000000..af9abe8
--- /dev/null
+++ b/src/main/java/com/yolo/keyborad/model/entity/KeyboardUser.java
@@ -0,0 +1,84 @@
+package com.yolo.keyborad.model.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.Date;
+
+/*
+* @author: ziin
+* @date: 2025/12/2 18:08
+*/
+
+@Data
+@Schema(description="用户信息")
+public class KeyboardUser {
+
+ @Schema(description="主键ID")
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ @Schema(description="用户ID")
+ private Long uid;
+
+ @Schema(description="用户昵称")
+ private String nickName;
+
+ @Schema(description="性别")
+ private Integer gender;
+
+ @Schema(description="头像URL")
+ private String avatarUrl;
+
+ /**
+ * 创建时间
+ */
+ @Schema(description="创建时间")
+ private Date createdAt;
+
+ /**
+ * 更新时间
+ */
+ @Schema(description="更新时间")
+ private Date updatedAt;
+
+ /**
+ * 是否删除(默认否)
+ */
+ @Schema(description="是否删除(默认否)")
+ private Boolean deleted;
+
+ /**
+ * 邮箱地址
+ */
+ @Schema(description="邮箱地址")
+ private String email;
+
+ /**
+ * 是否禁用
+ */
+ @Schema(description="是否禁用")
+ private Boolean status;
+
+ /**
+ * 密码
+ */
+ @Schema(description="密码")
+ private String password;
+
+ /**
+ * 苹果登录subjectId
+ */
+ @Schema(description="苹果登录subjectId")
+ private String subjectId;
+
+ /**
+ * 邮箱是否验证
+ */
+ @Schema(description="邮箱是否验证")
+ private Boolean emailVerified;
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/yolo/keyborad/model/vo/user/KeyboardUserRespVO.java b/src/main/java/com/yolo/keyborad/model/vo/user/KeyboardUserRespVO.java
new file mode 100644
index 0000000..9a5dd86
--- /dev/null
+++ b/src/main/java/com/yolo/keyborad/model/vo/user/KeyboardUserRespVO.java
@@ -0,0 +1,45 @@
+package com.yolo.keyborad.model.vo.user;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.Date;
+
+/*
+* @author: ziin
+* @date: 2025/12/2 18:08
+*/
+
+@Data
+@Schema(description="用户信息")
+public class KeyboardUserRespVO {
+
+
+ @Schema(description="用户ID")
+ private Long uid;
+
+ @Schema(description="用户昵称")
+ private String nickName;
+
+ @Schema(description="性别")
+ private Integer gender;
+
+
+ @Schema(description="头像URL")
+ private String avatarUrl;
+
+ /**
+ * 邮箱地址
+ */
+ @Schema(description="邮箱地址")
+ private String email;
+
+ /**
+ * 邮箱是否验证
+ */
+ @Schema(description="邮箱是否验证")
+ private Boolean emailVerified;
+
+ @Schema(description = "token")
+ private String token;
+}
\ No newline at end of file
diff --git a/src/main/java/com/yolo/keyborad/service/FileService.java b/src/main/java/com/yolo/keyborad/service/FileService.java
new file mode 100644
index 0000000..1bd764b
--- /dev/null
+++ b/src/main/java/com/yolo/keyborad/service/FileService.java
@@ -0,0 +1,13 @@
+package com.yolo.keyborad.service;
+
+import org.springframework.web.multipart.MultipartFile;
+
+/*
+ * @author: ziin
+ * @date: 2025/12/2 15:44
+ */
+public interface FileService {
+
+
+ String upload(MultipartFile file);
+}
diff --git a/src/main/java/com/yolo/keyborad/service/IAppleService.java b/src/main/java/com/yolo/keyborad/service/IAppleService.java
index 536418e..ab8640c 100644
--- a/src/main/java/com/yolo/keyborad/service/IAppleService.java
+++ b/src/main/java/com/yolo/keyborad/service/IAppleService.java
@@ -1,5 +1,7 @@
package com.yolo.keyborad.service;
+import com.yolo.keyborad.model.vo.user.KeyboardUserRespVO;
+
/**
* Apple相关API
*
@@ -13,5 +15,5 @@ public interface IAppleService {
*
* @param identityToken JWT身份令牌
*/
- String login(String identityToken) throws Exception;
+ KeyboardUserRespVO login(String identityToken) throws Exception;
}
diff --git a/src/main/java/com/yolo/keyborad/service/II18nService.java b/src/main/java/com/yolo/keyborad/service/II18nService.java
index 808ae82..149bd57 100644
--- a/src/main/java/com/yolo/keyborad/service/II18nService.java
+++ b/src/main/java/com/yolo/keyborad/service/II18nService.java
@@ -1,12 +1,15 @@
package com.yolo.keyborad.service;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.yolo.keyborad.model.entity.I18nMessage;
+
/**
* 国际化服务接口
*
* @author ziin
* @date 2025/12/1
*/
-public interface II18nService {
+public interface II18nService extends IService {
/**
* 根据错误码和语言获取错误消息
diff --git a/src/main/java/com/yolo/keyborad/service/UserService.java b/src/main/java/com/yolo/keyborad/service/UserService.java
new file mode 100644
index 0000000..c6ebf89
--- /dev/null
+++ b/src/main/java/com/yolo/keyborad/service/UserService.java
@@ -0,0 +1,15 @@
+package com.yolo.keyborad.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.yolo.keyborad.model.entity.KeyboardUser;
+
+/*
+ * @author: ziin
+ * @date: 2025/12/2 18:19
+ */
+public interface UserService extends IService {
+
+ KeyboardUser selectUserWithSubjectId(String sub);
+
+ KeyboardUser createUserWithSubjectId(String sub);
+}
diff --git a/src/main/java/com/yolo/keyborad/service/impl/AppleServiceImpl.java b/src/main/java/com/yolo/keyborad/service/impl/AppleServiceImpl.java
index 60be80a..182314c 100644
--- a/src/main/java/com/yolo/keyborad/service/impl/AppleServiceImpl.java
+++ b/src/main/java/com/yolo/keyborad/service/impl/AppleServiceImpl.java
@@ -1,12 +1,19 @@
package com.yolo.keyborad.service.impl;
+import cn.dev33.satoken.stp.SaTokenInfo;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.bean.BeanUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.yolo.keyborad.common.ErrorCode;
import com.yolo.keyborad.exception.BusinessException;
+import com.yolo.keyborad.model.entity.KeyboardUser;
+import com.yolo.keyborad.model.vo.user.KeyboardUserRespVO;
import com.yolo.keyborad.service.IAppleService;
+import com.yolo.keyborad.service.UserService;
import io.jsonwebtoken.*;
+import jakarta.annotation.Resource;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -30,13 +37,16 @@ import java.util.Objects;
@AllArgsConstructor
public class AppleServiceImpl implements IAppleService {
+ @Resource
+ private UserService userService;
+
/**
* 登录
*
* @param identityToken JWT身份令牌
*/
@Override
- public String login(String identityToken) throws Exception {
+ public KeyboardUserRespVO login(String identityToken) throws Exception {
// 1. 清理一下 token,防止前后多了引号/空格
identityToken = identityToken.trim();
@@ -66,17 +76,29 @@ public class AppleServiceImpl implements IAppleService {
// 3. 获取公钥
PublicKey publicKey = this.getPublicKey(kid);
if (Objects.isNull(publicKey)) {
- throw new RuntimeException("apple授权登录的公钥获取失败,kid=" + kid);
+ log.error("apple授权登录的公钥获取失败,kid={}", kid);
+ throw new BusinessException(ErrorCode.APPLE_LOGIN_ERROR);
}
// 4. 验证Apple登录的JWT令牌
boolean result = this.verifyAppleLoginCode(publicKey, identityToken, aud, sub);
+
// 返回用户标识符
if (result) {
- return sub;
+ KeyboardUser user = userService.selectUserWithSubjectId(sub);
+ if (user == null) {
+ KeyboardUser newUser = userService.createUserWithSubjectId(sub);
+ KeyboardUserRespVO keyboardUserRespVO = BeanUtil.copyProperties(newUser, KeyboardUserRespVO.class);
+ StpUtil.login(newUser.getId());
+ keyboardUserRespVO.setToken(StpUtil.getTokenValueByLoginId(newUser.getId()));
+ return keyboardUserRespVO;
+ }
+ KeyboardUserRespVO keyboardUserRespVO = BeanUtil.copyProperties(user, KeyboardUserRespVO.class);
+ StpUtil.login(user.getId());
+ keyboardUserRespVO.setToken(StpUtil.getTokenValueByLoginId(user.getId()));
+ return keyboardUserRespVO;
}
-
return null;
}
diff --git a/src/main/java/com/yolo/keyborad/service/impl/FileServiceImpl.java b/src/main/java/com/yolo/keyborad/service/impl/FileServiceImpl.java
new file mode 100644
index 0000000..d1908a3
--- /dev/null
+++ b/src/main/java/com/yolo/keyborad/service/impl/FileServiceImpl.java
@@ -0,0 +1,46 @@
+package com.yolo.keyborad.service.impl;
+
+import cn.hutool.core.lang.UUID;
+import com.yolo.keyborad.common.ErrorCode;
+import com.yolo.keyborad.exception.BusinessException;
+import com.yolo.keyborad.service.FileService;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.x.file.storage.core.FileInfo;
+import org.dromara.x.file.storage.core.FileStorageService;
+import org.dromara.x.file.storage.spring.SpringFileStorageProperties;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+/*
+ * @author: ziin
+ * @date: 2025/12/2 15:45
+ */
+@Service
+@Slf4j
+public class FileServiceImpl implements FileService {
+
+ @Resource
+ private FileStorageService fileStorageService;//注入实列
+
+ @Override
+ public String upload(MultipartFile file) {
+ if (file == null || file.isEmpty()) {
+ log.error("上传文件为空");
+ throw new BusinessException(ErrorCode.FILE_IS_EMPTY);
+ }
+
+ // 获取原始文件名
+ String originalFilename = file.getOriginalFilename();
+ if (originalFilename == null) {
+ log.error("无法获取文件名");
+ throw new BusinessException(ErrorCode.FILE_NAME_ERROR);
+ }
+ // 获取文件扩展名
+ String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
+ FileInfo upload = fileStorageService.of(file)
+ .setSaveFilename(UUID.randomUUID() + extension)
+ .upload();
+ return upload.getUrl();
+ }
+}
diff --git a/src/main/java/com/yolo/keyborad/service/impl/I18nServiceImpl.java b/src/main/java/com/yolo/keyborad/service/impl/I18nServiceImpl.java
index bb16ebc..faff73e 100644
--- a/src/main/java/com/yolo/keyborad/service/impl/I18nServiceImpl.java
+++ b/src/main/java/com/yolo/keyborad/service/impl/I18nServiceImpl.java
@@ -1,5 +1,6 @@
package com.yolo.keyborad.service.impl;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yolo.keyborad.mapper.I18nMessageMapper;
import com.yolo.keyborad.model.entity.I18nMessage;
import com.yolo.keyborad.service.II18nService;
@@ -21,7 +22,7 @@ import java.util.concurrent.TimeUnit;
*/
@Service
@Slf4j
-public class I18nServiceImpl implements II18nService {
+public class I18nServiceImpl extends ServiceImpl implements II18nService {
@Resource
private I18nMessageMapper i18nMessageMapper;
diff --git a/src/main/java/com/yolo/keyborad/service/impl/UserServiceImpl.java b/src/main/java/com/yolo/keyborad/service/impl/UserServiceImpl.java
new file mode 100644
index 0000000..408dd75
--- /dev/null
+++ b/src/main/java/com/yolo/keyborad/service/impl/UserServiceImpl.java
@@ -0,0 +1,43 @@
+package com.yolo.keyborad.service.impl;
+
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.RandomUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.yolo.keyborad.mapper.KeyboardUserMapper;
+import com.yolo.keyborad.model.entity.KeyboardUser;
+import com.yolo.keyborad.service.UserService;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/*
+ * @author: ziin
+ * @date: 2025/12/2 18:19
+ */
+@Service
+@Slf4j
+public class UserServiceImpl extends ServiceImpl implements UserService {
+
+ @Resource
+ private KeyboardUserMapper keyboardUserMapper;
+
+ @Override
+ public KeyboardUser selectUserWithSubjectId(String sub) {
+ KeyboardUser keyboardUser = keyboardUserMapper.selectOne(
+ new LambdaQueryWrapper()
+ .eq(KeyboardUser::getSubjectId, sub)
+ .eq(KeyboardUser::getStatus, false));
+ return keyboardUser;
+ }
+
+ @Override
+ public KeyboardUser createUserWithSubjectId(String sub) {
+ KeyboardUser keyboardUser = new KeyboardUser();
+ keyboardUser.setSubjectId(sub);
+ keyboardUser.setUid(IdUtil.getSnowflake().nextId());
+ keyboardUser.setNickName("User" + RandomUtil.randomString(6));
+ keyboardUserMapper.insert(keyboardUser);
+ return keyboardUser;
+ }
+}
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index 7e539a4..e1298ef 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -34,4 +34,47 @@ apple:
# 根证书路径(从 Apple PKI 下载)
root-certificates:
- "classpath:AppleRootCA-G2.cer"
- - "classpath:AppleRootCA-G3.cer"
\ No newline at end of file
+ - "classpath:AppleRootCA-G3.cer"
+
+dromara:
+ x-file-storage: #文件存储配置
+ default-platform: cloudflare-r2 #默认使用的存储平台
+ thumbnail-suffix: ".min.jpg" #缩略图后缀,例如【.min.jpg】【.png】
+ enable-byte-file-wrapper: false
+ #对应平台的配置写在这里,注意缩进要对齐
+ amazon-s3-v2: # Amazon S3 V2
+ - platform: cloudflare-r2 # 存储平台标识
+ enable-storage: true # 启用存储
+ access-key: 550b33cc4d53e05c2e438601f8a0e209
+ secret-key: df4d529cdae44e6f614ca04f4dc0f1f9a299e57367181243e8abdc7f7c28e99a
+ region: ENAM # 必填
+ end-point: https://b632a61caa85401f63c9b32eef3a74c8.r2.cloudflarestorage.com # 必填
+ bucket-name: keyborad-resource #桶名称
+ domain: https://resource.loveamorkey.com/ # 访问域名,注意“/”结尾,例如:https://abcd.s3.ap-east-1.amazonaws.com/
+ base-path: avatar/ # 基础路径
+
+
+mailgun:
+ api-key: ${MAILGUN_API_KEY} # 你的 Private API Key
+ domain: sandboxxxxxxx.mailgun.org # 或你自己的业务域名
+ from-email: no-reply@yourdomain.com # 发件人邮箱
+ from-name: Key Of Love # 发件人名称(可选)
+
+
+
+############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
+sa-token:
+ # token 名称(同时也是 cookie 名称)
+ token-name: auth-token
+ # token 有效期(单位:秒) 默认30天,-1 代表永久有效
+ timeout: 2592000
+ # token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
+ active-timeout: -1
+ # 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
+ is-concurrent: false
+ # 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
+ is-share: false
+ # token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
+ token-style: random-128
+ # 是否输出操作日志
+ is-log: true
diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml
index 1900b47..6a95f3a 100644
--- a/src/main/resources/application-prod.yml
+++ b/src/main/resources/application-prod.yml
@@ -1,6 +1,6 @@
spring:
datasource:
driver-class-name: org.postgresql.Driver
- url: jdbc:postgresql://localhost:5432/postgres
+ url: jdbc:postgresql://localhost:5432/keyborad_db
username: root
password: 123asd
\ No newline at end of file
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index f2e78e6..b6e5d8c 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -15,11 +15,6 @@ spring:
name: keyborad-backend
profiles:
active: dev
- datasource:
- driver-class-name: org.postgresql.Driver
- url: jdbc:postgresql://localhost:5432/keyborad_db
- username: root
- password: 123asd
mvc:
pathmatch:
matching-strategy: ANT_PATH_MATCHER
@@ -44,7 +39,7 @@ server:
enabled: true
mybatis-plus:
configuration:
- map-underscore-to-camel-case: false
+ map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
global-config:
db-config:
diff --git a/src/main/resources/mapper/KeyboardUserMapper.xml b/src/main/resources/mapper/KeyboardUserMapper.xml
new file mode 100644
index 0000000..8b32d84
--- /dev/null
+++ b/src/main/resources/mapper/KeyboardUserMapper.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ id, "uid", nick_name, gender, avatar_url, created_at, updated_at, deleted, email,
+ "status", "password", subject_id, email_verified
+
+
\ No newline at end of file