网站搭建实操(二)后台管理(1)登录

网站搭建实操(二)后台管理实现

模块架构

text 复制代码
forum-backend/
├── forum-common/              # 公共工具模块
├── forum-core/                # 核心配置模块
├── forum-framework/           # 框架封装模块
├── forum-dao/                 # 数据访问模块
├── forum-api/                 # API接口模块
├── forum-service/             # 业务服务模块
├── forum-auth-server/         # 认证服务
├── forum-gateway/             # 网关服务
├── forum-user-service/        # 用户服务
├── forum-post-service/        # 帖子服务
├── forum-comment-service/     # 评论服务
├── forum-message-service/     # 消息服务
├── forum-search-service/      # 搜索服务
└── forum-admin-service/       # 管理服务

后台代码架构

text 复制代码
forum-backend/
├── forum-common/                    # 公共模块
│   ├── src/main/java/com/forum/common/
│   │   ├── base/
│   │   │   ├── BaseEntity.java      # 基础实体类
│   │   │   ├── BaseController.java  # 基础控制器
│   │   │   ├── BaseService.java     # 基础服务接口
│   │   │   └── BaseServiceImpl.java # 基础服务实现
│   │   ├── constant/
│   │   │   ├── RedisKeyConstants.java
│   │   │   ├── SystemConstants.java
│   │   │   └── ApiConstants.java
│   │   ├── enums/
│   │   │   ├── ResultCodeEnum.java
│   │   │   ├── StatusEnum.java
│   │   │   └── DeleteEnum.java
│   │   ├── exception/
│   │   │   ├── BusinessException.java
│   │   │   ├── GlobalExceptionHandler.java
│   │   │   └── ServiceException.java
│   │   ├── utils/
│   │   │   ├── JwtUtils.java
│   │   │   ├── RedisUtils.java
│   │   │   ├── SnowflakeIdUtils.java
│   │   │   └── ValidateCodeUtils.java
│   │   └── annotation/
│   │       ├── RequiresPermission.java
│   │       ├── LogAnnotation.java
│   │       └── RateLimiter.java
│   └── pom.xml
│
├── forum-core/                      # 核心模块
│   ├── src/main/java/com/forum/core/
│   │   ├── config/
│   │   │   ├── MybatisPlusConfig.java
│   │   │   ├── RedisConfig.java
│   │   │   ├── WebMvcConfig.java
│   │   │   ├── ThreadPoolConfig.java
│   │   │   └── SwaggerConfig.java
│   │   ├── interceptor/
│   │   │   ├── AuthenticationInterceptor.java
│   │   │   ├── RateLimitInterceptor.java
│   │   │   └── DataScopeInterceptor.java
│   │   ├── filter/
│   │   │   ├── RequestLogFilter.java
│   │   │   ├── XssFilter.java
│   │   │   └── CorsFilter.java
│   │   └── aop/
│   │       ├── LogAspect.java
│   │       └── PermissionAspect.java
│   └── pom.xml
│
├── forum-framework/                 # 框架模块
│   ├── src/main/java/com/forum/framework/
│   │   ├── mybatis/
│   │   │   ├── MybatisPlusConfig.java
│   │   │   ├── MetaObjectHandler.java
│   │   │   ├── MybatisPlusSqlInjector.java
│   │   │   └── CustomSqlInjector.java
│   │   ├── redis/
│   │   │   ├── RedisConfig.java
│   │   │   ├── RedisTemplateConfig.java
│   │   │   └── RedisLock.java
│   │   ├── security/
│   │   │   ├── JwtAuthenticationFilter.java
│   │   │   ├── SecurityConfig.java
│   │   │   └── UserDetailsServiceImpl.java
│   │   └── task/
│   │       ├── ThreadPoolConfig.java
│   │       └── AsyncTask.java
│   └── pom.xml
│
├── forum-api/                       # API接口模块
│   ├── src/main/java/com/forum/api/
│   │   ├── controller/
│   │   │   ├── auth/
│   │   │   │   └── AuthController.java
│   │   │   ├── user/
│   │   │   │   ├── UserController.java
│   │   │   │   └── UserProfileController.java
│   │   │   ├── forum/
│   │   │   │   ├── PostController.java
│   │   │   │   ├── CommentController.java
│   │   │   │   └── CategoryController.java
│   │   │   └── admin/
│   │   │       ├── AdminUserController.java
│   │   │       └── SystemConfigController.java
│   │   ├── vo/
│   │   │   ├── request/
│   │   │   │   ├── LoginRequest.java
│   │   │   │   ├── PostRequest.java
│   │   │   │   └── PageRequest.java
│   │   │   └── response/
│   │   │       ├── LoginResponse.java
│   │   │       ├── PostResponse.java
│   │   │       └── PageResponse.java
│   │   ├── dto/
│   │   │   ├── UserDTO.java
│   │   │   ├── PostDTO.java
│   │   │   └── CommentDTO.java
│   │   └── feign/
│   │       ├── UserFeignClient.java
│   │       └── MessageFeignClient.java
│   └── pom.xml
│
├── forum-dao/                       # 数据访问模块
│   ├── src/main/java/com/forum/dao/
│   │   ├── entity/
│   │   │   ├── UserEntity.java
│   │   │   ├── PostEntity.java
│   │   │   ├── CommentEntity.java
│   │   │   └── CategoryEntity.java
│   │   ├── mapper/
│   │   │   ├── UserMapper.java
│   │   │   ├── PostMapper.java
│   │   │   ├── CommentMapper.java
│   │   │   └── CategoryMapper.java
│   │   └── repository/
│   │       ├── UserRepository.java
│   │       ├── PostRepository.java
│   │       └── impl/
│   │           ├── UserRepositoryImpl.java
│   │           └── PostRepositoryImpl.java
│   └── pom.xml
│
├── forum-service/                   # 业务服务模块
│   ├── src/main/java/com/forum/service/
│   │   ├── auth/
│   │   │   ├── AuthService.java
│   │   │   └── impl/AuthServiceImpl.java
│   │   ├── user/
│   │   │   ├── UserService.java
│   │   │   └── impl/UserServiceImpl.java
│   │   ├── post/
│   │   │   ├── PostService.java
│   │   │   ├── impl/PostServiceImpl.java
│   │   │   └── strategy/
│   │   │       ├── PostTypeStrategy.java
│   │   │       └── NormalPostStrategy.java
│   │   └── admin/
│   │       ├── AdminService.java
│   │       └── impl/AdminServiceImpl.java
│   └── pom.xml
│
├── forum-auth-server/               # 认证服务
│   ├── src/main/java/com/forum/auth/
│   │   ├── OAuth2ServerConfig.java
│   │   ├── AuthorizationServerConfig.java
│   │   └── ResourceServerConfig.java
│   └── pom.xml
│
├── forum-gateway/                   # 网关服务
│   ├── src/main/java/com/forum/gateway/
│   │   ├── GatewayConfig.java
│   │   ├── AuthFilter.java
│   │   └── GlobalExceptionHandler.java
│   └── pom.xml
│
├── sql/                             # SQL脚本
│   ├── init.sql
│   └── data.sql
│
├── docker/                          # Docker配置
│   ├── Dockerfile
│   └── docker-compose.yml
│
└── pom.xml                          # 父POM

