D8
1)登录功能需求说明
- 用户根据用户名和密码登录
- 密码需要手动加盐验证
- 需要返回用户的token和用户信息
2)模块搭建思路步骤
2.1)模块作用
先捋一下之前搭模块干了啥
feign-api 远程调用
自媒体保存时调用远程客户端进行增加文章,说白了就是数据拷贝到另一个数据库展示
定时任务客户端,自媒体审核完发布后根据发布时间进行延迟任务
这一块用到了redis的zset和list队列 然后判断了时间,数据库同步问题
好像和登录没关系ok跳过
gateway 网关服务
好像是起到一个转发api的作用,我们先启动user/app网关服务看看请求路径找下规律
其实也可以不用,启动,直接启动前端nginx发请求就行
前面带了一个app,估计和nginx代理那些差不多,识别/app/**,最后将/app去掉,定向到/** 路径下
okok一起来看下网关服务是怎么设置的
![image-20240911200530214](https://img-blog.csdnimg.cn/img_convert/2a248b99c26d1ba44b911ac3af01a141.png)
过滤器,鉴别token的,这个应该都一样,我们直接拷贝就行。jWT工具和引导类上的注解
@SpringBootApplication
@EnableDiscoveryClient
大差不差,全拷贝,就改配置文件即可,然后配置nacos,这看起来好像也没那么难,对吧?
![image-20240911201014375](https://img-blog.csdnimg.cn/img_convert/d9236924328f6f0181f4d225f116684d.png)
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
同样的模仿其他服务看看
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是怎么配置的?同样模仿别的服务看看
结构和application差不多但是为什么要写到这里面?
application.yml 由springboot内部管理在启动应用时加载,也就是说,当你修改了application.yml后,需要重新启动springboot
相当于是一个项目已经编码好了,打好了jar包,当你部署到服务器上时,发现配置文件不满意,因此你还得重新修改,打包,部署, 不过有另一种方法,在jar包的同级目录下创建配置文件yml,指定即可,但是,也挺麻烦的,也不好统一管理,还有一种就是以命令行的参数指定配置文件,不过这样一来,又涉及到重新部署jar包,在生产环境下,当你服务停止了1分钟,你的客户们就会比比赖赖,这什么破网站,天天崩溃,我还是找找有没有更好一点的替代品吧
好的因此我们总结出一个好处 (面试的时候可以吹牛逼了)
配置文件动态更新
并且,单体项目还好,如果是多个微服务呢?岂不是一堆的application.yml,也不好管理是吧,在nacos,你可以统一的在一个页面上进行修改,嘎嘎方便,第二个好处是
方便统一管理
2.4)配置详解
这个id就是springboot项目指定ip和服务名所找到的配置,然后合并springboot内部和nacos上的
![image-20240911203154850](https://img-blog.csdnimg.cn/img_convert/fdf20f507235f553ae27b866b9edb2f3.png)
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双重负载均衡,
然后代理到51601,然后由nacos网关路由再代理到 user服务,层层脱衣了属于是
接下来我们再看一下具体的微服务怎么配的
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启动服务测试
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需求说明里毛都没,自己看前端请求吧谢
封装一个只有这俩字段的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);
}
}
gogogo
很顺利的进入了噢老铁
mmp 数据库错了?
说我user库里的ad_user表不存在? 等等,user库?
oh谢,忘记改了,问题不大,md这网关一次性用品啊,每次都要重启服了
查询成功
比对成功,最后返回SUCCESS
这前端干啥吃的,怎么成功不给我跳转呢
还要返回name?
结构发现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](https://img-blog.csdnimg.cn/img_convert/56c92028c1b92d28b5b9a8dd908ce693.png)
4)频道管理
4.1)新增需求说明
- 前台输入内容进行频道的保存
- 频道名词不能重复
接口url, 网关6001,转发到服务wemedia,
负载内容字段
- description
- name
- ord
- status
首先来给网关增加个服务先
# 自媒体管理
- id: wemedia
uri: lb://leadnews-wemedia
predicates:
- Path=/wemedia/**
filters:
- StripPrefix= 1
在原来的服务上加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);
}
新增成功
4.2)查询
- 查询需要按照创建时间倒序查询
- 按照频道名称模糊查询
- 可以按照状态进行精确查找(1:启用 true 0:禁用 false)
这一块前端没传数据过来,不做了 - 分页查询
。。。。熟悉的crud既视感
- 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);
}
4.3)修改
4.3.1)需求说明
- 点击编辑后可以修改频道
- 如果频道被引用则不能禁用(根据当前频道id查询wmNews下有没有人引用)
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);
}
测试
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);
}
测试
5)敏感词管理
查
- 查询需要按照创建时间倒序查询
- 按照敏感词名称模糊查询
- 分页查询
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);
}
}
增
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](https://img-blog.csdnimg.cn/img_convert/ce9ee36a1c78277b6219222b3f65ed0c.png)
OMG,付费内容了,只能下一个需求了
…jiodo玛德,在app端的个人中心用户可以实名认证 这一块是不能认证的,已经写死在了数据库,(可能一开始是有的,为了收米阉割了)
我们直接遍历即可,由于数据库在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);
}