用户中心(下)

news2024/11/20 4:32:49

计划

  1. 开发完成后端登录功能 (单机登录 => 后续改造为分布式 / 第三方登录)✔
  2. 开发后端用户的管理接口 (用户的查询 / 状态更改)✔
  3. 后端接口测试 ✔
  4. 开发前端用户登录注册功能
  5. 讨论如何校验用户(星球的小伙伴可以使用)

登录逻辑

接口

接受参数:用户账户、密码
请求类型:POST
请求体:JSON 格式的数据

请求参数很长,或者无法预料的情况,不建议用 GET

返回值:用户信息(脱敏)

逻辑

  1. 校验用户和密码是否合法
    1. 非空
    2. 账户长度不小于 4 位
    3. 密码长度不小于 8 位
    4. 账户不包含特殊字符
  2. 校验密码是否输入正确,要和数据库中的密文密码对比
  3. 用户信息脱敏,隐藏敏感信息,防止数据库中的字段泄露
  4. 记录用户的登录态(我们用 Session),将其存到服务器上(用后端 SpringBoot 框架封装的服务器 Tomcat 记录即可)

如何知道是哪个用户登录了?
1连接上服务器后,得到一个 session 状态(匿名会话),返回给前端
2登录成功后,得到了登录成功的 session,并且给该 session 设置一些值(比如用户信息),返回给前端一个设置 cookie 的“命令”
3前端接收到后端的命令后,设置 cookie,保存到浏览器内
4前端再次请求后端的时候(相同的域名),在请求头中带上 cookie 去请求
5后端拿到前端传来的 cookie,找到对应的 session
6后端从 session 中可以取出基于该 session 存储的变量(用户的登录信息、登录名)

  1. 返回用户信息(脱敏后的)
简单说明cookie和session

首先,cookie是一种缓存机制,session是会话机制
:::info
🪔以最常见的登陆案例讲解cookie的使用过程:
(1)首先用户在客户端浏览器向服务器首次发起登陆请求
(2)登陆成功后,服务端会把登陆的用户信息设置在cookie 中,并将cookie返回给客户端浏览器
(3)客户端浏览器接收到 cookie 请求后,会把 cookie 保存到本地(可能是内存,也可能是磁盘,看具体使用情况而定)
(4)以后再次访问该 web 应用时,客户端浏览器就会把本地的 cookie 带上,这样服务端就能根据 cookie 获得用户信息了
:::

🪔同样以登陆案例为例子讲解 session 的使用过程:
(1)首先用户在客户端浏览器发起登陆请求
(2)登陆成功后,服务端会把用户信息保存在服务端,并返回一个唯一的 session 标识给客户端浏览器。
(3)客户端浏览器会把这个唯一的 session 标识保存在起来
(4)以后再次访问 web 应用时,客户端浏览器会把这个唯一的 session 标识带上,这样服务端就能根据这个唯一标识找到用户信息。

看到这里可能会引起疑问:把唯一的 session 标识返回给客户端浏览器,然后保存起来,以后访问时带上,这难道不是 cookie 吗?

没错,session 只是一种会话机制,在许多 web 应用中,session 机制就是通
过 cookie 来实现的。也就是说它只是使用了 cookie 的功能,并不是使用 cookie
完成会话保存。与 cookie 在保存客户端保存会话的机制相反,session 通过 cookie
的功能把会话信息保存到了服务端。

session和cookie有什么区别?

(1)cookie 是浏览器提供的一种缓存机制,它可以用于维持客户端与服务端之间的会话
(2)session 指的是维持客户端与服务端会话的一种机制,它可以通过 cookie 实现,也可以
通过别的手段实现。
(3)如果用 cookie 实现会话,那么会话会保存在客户端浏览器中
(4)而 session 机制提供的会话是保存在服务端的。