创建父项目

pom文件

java 复制代码
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>forum-backend</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>pom</packaging>
    <description>微服务论坛系统 - Java 8版本</description>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.5.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <!-- Java版本配置 -->
        <java.version>1.8</java.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <!-- Spring Cloud 2021.x - 与Spring Boot 2.7.x完美配合 -->
        <spring-cloud.version>2021.0.9</spring-cloud.version>
        <!-- Spring Cloud Alibaba - 服务注册、配置、限流 -->
        <spring-cloud-alibaba.version>2021.0.6.0</spring-cloud-alibaba.version>

        <!-- MyBatis Plus 3.5.x - 增强版ORM,Lambda查询 -->
        <mybatis-plus.version>3.5.3</mybatis-plus.version>

        <!-- Redisson 3.17.x - 分布式锁、Redis集群 -->
        <redisson.version>3.17.7</redisson.version>

        <!-- JWT 0.11.x - 无状态认证 -->
        <jjwt.version>0.11.5</jjwt.version>

        <!-- Hutool 5.8.x - Java工具库,减少重复代码 -->
        <hutool.version>5.8.11</hutool.version>

        <!-- MapStruct 1.5.x - 对象转换,性能优于BeanUtils -->
        <mapstruct.version>1.5.5.Final</mapstruct.version>
        <lombok.version>1.18.30</lombok.version>

        <!-- MySQL驱动 -->
        <mysql.version>8.0.33</mysql.version>

        <!-- API文档 -->
        <knife4j.version>3.0.3</knife4j.version>

        <!-- 测试框架 -->
        <junit.version>5.9.3</junit.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- Spring Cloud依赖管理 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- Spring Cloud Alibaba依赖管理 -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- MyBatis Plus -->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>${mybatis-plus.version}</version>
            </dependency>

            <!-- Redisson - 分布式锁实现 -->
            <dependency>
                <groupId>org.redisson</groupId>
                <artifactId>redisson-spring-boot-starter</artifactId>
                <version>${redisson.version}</version>
            </dependency>

            <!-- JWT -->
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt-api</artifactId>
                <version>${jjwt.version}</version>
            </dependency>
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt-impl</artifactId>
                <version>${jjwt.version}</version>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt-jackson</artifactId>
                <version>${jjwt.version}</version>
                <scope>runtime</scope>
            </dependency>

            <!-- Hutool - 避免重复造轮子 -->
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>${hutool.version}</version>
            </dependency>

            <!-- MapStruct - 高性能对象转换 -->
            <dependency>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct</artifactId>
                <version>${mapstruct.version}</version>
            </dependency>
            <dependency>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-processor</artifactId>
                <version>${mapstruct.version}</version>
            </dependency>

            <!-- Knife4j - Swagger增强,更好用的API文档 -->
            <dependency>
                <groupId>com.github.xiaoymin</groupId>
                <artifactId>knife4j-spring-boot-starter</artifactId>
                <version>${knife4j.version}</version>
            </dependency>

            <!-- MySQL驱动 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>${lombok.version}</version>
                        </path>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${mapstruct.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>

            <!-- Spring Boot打包插件 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

forum-common 公共模块

pom文件

java 复制代码
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>forum-backend</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>forum-common</artifactId>
    <description>公共模块:基础类、工具类、常量、异常处理</description>

    <dependencies>
        <!-- Spring Boot Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- MyBatis Plus注解,用于实体类 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>

        <!-- Lombok - 减少样板代码 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- Hutool - 丰富的工具类库 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>

        <!-- JWT -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- Jackson JSON处理 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
        </dependency>

        <!-- Bean Validation -->
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
        </dependency>

        <!-- Servlet API -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <scope>provided</scope>
        </dependency>

        <!-- Spring Security Core - 用于获取当前用户 -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

实体基类

我要先做出效果,所以按照最小可用产品的思路,先实现最核心的功能:用户注册、登录,以及一个简单的首页展示。这样可以先在服务器上部署并看到效果。

最小可用架构

模块依赖关系

text 复制代码
forum-common (已完成)
    ↑
forum-dao (依赖common)
    ↑
forum-service (依赖dao)
    ↑
forum-auth-server (依赖service)
    ↑
forum-user-service (依赖service)

forum-dao(数据访问模块)

pom文件

java 复制代码
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>forum-backend</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>forum-dao</artifactId>
    <description>数据访问模块 - 实体类、Mapper接口</description>
    <dependencies>
        <!-- Common模块 -->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>forum-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!-- MyBatis Plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>

        <!-- MySQL -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

实体类

user实体类

java 复制代码
package com.forum.dao.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import com.forum.common.base.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * 用户实体类
 */
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("uc_user")
public class UserEntity extends BaseEntity {
    /**
     * 用户名
     */
    private String username;

    /**
     * 密码(加密存储)
     */
    private String password;

    /**
     * 邮箱
     */
    private String email;

    /**
     * 手机号
     */
    private String phone;

    /**
     * 头像URL
     */
    private String avatar;

    /**
     * 昵称
     */
    private String nickname;

    /**
     * 性别 0:未知 1:男 2:女
     */
    private Integer gender;

    /**
     * 状态 0:禁用 1:启用 2:锁定
     */
    private Integer status;

    /**
     * 最后登录时间
     */
    private java.time.LocalDateTime lastLoginTime;

}

mapper

java 复制代码
package com.forum.dao.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.forum.dao.entity.UserEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface UserMapper extends BaseMapper<UserEntity> {
    /**
     * 根据用户名查询用户
     */
    @Select("SELECT * FROM uc_user WHERE username = #{username} AND is_deleted = 0")
    UserEntity selectByUsername(@Param("username") String username);

    /**
     * 根据邮箱查询用户
     */
    @Select("SELECT * FROM uc_user WHERE email = #{email} AND is_deleted = 0")
    UserEntity selectByEmail(@Param("email") String email);

    /**
     * 根据手机号查询用户
     */
    @Select("SELECT * FROM uc_user WHERE phone = #{phone} AND is_deleted = 0")
    UserEntity selectByPhone(@Param("phone") String phone);
}

forum-service (业务服务模块)

业务实现类

java 复制代码
package com.forum.service.user.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.forum.common.base.BaseServiceImpl;
import com.forum.common.exception.BusinessException;
import com.forum.common.utils.JwtUtils;
import com.forum.dao.entity.UserEntity;
import com.forum.dao.mapper.UserMapper;
import com.forum.service.user.dto.UserLoginDTO;
import com.forum.service.user.dto.UserRegisterDTO;
import com.forum.service.user.service.UserService;
import com.forum.service.user.vo.UserVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.time.LocalDateTime;

/**
 * 用户服务实现类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserEntity>
        implements UserService {
    private final JwtUtils jwtUtils;

    @Override
    public UserVO register(UserRegisterDTO dto) {
        log.info("用户注册开始: username={}", dto.getUsername());

        // 1. 校验两次密码是否一致
        if (!dto.getPassword().equals(dto.getConfirmPassword())) {
            throw new BusinessException("两次输入的密码不一致");
        }

        // 2. 检查用户名是否已存在
        UserEntity existUser = findByUsername(dto.getUsername());
        if (existUser != null) {
            throw new BusinessException("用户名已存在");
        }

        // 3. 检查邮箱是否已存在
        if (dto.getEmail() != null && !dto.getEmail().isEmpty()) {
            UserEntity existEmail = findByEmail(dto.getEmail());
            if (existEmail != null) {
                throw new BusinessException("邮箱已被注册");
            }
        }

        // 4. 创建用户
        UserEntity user = new UserEntity();
        BeanUtil.copyProperties(dto, user);
        // 密码加密(MD5 + 盐值,生产环境建议使用BCrypt)
        user.setPassword(DigestUtils.md5DigestAsHex(dto.getPassword().getBytes()));
        // 设置默认昵称
        if (user.getNickname() == null || user.getNickname().isEmpty()) {
            user.setNickname(dto.getUsername());
        }
        // 设置默认状态:启用
        user.setStatus(1);

        // 5. 保存用户
        this.save(user);

        log.info("用户注册成功: id={}, username={}", user.getId(), user.getUsername());

        // 6. 返回用户信息
        return convertToVO(user);
    }

    @Override
    public UserVO login(UserLoginDTO dto) {
        log.info("用户登录: username={}", dto.getUsername());

        // 1. 查询用户
        UserEntity user = findByUsername(dto.getUsername());
        if (user == null) {
            throw new BusinessException("用户名或密码错误");
        }

        // 2. 校验密码
        String encryptedPassword = DigestUtils.md5DigestAsHex(dto.getPassword().getBytes());
        if (!encryptedPassword.equals(user.getPassword())) {
            throw new BusinessException("用户名或密码错误");
        }

        // 3. 检查用户状态
        if (user.getStatus() == 0) {
            throw new BusinessException("账号已被禁用");
        }
        if (user.getStatus() == 2) {
            throw new BusinessException("账号已被锁定");
        }

        // 4. 更新最后登录时间
        user.setLastLoginTime(LocalDateTime.now());
        this.updateById(user);

        // 5. 生成JWT Token
        String token = jwtUtils.generateToken(user.getId(), user.getUsername());

        log.info("用户登录成功: id={}, username={}", user.getId(), user.getUsername());

        // 6. 返回用户信息
        UserVO userVO = convertToVO(user);
        userVO.setToken(token);

        return userVO;
    }

    @Override
    public UserEntity findByUsername(String username) {
        LambdaQueryWrapper<UserEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserEntity::getUsername, username);
        return this.getOne(wrapper);
    }

    @Override
    public UserEntity findByEmail(String email) {
        LambdaQueryWrapper<UserEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserEntity::getEmail, email);
        return this.getOne(wrapper);
    }

    @Override
    public UserVO getUserById(Long userId) {
        UserEntity user = getById(userId);
        if (user == null) {
            throw new BusinessException("用户不存在");
        }
        return convertToVO(user);
    }

    /**
     * 转换为视图对象
     */
    private UserVO convertToVO(UserEntity user) {
        UserVO vo = new UserVO();
        BeanUtil.copyProperties(user, vo);
        return vo;
    }
}

