黑马头条第八天实战(上)

news2024/11/23 16:34:31

D8

1)登录功能需求说明

  • 用户根据用户名和密码登录
  • 密码需要手动加盐验证
  • 需要返回用户的token和用户信息

2)模块搭建思路步骤

2.1)模块作用

先捋一下之前搭模块干了啥

image-20240911195444754

feign-api 远程调用

自媒体保存时调用远程客户端进行增加文章,说白了就是数据拷贝到另一个数据库展示

定时任务客户端,自媒体审核完发布后根据发布时间进行延迟任务

这一块用到了redis的zset和list队列 然后判断了时间,数据库同步问题

好像和登录没关系ok跳过

gateway 网关服务

好像是起到一个转发api的作用,我们先启动user/app网关服务看看请求路径找下规律

其实也可以不用,启动,直接启动前端nginx发请求就行

image-20240911200238344

前面带了一个app,估计和nginx代理那些差不多,识别/app/**,最后将/app去掉,定向到/** 路径下

okok一起来看下网关服务是怎么设置的

image-20240911200530214

过滤器,鉴别token的,这个应该都一样,我们直接拷贝就行。jWT工具和引导类上的注解

@SpringBootApplication
@EnableDiscoveryClient

大差不差,全拷贝,就改配置文件即可,然后配置nacos,这看起来好像也没那么难,对吧?

image-20240911201014375

2.2)模块配置文件

网关
server:
  port: 6001
spring:
  application:
    name: leadnews-admin-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.233.136:8848
      config:
        server-addr: 192.168.233.136:8848
        file-extension: yml

同样的模仿其他服务看看

image-20240911215924713

server:
  port: 51805
spring:
  application:
    name: leadnews-admin
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.233.136:8848
      config:
        server-addr: 192.168.233.136:8848
        file-extension: yml

2.3)引导类

网关
package com.heima.admin.gateway;

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


@SpringBootApplication
@EnableDiscoveryClient
public class AdminGatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(AdminGatewayApplication.class,args);
    }
}

admin服务
package com.heima.admin;

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


@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.heima.admin.mapper")
public class AdminApplication {

    public static void main(String[] args) {
        SpringApplication.run(AdminApplication.class,args);
    }
}

那nacos是怎么配置的?同样模仿别的服务看看

image-20240911202200065

结构和application差不多但是为什么要写到这里面?

application.yml 由springboot内部管理在启动应用时加载,也就是说,当你修改了application.yml后,需要重新启动springboot

相当于是一个项目已经编码好了,打好了jar包,当你部署到服务器上时,发现配置文件不满意,因此你还得重新修改,打包,部署, 不过有另一种方法,在jar包的同级目录下创建配置文件yml,指定即可,但是,也挺麻烦的,也不好统一管理,还有一种就是以命令行的参数指定配置文件,不过这样一来,又涉及到重新部署jar包,在生产环境下,当你服务停止了1分钟,你的客户们就会比比赖赖,这什么破网站,天天崩溃,我还是找找有没有更好一点的替代品吧

好的因此我们总结出一个好处 (面试的时候可以吹牛逼了)

配置文件动态更新

并且,单体项目还好,如果是多个微服务呢?岂不是一堆的application.yml,也不好管理是吧,在nacos,你可以统一的在一个页面上进行修改,嘎嘎方便,第二个好处是

方便统一管理

2.4)配置详解

这个id就是springboot项目指定ip和服务名所找到的配置,然后合并springboot内部和nacos上的

image-20240911203154850
2.4.1)CORS配置
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
        # 文章微服务
        - id: article
          uri: lb://leadnews-article
          predicates:
            - Path=/article/**
          filters:
            - StripPrefix= 1
        # 搜索微服务
        - id: leadnews-search
          uri: lb://leadnews-search
          predicates:
            - Path=/search/**
          filters:
            - StripPrefix= 1

  • globalcors:全局跨域资源共享 cross origin resource share

  • add-to-simple-url-handler-mapping: true: 表示将cors配置添加到url映射,以便处理跨域请求 (后端跨域问题解决)

  • corsConfigurations:跨域配置

  • ‘[/**]’: 针对所有的请求

  • allowedHeaders: "\*": 允许所有请求头。

    允许所有请求头的键key对应的值value

    常见请求头包括

    • Content-Type: 指定请求体的内容类型。
    • Authorization: 用于携带认证信息。(token)
    • 自定义请求头: 例如 X-Custom-Header

    例子

    GET /api/resource HTTP/1.1
    Host: example.com
    Content-Type: application/json
    Authorization: Bearer token
    X-Custom-Header: customValue
    
  • allowedOrigins: "\*": 指定了所有源也就是80/443 或是https/http 或者不同的域名ww.cn xx.cn 请求该服务器资源

  • allowedMethods:

    • 允许的 HTTP 方法,包括 GET、POST、DELETE、PUT 和 OPTIONS。

wc OPTIONS这是啥请求第一次见,查询得知是在跨域请求的情况下,浏览器发起的预检请求

当你从一个域名发post到另一个域名时,浏览器会发送OPTIONS请求(内容携带了实际请求)

OPTIONS /api/resource HTTP/1.1
Origin: http://frontend.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type

服务器根据cors策略返回允许的跨域方法和头部消息

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://frontend.example.com
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type

允许了get post 和options预检,和允许的头键名Content-Type

2.4.2)路由配置
  • routes: 定义所有路由
  • - id: user: 路由标识符,给开发人员区分用的
  • uri: lb://leadnews-user: 表示负载均衡的服务名,(如果该服务下有多台主机,采用不同机制进行负载均衡可能,轮询?随机?加权?就记得这三种)
    类似与代理,最终代理对象是leadnews-user服务
  • predicates: 翻译了一下,叫断言?肯定?声明? 我觉得应该叫regular好一点,因为该数组内容就是 什么特征规则的请求会被转发
  • - Path=/user/**: 所有/user打头的符合规则,开始转发到user服务
  • filters: 路由过滤器
  • - StripPrefix= 1: strip翻译为带,剥离,脱衣,prefix表示前缀, 整体意思为剥离由/分隔的前缀路径第一个 /user(第一个)/app(第二个)
    然后剩下的路径转发到 user服务的/user/app接口

突然想起来了,nginx里有个代理app前缀好像,在nginx里也做了负载均衡,666双重负载均衡,

image-20240911210100618

然后代理到51601,然后由nacos网关路由再代理到 user服务,层层脱衣了属于是

接下来我们再看一下具体的微服务怎么配的

image-20240911215509948

user和wemedia的服务配置了数据库,mybatis-plus

那我们就在admin服务下拷贝过去这些共同配置

Tip这里nginx给的conf配置文件代理到后端网关端口是6001,因此我们admin网关配置文件也得是6001,而不是在最后一个网关服务的端口+1

2.5)nacos服务配置

admin服务
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://192.168.233.136:3306/leadnews_admin?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: 123456
# 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置
mybatis-plus:
  mapper-locations: classpath*:mapper/*.xml
  # 设置别名包扫描路径,通过该属性可以给包中的类注册别名
  type-aliases-package: com.heima.model.admin.pojos
网关服务
spring:
  cloud:
    gateway:
      globalcors:
        add-to-simple-url-handler-mapping: true
        corsConfigurations:
          '[/**]':
            allowedHeaders: "*"
            allowedOrigins: "*"
            allowedMethods:
              - GET
              - POST
              - DELETE
              - PUT
              - OPTION
      routes:
        # 平台管理
        - id: admin
          uri: lb://leadnews-admin
          predicates:
            - Path=/admin/**
          filters:
            - StripPrefix= 1
       

2.6启动服务测试

image-20240911222205802

3)登录功能实现

盖好了地基了 现在就简单多了,嘎嘎堆shi就行

  • 用户根据用户名和密码登录
  • 密码需要手动加盐验证
  • 需要返回用户的token和用户信息

实体类

接口接收数据要封装到这里面的玩意,在资料里有提供,这里我直接放这里让你复制不用下载

AdUser
package com.heima.common.admin.pojos;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

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

/**
 * <p>
 * 管理员用户信息表
 * </p>
 *
 * @author itheima
 */