🦥举个小例子说明Cookie和Session之间的区别和联系
假如一个咖啡店有喝五杯赠一杯咖啡的优惠,但是一次性消费5杯咖啡的客人很少,这时就需要某种方式来记录某位顾客的消费数量。无外乎下面的几种方案:
1、该店的店员很厉害,能记住每位顾客的消费数量,只要顾客一走进咖啡店,店员就知道该怎么对待了。这种做法就是协议本身支持状态。但是http协议本身是无状态的。
2、发给顾客一张卡片,上面记录着消费的数量,一般还有个有效期限。每次消费时,如果顾客出示这张卡片,则此次消费就会与以前或以后的消费相联系起来。这种做法就是在客户端保持状态,也就是cookie,顾客就相当于浏览器。
3、发给顾客一张会员卡,除了卡号之外什么信息也不纪录,每次消费时,如果顾客出示该卡片,则店员在店里的记录本上找到这个卡号对应的记录添加一些消费信息。这种做法就是在服务器端保持状态。

源于星球炎大佬,解释的很清晰

写代码流程

先做设计
代码实现
持续优化!!!(代码复用性,提取公共逻辑/常量)

后端
逻辑层

UserService

public interface UserService extends IService<User> {
    /**
     *用户注册
     * @param userAccount 用户账户
     * @param userPassword 用户密码
     * @param checkPassword 校验密码
     * @return 用户id
     */
    long userRegister(String userAccount, String userPassword, String checkPassword);

    /**
     * 用户登录
     * @param userAccount 用户账户
     * @param userPassword 用户密码
     * @param request 请求
     * @return 脱敏后的用户信息
     */
    User userLogin(String userAccount, String userPassword, HttpServletRequest request);
}

UserServiceImpl

@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService{

    @Autowired
    private UserMapper userMapper;

    /**
     * 盐值, 混淆密码
     */
    private static final String SALT = "yupi";

    private static final String USER_LOGIN_STATE = "userLoginState";

    @Override
    public long userRegister(String userAccount, String userPassword, String checkPassword) {
        //1.校验
        if(StringUtils.isAnyBlank(userAccount, userPassword, checkPassword)){
            //TO do
            return  -1;
        }
        //账户不小于4位
        if(userAccount.length() < 4){
            return -1;
        }
        //密码不小于 8 位
        if(userPassword.length() < 8){
            return  -1;
        }
        //账户不包含特殊字符
        String validPattern = "[`~!@#$%^&*()+=|{}':;',\\\\\\\\[\\\\\\\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?\\s]";
        Matcher matcher = Pattern.compile(validPattern).matcher(userAccount);
        if(matcher.find()){
            return -1;
        }
        //校验密码和密码相同
        if(!userPassword.equals(checkPassword)){
            return -1;
        }
        //账户不能重复
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userAccount", userAccount);
        long count = userMapper.selectCount(queryWrapper);
        if(count > 0){
            return -1;
        }
        //2.加密
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());

        //3.插入数据
        User user = new User();
        user.setUserAccount(userAccount);
        user.setUserPassword(encryptPassword);
        boolean saveResult = this.save(user);
        if (!saveResult){
            return -1;
        }
        return user.getId();
    }

    @Override
    public User userLogin(String userAccount, String userPassword, HttpServletRequest request) {
        //1.校验
        if(StringUtils.isAnyBlank(userAccount, userPassword)){
            //TO do
            return  null;
        }
        //账户不小于4位
        if(userAccount.length() < 4){
            return null;
        }
        //密码不小于 8 位
        if(userPassword.length() < 8){
            return  null;
        }
        //账户不包含特殊字符
        String validPattern = "[`~!@#$%^&*()+=|{}':;',\\\\\\\\[\\\\\\\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?\\s]";
        Matcher matcher = Pattern.compile(validPattern).matcher(userAccount);
        if(matcher.find()){
            return null;
        }

        //2.加密
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());

        //查询用户是否存在
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userAccount", userAccount);
        queryWrapper.eq("userPassword", encryptPassword);
        User user = userMapper.selectOne(queryWrapper);
        // 用户不存在
        if(user == null) {
            log.info("user login failed, userAccount cannot match userPassword");
            return null;
        }
        //3. 用户脱敏
        User safetyUser = new User();
        safetyUser.setId(user.getId());
        safetyUser.setUsername(user.getUsername());
        safetyUser.setUserAccount(user.getUserAccount());
        safetyUser.setAvatarUrl(user.getAvatarUrl());
        safetyUser.setGender(user.getGender());
        safetyUser.setEmail(user.getEmail());
        safetyUser.setUserStatus(user.getUserStatus());
        safetyUser.setPhone(user.getPhone());
        safetyUser.setCreateTime(user.getCreateTime());
        //4. 记录用户的登录态
        request.getSession().setAttribute(USER_LOGIN_STATE, safetyUser);

        return safetyUser;
    }
}

