首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >从0到1学习微服务项目黑马头条day01-《APP端登录功能实现》

从0到1学习微服务项目黑马头条day01-《APP端登录功能实现》

作者头像
@VON
发布2025-12-21 09:23:24
发布2025-12-21 09:23:24
140
举报

一、前言

历时2552分钟将黑马商城项目拆分完毕,上述是学习时间表,黑马商城项目的更新先暂停一下,因为后续大都是知识点了,几乎也没有代码部分的东西了。另一方面也是因为快开学了,所以我还是想学点有关代码的知识,因此从今天开始学习黑马头条。

计划开学前将黑马头条完结,昨天其实已经开始了,只不过配置环境有点复杂,我这里大都是用的之前下载好的软件,并没有严格对标课程。本系列不再讲解有关环境配置方面的问题,如果有疑问就去直接看视频或者是看之前的文章就行。

废话不多说我就简单总结回顾一下我感觉有用的一些知识点。

特别声明:本系列所涉及资料皆为黑马程序员课程中的资料

二、项目概述

1、技术栈

这里是用到的相关技术,了解一下即可,后续都会有讲解

  • Spring-Cloud-Gateway : 微服务之前架设的网关服务,实现服务注册中的API请求路由,以及控制流速控制和熔断处理都是常用的架构手段,而这些功能Gateway天然支持
  • 运用Spring Boot快速开发框架,构建项目工程;并结合Spring Cloud全家桶技术,实现后端个人中心、自媒体、管理中心等微服务。
  • 运用Spring Cloud Alibaba Nacos作为项目中的注册中心和配置中心
  • 运用mybatis-plus作为持久层提升开发效率
  • 运用Kafka完成内部系统消息通知;与客户端系统消息通知;以及实时数据计算
  • 运用Redis缓存技术,实现热数据的计算,提升系统性能指标
  • 使用Mysql存储用户数据,以保证上层数据查询的高性能
  • 使用Mongo存储用户热数据,以保证用户热数据高扩展和高性能指标
  • 使用FastDFS作为静态资源存储器,在其上实现热静态资源缓存、淘汰等功能
  • 运用Hbase技术,存储系统中的冷数据,保证系统数据的可靠性
  • 运用ES搜索技术,对冷数据、文章数据建立索引,以保证冷数据、文章查询性能
  • 运用AI技术,来完成系统自动化功能,以提升效率及节省成本。比如实名认证自动化
  • PMD&P3C : 静态代码扫描工具,在项目中扫描项目代码,检查异常点、优化点、代码规范等,为开发团队提供规范统一,提升项目代码质量

2、项目引入

将资料中的项目拷贝到本地

这里说明一下,我拷贝的时候并没有log文件,需要自己创建出来,和其他模块同级别创建即可,下面会有一个地方需要改动,等会再说

这里的版本改成我这样就不会报错了

这两个别忘了启动,数据库中表的导入我就不多说了

这里的数据库也可以换成本地数据库

jdk全部换成1.8

编码全部改为utf-8

这里换成资料中的仓库就不用再下载其他依赖了,特别方便建议改一下,当然不改也没事

三、改造项目

上述准备工作完成后就开始改造项目了

1、创建heima-leadnews-user

要看好位置再创建,创建完成后按照我的目录结构创建相应的包结构

一定不要放错位置

2、创建实体类

一定要看好位置再创建

代码语言:javascript
复制
package com.heima.model.user.pojos;

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 lombok.Data;

import java.io.Serializable;
import java.util.Date;

/**
 * <p>
 * APP用户信息表
 * </p>
 *
 * @author itheima
 */
@Data
@TableName("ap_user")
public class ApUser implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 主键
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * 密码、通信等加密盐
     */
    @TableField("salt")
    private String salt;

    /**
     * 用户名
     */
    @TableField("name")
    private String name;

    /**
     * 密码,md5加密
     */
    @TableField("password")
    private String password;

    /**
     * 手机号
     */
    @TableField("phone")
    private String phone;

    /**
     * 头像
     */
    @TableField("image")
    private String image;

    /**
     * 0 男
     1 女
     2 未知
     */
    @TableField("sex")
    private Boolean sex;

    /**
     * 0 未
     1 是
     */
    @TableField("is_certification")
    private Boolean certification;

    /**
     * 是否身份认证
     */
    @TableField("is_identity_authentication")
    private Boolean identityAuthentication;

    /**
     * 0正常
     1锁定
     */
    @TableField("status")
    private Boolean status;

    /**
     * 0 普通用户
     1 自媒体人
     2 大V
     */
    @TableField("flag")
    private Short flag;

    /**
     * 注册时间
     */
    @TableField("created_time")
    private Date createdTime;

}