@Data
@TableName("ad_user")
public class AdUser implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 主键
     */
    private Integer id;

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

    /**
     * 登录密码
     */
    @TableField("password")
    private String password;

    /**
     * 盐
     */
    @TableField("salt")
    private String salt;

    /**
     * 昵称
     */
    @TableField("nickname")
    private String nickname;

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

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

    /**
     * 状态
            0 暂时不可用
            1 永久不可用
            9 正常可用
     */
    @TableField("status")
    private Integer status;

    /**
     * 邮箱
     */
    @TableField("email")
    private String email;

    /**
     * 最后一次登录时间
     */
    @TableField("login_time")
    private Date loginTime;

    /**
     * 创建时间
     */
    @TableField("created_time")
    private Date createdTime;

}
ApUserRealname
package com.heima.common.admin.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_realname")
public class ApUserRealname implements Serializable {

    private static final long serialVersionUID = 1L;

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

    /**
     * 账号ID
     */
    @TableField("user_id")
    private Integer userId;

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

    /**
     * 资源名称
     */
    @TableField("idno")
    private String idno;

    /**
     * 正面照片
     */
    @TableField("font_image")
    private String fontImage;

    /**
     * 背面照片
     */
    @TableField("back_image")
    private String backImage;

    /**
     * 手持照片
     */
    @TableField("hold_image")
    private String holdImage;

    /**
     * 活体照片
     */
    @TableField("live_image")
    private String liveImage;