forum-auth-server

AuthController

java 复制代码
package com.forum.auth.controller;

import com.forum.common.result.Result;
import com.forum.service.user.dto.UserLoginDTO;
import com.forum.service.user.dto.UserRegisterDTO;
import com.forum.service.user.service.UserService;
import com.forum.service.user.vo.UserVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

import static com.forum.common.result.Result.error;
import static com.forum.common.result.Result.success;
import static com.forum.common.utils.SecurityUtils.getCurrentUserId;

/**
 * 认证控制器
 * 提供登录、注册、登出等功能
 */
@Slf4j
@Api(tags = "认证管理")
@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
public class AuthController {
    private final UserService userService;

    @ApiOperation("用户注册")
    @PostMapping("/register")
    public Result<UserVO> register(@Valid @RequestBody UserRegisterDTO dto) {
        log.info("收到注册请求: username={}", dto.getUsername());
        UserVO userVO = userService.register(dto);
        return success(userVO);
    }

    @ApiOperation("用户登录")
    @PostMapping("/login")
    public Result<UserVO> login(@Valid @RequestBody UserLoginDTO dto) {
        log.info("收到登录请求: username={}", dto.getUsername());
        UserVO userVO = userService.login(dto);
        return success(userVO);
    }

    @ApiOperation("获取当前用户信息")
    @GetMapping("/current")
    public Result<UserVO> getCurrentUser() {
        Long userId = getCurrentUserId();
        if (userId == null) {
            return error("用户未登录");
        }
        UserVO userVO = userService.getUserById(userId);
        return success(userVO);
    }