3、改造heima-leadnews-user

(1)UserApplication引导类
代码语言:javascript
复制
package com.heima.user;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.heima.user.mapper")
public class UserApplication {
    public static void main(String[] args) {
        org.springframework.boot.SpringApplication.run(UserApplication.class, args);
    }
}
(2)bootstrap.yml配置文件

这里的addr换成自己的nacos地址,也就是运行nacos的虚拟机地址+端口号

代码语言:javascript
复制
server:
  port: 51801
spring:
  application:
    name: leadnews-user
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.73.134:8848
      config:
        server-addr: 192.168.73.134:8848
        file-extension: yml
(3)nacos配置

为了防止重复配置所以将公共部分抽取到nacos中了,有不会配置的参考下这篇文章👇

重生之我在暑假学习微服务第十一天《配置篇》+网关篇错误订正

代码语言:javascript
复制
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/leadnews_user?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
# 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置
mybatis-plus:
  mapper-locations: classpath*:mapper/*.xml
  # 设置别名包扫描路径,通过该属性可以给包中的类注册别名
  type-aliases-package: com.heima.model.user.pojos
(4)logback.xml

还记得上文提到的日志吗,这里改成./logs就行,将日志放在此文件夹下

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>

<configuration>
    <!--定义日志文件的存储地址,使用绝对路径-->
    <property name="LOG_HOME" value="./logs"/>

    <!-- Console 输出设置 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>

    <!-- 按照每天生成日志文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <fileNamePattern>${LOG_HOME}/leadnews.%d{yyyy-MM-dd}.log</fileNamePattern>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 异步输出 -->
    <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
        <queueSize>512</queueSize>
        <!-- 添加附加的appender,最多只能添加一个 -->
        <appender-ref ref="FILE"/>
    </appender>


    <logger name="org.apache.ibatis.cache.decorators.LoggingCache" level="DEBUG" additivity="false">
        <appender-ref ref="CONSOLE"/>
    </logger>
    <logger name="org.springframework.boot" level="debug"/>
    <root level="info">
        <!--<appender-ref ref="ASYNC"/>-->
        <appender-ref ref="FILE"/>
        <appender-ref ref="CONSOLE"/>
    </root>
</configuration>

4、功能实现

这里就是一个简单的登录功能的实现,就按照123的顺序讲解了

(1)controller层

这里我画住的地方先不用写,这是用来测试的接口工具

这里要引入一个实体类

实体类代码👇

代码语言:javascript
复制
package com.heima.model.user.dtos;

import lombok.Data;

@Data
public class LoginDto {
    //手机号
    private String phone;
    //密码
    private String password;
}
(2)service层

实现类👇

一定要放好位置,位置千万不要错了

代码语言:javascript
复制
package com.heima.model.common.dtos;

import com.alibaba.fastjson.JSON;
import com.heima.model.common.enums.AppHttpCodeEnum;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 通用的结果返回类
 * @param <T>
 */
public class ResponseResult<T> implements Serializable {

    private String host;

    private Integer code;

    private String errorMessage;

    private T data;

    public ResponseResult() {
        this.code = 200;
    }

    public ResponseResult(Integer code, T data) {
        this.code = code;
        this.data = data;
    }

    public ResponseResult(Integer code, String msg, T data) {
        this.code = code;
        this.errorMessage = msg;
        this.data = data;
    }

    public ResponseResult(Integer code, String msg) {
        this.code = code;
        this.errorMessage = msg;
    }

    public static ResponseResult errorResult(int code, String msg) {
        ResponseResult result = new ResponseResult();
        return result.error(code, msg);
    }

    public static ResponseResult okResult(int code, String msg) {
        ResponseResult result = new ResponseResult();
        return result.ok(code, null, msg);
    }