    /**
     * 状态
            0 创建中
            1 待审核
            2 审核失败
            9 审核通过
     */
    @TableField("status")
    private Short status;

    /**
     * 拒绝原因
     */
    @TableField("reason")
    private String reason;

    /**
     * 创建时间
     */
    @TableField("created_time")
    private Date createdTime;

    /**
     * 提交时间
     */
    @TableField("submited_time")
    private Date submitedTime;

    /**
     * 更新时间
     */
    @TableField("updated_time")
    private Date updatedTime;

}

接口定义

wc需求说明里毛都没,自己看前端请求吧谢

image-20240911223659690

封装一个只有这俩字段的dto

AdUserDto

package com.heima.model.admin.dtos;


import lombok.Data;

@Data
public class AdUserDto {
    private String name;
    private String password;
}

由于该请求负载 为请求体,接收加RequestBody注解 如果不加注解,无法解析为Dto,其格式为Content-Type对应的值

controller后面再贴上来,因为还没写service(我觉得先service再controller的顺序好一点,不然你一开始给个破接口return null后面还要改,重复操作)

Service

public interface AdminLoginService {
    /**
     * 管理员登录
     * @param adUserDto 密码和用户名dto
     * @return
     */
    ResponseResult login (AdUserDto adUserDto);
}

Impl思路

密码加盐对比?我回想一下,好像是根据前端传来的用户名,查出密码和盐,盐+前端初始密码生成加密后的 密文与数据库加密后的密文比对,如果一致则通过

okok,那我们就把用户查出来先

1.查询用户,不为空则查盐+原始密码进行加密处理,比对数据库 (mapper还没加呢大哥)

mapper

@Mapper
public interface AdLoginMapper extends BaseMapper<AdUser> {
}

回归正题

Impl代码

package com.heima.admin.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.heima.admin.mapper.AdminLoginMapper;
import com.heima.admin.service.AdminLoginService;
import com.heima.model.admin.dtos.AdUserDto;
import com.heima.model.admin.pojos.AdUser;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

@Service
@Slf4j
public class AdminLoginServiceImpl implements AdminLoginService {
    @Autowired
    private AdminLoginMapper adminLoginMapper;

    /**
     * 管理员登录
     *
     * @param adUserDto 密码和用户名dto
     * @return
     */
    @Override
    public ResponseResult login(AdUserDto adUserDto) {
        // 1.参数校验
        if (adUserDto != null) {
            // 1.查询用户,不为空则查盐+原始密码进行加密处理,比对数据库
            // 我的盐我决定,先放盐!!在放密码 md5加密需要获取字节码进行加密
            AdUser dbAdUser = adminLoginMapper.selectOne(Wrappers.<AdUser>lambdaQuery().eq(AdUser::getName, adUserDto.getName()));
            if (dbAdUser == null) {
                return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
            }
            String md5DigestAsHex = DigestUtils.md5DigestAsHex((dbAdUser.getSalt() + adUserDto.getPassword()).getBytes());
            if (md5DigestAsHex.equals(dbAdUser.getPassword())) {
                log.info("登录成功!");
                return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
            }
        }
        return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
    }
}

测试

让给我们来debug启动测试一下吧!,谢特,忘记补controller了

controller

@RestController
@RequestMapping("/login")
public class AdminLoginController {
    @Autowired
    private AdminLoginService adminLoginService;
    @PostMapping("/in")
    public ResponseResult login(@RequestBody AdUserDto dto){
        return  adminLoginService.login(dto);
    }
}

image-20240911230636377

gogogo

image-20240911230753570

很顺利的进入了噢老铁

image-20240911231059248

mmp 数据库错了?

image-20240911231126330

说我user库里的ad_user表不存在? 等等,user库?

image-20240911231232213

oh谢,忘记改了,问题不大,md这网关一次性用品啊,每次都要重启服了

image-20240911231443650

查询成功

image-20240911231506509

比对成功,最后返回SUCCESS

image-20240911231621501

这前端干啥吃的,怎么成功不给我跳转呢

image-20240911231700783

还要返回name?

image-20240912094749022

结构发现data里需要包含user对象还有 token值,那我们就给他返回一个map

登录成功后添加

  Map<String, Object> map = new HashMap<>();
                String token = AppJwtUtil.getToken(Long.valueOf(dbAdUser.getId()));
                map.put("user",dbAdUser);
                map.put("token",token);
                return ResponseResult.okResult(map);
image-20240912095639931

4)频道管理

4.1)新增需求说明

image-20210725225940936

  • 前台输入内容进行频道的保存
  • 频道名词不能重复

image-20240912100135301

image-20240912100340937

接口url, 网关6001,转发到服务wemedia,

负载内容字段

  • description
  • name
  • ord
  • status