逻辑删除,MyBatis-Plus 能自动实现逻辑删除,Mybatis-Plus 逻辑删除

mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

这里的 flag 我们是 isDelete

还要在 User 这个类中找到这个属性,然后我们加上注解 @TableLogic

/**
 * 是否删除
 */
@TableLogic
private Integer isDelete;
控制层

创建 UserController
记得加上注解 @RestController,适用于编写 Restful 风格的 API,返回值默认为 JSON 类型
还有 @RequestMapping 请求路径

@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private UserService userService;

    @PostMapping("/register")
    public Long userRegister(@RequestBody UserRegisterRequest userRegisterRequest) {
        if (userRegisterRequest == null)
        {
            return null;
        }
        String userAccount = userRegisterRequest.getUserAccount();
        String userPassword = userRegisterRequest.getUserPassword();
        String checkPassword = userRegisterRequest.getCheckPassword();
        if(StringUtils.isAnyBlank(userAccount,userPassword,checkPassword)) {
            return null;
        }

        return userService.userRegister(userAccount, userPassword, checkPassword);
    }

    @PostMapping("/login")
    public User userLogin(@RequestBody UserLoginrequest userLoginrequest, HttpServletRequest request) {
        if (userLoginrequest == null)
        {
            return null;
        }
        String userAccount = userLoginrequest.getUserAccount();
        String userPassword = userLoginrequest.getUserPassword();
        if(StringUtils.isAnyBlank(userAccount,userPassword)) {
            return null;
        }

        return userService.userLogin(userAccount, userPassword, request);
    }
}

我们这里需要建立封装一个请求对象,接收请求
在 model 包下建立 request 包,新增对象 UserRegisterRequest、UserLoginRequest(这里列举一个)
这里实现序列化接口(具体详细可以百度)应用较为广泛

private static final long serialVersionUID = -2406654025944442247L; 是一个序列化版本号,用于标识序列化类的版本。当类的结构发生变化时,如果没有显式指定 serialVersionUID,Java 编译器会自动生成一个版本号,如果类的结构发生了变化,反序列化时可能会导致版本不一致的问题。因此,显式地指定 serialVersionUID 可以确保在类结构发生变化时,版本号保持一致,从而避免反序列化时的问题。

@Data
public class UserRegisterRequest implements Serializable {

    private static final long serialVersionUID = -2406654025944442247L;
    private String userAccount;
    private String userPassword;

    private String checkPassword;
}

为什么在逻辑层已经做过参数校验这里控制层也要做呢?
控制层倾向于对请求参数本身的校验,不涉及业务逻辑本身
而逻辑层是对业务逻辑的校验,有可能被除了控制层以外的类调用

测试

这里可以用IDEA自带的接口测试,也可以借助其他软件Postman等
image.png
或者直接点击这个按钮也可以
image.png

POST http://localhost:8080/user/login
Content-Type: application/json

{
  "userAccount": "yupi",
  "userPassword": 12345678
}

可以debug 模式感受session 登录态的记录,以及数据的传递

用户管理接口

必须要鉴权!

  1. 查询用户
    1. 允许根据用户名查询
  2. 删除用户

这里偷懒了,直接在控制层写了,但其实合理点的话应该在逻辑层写
较为简单的查询等操作可以直接在控制层写(不规范)

@GetMapping("/search")
    public List<User> searchUsers(String username, HttpServletRequest request) {
        if (!isAdmin(request)) {
            return new ArrayList<>();
        }
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(username)) {
            queryWrapper.like("username", username);
        }
        List<User> userList = userService.list();
        return userList.stream().map(user -> userService.getSafetyUser(user)).collect(Collectors.toList());
    }

    @PostMapping("/delete")
    public boolean deleteUser(@RequestBody Long id , HttpServletRequest request) {
        if (!isAdmin(request)) {
            return false;
        }
        if(id <= 0) {
            return  false;
        }
        return userService.removeById(id);
    }

这里将相同的代码逻辑摘出来(即鉴权)