    public static ResponseResult okResult(Object data) {
        ResponseResult result = setAppHttpCodeEnum(AppHttpCodeEnum.SUCCESS, AppHttpCodeEnum.SUCCESS.getErrorMessage());
        if(data!=null) {
            result.setData(data);
        }
        return result;
    }

    public static ResponseResult errorResult(AppHttpCodeEnum enums){
        return setAppHttpCodeEnum(enums,enums.getErrorMessage());
    }

    public static ResponseResult errorResult(AppHttpCodeEnum enums, String errorMessage){
        return setAppHttpCodeEnum(enums,errorMessage);
    }

    public static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums){
        return okResult(enums.getCode(),enums.getErrorMessage());
    }

    private static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums, String errorMessage){
        return okResult(enums.getCode(),errorMessage);
    }

    public ResponseResult<?> error(Integer code, String msg) {
        this.code = code;
        this.errorMessage = msg;
        return this;
    }

    public ResponseResult<?> ok(Integer code, T data) {
        this.code = code;
        this.data = data;
        return this;
    }

    public ResponseResult<?> ok(Integer code, T data, String msg) {
        this.code = code;
        this.data = data;
        this.errorMessage = msg;
        return this;
    }

    public ResponseResult<?> ok(T data) {
        this.data = data;
        return this;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getErrorMessage() {
        return errorMessage;
    }

    public void setErrorMessage(String errorMessage) {
        this.errorMessage = errorMessage;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }


    public static void main(String[] args) {
        //前置
        /*AppHttpCodeEnum success = AppHttpCodeEnum.SUCCESS;
        System.out.println(success.getCode());
        System.out.println(success.getErrorMessage());*/

        //查询一个对象
        /*Map map = new HashMap();
        map.put("name","zhangsan");
        map.put("age",18);
        ResponseResult result = ResponseResult.okResult(map);
        System.out.println(JSON.toJSONString(result));*/


        //新增,修改,删除  在项目中统一返回成功即可
       /* ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.SUCCESS);
        System.out.println(JSON.toJSONString(result));*/


        //根据不用的业务返回不同的提示信息  比如:当前操作需要登录、参数错误
        /*ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        System.out.println(JSON.toJSONString(result));*/

        //查询分页信息
//        PageResponseResult responseResult = new PageResponseResult(1,5,50);
//        List list = new ArrayList();
//        list.add("itcast");
//        list.add("itheima");
//        responseResult.setData(list);
//        System.out.println(JSON.toJSONString(responseResult));

    }

}

这里的逻辑相对来说不是很难理解

这里忘记了,引入项目的时候导入过了

(3)mapper层

mapper层更简单了,就直接继承下BaseMapper就行了

(4)接口测试工具

我这里用习惯了apifox所以就还是用apifox就行测试了,如果想用其他的可以去了解下knife4j

四、网关

不了解网关的可以看下这篇文章,简而言之就是一对多,是微服务中必不可少的

重生之我在暑假学习微服务第十天《网关篇》

1、创建heima-leadnews-app-gateway

引入依赖,在父工程下引入即可,一定要看好位置

代码语言:javascript
复制
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
        </dependency>
    </dependencies>

2、改造模块

(1)实体类
代码语言:javascript
复制
package com.heima.app.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class AppGatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(AppGatewayApplication.class, args);
    }
}
(2)bootstrap.yml配置文件

这里别忘了换ip地址

代码语言:javascript
复制
server:
  port: 51601
spring:
  application:
    name: leadnews-app-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.73.134:8848
      config:
        server-addr: 192.168.73.134:8848
        file-extension: yml
(3)nacos配置

这里直接复制过去就行