首先来给网关增加个服务先

 # 自媒体管理
        - id: wemedia
          uri: lb://leadnews-wemedia
          predicates:
            - Path=/wemedia/**
          filters:
            - StripPrefix= 1

image-20240912100309890

在原来的服务上加save接口即可

4.1.1)实现步骤
dto
package com.heima.model.wemedia.dtos;

import lombok.Data;

@Data
public class WmChannelDto {
    private String description;
    private String name;
    private Short ord;
    private Integer status;
}

service
 ResponseResult saveChannel(WmChannelDto wmChannelDto);
impl
 @Override
    public ResponseResult saveChannel( WmChannelDto wmChannelDto) {
        WmChannel wmChannel = new WmChannel();
        BeanUtils.copyProperties(wmChannelDto,wmChannel);
        return ResponseResult.okResult(save(wmChannel));
    }
controller
@PostMapping("/save")
    public  ResponseResult saveChannel(@RequestBody WmChannelDto dto){
       return wmchannelService.saveChannel(dto);
    }

image-20240912102510055

新增成功

4.2)查询

  • 查询需要按照创建时间倒序查询
  • 按照频道名称模糊查询
  • 可以按照状态进行精确查找(1:启用 true 0:禁用 false)
    这一块前端没传数据过来,不做了
  • 分页查询

。。。。熟悉的crud既视感

image-20240912103625751

  • name
  • page
  • size
dto
@Data
public class WmChannelSeachDto {
    String name;
    int page;
    int size;
}

service

wm服务下

  /**
     * 管理员频道列表
     * @param wmChannelSeachDto
     * @return
     */
    ResponseResult listForAdmin(WmChannelSeachDto wmChannelSeachDto);
impl
  /**
     * 管理员频道列表
     *
     * @param wmChannelSeachDto
     * @return
     */
    @Override
    public ResponseResult listForAdmin(WmChannelSeachDto wmChannelSeachDto) {
        // - 查询需要按照创建时间倒序查询
        LambdaQueryWrapper<WmChannel> wrapper = new LambdaQueryWrapper<>();
        // WmChannel::getCreatedTime是一种方法引用(引用字段),
        // 你也可以使用Lambda 表达式item->item.getCreatedTiem
        wrapper.orderByDesc(WmChannel::getCreatedTime);
        // - 按照频道名称模糊查询
        wrapper.like(StringUtils.isNotBlank(wmChannelSeachDto.getName()),
                WmChannel::getName, wmChannelSeachDto.getName());
        // - 可以按照状态进行精确查找(1:启用   true    0:禁用   false)
        // wrapper.eq(Integer.valueOf(wmChannelSeachDto.getStatus())!=null,WmChannel::getStatus,wmChannelSeachDto.getStatus());
        // - 分页查询
        IPage page = new Page(wmChannelSeachDto.getPage(), wmChannelSeachDto.getSize());
        page(page, wrapper);
        ResponseResult responseResult = new PageResponseResult(wmChannelSeachDto.getPage(), wmChannelSeachDto.getSize(), (int) page.getTotal());
        responseResult.setData(page.getRecords());
        return responseResult;
    }
controller
    /**
     * 管理员的频道列表
     */
    @PostMapping("/list")
    public ResponseResult listForAdmin(@RequestBody WmChannelSeachDto dto) {
        return wmchannelService.listForAdmin(dto);
    }

image-20240912112624537

4.3)修改

4.3.1)需求说明

image-20240912114609586

  • 点击编辑后可以修改频道
  • 如果频道被引用则不能禁用(根据当前频道id查询wmNews下有没有人引用)

image-20240912114100186

description

id

name

ord

status

实体

和channel实体大致差不多,就用这个了

service
 /**管理员修改操作
     *
     * @param wmChannel
     * @return
     */
    ResponseResult updateForAdmin(WmChannel wmChannel);
impl
/**
     * 管理员修改操作
     *
     * @param wmChannel
     * @return
     */
    @Override
    public ResponseResult updateForAdmin(WmChannel wmChannel) {
        if (wmChannel != null) {
            // 如果用户禁用,查询wmNews表有无该频道,没有则可以禁用,有则返回错误
            if (!wmChannel.getStatus()) {
                // 找有和当前要禁用频道有一腿的news,有的话就报错给前面
                List<WmNews> wmNews = wmNewsMapper.selectList(Wrappers.<WmNews>lambdaQuery().eq(WmNews::getChannelId, wmChannel.getId()));
                if (wmNews!=null&&wmNews.size()>0) {
                    return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "该频道下有文章,禁用失败");
                }
            }
            return ResponseResult.okResult(updateById(wmChannel));
        }
        // 编辑的参数校验
        return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
    }
controller
 /**
     * 管理员修改操作
     */
    @PostMapping("/update")
    public ResponseResult updateForAdmin(@RequestBody WmChannel wmChannel) {
        return wmchannelService.updateForAdmin(wmChannel);
    }

测试

image-20240912120630282

image-20240912120646659

4.4)删除

只有禁用的频道才能删除

