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