【.NET 8 实战--孢子记账--从单体到微服务】--用户(删除/修改/查询)

news/2024/9/23 5:11:07 标签: .net, 微服务, 数据库

本文将继续讲解和用户相关的接口,包括用户的删除、修改和查询

一、需求

我们先来看一下这篇文章的目标也就是需求表:

编号需求标题需求内容
1删除对某一个用户进行删除,注意:只能删除用户,和用户相关联的数据不能删除,方便后续查询
2修改根据传入修改内容,修改用户名、邮箱和手机
3查询按照用户名做模糊查询

二、删除

根据删除用户的需求,我们分析出如下三个子需求:

  1. 使用用户id删除一个用户;
  2. 用户要做假删除,也就是逻辑删除;
  3. 用户必须存在;

接下来,我们按照分析出来的子需求来做具体的代码开发。

2.1 服务开发

我们在服务接口ISysUserServer中增加 Delete方法:

/// <summary>
/// 删除用户
/// </summary>
/// <param name="userId"></param>
void Delete(string userId);

接着,我们在SysUserImp类中实现Delete方法:

/// <summary>
  /// 删除用户(逻辑删除)
  /// </summary>
  /// <param name="userId"></param>
  public void Delete(string userId)
  {
      try
      {
          var sysUser = _dbContext.SysUsers.FirstOrDefault(p => p.Id == userId);
          sysUser.IsDeleted = true;
          sysUser.UpdateDateTime= DateTime.Now;
          _dbContext.SysUsers.Update(sysUser);
          _dbContext.SaveChanges();
      }
      catch (Exception ex)
      {
          throw ex;
      }
  }

在上面的代码中,我们实现了ISysUserServer接口中删除用户的方法 Delete(string userId),这个方法执行逻辑删除操作。我们传入的 userId,通过数据库上下文 _dbContext 查找 SysUsers 集合中符合要求的用户,返回第一个匹配的用户对象 sysUser。然后我们将找到的用户对象的 IsDeleted 属性设置为 true,表示逻辑删除,接着更新修改 UpdateDateTime 为当前时间,我们调用 _dbContext.SysUsers.Update(sysUser) 方法,标记该用户记录为已更新。最后调用 _dbContext.SaveChanges(),将更改保存到数据库。这个方法不会物理删除用户,而是将用户的状态标记为“已删除”。