service
    /**管理员删除操作
     *
     *
     * @return
     */
    ResponseResult removeForAdmin(Integer id);
impl
  /**
     * 管理员删除操作
     *
     * @return
     */
    @Override
    public ResponseResult removeForAdmin(Integer id) {
        WmChannel byId = getById(id);
        if (byId.getStatus()){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"必须是禁用频道才能删除~");
        }
        return ResponseResult.okResult(removeById(id));
    }
controller
 /**
     * 管理员删除操作
     */
    @GetMapping("/del/{id}")
    public ResponseResult removeForAdmin(@PathVariable("id") Integer id) {
        return wmchannelService.removeForAdmin(id);
    }
测试

image-20240912122702858

image-20240912122744132

5)敏感词管理

image-20240912122830900

  • 查询需要按照创建时间倒序查询
  • 按照敏感词名称模糊查询
  • 分页查询
dto
@Data
public class WmSensitiveDto {
    String name;
    int page;
    int size;
}
service
   /**
     * 敏感词分页查询
     */

    ResponseResult selectList(WmSensitiveDto wmSensitiveDto);
impl
 /**
     * 敏感词分页查询
     *
     * @param wmSensitiveDto
     */
    @Override
    public ResponseResult selectList(WmSensitiveDto wmSensitiveDto) {
        IPage<WmSensitive> page = new Page(wmSensitiveDto.getPage(), wmSensitiveDto.getSize());
        LambdaQueryWrapper<WmSensitive> wrapper = Wrappers.<WmSensitive>lambdaQuery()
                .eq(StringUtils.isNotBlank(wmSensitiveDto.getName()), WmSensitive::getSensitives, wmSensitiveDto.getName());
        page(page,wrapper);
        ResponseResult pageResponseResult =
                new PageResponseResult(wmSensitiveDto.getPage(), wmSensitiveDto.getSize(), (int)page.getTotal());
        pageResponseResult.setData(page.getRecords());
        return pageResponseResult;
    }
controller
@RestController
@RequestMapping("/api/v1/sensitive")
public class WmSensitiveController {

    @Autowired
    private WmSensitiveService wmSensitiveService;

    @PostMapping("/list")
    public ResponseResult selectList(@RequestBody WmSensitiveDto wmSensitiveDto){
       return wmSensitiveService.selectList(wmSensitiveDto);
    }
}

image-20240912135010559

service
 /**
     * 敏感词增加
     */
    ResponseResult add(WmSensitive wmSensitive);
impl
 /**
     * 敏感词增加
     *
     * @param wmSensitive
     */
    @Override
    public ResponseResult add(WmSensitive wmSensitive) {
        if (wmSensitive==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        wmSensitive.setCreatedTime(new Date());
        return ResponseResult.okResult(wmSensitiveMapper.insert(wmSensitive));
    }
controller
@PostMapping
    public ResponseResult add(@RequestBody WmSensitive wmSensitive){
        return ResponseResult.okResult(wmSensitiveService.add(wmSensitive));
    }

service
/**
     * 敏感词修改
     */
    ResponseResult update(WmSensitive wmSensitive);
impl
/**
     * 敏感词修改
     *
     * @param wmSensitive
     */
    @Override
    public ResponseResult update(WmSensitive wmSensitive) {
        return ResponseResult.okResult(wmSensitiveMapper.updateById(wmSensitive));
    }
controller
@PostMapping("/update")
    public ResponseResult update(@RequestBody WmSensitive wmSensitive){
        return ResponseResult.okResult(wmSensitiveService.update(wmSensitive));
    }

controller 麻了,直接调mapper吧
 @Autowired
    private WmSensitiveMapper wmSensitiveMapper; 
@PostMapping("/del/{id}")
    public ResponseResult delete(@PathVariable("id") Integer id){
        return ResponseResult.okResult(wmSensitiveMapper.deleteById(id));
    }

6)用户认证

  • 在app端的个人中心用户可以实名认证,需要材料为:姓名、身份证号、身份证正面照、身份证反面照、手持照片、活体照片(通过微笑、眨眼、张嘴、摇头、点头等组合动作,确保操作的为真实活体人脸。),当用户提交审核后就到了后端让运营管理人员进行审核

这里不涉及到任何的延迟队列以及消息发送,我们使用feign远程调用即可,具体操作参考添加文章后进入自动审核成功后添加到app端进行展示

  • 平台运营端查看用户认证信息,进行审核,其中审核包括了用户身份审核,需要对接公安系统校验身份证信息
  • 用户通过审核后需要开通自媒体账号(该账号的用户名和密码与app一致)

oh谢,看到现在才发现其实是有接口文档的

image-20240912204431707

OMG,付费内容了,只能下一个需求了

…jiodo玛德,在app端的个人中心用户可以实名认证 这一块是不能认证的,已经写死在了数据库,(可能一开始是有的,为了收米阉割了)