    @ApiOperation("健康检查")
    @GetMapping("/health")
    public Result<String> health() {
        return success("服务正常运行");
    }
}

forum-user-service (用户服务)

userController

java 复制代码
package com.forum.user.controller;

import com.forum.common.base.BaseController;
import com.forum.common.result.Result;
import com.forum.service.user.service.UserService;
import com.forum.service.user.vo.UserVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 用户控制器
 */
@Slf4j
@Api(tags = "用户管理")
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController extends BaseController {
    private final UserService userService;

    @ApiOperation("获取当前用户信息")
    @GetMapping("/current")
    public Result<UserVO> getCurrentUser() {
        Long userId = getCurrentUserId();
        if (userId == null) {
            return error("用户未登录");
        }
        UserVO userVO = userService.getUserById(userId);
        return success(userVO);
    }

    @ApiOperation("根据ID获取用户信息")
    @GetMapping("/{userId}")
    public Result<UserVO> getUserById(@PathVariable Long userId) {
        UserVO userVO = userService.getUserById(userId);
        return success(userVO);
    }

    @ApiOperation("健康检查")
    @GetMapping("/health")
    public Result<String> health() {
        return success("用户服务正常运行");
    }
}

前端简单页面

先用来看效果,做一个html

前端HTML文件 - 放在Nginx静态目录