private boolean isAdmin(HttpServletRequest request) {
        // 鉴权,只有管理员可以查询
        Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
        User user = (User) userObj;
        if(user == null || user.getUserRole() != ADMIN_ROLE) {
            return false;
        }
        return  true;
    }

注意:只是这么写还不行,因为这么写的话,什么人都能够调用,就很危险了
而我们此时的 user 表里面没有管理员这个字段,因此我们加一个

/**
 * 用户角色 0-普通用户 1-管理员
 */
private Integer role;

注意其他相关地方都要改, 数据库相关的可以用MybatisX插件重写

由于常量过多调用,这里定义用户常量类

public interface UserConstant {
    //用户登录态键
    String USER_LOGIN_STATE = "userLoginState";

    /**
     * 用户权限
     */
    int DEFAULT_ROLE = 0;//普通用户,默认权限
    int ADMIN_ROLE = 1;//管理员
}

定义session 失效时间,减少记录登录态,方便测试

spring:
  application:
    name: user-center
  datasource:
    # 驱动类名称
    driver-class-name: com.mysql.cj.jdbc.Driver
    # 数据库连接的url
    url: jdbc:mysql://localhost:3306/ania
    # 连接数据库的用户名
    username: root
    # 连接数据库的密码
    password: root
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 100MB
# session 失效时间
  session:
    timeout: 86400
server:
  port: 8080

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: false
    global-config:
        db-config:
          logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
          logic-delete-value: 1 # 逻辑已删除值(默认为 1)
          logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

为了避免用户密码被返回, 过滤优化代码
UserService

/**
     * 用户脱敏
     * @param originUser 起始用户
     * @return 安全用户
     */
    User getSafetyUser(User originUser);

UserServiceImpl

/**
     * 用户脱敏
     * @param originUser 起始用户
     * @return 安全用户
     */
    @Override
    public User getSafetyUser(User originUser) {
        User safetyUser = new User();
        safetyUser.setId(originUser.getId());
        safetyUser.setUsername(originUser.getUsername());
        safetyUser.setUserAccount(originUser.getUserAccount());
        safetyUser.setAvatarUrl(originUser.getAvatarUrl());
        safetyUser.setGender(originUser.getGender());
        safetyUser.setEmail(originUser.getEmail());
        safetyUser.setUserRole(originUser.getUserRole());
        safetyUser.setUserStatus(originUser.getUserStatus());
        safetyUser.setPhone(originUser.getPhone());
        safetyUser.setCreateTime(originUser.getCreateTime());
        return safetyUser;
    }

UserController

@GetMapping("/search")
    public List<User> searchUsers(String username, HttpServletRequest request) {
        if (!isAdmin(request)) {
            return new ArrayList<>();
        }
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(username)) {
            queryWrapper.like("username", username);
        }
        List<User> userList = userService.list();
        // 调用getSafetyUser方法,得到脱敏后的User也实现了返回结果过滤
        return userList.stream().map(user -> userService.getSafetyUser(user)).collect(Collectors.toList());
    }

前端
简化代码

image.png
页脚部分·
image.png
修改页脚链接,title等,换成想要的
image.png

标题,logo部分
image.png
删除不用功能(代码)
image.png
image.png
修改显示内容
image.png
增添密码长度提示
image.png
定义全局常量包constants,便于使用常量(如logo的链接,编程导航链接等)

export const SYSTEM_LOGO = "data:image/png;base64,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";

export const PLANTE_LINK = "https://wx.zsxq.com/dweb2/index/group/51122858222824"

image.png
精简后页面
image.png

对接后端

调整参数名称一致,便于进行类型检查和参数传递
image.png
这里可以Ctrl + r 全局修改
image.png

这里改成user,如果user存在的话,就显示登录成功,并且设置用户的登录状态为user

image.png
修改接口
image.png

这里返回结果不太一样,后面会再调
前端需要向后端发送请求
前端用 ajax 来请求后端,axios 封装了 ajax,request 是 ant design 项目又封装了一次
追踪 request 源码:用到了 umi 插件,requestConfig 是一个配置

image.png
按照官网的提示,我们修改 request 后面的请求地址,还有修改一下配置Ant Design Pro的官方文档,搜索请求

import { RequestConfig } from 'umi';

export const request: RequestConfig = {
  timeout: 1000,
  errorConfig: {},
  middlewares: [],
  requestInterceptors: [],
  responseInterceptors: [],
  errorHandler,
};

