插件使用权限管理软件(二)数据库和接口设计

需求描述

  1. 一个插件属于一个部门,一个部门存在多个插件
  2. 一个用户属于一个部门,一个部门存在多个用户
  3. 一个用户可以使用多个插件,一个插件可以给多个用户使用
  4. 用户是电脑的IP地址,实现制定某台电脑能用的效果
  5. 用户对插件的使用有时间上的限制

数据模型

我们系统的业务模块有:

  • 部门模块
  • 用户模块
  • 插件模块
  • 插件使用权限模块
  • 插件使用记录模块
    根据上述模块和需求的分析,我们得到如下模块之间关系图

数据表设计

根据上图,可以得出5个表,部门表(Department)、插件表(CADPlug)、成员表(User)、插件使用权限表(UserPlugAuthority)和插件使用记录表(UserLog)

部门表(Department)

  • 继承EntityBase类,内含自增Id属性的设置
  • Name属性:部门名字
  • CADPlugList集合属性:是EFCore架构中附带的设置类,是插件多对1部门的集合类
  • UserList集合属性:是EFCore架构中附带的设置类,是用户多对1部门的集合类

插件表(CADPlug)

  • 继承EntityBase类,内含自增Id属性的设置
  • Name属性:插件名字
  • DepartmentId 和 Department 属性:EFCore架构中附带的设置类,是插件多对1部门的集合类,也是外键。
  • UserPlugAuthorityList 集合属性:EFCore架构中附带的设置类,是插件使用权限类多对1插件的集合类
  • UserList 集合属性:EFCore架构中附带的设置类,是插件类多对多用户的集合类
csharp 复制代码
/// <summary>
    /// 插件
    /// </summary>
    public class CADPlug: EntityBase
    {
        /// <summary>
        /// 插件名字
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 插件所属部门ID
        /// </summary>
        public int DepartmentId { get; set; }
        /// <summary>
        /// 部门表
        /// </summary>
        public Department Department { get; set; }
        /// <summary>
        /// 插件使用权限中间表
        /// </summary>
        public ICollection<UserPlugAuthority> UserPlugAuthorityList { get; set; }
        /// <summary>
        /// 插件用户
        /// </summary>
        public ICollection<User> UserList { get; set; }

        public CADPlug() { }
    }

成员表(User)

  • 继承Entity类,内置了Id、CreateTime、UpdateTime属性
  • DepartmentId 和 Department 属性:EFCore架构中附带的设置类,是插件多对1部门的集合类,也是外键。
  • UserPlugAuthorityList 集合属性:EFCore架构中附带的设置类,是插件使用权限类多对1用户的集合类
  • CADPlugList 集合属性:EFCore架构中附带的设置类,是插件类多对多用户的集合类
csharp 复制代码
    /// <summary>
    /// 用户表
    /// </summary>
    public class User:EntityBase,IEntityTypeBuilder<User>
    {
        public string IP { get; set; }
        /// <summary>
        /// 部门Id
        /// </summary>
        public int DepartmentId { get; set; }
        /// <summary>
        /// 部门
        /// </summary>
        public Department Department { get; set; }
        /// <summary>
        /// 多对多CAD插件表
        /// </summary>
        public ICollection<CADPlug> CADPlugList { get; set; }
        /// <summary>
        /// 用户权限使用中间表
        /// </summary>
        public ICollection<UserPlugAuthority> UserPlugAuthorityList { get; set; }

        /// <summary>
        /// 配置多对多的关系
        /// </summary>
        /// <param name="entityBuilder"></param>
        /// <param name="dbContext"></param>
        /// <param name="dbContextLocator"></param>
        public void Configure(EntityTypeBuilder<User> entityBuilder, DbContext dbContext, Type dbContextLocator)
        {
            entityBuilder.HasMany(u => u.CADPlugList)
                .WithMany(c => c.UserList)
                .UsingEntity<UserPlugAuthority>(a => a.HasOne(b => b.CADPlug).WithMany(c => c.UserPlugAuthorityList).HasForeignKey(c => c.CADPlugId), a => a.HasOne(b => b.User).WithMany(u => u.UserPlugAuthorityList).HasForeignKey(c => c.UserId));
        }

        public User() { }
    }
  • IEntityTypeBuilder接口是对User类中多对多的关系进行配置。

插件使用权限表(UserPlugAuthority)

  • 继承Entity类,内置了Id、CreateTime、UpdateTime属性
  • UserId 和User 属性:EFCore架构中附带的设置类,是用户的外键。
  • CADPlugId 和 CADPlug 属性:EFCore架构中附带的设置类,是插件的外键。
csharp 复制代码
/// <summary>
    /// 插件使用授权
    /// </summary>
    public class UserPlugAuthority : Entity
    {
        public UserPlugAuthority()
        {
            CreatedTime = DateTime.Now;
        }
        /// <summary>
        /// 使用期限
        /// </summary>
        public DateTime UserDataTime { get; set; }
        /// <summary>
        /// 用户Id
        /// </summary>
        public int UserId { get; set; }
        /// <summary>
        /// 用户主表
        /// </summary>
        public User User { get; set; }
        /// <summary>
        /// 插件Id
        /// </summary>
        public int CADPlugId { get; set; }
        /// <summary>
        /// 插件信息
        /// </summary>
        public CADPlug CADPlug { get; set; }
        
    }

