first commit

This commit is contained in:
2025-07-08 17:52:38 +08:00
commit 9358f84a7d
18 changed files with 672 additions and 0 deletions

33
.gitignore vendored Normal file
View File

@ -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/

133
pom.xml Normal file
View File

@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ctgu</groupId>
<artifactId>alex-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>alex-api</name>
<description>alex-api</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.6.13</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.7.6</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>com.ctgu.alexapi.AlexApiApplication</mainClass>
<!-- <skip>true</skip>-->
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -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);
}
}

View File

@ -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<String, Object> redisTemplate(
@Qualifier("redisConnectionFactory")RedisConnectionFactory redisConnectionFactory
) {
// 创建 RedisTemplate 对象
RedisTemplate<String, Object> 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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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<UsersEntity> {
}

View File

@ -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<UsersEntity> {
ApiResult getAllUser(Integer pageNum, Integer pageSize);
ApiResult getUserById(Integer id);
}

View File

@ -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<UsersMapper, UsersEntity>
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<UsersEntity> list = query().list();
PageInfo<UsersEntity> 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);
}
}

View File

@ -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 <T> Optional<T> getData(Class<T> clazz) {
return Optional.ofNullable(clazz.cast(data));
}
@Override
public String toString() {
return "ApiResult{" +
"code=" + code +
", msg='" + msg + '\'' +
", data=" + (data != null ? data.toString() : "null") +
'}';
}
}

View File

@ -0,0 +1,16 @@
package com.ctgu.alexapi.utils;
import lombok.Builder;
import lombok.Data;
import java.util.List;
@Data
@Builder
public class PageData<T> {
private Long totalNum; //总条数
private Integer totalPage; //总页数
private List<T> data; //数据
}

View File

@ -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 <T> ApiResult pageHelperResult(List<T> list, PageInfo<T> pageInfo, String TAG) {
if (list.isEmpty()) {
log.debug("{} : 获取列表为空 = {}", TAG, Collections.emptyList());
return ApiResult.success("获取列表为空", Collections.emptyList());
}
PageData.PageDataBuilder<T> 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;
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ctgu.alexapi.mapper.UsersMapper">
<resultMap id="BaseResultMap" type="com.ctgu.alexapi.entity.UsersEntity">
<id property="id" column="id" jdbcType="INTEGER"/>
<result property="username" column="username" jdbcType="VARCHAR"/>
<result property="password" column="password" jdbcType="VARCHAR"/>
</resultMap>
<sql id="Base_Column_List">
id,username,password
</sql>
</mapper>

View File

@ -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() {
}
}