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

需求描述

  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:对应暂时/瞬时作用域服务生存期
    • 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;
                    });
相关推荐
r i c k2 小时前
数据库系统学习笔记
数据库·笔记·学习
野犬寒鸦2 小时前
从零起步学习JVM || 第一章:类加载器与双亲委派机制模型详解
java·jvm·数据库·后端·学习
czhc11400756633 小时前
通信 28
c#
IvorySQL3 小时前
PostgreSQL 分区表的 ALTER TABLE 语句执行机制解析
数据库·postgresql·开源
·云扬·3 小时前
MySQL 8.0 Redo Log 归档与禁用实战指南
android·数据库·mysql
IT邦德3 小时前
Oracle 26ai DataGuard 搭建(RAC到单机)
数据库·oracle
惊讶的猫4 小时前
redis分片集群
数据库·redis·缓存·分片集群·海量数据存储·高并发写
不爱缺氧i4 小时前
完全卸载MariaDB
数据库·mariadb
纤纡.4 小时前
Linux中SQL 从基础到进阶:五大分类详解与表结构操作(ALTER/DROP)全攻略
linux·数据库·sql
jiunian_cn4 小时前
【Redis】渐进式遍历
数据库·redis·缓存