image-20240912210432420

我们直接遍历即可,由于数据库在user库,我们在user服务做文章即可…那用户认证推送功能阉割了只剩查询和通过审核和驳回了

dto (前面忘记继承请求dto然后校验page参数了,现在补救一下)
@Data
public class AuthDto extends PageRequestDto {
    /**
     *              状态
     *             0 创建中
     *             1 待审核
     *             2 审核失败
     *             9 审核通过
     */
    private Short status;
    // 申请人id
    private Integer id;
    // 驳回的信息
    private String msg;

}

pojo
/**
 * <p>
 * APP实名认证信息表
 * </p>
 *
 * @author itheima
 */
@Data
@TableName("ap_user_realname")
public class ApUserRealname implements Serializable {

    private static final long serialVersionUID = 1L;

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

    /**
     * 账号ID
     */
    @TableField("user_id")
    private Integer userId;

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

    /**
     * 资源名称
     */
    @TableField("idno")
    private String idno;

    /**
     * 正面照片
     */
    @TableField("font_image")
    private String fontImage;

    /**
     * 背面照片
     */
    @TableField("back_image")
    private String backImage;

    /**
     * 手持照片
     */
    @TableField("hold_image")
    private String holdImage;

    /**
     * 活体照片
     */
    @TableField("live_image")
    private String liveImage;

    /**
     * 状态
            0 创建中
            1 待审核
            2 审核失败
            9 审核通过
     */
    @TableField("status")
    private Short status;

    /**
     * 拒绝原因
     */
    @TableField("reason")
    private String reason;

    /**
     * 创建时间
     */
    @TableField("created_time")
    private Date createdTime;

    /**
     * 提交时间
     */
    @TableField("submited_time")
    private Date submitedTime;

    /**
     * 更新时间
     */
    @TableField("updated_time")
    private Date updatedTime;

}
service
public interface ApUserAuthService {

    /**
     * 分页查询
     * @param authDto
     * @return
     */
    ResponseResult selectList(AuthDto authDto);
}

impl
@Service
public class ApUserAuthServiceImpl
        extends ServiceImpl<ApUserRealnameMapper,ApUserRealname>
        implements ApUserAuthService {


    /**
     * 分页查询
     *
     * @param authDto
     * @return
     */
    @Override
    public ResponseResult selectList(AuthDto authDto) {
        // 分页数据校验
        authDto.checkParam();
        //设置分页信息
        Page<ApUserRealname> page = new Page<>(authDto.getPage(), authDto.getSize());
        LambdaQueryWrapper<ApUserRealname> wrapper = Wrappers.<ApUserRealname>lambdaQuery().eq(Integer.valueOf(authDto.getStatus()) != null
                , ApUserRealname::getStatus, authDto.getStatus());
        page(page,wrapper);
        ResponseResult result = new PageResponseResult(authDto.getPage(), authDto.getSize(), (int) page.getTotal());
        result.setData(page.getRecords());
        return result;
    }
}

controller
@RestController
@RequestMapping("/api/vi/auth")
public class ApUserAuthController {
    @Autowired
    private ApUserAuthService apUserAuthService;
    @PostMapping("/list")
    public ResponseResult selectList(@RequestBody AuthDto authDto){
        return  ResponseResult.okResult(apUserAuthService.selectList(authDto));
    }
}
nacos
# 平台管理
        - id: user
          uri: lb://leadnews-user
          predicates:
            - Path=/user/**
          filters:
            - StripPrefix= 1

驳回/同意(改)

service
    /**
     * 驳回/同意申请
     * @param authDto
     * @return
     */
    ResponseResult updateStatus(AuthDto authDto);
常量
package com.heima.common.constants;

public class UserConstants {
    public static final Short PASS_AUTH = 9;
    public static final Short FAIL_AUTH = 2;
    public static final Integer AUTH_TYPE = 2;
}
impl

思路,更改状态,搞在一个方法里即可

接口不同,调用方法一样 (接口里修改short)

如果是驳回,不传status,直接设置为2,如果是同意,把ap端的user查出来,然后拷贝相关信息

远程客户端可能注入会爆红,在引导类上加@EnableFeignClients(basePackages = “com.heima.apis”)