代码语言:javascript
复制
spring:
  cloud:
    gateway:
      globalcors:
        add-to-simple-url-handler-mapping: true
        corsConfigurations:
          '[/**]':
            allowedHeaders: "*"
            allowedOrigins: "*"
            allowedMethods:
              - GET
              - POST
              - DELETE
              - PUT
              - OPTION
      routes:
        # 平台管理
        - id: user
          uri: lb://leadnews-user
          predicates:
            - Path=/user/**
          filters:
            - StripPrefix= 1
(4)配置过滤器

这里的主要功能就是进行过滤的,整体逻辑也不复杂

代码语言:javascript
复制
package com.heima.app.gateway.filter;

import com.alibaba.cloud.commons.lang.StringUtils;
import com.heima.app.gateway.util.AppJwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
@Slf4j
public class AuthorizeFilter implements Ordered, GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1获取对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        //2.判断是否登录
        if (request.getURI().getPath() .contains("/login")) {
            //放行
            return chain.filter(exchange);
        }

        //3.获取token
        String token = request.getHeaders().getFirst("token");

        //4.判断token是否存在
        if(StringUtils.isBlank(token)){
            //token不存在
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }

        //5.判断token是否正确
        try {
            Claims claimsBoby = AppJwtUtil.getClaimsBody(token);
            if (AppJwtUtil.verifyToken(claimsBoby) == 1 || AppJwtUtil.verifyToken(claimsBoby) == 2) {
                //token错误
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                return response.setComplete();
            }
        } catch (Exception e) {
            e.printStackTrace();
            //token错误
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }

        //6.放行
        return chain.filter(exchange);
    }
    @Override
    public int getOrder() {
        return 0;
    }
}

3、部署前端项目

主要工作就是对nginx的改造

(1)创建存放conf的目录

因为项目所涉及不同的网关,所以要放在文件夹下统一保存

(2)创建APP端conf
(3)改造conf文件

将所画的改为自己的信息即可

这是我的文件内容

代码语言:javascript
复制
upstream  heima-app-gateway{
    server localhost:51601;
}

server {
	listen 8801;
	location / {
		root D:\AllCode\java_Code\springcloud\qianduan_All\app-web;
		index index.html;
	}
	
	location ~/app/(.*) {
		proxy_pass http://heima-app-gateway/$1;
		proxy_set_header HOST $host;  # 不改变源请求头的值
		proxy_pass_request_body on;  #开启获取请求体
		proxy_pass_request_headers on;  #开启获取请求头
		proxy_set_header X-Real-IP $remote_addr;   # 记录真实发出请求的客户端IP
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  #记录代理信息
	}
}
(4)改造原本的conf文件

这里记得修改为自己的路径,不过如果放在一个文件夹下其实不用更改

直接cv即可

代码语言:javascript
复制
#user  nobody;
worker_processes  1;

events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
	# 引入自定义配置文件
	include leadnews.conf/*.conf;
}

4、测试

准备工作完成开始测试

介绍下网关发挥的作用,可以看到这里可以通过网关的端口来访问登录功能

前后端联调测试

访问:http://localhost:8801/

测试登录功能

进入这个页面就测试成功了

结语

本文记录了从黑马商城项目转向黑马头条项目的过程。文章详细介绍了项目技术栈(包括SpringCloud、Nacos、Kafka等)、环境配置、模块改造(用户服务、网关服务)的具体步骤,并重点讲解了登录功能的实现和网关过滤器的配置。还分享了前后端联调测试经验,展示了如何通过Nginx配置实现前后端对接。该项目采用微服务架构,包含用户中心、自媒体等多个微服务模块,通过SpringCloudGateway实现统一API路由和权限控制。文章为开发者提供了完整的项目搭建指南和技术实现细节。


下篇文章地址:从0到1学习微服务项目黑马头条day02-《APP端文章列表功能实现》

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-10-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、前言
  • 二、项目概述
    • 1、技术栈
    • 2、项目引入
  • 三、改造项目
    • 1、创建heima-leadnews-user
    • 2、创建实体类
    • 3、改造heima-leadnews-user
      • (1)UserApplication引导类
      • (2)bootstrap.yml配置文件
      • (3)nacos配置
      • (4)logback.xml
    • 4、功能实现
      • (1)controller层
      • (2)service层
      • (3)mapper层
      • (4)接口测试工具
  • 四、网关
    • 1、创建heima-leadnews-app-gateway
    • 2、改造模块
      • (1)实体类
      • (2)bootstrap.yml配置文件
      • (3)nacos配置
      • (4)配置过滤器
    • 3、部署前端项目
      • (1)创建存放conf的目录
      • (2)创建APP端conf
      • (3)改造conf文件
      • (4)改造原本的conf文件
    • 4、测试
  • 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档