插件使用记录表(UserLog)

  • 这个类是用来记录插件使用的情况,不和其他表有主外键的关系。(这里没有设置验证用户登录的机制,单纯的记录)
  • UserIp 属性:使用者电脑的IP
  • CADPlugName 属性:使用者的插件名字
  • PlugFunctiontName 属性:使用者插件的方法名
csharp 复制代码
	/// <summary>
    /// 插件使用记录
    /// </summary>
    public class UserLog : Entity
    {
        public UserLog()
        {
            CreatedTime = DateTime.Now;
        }
        /// <summary>
        /// 用户IP
        /// </summary>
        public string UserIp { get; set; }
        /// <summary>
        /// 插件方法名
        /// </summary>
        public string PlugFunctiontName { get; set; }
        /// <summary>
        /// 插件名
        /// </summary>
        public string CADPlugName { get; set; }
    }

数据库初始化

  1. RightsManagementSystems.EntityFramework.Core项目中添加数据库上下文。
csharp 复制代码
    public class RightsManagementDbContext : AppDbContext<RightsManagementDbContext>
    {
        public RightsManagementDbContext(DbContextOptions<RightsManagementDbContext> options) : base(options)
        {
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
        }
    }
  1. 修改RightsManagementSystems.EntityFramework.Core项目中Startup类
csharp 复制代码
    public class Startup : AppStartup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDatabaseAccessor(options =>
            {
                string dbValue = App.Configuration["DataProvider"];
                string connString = "";
                string connDbProvider = "";
                switch (dbValue)
                {
                    case "Sqlite":
                        connString = App.Configuration["ConnectionString:SqliteConnection"];
                        connDbProvider = DbProvider.Sqlite;
                        break;
                    case "MySql":
                        connString = App.Configuration["ConnectionString:MySqlConnection"];
                        connDbProvider = $"{DbProvider.MySql}@8.0.22";
                        break;
                }
                options.AddDbPool<RightsManagementDbContext>(connDbProvider, connectionMetadata:connString);
            }, "RightsManagementSystems.Database.Migrations");
        }
    }
  • 会遇到多种数据库的使用配置,这里加了一个对配置的判断。
    因此我们要去修改RightsManagementSystems.Web.Entry项目中appsettings.json文件