javascript 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>论坛系统 - 登录/注册</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
        }
        
        .container {
            width: 100%;
            max-width: 450px;
            padding: 20px;
        }
        
        /* 卡片样式 */
        .card {
            background: white;
            border-radius: 16px;
            padding: 40px;
            box-shadow: 0 20px 60px rgba(0,0,0,0.3);
            animation: fadeIn 0.5s ease;
        }
        
        @keyframes fadeIn {
            from {
                opacity: 0;
                transform: translateY(-20px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }
        
        .logo {
            text-align: center;
            font-size: 48px;
            margin-bottom: 20px;
        }
        
        .title {
            text-align: center;
            font-size: 28px;
            font-weight: 600;
            color: #333;
            margin-bottom: 30px;
        }
        
        /* 表单样式 */
        .form-group {
            margin-bottom: 20px;
        }
        
        .form-group label {
            display: block;
            margin-bottom: 8px;
            color: #666;
            font-weight: 500;
            font-size: 14px;
        }
        
        .form-group input {
            width: 100%;
            padding: 12px 15px;
            border: 2px solid #e0e0e0;
            border-radius: 8px;
            font-size: 16px;
            transition: all 0.3s;
            outline: none;
        }
        
        .form-group input:focus {
            border-color: #667eea;
            box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
        }
        
        .form-group input.error {
            border-color: #f56565;
        }
        
        /* 按钮样式 */
        .btn {
            width: 100%;
            padding: 14px;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            border-radius: 8px;
            font-size: 16px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s;
            margin-top: 10px;
        }
        
        .btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
        }
        
        .btn:active {
            transform: translateY(0);
        }
        
        .btn:disabled {
            opacity: 0.6;
            cursor: not-allowed;
            transform: none;
        }
        
        /* 切换链接 */
        .switch-link {
            text-align: center;
            margin-top: 25px;
            color: #666;
            font-size: 14px;
        }
        
        .switch-link a {
            color: #667eea;
            text-decoration: none;
            cursor: pointer;
            font-weight: 500;
        }
        
        .switch-link a:hover {
            text-decoration: underline;
        }
        
        /* 消息提示 */
        .message {
            padding: 12px;
            border-radius: 8px;
            margin-bottom: 20px;
            font-size: 14px;
            display: none;
            animation: slideDown 0.3s ease;
        }
        
        @keyframes slideDown {
            from {
                opacity: 0;
                transform: translateY(-10px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }
        
        .message.success {
            background: #c6f6d5;
            color: #22543d;
            border-left: 4px solid #48bb78;
            display: block;
        }
        
        .message.error {
            background: #fed7d7;
            color: #742a2a;
            border-left: 4px solid #f56565;
            display: block;
        }
        
        .message.info {
            background: #bee3f8;
            color: #2c5282;
            border-left: 4px solid #4299e1;
            display: block;
        }
        
        /* 隐藏类 */
        .hidden {
            display: none;
        }
        
        /* 加载动画 */
        .loading {
            display: inline-block;
            width: 16px;
            height: 16px;
            border: 2px solid white;
            border-radius: 50%;
            border-top-color: transparent;
            animation: spin 0.6s linear infinite;
            margin-right: 8px;
            vertical-align: middle;
        }
        
        @keyframes spin {
            to { transform: rotate(360deg); }
        }
        
        /* 密码强度提示 */
        .password-strength {
            margin-top: 5px;
            font-size: 12px;
        }
        
        .strength-weak { color: #f56565; }
        .strength-medium { color: #ed8936; }
        .strength-strong { color: #48bb78; }
        
        /* 记住我复选框 */
        .checkbox-group {
            display: flex;
            align-items: center;
            justify-content: space-between;
            margin-bottom: 20px;
        }
        
        .checkbox-label {
            display: flex;
            align-items: center;
            cursor: pointer;
        }
        
        .checkbox-label input {
            width: auto;
            margin-right: 8px;
        }
        
        .forgot-link {
            color: #667eea;
            text-decoration: none;
            font-size: 14px;
        }
        
        /* 页脚 */
        .footer {
            text-align: center;
            margin-top: 20px;
            color: rgba(255,255,255,0.8);
            font-size: 12px;
        }
    </style>
</head>
<body>
    <div class="container">
        <!-- 登录表单 -->
        <div id="loginForm" class="card">
            <div class="logo">📝</div>
            <div class="title">欢迎回来</div>
            
            <div id="loginMessage" class="message"></div>
            
            <form id="loginFormElement">
                <div class="form-group">
                    <label>用户名</label>
                    <input type="text" id="loginUsername" placeholder="请输入用户名" autocomplete="username">
                </div>
                
                <div class="form-group">
                    <label>密码</label>
                    <input type="password" id="loginPassword" placeholder="请输入密码" autocomplete="current-password">
                </div>
                
                <div class="checkbox-group">
                    <label class="checkbox-label">
                        <input type="checkbox" id="rememberMe"> 记住我
                    </label>
                    <a class="forgot-link" href="javascript:void(0)" onclick="showForgotPassword()">忘记密码?</a>
                </div>
                
                <button type="submit" class="btn" id="loginBtn">登 录</button>
            </form>
            
            <div class="switch-link">
                还没有账号? <a onclick="showRegister()">立即注册</a>
            </div>
        </div>
        
        <!-- 注册表单 -->
        <div id="registerForm" class="card hidden">
            <div class="logo">📝</div>
            <div class="title">创建账号</div>
            
            <div id="registerMessage" class="message"></div>
            
            <form id="registerFormElement">
                <div class="form-group">
                    <label>用户名</label>
                    <input type="text" id="regUsername" placeholder="3-20位字母、数字、下划线">
                    <small style="color: #999; font-size: 12px;">用户名长度3-20,只能包含字母、数字和下划线</small>
                </div>
                
                <div class="form-group">
                    <label>邮箱</label>
                    <input type="email" id="regEmail" placeholder="example@email.com">
                </div>
                
                <div class="form-group">
                    <label>手机号</label>
                    <input type="tel" id="regPhone" placeholder="手机号码">
                </div>
                
                <div class="form-group">
                    <label>密码</label>
                    <input type="password" id="regPassword" placeholder="请输入密码">
                    <div id="passwordStrength" class="password-strength"></div>
                </div>
                
                <div class="form-group">
                    <label>确认密码</label>
                    <input type="password" id="regConfirmPassword" placeholder="请再次输入密码">
                </div>
                
                <div class="form-group">
                    <label>昵称</label>
                    <input type="text" id="regNickname" placeholder="昵称(选填)">
                </div>
                
                <button type="submit" class="btn" id="registerBtn">注 册</button>
            </form>
            
            <div class="switch-link">
                已有账号? <a onclick="showLogin()">立即登录</a>
            </div>
        </div>
        
        <!-- 首页(登录后显示) -->
        <div id="homePage" class="card hidden">
            <div class="logo">🎉</div>
            <div class="title" id="welcomeTitle">欢迎回来!</div>
            
            <div id="homeMessage" class="message success" style="display: none;"></div>
            
            <div id="userInfo" style="margin-bottom: 20px;">
                <div style="background: #f7fafc; padding: 20px; border-radius: 12px; margin-bottom: 20px;">
                    <div style="display: flex; align-items: center; margin-bottom: 15px;">
                        <div style="width: 60px; height: 60px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 24px; color: white; margin-right: 15px;">
                            👤
                        </div>
                        <div>
                            <h3 id="userNickname" style="margin-bottom: 5px;">用户</h3>
                            <p id="userUsername" style="color: #666; font-size: 14px;">@username</p>
                        </div>
                    </div>
                    <div style="border-top: 1px solid #e2e8f0; padding-top: 15px;">
                        <p><strong>邮箱:</strong> <span id="userEmail">-</span></p>
                        <p><strong>手机:</strong> <span id="userPhone">-</span></p>
                        <p><strong>用户ID:</strong> <span id="userId">-</span></p>
                        <p><strong>状态:</strong> <span id="userStatus">-</span></p>
                    </div>
                </div>
            </div>
            
            <div style="display: flex; gap: 15px;">
                <button class="btn" onclick="getCurrentUser()" style="background: #48bb78;">刷新信息</button>
                <button class="btn" onclick="logout()" style="background: #f56565;">退出登录</button>
            </div>
            
            <div class="switch-link" style="margin-top: 20px;">
                <a onclick="showLogin()">返回登录页</a>
            </div>
        </div>
        
        <div class="footer">
            © 2024 论坛系统 | 连接思想,分享知识
        </div>
    </div>

    <script>
        // API配置
        const API_BASE_URL = 'http://localhost:8081'; // 修改为你的服务器地址
        
        // Token存储key
        const TOKEN_KEY = 'forum_token';
        const USER_KEY = 'forum_user';
        
        // 页面加载时检查登录状态
        document.addEventListener('DOMContentLoaded', function() {
            checkLoginStatus();
            initEventListeners();
        });
        
        // 初始化事件监听
        function initEventListeners() {
            // 登录表单提交
            document.getElementById('loginFormElement').addEventListener('submit', function(e) {
                e.preventDefault();
                login();
            });
            
            // 注册表单提交
            document.getElementById('registerFormElement').addEventListener('submit', function(e) {
                e.preventDefault();
                register();
            });
            
            // 密码强度检测
            document.getElementById('regPassword').addEventListener('input', function() {
                checkPasswordStrength(this.value);
            });
        }
        
        // 检查登录状态
        function checkLoginStatus() {
            const token = localStorage.getItem(TOKEN_KEY);
            if (token) {
                // 验证token有效性
                getCurrentUser();
            } else {
                showLogin();
            }
        }
        
        // 显示登录表单
        function showLogin() {
            document.getElementById('loginForm').classList.remove('hidden');
            document.getElementById('registerForm').classList.add('hidden');
            document.getElementById('homePage').classList.add('hidden');
            clearMessages();
        }
        
        // 显示注册表单
        function showRegister() {
            document.getElementById('loginForm').classList.add('hidden');
            document.getElementById('registerForm').classList.remove('hidden');
            document.getElementById('homePage').classList.add('hidden');
            clearMessages();
        }
        
        // 显示首页
        function showHome() {
            document.getElementById('loginForm').classList.add('hidden');
            document.getElementById('registerForm').classList.add('hidden');
            document.getElementById('homePage').classList.remove('hidden');
        }
        
        // 清空消息
        function clearMessages() {
            document.querySelectorAll('.message').forEach(msg => {
                msg.style.display = 'none';
                msg.textContent = '';
            });
        }
        
        // 显示消息
        function showMessage(elementId, message, type) {
            const element = document.getElementById(elementId);
            element.textContent = message;
            element.className = `message ${type}`;
            element.style.display = 'block';
            
            // 3秒后自动隐藏
            setTimeout(() => {
                if (element.style.display === 'block') {
                    element.style.display = 'none';
                }
            }, 5000);
        }
        
        // 设置按钮加载状态
        function setButtonLoading(buttonId, loading) {
            const btn = document.getElementById(buttonId);
            if (loading) {
                btn.disabled = true;
                btn.innerHTML = '<span class="loading"></span>处理中...';
            } else {
                btn.disabled = false;
                btn.innerHTML = btn.id === 'loginBtn' ? '登 录' : '注 册';
            }
        }
        
        // 登录方法
        async function login() {
            const username = document.getElementById('loginUsername').value.trim();
            const password = document.getElementById('loginPassword').value;
            const rememberMe = document.getElementById('rememberMe').checked;
            
            // 表单验证
            if (!username) {
                showMessage('loginMessage', '请输入用户名', 'error');
                return;
            }
            if (!password) {
                showMessage('loginMessage', '请输入密码', 'error');
                return;
            }
            
            setButtonLoading('loginBtn', true);
            
            try {
                const response = await fetch(`${API_BASE_URL}/api/auth/login`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({ username, password })
                });
                
                const result = await response.json();
                
                if (result.code === 200) {
                    // 登录成功
                    if (rememberMe) {
                        localStorage.setItem(TOKEN_KEY, result.data.token);
                        localStorage.setItem(USER_KEY, JSON.stringify(result.data));
                    } else {
                        sessionStorage.setItem(TOKEN_KEY, result.data.token);
                        sessionStorage.setItem(USER_KEY, JSON.stringify(result.data));
                    }
                    
                    showMessage('loginMessage', '登录成功!正在跳转...', 'success');
                    setTimeout(() => {
                        displayUserInfo(result.data);
                        showHome();
                    }, 1000);
                } else {
                    showMessage('loginMessage', result.message || '登录失败', 'error');
                }
            } catch (error) {
                console.error('登录错误:', error);
                showMessage('loginMessage', '网络错误,请稍后重试', 'error');
            } finally {
                setButtonLoading('loginBtn', false);
            }
        }
        
        // 注册方法
        async function register() {
            const username = document.getElementById('regUsername').value.trim();
            const email = document.getElementById('regEmail').value.trim();
            const phone = document.getElementById('regPhone').value.trim();
            const password = document.getElementById('regPassword').value;
            const confirmPassword = document.getElementById('regConfirmPassword').value;
            const nickname = document.getElementById('regNickname').value.trim() || username;
            
            // 表单验证
            if (!username) {
                showMessage('registerMessage', '请输入用户名', 'error');
                return;
            }
            if (username.length < 3 || username.length > 20) {
                showMessage('registerMessage', '用户名长度必须在3-20之间', 'error');
                return;
            }
            if (!/^[a-zA-Z0-9_]+$/.test(username)) {
                showMessage('registerMessage', '用户名只能包含字母、数字和下划线', 'error');
                return;
            }
            if (!password) {
                showMessage('registerMessage', '请输入密码', 'error');
                return;
            }
            if (password.length < 6) {
                showMessage('registerMessage', '密码长度不能少于6位', 'error');
                return;
            }
            if (password !== confirmPassword) {
                showMessage('registerMessage', '两次输入的密码不一致', 'error');
                return;
            }
            if (email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
                showMessage('registerMessage', '邮箱格式不正确', 'error');
                return;
            }
            if (phone && !/^1[3-9]\d{9}$/.test(phone)) {
                showMessage('registerMessage', '手机号格式不正确', 'error');
                return;
            }
            
            setButtonLoading('registerBtn', true);
            
            try {
                const response = await fetch(`${API_BASE_URL}/api/auth/register`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({ 
                        username, 
                        email: email || null,
                        phone: phone || null,
                        password, 
                        confirmPassword,
                        nickname 
                    })
                });
                
                const result = await response.json();
                
                if (result.code === 200) {
                    showMessage('registerMessage', '注册成功!请登录', 'success');
                    setTimeout(() => {
                        showLogin();
                        // 清空注册表单
                        document.getElementById('registerFormElement').reset();
                    }, 1500);
                } else {
                    showMessage('registerMessage', result.message || '注册失败', 'error');
                }
            } catch (error) {
                console.error('注册错误:', error);
                showMessage('registerMessage', '网络错误,请稍后重试', 'error');
            } finally {
                setButtonLoading('registerBtn', false);
            }
        }
        
        // 获取当前用户信息
        async function getCurrentUser() {
            const token = localStorage.getItem(TOKEN_KEY) || sessionStorage.getItem(TOKEN_KEY);
            
            if (!token) {
                showLogin();
                return;
            }
            
            try {
                const response = await fetch(`${API_BASE_URL}/api/auth/current`, {
                    method: 'GET',
                    headers: {
                        'Authorization': `Bearer ${token}`
                    }
                });
                
                const result = await response.json();
                
                if (result.code === 200) {
                    displayUserInfo(result.data);
                    showHome();
                } else {
                    // token无效,清除本地存储
                    localStorage.removeItem(TOKEN_KEY);
                    sessionStorage.removeItem(TOKEN_KEY);
                    localStorage.removeItem(USER_KEY);
                    sessionStorage.removeItem(USER_KEY);
                    showLogin();
                    if (result.message) {
                        showMessage('loginMessage', result.message, 'error');
                    }
                }
            } catch (error) {
                console.error('获取用户信息错误:', error);
                showLogin();
            }
        }
        
        // 显示用户信息
        function displayUserInfo(user) {
            document.getElementById('welcomeTitle').textContent = `欢迎回来,${user.nickname || user.username}!`;
            document.getElementById('userNickname').textContent = user.nickname || user.username;
            document.getElementById('userUsername').textContent = `@${user.username}`;
            document.getElementById('userEmail').textContent = user.email || '未设置';
            document.getElementById('userPhone').textContent = user.phone || '未设置';
            document.getElementById('userId').textContent = user.id;
            
            const statusMap = {0: '已禁用', 1: '正常', 2: '已锁定'};
            document.getElementById('userStatus').textContent = statusMap[user.status] || '未知';
            
            const homeMessage = document.getElementById('homeMessage');
            homeMessage.textContent = `欢迎回来!上次登录时间: ${user.lastLoginTime || '首次登录'}`;
            homeMessage.style.display = 'block';
            
            setTimeout(() => {
                homeMessage.style.display = 'none';
            }, 5000);
        }
        
        // 退出登录
        function logout() {
            localStorage.removeItem(TOKEN_KEY);
            sessionStorage.removeItem(TOKEN_KEY);
            localStorage.removeItem(USER_KEY);
            sessionStorage.removeItem(USER_KEY);
            
            showMessage('loginMessage', '已安全退出登录', 'success');
            setTimeout(() => {
                showLogin();
                // 清空登录表单
                document.getElementById('loginFormElement').reset();
            }, 1000);
        }
        
        // 密码强度检测
        function checkPasswordStrength(password) {
            const strengthDiv = document.getElementById('passwordStrength');
            
            if (!password) {
                strengthDiv.innerHTML = '';
                return;
            }
            
            let strength = 0;
            let message = '';
            let className = '';
            
            // 长度检查
            if (password.length >= 8) strength++;
            if (password.length >= 12) strength++;
            
            // 包含数字
            if (/\d/.test(password)) strength++;
            
            // 包含小写字母
            if (/[a-z]/.test(password)) strength++;
            
            // 包含大写字母
            if (/[A-Z]/.test(password)) strength++;
            
            // 包含特殊字符
            if (/[!@#$%^&*(),.?":{}|<>]/.test(password)) strength++;
            
            if (strength <= 2) {
                message = '弱密码';
                className = 'strength-weak';
            } else if (strength <= 4) {
                message = '中等密码';
                className = 'strength-medium';
            } else {
                message = '强密码';
                className = 'strength-strong';
            }
            
            strengthDiv.innerHTML = `<span class="${className}">密码强度:${message}</span>`;
        }
        
        // 忘记密码(演示功能)
        function showForgotPassword() {
            alert('请联系管理员重置密码\n邮箱:admin@forum.com');
        }
        
        // 添加回车键支持
        document.getElementById('loginPassword').addEventListener('keypress', function(e) {
            if (e.key === 'Enter') {
                login();
            }
        });
        
        document.getElementById('regConfirmPassword').addEventListener('keypress', function(e) {
            if (e.key === 'Enter') {
                register();
            }
        });
    </script>
</body>
</html>           

部署jar包

将auth和user模块打包发到服务器

注意这两个模块的pom加打包插件

java 复制代码
<build>
        <!--设置打包名称-->
        <finalName>forum-user</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

设置打包的jar包名称

打包方法

进入命令框

分步构建

bash 复制代码
# 1. 进入项目根目录
cd /path/to/forum-backend

# 2. 先清理所有
mvn clean

# 3. 只编译不打包(安装到本地仓库)
# 注意:这里用 install,不是 package
mvn install -DskipTests

# 4. 如果第3步成功,再单独打包认证服务
cd forum-auth-server
mvn package -DskipTests

# 5. 打包用户服务
cd ../forum-user-service
mvn package -DskipTests

找到打包后的jar包

连接服务器,创建目录

上传到服务器需要第三方软件

下载链接:winSCP

如果链接不了,在阿里云使用密码链接,如果有以下提示

说明服务器没有开密码登录,

实例充值密码即可

连接后上传jar包

创建auth启动脚本

bash 复制代码
vim auth.sh

输入命令

bash 复制代码
nohup java -jar forum-auth.jar  > auth.log 2>&1 &

创建auth启动脚本

bash 复制代码
vim user.sh

输入命令

bash 复制代码
nohup java -jar forum-user.jar  > user.log 2>&1 &

给jar包添加执行权限

bash 复制代码
chmod +x forum-auth.jar
chmod +x forum-user.jar 

启动auth

bash 复制代码
bash auth.sh

两个服务启动成功后准备页面

在宝塔页面添加html服务

完成后在/www/wwwroot/121.40.199.142目录已有宝塔默认文件

在浏览器输入服务器IP即可看到页面

然后把/www/wwwroot/121.40.199.142下的index.html文件换成我们的文件。刷新页面

此时登录会报错,有跨域问题

跨域问题在 forum-core模块进行配置,下一个博客进行 forum-core的编写。

源码地址

源码地址 gitee

相关推荐
柒.梧.2 小时前
深入理解AQS:Java并发编程的核心基石
java·开发语言
ShineWinsu2 小时前
MySQL安全加固十大硬核操作:硬核防护指南
数据库·mysql·安全
朱一头zcy2 小时前
在CentOS7环境下安装MySQL详细步骤
linux·mysql
骑龙赶鸭2 小时前
mysql binlog中QUERY_EVENT的status_var结构
数据库·mysql
拾贰_C2 小时前
【Ubuntu】安装Nginx(nVidia驱动未安装成功阻止版)
linux·运维·服务器·ubuntu
2301_792674863 小时前
java学习day24
java
克莱因3587 小时前
Linux CentOS7 进程基础知识
linux·运维·服务器
Skilce8 小时前
ZrLog 高可用部署
运维·服务器·数据库·阿里云·maven
我爱学习好爱好爱10 小时前
Ansible 常用模块详解:yum、service/systemd、copy实战
linux·服务器·ansible