image.png
此时访问的请求地址就是
image.png
但此时后端是用 8080,我们前端使用 8000,这样就对不上,要么跨域,要么代理,后者比较简便一些

代理

正向代理:替客户端向服务器发送请求
反向代理:替服务器接收请求
怎么弄?
通过 Nginx 服务器、Node.js 服务器

image.png
后端 application.yml 指定接口全局 api
image.png
修改超时时间为10s
image.png
debug启动UserCenterApplication
后端成功拿到数据
image.png
前端也有相关响应
image.png

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

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

相关文章

LLaMA详细解读

LLaMA 是目前为止&#xff0c;效果最好的开源 LLM 之一。精读 LLaMA 的论文及代码&#xff0c;可以很好的了解 LLM 的内部原理。本文对 LLaMA 论文进行了介绍&#xff0c;同时附上了关键部分的代码&#xff0c;并对代码做了注释。 摘要 LLaMA是一个系列模型&#xff0c;模型参…

u盘格式化后电脑读不出来怎么办?u盘格式化的东西还能恢复吗

随着科技的快速发展&#xff0c;U盘已成为我们日常生活和工作中不可或缺的数据存储工具。然而&#xff0c;有时我们可能会遇到U盘格式化后电脑无法读取的情况&#xff0c;或是误格式化导致重要数据丢失。面对这些问题&#xff0c;我们该如何应对&#xff1f;本文将为您详细解答…

python邮件发送

第一种方式 一&#xff1a;发送的邮件要设置授权码&#xff0c;通过邮箱邮箱授权码去验证&#xff0c;让邮件服务器帮我们去转发邮件到要接收的邮件&#xff0c;代码中的授权码&#xff0c;是需要登录126邮箱&#xff08;我这里是以126邮件发送的&#xff0c;具体的以自己为准…

概念解析 | 互补学习系统

注1:本文系"概念解析"系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次辨析的概念是:互补学习系统(Complementary Learning Systems) 概念解析:互补学习系统 Paper Summary - “Complementary Learning Systems Theory Updated” | Rylan Schaeffer…

数据库MySQL的基本操作

在Linux里面&#xff0c;我们要对数据库MySQL进行操作时&#xff08;例如修改MySQL的密码&#xff09;&#xff0c;不是直接在我们的终端上进行操作&#xff0c;而是通过终端连接进入到MySQL里面去&#xff0c;在进行操作&#xff0c;写SQL语句。 而安装C等的开发库sudo命令&a…

Crocoddyl 使用教程(二)

系列文章目录 前言 小车摆杆是另一个经典的控制实例。在这个系统中&#xff0c;一根欠驱动的杆子被固定在一辆一维驱动的小车顶部。游戏的目的是将杆子升到站立位置。 模型如下&#xff1a; https://en.wikipedia.org/wiki/Inverted_pendulum 我们用 表示小车质量、 表示摆杆质…

Visual studio调试技巧

Visual studio调试技巧 bug是什么&#xff1f;Debug和ReleaseDebugRelease 如何调试VS调试快捷键调试过程中查看程序信息查看临时变量的值查看内存信息查看调用堆栈查看汇编信息查看寄存器信息 编译常见错误编译型错误链接型错误运行时错误 bug是什么&#xff1f; bug的英文释…

机器学习笔记-22

终章 至此吴恩达老师的机器学习课程已经完成啦&#xff0c;总结一下&#xff1a; 1.监督学习的算法&#xff1a;线性回归、逻辑回归、神经网络和向量机 2.无监督学习的算法&#xff1a;K-Means、PCA、异常检测 3.推荐系统、大规模数据处理、正则化、如何评估算法 4.上限分析、…

Servlet_JSP

1.一些回顾 对于Tomcat部署中 我们有一些补充的点需要在此说明一下 1.如果我们想要查询MINEType的话 可以到TOMCAT_HOME/conf/web.xml中进行查询 里面记录了不同类型对应的MINEType 2.我们客户端发送请求数据给服务器之后 服务器会调用父类中的service方法 然后在内部决定调用…

用Jenkins Gerrit-Trigger插件实现提交gerrit后自动启动编译验证-解决编译依赖问题