Tip:各位同学在写代码时一定发现了sysUser实例对象中还有一个DeleteUserId这个属性,但是在代码中我们并没有给它赋值,这是因为我们目前还没具体涉及到角色和权限这块的开发,因此UpdateUserIdCreateUserId以及DeleteUserId这三个属性我们暂时先不赋值。其中```CreateUserId````属性不赋值的话我们无法正常的提交数据,这个没关系,我们可以先给一个默认值就行。

2.2. Controller 开发

Controller 的开发比较简单,将传入的userId作为参数调用上一小节我们编写的服务的Delete方法即可,代码如下:

 /// <summary>
 /// 删除用户(逻辑删除)
 /// </summary>
 /// <param name="userId"></param>
 /// <returns></returns>
 [HttpDelete]
 [Route("Remove/{userId}")]
 public ActionResult<ResponseData<bool>> Remove([FromRoute] string userId)
 {
     try
     {
         bool exist = UserExist(userId);
         if (exist)
         {
             _sysUserServer.Delete(userId);
             return Ok(new ResponseData<bool>(HttpStatusCode.OK, data: true));
         }
         else
         {
             return Ok(new ResponseData<bool>(HttpStatusCode.NotFound, $"用户 {userId} 不存在", false));
         }
     }
     catch (Exception ex)
     {
         return Ok(new ResponseData<bool>(HttpStatusCode.InternalServerError, "服务器异常", false));
     }
 }

上面代码中通过 HTTP DELETE请求删除用户,返回结果是一个包含操作成功或失败状态的响应。我们首先调用 UserExist(userId) 检查指定的用户是否存在,如果用户存在就调用 _sysUserServer.Delete(userId) 方法逻辑删除用户。如果用户不存在就 返回 HTTP 404 状态码,告知客户端用户未找到,同时返回一条错误信息 用户 {userId} 不存在。这个web 接口的返回类型是 ActionResult<ResponseData<bool>>,表示响应数据的包装类型,ResponseData<bool> 包含 HTTP 状态码、数据(布尔值)和错误消息。

三、修改

修改用户的需求就比较简单了,分析出来的子需求就一个:

  1. 用户必须存在

跟新的接口服务我们已经在上一篇文章中实现了,因此这里不再讲解,我们直接看Controller的开发。

3.1 Controller 开发

直接上代码:

/// <summary>
/// 修改用户信息
/// </summary>
/// <param name="sysUserEditView"></param>
/// <returns></returns>
[HttpPut]
[Route("Edit")]
public ActionResult<ResponseData<bool>> Edit([FromBody] SysUserEditViewModel sysUserEditView)
{
    try
    {
        bool exist = UserExist(sysUserEditView.Id);
        if (exist)
        {
            SysUser sysUser = _sysUserServer.GetById(sysUserEditView.Id);
            sysUser.Email = sysUserEditView.Email;
            sysUser.PhoneNumber = sysUserEditView.PhoneNumber;
            sysUser.UserName = sysUserEditView.UserName;
            _sysUserServer.Update(sysUser);
            return Ok(new ResponseData<bool>(HttpStatusCode.NotFound, data: true));
        }
        else
        {
            return Ok(new ResponseData<bool>(HttpStatusCode.NotFound, $"用户 {sysUserEditView.Id} 不存在", false));
        }
    }
    catch (Exception ex)
    {
        return Ok(new ResponseData<bool>(HttpStatusCode.InternalServerError, "服务器异常", false));
    }
}

这段代码是通过 HTTP PUT请求来更新用户的相关信息,要更新的数据通过HTTP的请求体发送过来。首先我们调用 UserExist(sysUserEditView.Id) 检查用户是否存在,如果用户存在就调用 _sysUserServer.GetById(sysUserEditView.Id) 获取当前用户对象 sysUser,然后将 sysUserEditView 中的更新字段(如 Email, PhoneNumber, UserName)赋值给对应的 sysUser 属性,接着调用 _sysUserServer.Update(sysUser) 执行用户更新操作,将修改后的用户信息保存到数据库。反之如果用户不存在就返回 HTTP 404 状态码。

四、查询

查询的需求现在看来已经很详细了,因此这里就不做子需求分析了,我们直接写代码。

4.1 服务开发

和删除用户类似,我们需要在ISysUserServer增加方法GetByPage,并在SysUserImp类中实现它,代码如下:

  1. ISysUserServer
/// <summary>
/// 分页查询用户
/// </summary>
/// <param name="userPage"></param>
(int rowCount, int pageCount, List<SysUser> sysUsers) GetByPage(UserPageViewModel userPage);
  1. SysUserImp
/// <summary>
/// 分页查询用户
/// </summary>
/// <param name="userPage"></param>
/// <returns></returns>
public (int rowCount, int pageCount, List<SysUser> sysUsers) GetByPage(UserPageViewModel userPage)
{
    try
    {
        //计算跳过的数据量
        int skip = (userPage.PageNumber - 1) * userPage.PageSize;
        var users = _dbContext.SysUsers
            .Where(p => !p.IsDeleted);

        if (!string.IsNullOrEmpty(userPage.UserName))
        {
            users = users
                .Where(p =>
                    p.UserName.Contains(userPage.UserName));
        }
        //总行数
        int rowCount = 0;
        //总页数
        int pageCount = 0;
        var usersList = users.ToList();
        rowCount = usersList.Count();
        pageCount = rowCount % userPage.PageSize == 0
            ? rowCount / userPage.PageSize
            : (rowCount / userPage.PageSize) + 1;

        users = _dbContext.SysUsers
            .OrderByDescending(p => p.CreateDateTime)
            .Skip(skip)
            .Take(userPage.PageSize);
      
        return (rowCount, pageCount, usersList);
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

该代码实现了一个分页查询用户的方法 GetByPage,根据传入的分页信息 UserPageViewModel 返回符合条件的用户列表以及分页信息。参数 userPage的类型为 UserPageViewModel,它包含分页相关参数:页码 PageNumber、每页大小 PageSize,以及可选的查询条件 UserName)。返回值一共有三个:

  • rowCount:总的用户记录数,符合查询条件的用户条数。
  • pageCount:总页数,根据 rowCount 和分页大小计算得出。
  • sysUsers:当前页的用户列表。

首先我们通过每页大小和当前页码计算用于分页查询要跳过的记录数,接着通过Where方法过滤出来未被逻辑删除的用户,如果 userPage.UserName 不为空的话就筛选出只包含指定用户名的字符串的用户。最后按照创建时间CreateDateTime 进行降序排序,接着使用刚才计算的要跳过的记录数以及每页的数据量过滤出符合返回的数据。
由于几乎全部客户端的列表分页都需要知道数据的总行数和总页数,因此我们还需要对部分数据进行处理。首先我们通过Count方法计算出数据的总行数,然后使用总行是和每页的大小来计算总页数。

pageCount = rowCount % userPage.PageSize == 0
           ? rowCount / userPage.PageSize
           : (rowCount / userPage.PageSize) + 1;

如果总行数能被分页大小整除,则页数为 rowCount / userPage.PageSize;否则页数为 rowCount / userPage.PageSize 再加 1。
最后,我们将前面的数据作为一个元组返回给调用方。

Tip:在代码中我们看到有一行代码是将 users集合执行了一个ToList操作,那么为什么要这么做呢,原因就是在EF Core 类似ToListCount等这种操作都会对数据库进行一次查询,因此为了减少对数据库的查询,我们需要提前进行数据库查询将数据拿出来。

4.2 Controller 开发
/// <summary>
/// 查询用户
/// </summary>
/// <param name="userPage"></param>
/// <returns></returns>
[HttpPost]
[Route("Query")]
public ActionResult<ResponseData<PageResponseViewModel<SysUserViewModel>>> Query([FromBody] UserPageViewModel userPage)
{
   try
   {
       (int rowCount, int pageCount, List<SysUser> sysUsers) = _sysUserServer.GetByPage(userPage);
       List<SysUserViewModel> sysUsersView = _mapper.Map<List<SysUserViewModel>>(sysUsers);
       PageResponseViewModel<SysUserViewModel>
           pageResponseView = new PageResponseViewModel<SysUserViewModel>();
       pageResponseView.Data = sysUsersView;
       pageResponseView.PageCount = pageCount;
       pageResponseView.RowCount = rowCount;
       return Ok(new ResponseData<PageResponseViewModel<SysUserViewModel>>(HttpStatusCode.OK, data: pageResponseView));

   }
   catch (Exception ex)
   {
       return Ok(new ResponseData<bool>(HttpStatusCode.InternalServerError, "服务端异常", false));
   }
}

代码中通过 HTTP POST请求,根据传入的分页信息查询用户,并将结果返回给客户端。首先我们调用分页查询方法,让后将收到的用户数据通过 AutoMapperList<SysUser> 转换为 List<SysUserViewModel>,即用户的数据模型转换,最后我们构建响应数据,创建一个 PageResponseViewModel<SysUserViewModel> 对象 pageResponseView,并填充查询出来的数据。

六、涉及的其它代码

这里还涉及了AutoMapper的对象转换配置代码,这里就不详细讲解了,AutoMapper的使用同学们可以在其官网或者关注我后续关于AutoMapper的专栏进一步学习。

using AutoMapper;
using SporeAccounting.Models.ViewModels;

namespace SporeAccounting.Models;

public class SporeAccountingProfile : Profile
{
    public SporeAccountingProfile()
    {
        CreateMap<SysUserViewModel, SysUser>();
        CreateMap<SysUser, SysUserQueryViewModel>()
            .ForMember(d => d.Id, opt =>
                opt.MapFrom(s => s.Id))
            .ForMember(d => d.UserName, opt =>
                opt.MapFrom(s => s.UserName))
            .ForMember(d => d.CreateDateTime, opt =>
                opt.MapFrom(s => s.CreateDateTime))
            .ForMember(d => d.Email, opt =>
                opt.MapFrom(s => s.Email))
            .ForMember(d => d.PhoneNumber, opt =>
                opt.MapFrom(s => s.PhoneNumber));
    }
}

判断用户是否存在:

/// <summary>
/// //判断用户是否存在
/// </summary>
/// <returns></returns>

private bool UserExist(string userId)
{
    try
    {
        bool exist = _sysUserServer.GetById(userId) == null;
        return exist;
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

七、总结

本文介绍了与用户相关的接口开发,包括删除、修改和查询操作。首先,删除用户功能通过逻辑删除标记用户,而不删除关联数据,以便后续查询。其次,修改用户信息时,需确认用户存在,更新用户名、邮箱和手机号。查询用户功能支持分页和模糊查询,根据用户名筛选结果。每个功能的实现均涉及接口、服务层和控制器的开发,确保系统能够通过 RESTful API 进行用户管理。此外,使用 AutoMapper 简化对象转换,使数据库实体和视图模型的映射更加便捷。


http://www.niftyadmin.cn/n/5671375.html

相关文章

分布式项目-开盒头条

开盒头条 前言 只懂得技术理论是远远不够的&#xff0c;还需要熟练掌握很多业务功能逻辑的实现&#xff0c;这样才能真正的提高自己的开发水平。因此&#xff0c;我新开了这个专栏&#xff0c;专门做项目&#xff0c;教给大家很多业务功能实现的逻辑以及在实现这些业务功能时…

Linux 静态库与动态库的制作与使用

在Linux中&#xff0c;库library是一组函数和资源的集合&#xff0c;他们可以被不同的程序共享和使用&#xff0c;库的主要目的是代码重用&#xff0c;减少内存占用&#xff0c;并简化程序的维护。 Linux操作系统支持的函数库分为&#xff1a;静态库和动态库。 静态库&#xf…

828华为云征文 | 使用Flexus X实例搭建Dubbo-Admin服务

一、Flexus X实例简介 华为云推出的Flexus云服务&#xff0c;作为专为中小企业及开发者设计的新一代云服务产品&#xff0c;以其开箱即用、体验卓越及高性价比而著称。其中的Flexus云服务器X实例&#xff0c;更是针对柔性算力需求量身打造&#xff0c;能够智能适应业务负载变化…

html实现TAB选项卡切换

<!DOCTYPE html> <html> <head> <title>选项卡示例</title> <style> .tabs { overflow: hidden; /* 防止选项卡溢出容器 */ border: 1px solid #ccc; background-color: #f1f1f1; } .tab-links { margin: 0; padding: 0; l…

OpenHarmony(鸿蒙南向开发)——标准系统方案之瑞芯微RK3568移植案例(下)

往期知识点记录&#xff1a; OpenHarmony&#xff08;鸿蒙南向开发&#xff09;——轻量系统STM32F407芯片移植案例 OpenHarmony&#xff08;鸿蒙南向开发&#xff09;——Combo解决方案之W800芯片移植案例 OpenHarmony&#xff08;鸿蒙南向开发&#xff09;——小型系统STM32M…

dhtmlxGantt 甘特图 一行展示多条任务类型

效果如图: 后台拿到数据 处理之后如图: 含义: 如上图所示, 如果一行需要展示多个 需要给父数据的那条添加render:split属性, 子数据的parent为父数据的Id即可 切记 父数据的id 别为0 为0 时 会出现错乱 因为有些小伙伴提出分段展示的数据结构还是有点问题,下面展示一个完整…

分布式安装LNMP

目录 搭建LNMP架构 安装mysql 1.上传mysql软件包&#xff0c;关闭防火墙和核心防护 2.安装环境依赖包&#xff0c;桌面安装可能有自带的数据库除 3.配置软件模块 4.编译及安装 5.创建mysql用户 6.修改mysql 配置文件 7.更改mysql安装目录和配置文件的属主属组 8.设置…

【功能详解】IoTDB 与 ThingsBoard 成功集成!

可视化工具集成1 IoTDB 实现了 ThingsBoard 的无缝集成对接&#xff0c;IoTDB 构建的工业数据存储处理-可视化呈现链路又多了一种可用、易用的工具选择。 我们的代码已贡献到 ThingsBoard 社区&#xff08;待发版&#xff09;&#xff0c;用户手册也已发布&#xff08;可点击下…