此处想着学一下 示例代码的,不小心看了点答案,罪过QAQ

 /**
     * 驳回/同意申请,修改状态为2
     *
     * @param authDto
     * @return
     */
    @Override
    public ResponseResult updateStatus(AuthDto authDto,Short status) {
        // 默认不给status就是拒绝
        ApUserRealname apUserRealname = new ApUserRealname();
        apUserRealname.setStatus(status);
        apUserRealname.setId(authDto.getId());
        if (StringUtils.isNotBlank(authDto.getMsg())) {
            apUserRealname.setReason(authDto.getMsg());
        }
        updateById(apUserRealname);
        // 如果是同意申请那就创建自媒体账号
        if (status == UserConstants.PASS_AUTH) {
            return createWmAccount(authDto);
        }
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    @Autowired
    private ApUserMapper apUserMapper;
    @Autowired
    private IWemediaClient wemediaClient;

    private ResponseResult createWmAccount(AuthDto authDto) {
        // 判断在app端有无该用户
        ApUser apUser = apUserMapper.selectById(authDto.getId());
        if (apUser == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        // 如果有该用户,拷贝ap端表的数据到WmUser,后续创建insert即可
        WmUser wmUser = new WmUser();
        wmUser.setApUserId(apUser.getId());
        wmUser.setSalt(apUser.getSalt());
        wmUser.setPassword(apUser.getPassword());
        wmUser.setStatus(Integer.valueOf(UserConstants.PASS_AUTH));
        wmUser.setPhone(apUser.getPhone());
        wmUser.setName(apUser.getName());
        // okok.开始插入,等等,这里好像没wmUser的mapper啊
        // 使用远程客户端,feign先写接口,然后要到达的服务层实现,要远程到哪就将客户端设置到哪的意思,
        // 注入客户端调方法
        wemediaClient.saveWmAccount(wmUser);
        apUser.setFlag((short)1);
        apUserMapper.updateById(apUser);
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }
controller
/**
     *  驳回/同意申请
     */
    @PostMapping
    public ResponseResult updateStatus(@RequestBody AuthDto authDto){
        return apUserAuthService.updateStatus(authDto);
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2130144.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

UE5中使用UTexture2D进行纹理绘制

在UE中有时需要在CPU阶段操作像素&#xff0c;生成纹理贴图等&#xff0c;此时可以通过UTexture2D来进行处理&#xff0c;例子如下&#xff1a; 1.CPP部分 首先创建一个蓝图函数库&#xff0c;将UTexture2D的绘制逻辑封装成单个函数&#xff1a; .h&#xff1a; #include &…

文本转语音工具 ChatTTS 使用教程

文章目录 Part.I IntroductionPart.II 一键安装部署Chap.I 下载Chap.II 使用Chap.III 存在的问题 Part.III 手动部署Chap.I 快速使用Chap.II 开发教程 Reference Part.I Introduction ChatTTS (Chat Text To Speech) 是专门为对话场景设计的文本转语音模型&#xff0c;例如LLM…

arcgisPro添加属性域

1、创建一个面要素&#xff0c;结果如下&#xff1a; 2、在【内容】列表中&#xff0c;选中该要素&#xff0c;点击【数据】选项卡&#xff0c;如下&#xff1a; 3、点击【属性域】按钮&#xff0c;如下&#xff1a; 4、点击【新建域】 5、添加一行属性域&#xff0c;如 6、保存…

【生日视频制作】海底石碑雕刻AE模板修改文字软件生成器教程特效素材【AE模板】

生日视频制作教程海底石碑雕刻AE模板修改文字特效广告生成神器素材祝福玩法AE模板工程 AE模板套用改图文教程↓↓&#xff1a; 怎么如何做的【生日视频制作】海底石碑雕刻AE模板修改文字软件生成器教程特效素材【AE模板】 生日视频制作步骤&#xff1a; 下载AE模板 安装AE软件…

python 注册 Nacos

根据项目需要 将python服务也纳入Nacos 中进行统一管理&#xff0c;所以进行python Nacos 项目适配。 记录本此适配过程。 python 安装不在说明。 系统版本&#xff1a;Linux 5.4.18-87.76-generic KYLINOS SMP Thu Aug 31 09:05:44 UTC 2023 aarch64 aarch64 aarch64 GNU/L…

文心一言 VS 讯飞星火 VS chatgpt (345)-- 算法导论23.2 4题

四、假定图中的边权重全部为整数&#xff0c;且在范围 1 ∼ ∣ V ∣ 1 \sim |V| 1∼∣V∣内。在此种情况下&#xff0c;Kruskal算法最快能多快&#xff1f;如果边的权重取值范围在1到某个常数 W W W之间呢&#xff1f;如果要写代码&#xff0c;请用go语言。 文心一言&#xff…

RPY角的具体描述

目录 一、 RPY角度 二、左乘与右乘 三、xyz固定角和zyx欧拉角旋转矩阵等价 四、参考文献 一、 RPY角度 1.1、X-Y-Z固定角[1] 首先将坐标系{B}和一个已知参考坐标系{A}重合。先将{B}绕旋转γ角&#xff0c;在绕旋转β角&#xff0c;在绕旋转α角&#xff0c;每次旋…

做统计(蓝桥杯初级)

系列文章目录 e&#xff0c;新系列没有目录&#xff09; 文章目录 系列文章目录前言一、个人名片二、描述三、输入输出以及代码示例1.输入输入样例&#xff1a; 2.输出输出样例&#xff1a; 3.代码示例 四、思路总结 前言 今天我们来做《做统计》 一、个人名片 个人主页&…

Flutter-底部选择弹窗(showModalBottomSheet)

前言 现在有个需求&#xff0c;需要用底部弹窗来添加定时的重复。在这里使用原生的showModalBottomSheet来实现 showModalBottomSheet的Props 名称 描述 isScrollControlled全屏还是半屏isDismissible外部是否可以点击&#xff0c;false不可以点击&#xff0c;true可以点击&a…

剪花布条(KPM模板题)

思路&#xff1a;套用KMP模板即可。 #include<bits/stdc.h> using namespace std; #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define endl \n int ne[200005]; int main() {IOSstring a,b;while(cin >> a){if(a"#") break;cin …

LEAN 类型系统属性 之 算法式相等的非传递性(Algorithm equality is not transitive)注解

由于 subsingleton 使用函数&#xff08;eliminator&#xff09; 的存在&#xff0c;导致算法式相等&#xff08;Algorithm defintional equality&#xff09;的非传递性。 在《定义上相等的非确定性&#xff08;Undecidability of Definitional Equality&#xff09;》 中有&…

[基于 Vue CLI 5 + Vue 3 + Ant Design Vue 4 搭建项目] 10 Ant Design Vue 的注册

1.全局全部注册 这样就可以将 ant design vue 全部组件注册进来 2.全局部分注册 这样就是按需注册了 本次&#xff0c; 我们选择第1种方式&#xff0c;全部注册进来 3.注册全局 css 4.测试一下 在 AboutView.vue 中添加一个 Test 按钮 使用 npm run serve 启动服务 访问 A…

如何通过subprocess在数据采集中执行外部命令 —以微博为例

介绍 在现代网络爬虫开发中&#xff0c;爬虫程序常常需要与外部工具或命令交互&#xff0c;以完成一些特定任务。subprocess 是 Python 提供的强大模块&#xff0c;用于启动和管理外部进程&#xff0c;广泛应用于爬虫技术中。本文将探讨如何通过 subprocess 在爬虫中执行外部命…

k8s 常见问题梳理

1、“cni0” already has an IP address different from 10.244.2.1/24 删除网卡 ifconfig cni0 down ip link delete cni0ip link add cni0 type bridge ip link set dev cni0 up ifconfig cni0 10.244.2.1/24 ifconfig cni0 mtu 1450 up

二.Unity中使用虚拟摇杆来控制角色移动

上一篇中我们完成了不借助第三方插件实现手游的虚拟摇杆&#xff0c;现在借助这个虚拟摇杆来实现控制角色的移动。 虚拟摇杆实际上就给角色输出方向&#xff0c;类似于键盘的WSAD&#xff0c;也是一个二维坐标&#xff0c;也就是(-1,1)的范围&#xff0c;将摇杆的方向进行归一化…

Windows与Linux下 SDL2的第一个窗口程序

Windows效果和Linux效果如下&#xff1a; 下面是代码&#xff1a; #include <stdio.h> #include "SDL.h"int main(int argc, char* argv[]) { // 初始化SDL视频子系统if (SDL_Init(SDL_INIT_VIDEO) ! 0){// 如果初始化失败&#xff0c;打印错误信息printf(&…

HPA自动扩缩容和命名空间资源限制

目录 HPA概念 安装HPA的依赖环境 安装metrics-server 手动扩缩容 自动扩缩容 yaml文件 创建HPA 自动扩容 自动缩容 命名空间资源限制 HPA概念 HPA是针对pod的数量进行自动扩缩容。&#xff08;是针对控制器deployment、replicaset、StatefulSet创建的pod&#xff0…

TS接口、泛型、自定义类型

这里记录下typescript中接口、泛型和自定义类型的使用 接口定义 // 定义一个接口,用来限制Teacher的属性 export interface Teacher {name: string;age: number;gender: string; }export type teacherList Teacher[];// 一个自定义类型 export type Teachers Array<Teach…

【UE5 C++课程系列笔记】02——创建C++类的三种方式

目录 一、从UE编辑器中创建 引用头文件报错的两种解决方式 &#xff08;1&#xff09;方式1 &#xff08;2&#xff09;方式2 二、在文件夹中直接创建 三、在Visual Studio中创建 一、从UE编辑器中创建 在UE编辑器中选择“Tools-》New C Class” 这里新建的类的父类选择…

Gitlab 中几种不同的认证机制(Access Tokens,SSH Keys,Deploy Tokens,Deploy Keys)

前言 公司主要使用 Go 语言做项目&#xff0c;有一些 Gitlab 私有仓库需要引用&#xff0c;在做 CI 时&#xff0c;要自行配置权限以获取代码。 最近发现各个项目组在做 CI 遇到仓库权限问题时的解决方式不尽相同&#xff0c;有用 Project Token 的&#xff0c;有用 Deploy K…