用Jenkins Gerrit-Trigger插件实现提交gerrit后自动启动编译验证-CSDN博客讨论了如何利用插件在提交gerrit的时候自动出发一个jenkins job编译固件,但是没有解决编译依赖问题。本文提出一种解决方案 首先在git commit -m ""的时候在commit message中设置Depend-On:…

ControlNet官方资源链接【ControlNet论文原文】【持续更新中~】

ControlNet官方资源链接 ControlNet论文原文&#xff1a;https://arxiv.org/abs/2302.05543ControlNet官方GitHub&#xff1a;https://github.com/lllyasviel/ControlNetControlNet 1.1官方GitHub&#xff1a;https://github.com/lllyasviel/ControlNet-v1-1-nightlyControlNe…

深度学习之基于Vgg16卷积神经网络印度交警手势识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景 随着智能交通系统的不断发展&#xff0c;手势识别技术在其中扮演着越来越重要的角色。特别是在印度等…

CVE-2017-11882分析和白象样本分析

CVE-2017-11882分析和白象样本分析 CVE-2017-11882是微软公布的一个远程代码执行漏洞&#xff0c;漏洞是由模块EQNEDT32.EXE公式编辑器引起&#xff0c;该模块在Office的安装过程中被默认安装&#xff0c;该模块以OLE技术&#xff08;Object Linking and Embedding&#xff0c…

《网络安全---frida应用实践---某付费视频应用一举拿下》

文章目录 目标应用环境:步骤1、查壳2、定位付费界面布局3、找到可疑方法4、那就看下请求信息吧,看下有没有思路5、其他请求(列表,视频信息,获取播放url)6、请求参数加密算法7、图片信息解密8、数据请求关键点9、以上都是废话10、直接找关键hook点总结相关源码1、文章仅供…

2.初探MPI——点对点通信(阻塞)

系列文章目录 初探MPI——MPI简介初探MPI——&#xff08;阻塞&#xff09;点对点通信初探MPI——&#xff08;非阻塞&#xff09;点对点通信初探MPI——集体通信 文章目录 系列文章目录前言一、Sending & Receiving message1.1 简介1.2 发送消息1.3 接收消息1.4 MPI 发送…

AI智能名片商城小程序构建企业级私域的IMC模型:IP、MarTech与Content的深度融合

在数字化营销的新时代&#xff0c;为企业定制开发的AI智能名片B2B2C商城小程序&#xff0c;结合我们丰富的私域运营实践&#xff0c;我们深刻领悟到构建企业级私域的三大核心要素&#xff1a;IP&#xff08;企业人设&#xff09;、MarTech&#xff08;营销技术&#xff09;和Co…

【自动化测试】使用MeterSphere进行接口测试

一、接口介绍二、接口测试的过程三、接口自动化测试执行自动化流程 四、接口之间的协议HTTP协议 五、 接口测试用例设计接口文档 六、使用MeterSphere创建接口测试创建接口定义设计接口测试用例 一、接口介绍 自动化测试按对象分为&#xff1a;单元测试、接口测试、UI测试等。…

C语言/数据结构——每日一题(移除链表元素)

一.前言 今天在leetcode刷到了一道关于单链表的题。想着和大家分享一下。废话不多说&#xff0c;让我们开始今天的知识分享吧。 二.正文 1.1题目要求 1.2思路剖析 我们可以创建一个新的单链表&#xff0c;然后通过对原单链表的遍历&#xff0c;将数据不等于val的节点移到新…

【Java从入门到精通】Java 流(Stream)、文件(File)和IO

Java.io 包几乎包含了所有操作输入、输出需要的类。所有这些流类代表了输入源和输出目标。 Java.io 包中的流支持很多种格式&#xff0c;比如&#xff1a;基本类型、对象、本地化字符集等等。 一个流可以理解为一个数据的序列。输入流表示从一个源读取数据&#xff0c;输出流…

获取淘宝商品销量数据接口

淘宝爬虫商品销量数据采集通常涉及以下几个步骤&#xff1a; 1、确定采集目标&#xff1a;需要明确要采集的商品类别、筛选条件&#xff08;如天猫、价格区间&#xff09;、销量和金额等数据。例如&#xff0c;如果您想了解“小鱼零食”的销量和金额&#xff0c;您需要设定好价…