commit 9358f84a7df11717bf4f257d3a90da25574e8648 Author: ALEX <2604434353@qq.com> Date: Tue Jul 8 17:52:38 2025 +0800 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..6a75b63 --- /dev/null +++ b/pom.xml @@ -0,0 +1,133 @@ + + + 4.0.0 + com.ctgu + alex-api + 0.0.1-SNAPSHOT + alex-api + alex-api + + 1.8 + UTF-8 + UTF-8 + 2.6.13 + + + + org.springframework.boot + spring-boot-starter-web + + + + com.mysql + mysql-connector-j + runtime + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + com.github.pagehelper + pagehelper-spring-boot-starter + 2.1.0 + + + + org.apache.logging.log4j + log4j-core + 2.19.0 + + + + org.apache.logging.log4j + log4j-slf4j2-impl + 2.19.0 + + + + org.springframework.boot + spring-boot-starter-data-redis + 2.7.6 + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 2.3.0 + + + + org.springframework.boot + spring-boot-devtools + true + + + com.baomidou + mybatis-plus-boot-starter + 3.5.1 + + + com.baomidou + mybatis-plus-generator + 3.5.1 + + + com.google.code.gson + gson + 2.10.1 + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + UTF-8 + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + com.ctgu.alexapi.AlexApiApplication + + + + + repackage + + repackage + + + + + + + + diff --git a/src/main/java/com/ctgu/alexapi/AlexApiApplication.java b/src/main/java/com/ctgu/alexapi/AlexApiApplication.java new file mode 100644 index 0000000..c93cca9 --- /dev/null +++ b/src/main/java/com/ctgu/alexapi/AlexApiApplication.java @@ -0,0 +1,15 @@ +package com.ctgu.alexapi; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +@MapperScan("com.ctgu.alexapi.mapper") +public class AlexApiApplication { + + public static void main(String[] args) { + SpringApplication.run(AlexApiApplication.class, args); + } + +} diff --git a/src/main/java/com/ctgu/alexapi/config/RedisConfig.java b/src/main/java/com/ctgu/alexapi/config/RedisConfig.java new file mode 100644 index 0000000..7a49a6c --- /dev/null +++ b/src/main/java/com/ctgu/alexapi/config/RedisConfig.java @@ -0,0 +1,42 @@ +package com.ctgu.alexapi.config; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.data.redis.RedisProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@Configuration +public class RedisConfig { + + @Bean + public RedisConnectionFactory redisConnectionFactory(RedisProperties redisProperties) { + RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(); + config.setHostName(redisProperties.getHost()); + config.setPort(redisProperties.getPort()); + config.setPassword(redisProperties.getPassword()); + config.setDatabase(0); + return new LettuceConnectionFactory(config); + } + + @Bean(name = "redisTemplate") + public RedisTemplate redisTemplate( + @Qualifier("redisConnectionFactory")RedisConnectionFactory redisConnectionFactory + ) { + // 创建 RedisTemplate 对象 + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory); + + // 设置默认的序列化器(String 类型) + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setHashKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new StringRedisSerializer()); + redisTemplate.setHashValueSerializer(new StringRedisSerializer()); + + return redisTemplate; + } +} diff --git a/src/main/java/com/ctgu/alexapi/controller/UsersController.java b/src/main/java/com/ctgu/alexapi/controller/UsersController.java new file mode 100644 index 0000000..b12637a --- /dev/null +++ b/src/main/java/com/ctgu/alexapi/controller/UsersController.java @@ -0,0 +1,36 @@ +package com.ctgu.alexapi.controller; + +import com.ctgu.alexapi.service.UsersService; +import com.ctgu.alexapi.utils.ApiResult; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * @ClassName UserController + * @Author Alex2 + * @Date 2025/7/2 14:43 + **/ +@RestController +@RequestMapping("/api/users") +public class UsersController { + private final UsersService usersService; + + @Autowired + public UsersController(UsersService usersService) { + this.usersService = usersService; + } + + // http://localhost:8888/api/users/getAllUser?pageNum=1&pageSize=10 + @GetMapping("/getAllUser") + public ApiResult getAllUser( + @RequestParam(value = "pageNum", defaultValue = "0", required = false) Integer pageNum, + @RequestParam(value = "pageSize", defaultValue = "10", required = false) Integer pageSize) { + return usersService.getAllUser(pageNum, pageSize); + } + + // http://localhost:8888/api/users/1 + @GetMapping("/{id}") + public ApiResult getUserById(@PathVariable("id") Integer id) { + return usersService.getUserById(id); + } +} diff --git a/src/main/java/com/ctgu/alexapi/entity/UsersEntity.java b/src/main/java/com/ctgu/alexapi/entity/UsersEntity.java new file mode 100644 index 0000000..93c88a9 --- /dev/null +++ b/src/main/java/com/ctgu/alexapi/entity/UsersEntity.java @@ -0,0 +1,37 @@ +package com.ctgu.alexapi.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.io.Serializable; +import lombok.Data; + +/** + * + * @TableName t_users + */ +@TableName(value ="t_users") +@Data +public class UsersEntity implements Serializable { + /** + * 主键 + */ + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + /** + * 用户名 + */ + @TableField(value = "username") + private String username; + + /** + * 密码 + */ + @TableField(value = "password") + private String password; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/com/ctgu/alexapi/mapper/UsersMapper.java b/src/main/java/com/ctgu/alexapi/mapper/UsersMapper.java new file mode 100644 index 0000000..c0d7c03 --- /dev/null +++ b/src/main/java/com/ctgu/alexapi/mapper/UsersMapper.java @@ -0,0 +1,19 @@ +package com.ctgu.alexapi.mapper; + +import com.ctgu.alexapi.entity.UsersEntity; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** +* @author Alex2 +* @description 针对表【t_users】的数据库操作Mapper +* @createDate 2025-07-02 14:37:56 +* @Entity com.ctgu.alexapi.entity.UsersEntity +*/ +public interface UsersMapper extends BaseMapper { + +} + + + + diff --git a/src/main/java/com/ctgu/alexapi/service/UsersService.java b/src/main/java/com/ctgu/alexapi/service/UsersService.java new file mode 100644 index 0000000..7fe9695 --- /dev/null +++ b/src/main/java/com/ctgu/alexapi/service/UsersService.java @@ -0,0 +1,17 @@ +package com.ctgu.alexapi.service; + +import com.ctgu.alexapi.entity.UsersEntity; +import com.baomidou.mybatisplus.extension.service.IService; +import com.ctgu.alexapi.utils.ApiResult; + +/** +* @author Alex2 +* @description 针对表【t_users】的数据库操作Service +* @createDate 2025-07-02 14:37:56 +*/ +public interface UsersService extends IService { + + ApiResult getAllUser(Integer pageNum, Integer pageSize); + + ApiResult getUserById(Integer id); +} diff --git a/src/main/java/com/ctgu/alexapi/service/impl/UsersServiceImpl.java b/src/main/java/com/ctgu/alexapi/service/impl/UsersServiceImpl.java new file mode 100644 index 0000000..33d9e76 --- /dev/null +++ b/src/main/java/com/ctgu/alexapi/service/impl/UsersServiceImpl.java @@ -0,0 +1,72 @@ +package com.ctgu.alexapi.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ctgu.alexapi.entity.UsersEntity; +import com.ctgu.alexapi.service.UsersService; +import com.ctgu.alexapi.mapper.UsersMapper; +import com.ctgu.alexapi.utils.ApiResult; +import com.ctgu.alexapi.utils.Tools; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.google.gson.Gson; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** +* @author Alex2 +* @description 针对表【t_users】的数据库操作Service实现 +* @createDate 2025-07-02 14:37:56 +*/ +@Log4j2 +@Service +public class UsersServiceImpl extends ServiceImpl + implements UsersService{ + + private static final String TAG = UsersServiceImpl.class.getSimpleName(); + + @Autowired + private StringRedisTemplate redisTemplate; + + private static final String USER_CACHE_PREFIX = "user:"; + + @Override + public ApiResult getAllUser(Integer pageNum, Integer pageSize) { + PageHelper.startPage(pageNum, pageSize); + List list = query().list(); + PageInfo pageInfo = new PageInfo<>(list, 5); + return Tools.pageHelperResult(list, pageInfo, TAG); + } + + @Override + public ApiResult getUserById(Integer userId) { + String key = USER_CACHE_PREFIX + userId; + // 1. 先从 Redis 取缓存 + String cachedUserJson = redisTemplate.opsForValue().get(key); + if (cachedUserJson != null) { + // 有缓存,反序列化后返回 + UsersEntity cachedUser = new Gson().fromJson(cachedUserJson, UsersEntity.class); + log.debug("{}: 从缓存获取用户成功 = {}", TAG, cachedUser); + return ApiResult.success("获取用户成功(缓存)", cachedUser); + } + + // 2. 缓存没命中,从数据库查 + UsersEntity usersEntity = lambdaQuery().eq(UsersEntity::getId, userId).one(); + if (usersEntity == null) { + log.error("{}: 用户 Id = {} 不存在", TAG, userId); + return ApiResult.error("用户不存在"); + } + + // 3. 查到后写入缓存,设置过期时间,比如10分钟 + String userJson = new Gson().toJson(usersEntity); + redisTemplate.opsForValue().set(key, userJson, 10, TimeUnit.MINUTES); + + log.debug("{}: 获取用户成功 = {}", TAG, usersEntity); + return ApiResult.success("获取用户成功", usersEntity); + } +} \ No newline at end of file diff --git a/src/main/java/com/ctgu/alexapi/utils/ApiResult.java b/src/main/java/com/ctgu/alexapi/utils/ApiResult.java new file mode 100644 index 0000000..4050f6e --- /dev/null +++ b/src/main/java/com/ctgu/alexapi/utils/ApiResult.java @@ -0,0 +1,100 @@ +package com.ctgu.alexapi.utils; + +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Optional; + +@Data +@Builder +@NoArgsConstructor +public class ApiResult implements Serializable { + + private static final long serialVersionUID = -8440958610795020343L; + + public static final int CODE_SUCCESS = 200; + public static final int CODE_ERROR = 500; + public static final int CODE_WARNING = 501; + public static final int CODE_NOT_PERMISSION = 403; + public static final int CODE_NOE_LOGIN = 401; + public static final int CODE_INVALID_REQUEST = 400; + + private int code; + private String msg; + private Object data; + + // 构造方法 + public ApiResult(int code, String msg, Object data) { + this.code = code; + this.msg = msg; + this.data = data; + } + + // 静态构造方法 + public static ApiResult success() { + return new ApiResult(CODE_SUCCESS, null, null); + } + + public static ApiResult success(String msg) { + return new ApiResult(CODE_SUCCESS, msg, null); + } + + public static ApiResult success(String msg, Object data) { + return new ApiResult(CODE_SUCCESS, msg, data); + } + + public static ApiResult success(Object data) { + return new ApiResult(CODE_SUCCESS, null, data); + } + + public static ApiResult error() { + return new ApiResult(CODE_ERROR, "出错了", null); + } + + public static ApiResult error(String msg) { + return new ApiResult(CODE_ERROR, msg, null); + } + + public static ApiResult error(String msg, Object data) { + return new ApiResult(CODE_ERROR, msg, data); + } + + public static ApiResult notLogin() { + return new ApiResult(CODE_NOE_LOGIN, "未登录,请先进行登录", null); + } + + public static ApiResult noPermissions() { + return new ApiResult(CODE_NOT_PERMISSION, "权限不足", null); + } + + public static ApiResult build(int code, String msg) { + return new ApiResult(code, msg, null); + } + + public static ApiResult getByBoolean(boolean b) { + return b ? success() : error(); + } + + public static ApiResult warning(String msg, Object data) { + return new ApiResult(CODE_WARNING, msg, data); + } + + public static ApiResult warning(String msg) { + return new ApiResult(CODE_WARNING, msg, null); + } + + public Optional getData(Class clazz) { + return Optional.ofNullable(clazz.cast(data)); + } + + @Override + public String toString() { + return "ApiResult{" + + "code=" + code + + ", msg='" + msg + '\'' + + ", data=" + (data != null ? data.toString() : "null") + + '}'; + } +} \ No newline at end of file diff --git a/src/main/java/com/ctgu/alexapi/utils/PageData.java b/src/main/java/com/ctgu/alexapi/utils/PageData.java new file mode 100644 index 0000000..67b06e0 --- /dev/null +++ b/src/main/java/com/ctgu/alexapi/utils/PageData.java @@ -0,0 +1,16 @@ +package com.ctgu.alexapi.utils; + +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +@Data +@Builder +public class PageData { + private Long totalNum; //总条数 + + private Integer totalPage; //总页数 + + private List data; //数据 +} \ No newline at end of file diff --git a/src/main/java/com/ctgu/alexapi/utils/Tools.java b/src/main/java/com/ctgu/alexapi/utils/Tools.java new file mode 100644 index 0000000..cadb772 --- /dev/null +++ b/src/main/java/com/ctgu/alexapi/utils/Tools.java @@ -0,0 +1,46 @@ +package com.ctgu.alexapi.utils; + +import com.github.pagehelper.PageInfo; +import lombok.extern.log4j.Log4j2; + +import java.util.Collections; +import java.util.List; + +@Log4j2 +public class Tools { + public static ApiResult pageHelperResult(List list, PageInfo pageInfo, String TAG) { + if (list.isEmpty()) { + log.debug("{} : 获取列表为空 = {}", TAG, Collections.emptyList()); + return ApiResult.success("获取列表为空", Collections.emptyList()); + } + PageData.PageDataBuilder builder = PageData.builder(); + log.debug("{} : 获取列表成功 = {}", TAG, list); + return ApiResult.success("获取列表成功", builder + .totalNum(pageInfo.getTotal()) + .totalPage(pageInfo.getPages()) + .data(list) + .build()); + } + + /** + * @Author: Alex + * @Description: 获取图片MD5 + */ + public static String getMd5(String url) { + if (url.contains("aliyuncs.com")) { + int lastSlash = url.lastIndexOf('/'); + int lastDot = url.lastIndexOf('.'); + if (lastSlash != -1 && lastDot != -1 && lastDot > lastSlash) { + return url.substring(lastSlash + 1, lastDot); + } + } else if (url.contains("/image/")) { + int startIndex = url.lastIndexOf("/image/") + "/image/".length(); + int endIndex = url.indexOf("?", startIndex); + if (endIndex == -1) { + endIndex = url.length(); + } + return url.substring(startIndex, endIndex); + } + return null; + } +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml new file mode 100644 index 0000000..5e58b5a --- /dev/null +++ b/src/main/resources/application-dev.yml @@ -0,0 +1,12 @@ +spring: + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/test_alex?characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true + username: root + password: ALEXzcz123456 + redis: + host: localhost + password: ALEXzcz123456 + port: 6379 + database: 0 \ No newline at end of file diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml new file mode 100644 index 0000000..5e58b5a --- /dev/null +++ b/src/main/resources/application-prod.yml @@ -0,0 +1,12 @@ +spring: + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/test_alex?characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true + username: root + password: ALEXzcz123456 + redis: + host: localhost + password: ALEXzcz123456 + port: 6379 + database: 0 \ No newline at end of file diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml new file mode 100644 index 0000000..25e6375 --- /dev/null +++ b/src/main/resources/application-test.yml @@ -0,0 +1,12 @@ +spring: + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://117.72.202.202:3306/test_alex?characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true + username: alex + password: ALEXzcz123456 + redis: + host: 117.72.202.202 + password: ALEXzcz123456 + port: 6379 + database: 0 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..e8e145a --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,41 @@ +server: + address: 0.0.0.0 + port: 8888 +spring: + profiles: + active: dev # 默认激活开发环境 +#配置mybatis +mybatis: + configuration: + auto-mapping-behavior: full + map-underscore-to-camel-case: true + log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl + type-aliases-package: com.anytrek.entity # 配置别名路径 + mapper-locations: classpath:mapper/*.xml # 配置 XML 文件位置 +#配置mybatis-plus +mybatis-plus: + configuration: + log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl + global-config: + db-config: + table-prefix: t_ + id-type: auto +#配置分页插件 +pagehelper: + helperDialect: mysql + params: count=countSql + reasonable: true + supportMethodsArguments: true +#配置日志 +logging: + level: + root: INFO #根日志配置,整个应用程序的默认日志级别为 INFO + com.anytrek: DEBUG #com.anytrek 包下的类的日志级别设置为 DEBUG + org.apache.ibatis: INFO #MyBatis 日志级别设置为 INFO + com.zaxxer.hikari: INFO #HikariCP 的日志级别设置为 INFO + org.springframework.web.socket: DEBUG + org.springframework.web.socket.server.support: DEBUG + org: + springframework: + integration: DEBUG + messaging: DEBUG \ No newline at end of file diff --git a/src/main/resources/mapper/UsersMapper.xml b/src/main/resources/mapper/UsersMapper.xml new file mode 100644 index 0000000..6de296e --- /dev/null +++ b/src/main/resources/mapper/UsersMapper.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + id,username,password + + diff --git a/src/test/java/com/ctgu/alexapi/AlexApiApplicationTests.java b/src/test/java/com/ctgu/alexapi/AlexApiApplicationTests.java new file mode 100644 index 0000000..285e7bf --- /dev/null +++ b/src/test/java/com/ctgu/alexapi/AlexApiApplicationTests.java @@ -0,0 +1,13 @@ +package com.ctgu.alexapi; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class AlexApiApplicationTests { + + @Test + void contextLoads() { + } + +}