csharp 复制代码
{
  "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Microsoft.EntityFrameworkCore": "Information"
    }
  },
  "ConnectionString": {
    "SqliteConnection": "Data Source = ./RightsManagementSystem.db",
    "MySqlConnection": ""
  },
  "DataProvider": "Sqlite",
  }
}
  1. 完成了数据库的配置后,需要去RightsManagementSystems.Database.Migrations 项目中做数据库的数据迁移(Furion操作步骤
  2. 示例使用的是Sqlite数据库,迁移后可以直接看到数据库文件

接口设计

根据上诉模块,分别实现对应的接口类

  • 部门接口
  • 用户接口
  • 插件接口
  • 插件使用权限接口
  • 插件使用记录接口

下面展示接口仅实现部门接口的类使用,其余可以去资源免费下载

部门接口

WebAPI类

csharp 复制代码
    /// <summary>
    /// 部门操作类
    /// </summary>
    [DynamicApiController]
    public class DepartmentAppService
    {
        private readonly IDepartmentService _departmentService;

        public DepartmentAppService(IDepartmentService departmentService)
        {
            this._departmentService = departmentService;
        }

        /// <summary>
        /// 新建部门
        /// </summary>
        /// <param name="departmentCreateDto"></param>
        /// <returns></returns>
        public async Task<IActionResult> PostDepartmentCreate([FromBody] DepartmentCreateDto departmentCreateDto) => await _departmentService.DepartmentCreateAsync(departmentCreateDto); 
        /// <summary>
        /// 修改部门名字
        /// </summary>
        /// <param name="departmentId"></param>
        /// <param name="departmentUpdateDto"></param>
        /// <returns></returns>
        public async Task<IActionResult> PutDepartmentUpdate(int departmentId,[FromBody]DepartmentUpdateDto departmentUpdateDto)=>await _departmentService.DepartmentUpdateAsync(departmentId, departmentUpdateDto);

        /// <summary>
        /// 删除部门
        /// </summary>
        /// <param name="departmentId"></param>
        /// <returns></returns>
        [HttpDelete]
        public async Task<IActionResult> DepartmentDelete(int departmentId) => await _departmentService.DepartmentDeleteAsync(departmentId);
        /// <summary>
        /// 取得一个部门
        /// </summary>
        /// <param name="departmentId"></param>
        /// <returns></returns>
        public async Task<IActionResult> GetDepartment(int departmentId) => await _departmentService.DepartmentGetAsync(departmentId);

        /// <summary>
        /// 得到所有的部门信息
        /// </summary>
        /// <returns></returns>
        public async Task<IActionResult> GetAllDepartment() => await _departmentService.DepartmentGetAllAsync();
    }

通过IOC容器反向代理得到IDepartmentService类

部门api接口服务接口

部门api接口服务接口实现类

csharp 复制代码
    public class DepartmentService : ITransient, IDepartmentService
    {
        private readonly IRepository<Department> _departmentRepository;

        public DepartmentService(IRepository<Department> departmentRepository)
        {
            this._departmentRepository = departmentRepository;
        }
        public async Task<IActionResult> DepartmentCreateAsync(DepartmentCreateDto departmentCreateDto)
        {
            try
            {
                if (departmentCreateDto == null) return ApiResponse.Error("null");
                if (string.IsNullOrEmpty(departmentCreateDto.Name)) return ApiResponse.Error("部门名字为null");
                Department department = new Department() { Name = departmentCreateDto.Name };
                await _departmentRepository.InsertNowAsync(department);
                return ApiResponse.OK("部门添加成功");
            }
            catch (Exception ex)
            {
                return ApiResponse.Error(ex.Message);
            }
        }

        public async Task<IActionResult> DepartmentDeleteAsync(int departmentId)
        {
            try
            {
                if (departmentId < 0) return ApiResponse.Error("部门Id不存在");
                await _departmentRepository.DeleteNowAsync(departmentId);
                return ApiResponse.OK("删除成功");
            }
            catch (Exception ex)
            {
                return ApiResponse.Error(ex.Message);
            }
        }

        public async Task<IActionResult> DepartmentGetAsync(int departmentId)
        {
            if (departmentId < 0) return ApiResponse.Error("部门Id不存在");
            Department department = await _departmentRepository.Include(x => x.CADPlugList).Include(x => x.UserList).FirstOrDefaultAsync(x=>x.Id == departmentId);
            return ApiResponse.OK(department);
        }

        public async Task<IActionResult> DepartmentGetAllAsync()
        {
            List<Department> allDepartmentList = await _departmentRepository.Include(x=>x.CADPlugList).Include(x=>x.UserList).AsQueryable().ToListAsync();
            return ApiResponse.OK(allDepartmentList);
        }

        public async Task<IActionResult> DepartmentUpdateAsync(int departmentId, DepartmentUpdateDto departmentUpdateDto)
        {
            try
            {
                if (departmentUpdateDto == null) return ApiResponse.Error("null");
                Department updateDepartment = await _departmentRepository.FindOrDefaultAsync(departmentId);
                if (updateDepartment == null) return ApiResponse.Error("找不到部门");
                if (await _departmentRepository.FirstOrDefaultAsync(x => x.Name.Equals(departmentUpdateDto.Name)) != null) return ApiResponse.Error("修改的名字已经存在");
                if (!string.IsNullOrEmpty(departmentUpdateDto.Name)) updateDepartment.Name = departmentUpdateDto.Name;
                var updateResult = await _departmentRepository.UpdateNowAsync(updateDepartment);
                return ApiResponse.OK(updateResult.Entity);
            }
            catch (Exception ex)
            {
                return ApiResponse.Error(ex.Message);
            }
        }
    }
  • [DynamicApiController]是Furion动态WebAPI注入的其中一种方式
  • ITransient是Furion框架的服务生存期接口,一共有三个接口
    • ITransient:对应暂时/瞬时作用域服务生存期
    • IScoped:对应请求作用域服务生存期
    • ISingleton:对应单例作用域服务生存期
  • IRepository < Department >是Furion框架内置的仓储类接口,可以实现对数据库中的数据进行操作,同时封装了很多对数据库使用的方法,详细可以去看文档(API文档
  • ApiResponse的静态方法是自己定义的一个返回值类,大家也可以自定义

运行查看swagger



需要注意的问题

  • 在写业务代码时使用到Include在返回数据的时候出现Json文件套娃访问的问题,需要在RightsManagementSystems.Web.CoreStartup.cs类的ConfigureServices方法中添加以下代码
csharp 复制代码
services.AddControllers().AddInjectWithUnifyResult().AddJsonOptions(options =>
                    {
                        options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
                    });
相关推荐
大数据面试宝典1 分钟前
用AI来写SQL:让ChatGPT成为你的数据库助手
数据库·人工智能·chatgpt
努力的小雨6 分钟前
快速上手 KSQL:轻松与数据库交互的利器
数据库·经验分享
Gentle5868 分钟前
labview中连接sql server数据库查询语句
数据库·labview
Gentle5869 分钟前
labview用sql server数据库存取数据到一个单元格
数据库·labview
2401_8576363912 分钟前
共享汽车管理新纪元:SpringBoot框架应用
数据库·spring boot·汽车
菲兹园长12 分钟前
表的设计(MYSQL)
数据库·mysql
wyh要好好学习27 分钟前
C# WPF 记录DataGrid的表头顺序,下次打开界面时应用到表格中
开发语言·c#·wpf
AitTech27 分钟前
C#实现:电脑系统信息的全面获取与监控
开发语言·c#
Java Fans28 分钟前
MySQL数据库常用命令大全(完整版——表格形式)
数据库·mysql
起飞的风筝40 分钟前
【redis】—— 环境搭建教程
数据库·redis·缓存