• 新做的NHibernate项目,大家来拍拍砖,同时还要请多多提些问题


    简单说明:

    这个项目的结构,大家来拍拍砖,同时还要请多多提些问题。

    项目不是为了做新闻,由于对新闻比较了解,不同的项目中也用的多,我就想通过这个项目吧新闻功能做的深入完善些,这里也就用新闻做演示用。

    这个结构是根据之前的项目结构,以及院子里不是朋友们的提供的方案、代码等,整合起来的。不敢妄称为啥框架,只是希望对开发的项目能起到一些帮助作用。

    第一次写blog,可能有点乱,大家见谅了。


    使用结构资源

    NHibernate.net
    FluentNHibernate
    Autofac
    MVC
    Jquery

    项目结构

    总体结构

    说明下为啥这样做: 我工作的单位都是变化比较快的公司(命苦呀),很多事情都是比较杂,之前的项目就是模块太多了,维护起来很麻烦,我就想可不可以按照功能模块来划分, 吧一个模块深入,又不影响其他人的工作。。。 所以倒腾了这样的一个结果。(受到SpaceBuilder影响).

    另外一个想法是:今后项目扩展性会好些; 模块的延续行也会提升。像之前在不同的公司,项目中的新闻还要拿出来改啊啥的。 如果这个结果吧新闻模块完善后,其他项目只需要引用改模块即可。



    文章咨询模块


    稍微了解过NHibernate.net应该能开出来大概的结构意思吧? 还是上几段代码吧。

    using System;
    using System.Linq;
    using System.Text;
    
    using Job.FluenCore.Common;
    using Iesi.Collections.Generic;
    
    namespace Job.FluenCore.Article.Model
    {
        public class News : IEntity
        {
            /// <summary>
            /// 文章编号
            /// </summary>
            public virtual int Id { get; set; }
    
            /// <summary>
            /// 新闻类型何种类型
            /// 文章、单页
            /// News.TypeId =NewsClass.TypeId
            /// </summary>
            public virtual NewsClassType TypeId { get; set; }
    
            /// <summary>
            /// News.ClassId =NewsClass.ClassId
            /// </summary>
            public virtual int ClassId { get; set; }
    
            /// <summary>
            /// 访问次数
            /// </summary>
            public virtual int Visits { get; set; }
            /// <summary>
            /// 添加时间
            /// </summary>
            public virtual DateTime AddTime { get; set; }
            /// <summary>
            /// 编辑时间
            /// </summary>
            public virtual DateTime EditTime { get; set; }
            /// <summary>
            /// 文章状态
            /// </summary>
            public virtual NewAuditType ContentStatus { get; set; }
    
    
            /// <summary>
            /// 主题 详细页
            /// </summary>
            public virtual string Title { get; set; }
            /// <summary>
            /// 主题颜色
            /// </summary>
            public virtual string TitleColor { get; set; }
            /// <summary>
            /// 复标题 用于首页等。
            /// </summary>
            public virtual string TitleSub { get; set; }
            /// <summary>
            /// 内容
            /// </summary>
            public virtual string Content { get; set; }
            /// <summary>
            /// 作者
            /// </summary>
            public virtual string Author { get; set; }
            /// <summary>
            /// 简介
            /// </summary>
            public virtual string Introduction { get; set; }
    
            /// <summary>
            /// seo
            /// </summary>
            public virtual string PageTitle { get; set; }
            /// <summary>
            /// seo
            /// </summary>
            public virtual string PageKeywords { get; set; }
            /// <summary>
            /// seo
            /// </summary>
            public virtual string PageDescription { get; set; }
    
            /// <summary>
            /// 置顶
            /// </summary>
            public virtual byte IsTop { get; set; }
            /// <summary>
            /// 推荐
            /// </summary>
            public virtual byte IsRecommend { get; set; }
            /// <summary>
            /// 回复
            /// </summary>
            public virtual byte IsNoComment { get; set; }
            /// <summary>
            /// 文章星级 默认 0
            /// </summary>
            public virtual short Star { get; set; }
            /// <summary>
            /// 文章排序 默认 0
            /// </summary>
            public virtual int ContentOrder { get; set; }
            /// <summary>
            /// 文章Url
            /// </summary>
            public virtual string ContentUrl { get; set; }
    
            /// <summary>
            /// 缩略图 编号
            /// </summary>
            public virtual int IndexImage { get; set; }
            
            /*
            /// <summary>
            /// 文章分类\
            /// 数据库字段=ClassID
            /// </summary>
            public virtual NewsClass Class { get; set; }
            /// <summary>
            /// 文章评论
            /// </summary>
            public virtual ISet<NewsComments> Comments { get; set; } 
            ///// <summary>
            ///// 图片 下载文件
            ///// </summary>
            public virtual ISet<NewsFile> Files { get; set; }
            */
        }
    }
    模型
    using System;
    //using System.Collections.Generic;
    using System.Linq;
    
    using Iesi.Collections.Generic;
    using FluentNHibernate.Mapping;
    using Job.FluenCore.Article.Model;
    
    namespace Job.FluenCore.Article.Mapping
    {
        public class NewsMap : ClassMap<News>
        {
            public NewsMap()
            {
                Table("[t_News]");
                Id(p => p.Id).GeneratedBy.Identity();
                Map(p => p.ClassId).Not.Nullable().Default("0");
                Map(p => p.Title).Not.Nullable().Length(255);
                Map(p => p.TitleColor).Nullable().Length(10);
                Map(p => p.TitleSub).Nullable().Length(255);
                Map(p => p.Content).Nullable().CustomSqlType("text");
                Map(p => p.Author).Nullable().Length(50);
                Map(p => p.Introduction).Nullable().Length(1000);
    
                Map(p => p.PageTitle).Nullable().Length(100);
                Map(p => p.PageKeywords).Nullable().Length(100);
                Map(p => p.PageDescription).Nullable().Length(255);
    
                Map(p => p.IsTop).Not.Nullable().Default("0");
                Map(p => p.IsRecommend).Not.Nullable().Default("0");
                Map(p => p.IsNoComment).Not.Nullable().Default("0");
                Map(p => p.Star).Not.Nullable().Default("0");
                Map(p => p.ContentOrder).Not.Nullable().Default("0");
                Map(p => p.ContentUrl).Nullable().Length(255);
                Map(p => p.IndexImage).Not.Nullable().Default("0");
    
                Map(p => p.Visits).Not.Nullable().Default("0");
                Map(p => p.AddTime).Not.Nullable().Default("getdate()");
                Map(p => p.EditTime).Not.Nullable().Default("getdate()");
                Map(p => p.ContentStatus).CustomType<NewAuditType>();
    
                /*
                References<NewsClass>(p => p.Class)
                    .LazyLoad()
                    .Column("ClassID")
                    .Cascade.All();
                
                HasMany<NewsComments>(p => p.Comments)
                    .AsSet().LazyLoad()
                    .Inverse() //    Inverse="false"(默认):父实体负责维护关联关系     Inverse="true":子实体负责维护关联关系
                    .KeyColumn("NewsID")
                    .Cascade.All();
    
                HasMany<NewsFile>(p => p.Files)
                    .AsSet().LazyLoad()
                    .Inverse() //    Inverse="false"(默认):父实体负责维护关联关系     Inverse="true":子实体负责维护关联关系
                    .KeyColumn("NewsID")
                    .Cascade.All();
                */
            }
        }
    }
    映射
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    
    using Webdiyer.WebControls.Mvc;
    
    using Job.FluenCore.Common;
    using Job.FluenCore.DateFactory;
    using Job.FluenCore.Article.Mapping;
    using Job.FluenCore.Article.Model;
    
    namespace Job.FluenCore.Article.Repository
    {
        #region Interface
        public interface INewsRepository : IRepositoryBase<News>
        {
            void UpdateHitClick(int Id);
        }
        #endregion
    
        public class NewsRepository : RepositoryBase<News>, INewsRepository
        {
            public NewsRepository(IDatabaseFactory databaseFactory)
                : base(databaseFactory)
            {
            }
    
            public void UpdateHitClick(int Id)
            {
                var s = "update [t_News] set Visits=Visits+1 whre Id=:f1";
    
                base.CreateSQLQuery(s)
                    .SetInt32("f1", Id)
                    .ExecuteUpdate();
                //base.Commit();
            }
        }
    }
    数据工厂
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    //using System.Data.Entity.Validation;
    
    using Webdiyer.WebControls.Mvc;
    
    using Job.FluenCore.Common;
    using Job.FluenCore.DateFactory;
    using Job.FluenCore.Article.Model;
    using Job.FluenCore.Article.Repository;
    
    namespace Job.FluenCore.Article.Service
    {
        public interface INewsService : IServiceBase<INewsRepository>
        {
            void UpdateHitClick(int Id, string Ip);
        }
    
        public class NewsService : ServiceBase<INewsRepository>, INewsService
        {
            public NewsService(IUnitOfWork _unitwork, INewsRepository _repository)
                : base(_unitwork, _repository)
            { 
            }
    
            public void UpdateHitClick(int Id, string Ip)
            {
                base.Repository().UpdateHitClick(Id);
    
                /*
                var s = "update [t_News] set Visits=Visits+1,ip=:f2 whre Id=:f1";            
                base.Repository().CreateSQLQuery(s)
                    .SetInt32("f1", Id)
                    .SetString("f2", Ip)
                    .ExecuteUpdate();
                base.Repository().Commit();
                */
            }
    
        }
    }
    数据服务
    using System;
    using System.Collections.Specialized;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    
    using Autofac;
    
    using Job.FluenCore.Article.Mapping;
    using Job.FluenCore.Article.Model;
    using Job.FluenCore.Article.Repository;
    using Job.FluenCore.Article.Service;
    
    namespace Job.FluenCore.Article.Modules
    {
        public class NewsModule : Module
        {
            protected override void Load(ContainerBuilder builder)
            {
                if (builder == null)
                {
                    throw new ArgumentNullException("Job.FluenCore.Article.Modules ContainerBuilder");
                }
    
                builder.RegisterType<NewsService>().As<INewsService>().InstancePerLifetimeScope();
                builder.RegisterType<NewsRepository>().As<INewsRepository>().InstancePerLifetimeScope();
    
                builder.RegisterType<NewsClassService>().As<INewsClassService>().InstancePerLifetimeScope();
                builder.RegisterType<NewsClassRepository>().As<INewsClassRepository>().InstancePerLifetimeScope();
    
    
                base.Load(builder);
            }
    
            public System.Reflection.Assembly GetAssembly()
            {
                return System.Reflection.Assembly.GetExecutingAssembly();
            }
        }
    }
    注册服务
    namespace Job.FluenCore.DateFactory
    {
        public interface IRepositoryBase<T> where T : class
        {
            /// <summary>
            /// 提交事务
            /// </summary>
            void Commit();
    
            /// <summary>
            /// 添加
            /// </summary>
            /// <param name="entity"></param>
            /// <param name="IsNowSave"></param>
            /// <returns></returns>
            T Add(T entity, bool IsNowSave = true);
    
            /// <summary>
            /// 更新
            /// </summary>
            /// <param name="entity"></param>
            /// <param name="IsNowSave"></param>
            void Update(T entity, bool IsNowSave = true);
    
            void SaveOrUpdate(T entity, bool IsNowSave = true);
    
            void Delete(T entity);
            void Delete(int Id, bool Load = false);
            void Delete(Expression<Func<T, bool>> where);
    
            T GetById(int Id);
            T GetById(string Id);
    
            /// <summary>
            /// 根据条件获得模型 Linq语句
            /// </summary>
            /// <param name="where"></param>
            /// <returns></returns>
            T Get(Expression<Func<T, bool>> where);
    
            /// <summary>
            /// 支持 selector where查询
            /// var list= GetMany(o => o.Id > 0).Select(o => o).OrderByDescending(o=>o.AddTime).ToPagedList(pageindex, pagesize);
            /// </summary>
            T Get(Expression<Func<T, bool>> where, Expression<Func<T, T>> selector);
    
            /// <summary>
            /// 支持 select where orderby查询
            /// var list= GetMany().Select(o => o).OrderByDescending(o=>o.AddTime).ToPagedList(pageindex, pagesize);
            /// </summary>
            IQueryable<T> GetMany();
    
            /// <summary>
            /// 支持 select orderby查询
            /// var list= GetMany(o => o.Id > 0).Select(o => o).OrderByDescending(o=>o.AddTime).ToPagedList(pageindex, pagesize);
            /// </summary>
            IQueryable<T> GetMany(Expression<Func<T, bool>> where);
    
            /// <summary>
            /// 支持 where查询
            /// var list= GetList(o => o.Id > 0).Select(o => o).OrderByDescending(o=>o.AddTime).ToPagedList(pageindex, pagesize);
            /// </summary>
            PagedList<T> GetList(Expression<Func<T, bool>> where, int pagesize, int pageindex);
    
            /// <summary>
            /// 支持 select where查询
            /// var list= GetList(o => o.Id > 0).Select(o => o).OrderByDescending(o=>o.AddTime).ToPagedList(pageindex, pagesize);
            /// </summary>
            PagedList<T> GetList(Expression<Func<T, bool>> where, Expression<Func<T, T>> select, int pagesize, int pageindex);
    
            /// <summary>
            /// 支持 select where查询
            /// var list= GetList(o => o.Id > 0).Select(o => o).OrderByDescending(o=>o.AddTime).ToPagedList(pageindex, pagesize);
            /// </summary>
            PagedList<T> GetList(Expression<Func<T, bool>> where, Expression<Func<T, T>> select, Func<T, string> orderName, string sortOrder, int pagesize, int pageindex);
    
            /// <summary>
            /// 支持 select where查询
            /// var list= GetForModel[T](o => o.Id > 0).Select(o => o).OrderByDescending(o=>o.AddTime).ToPagedList(pageindex, pagesize);
            /// </summary>
            IQueryable<TModel> GetForModel<TModel>();
    
            /// <summary>
            /// 支持NHibernate语句,查询、更新、删除
            /// eg .CreateCriteria().add(Expression.like("name", "Fritz%")).add( Expression.between("weight", minWeight, maxWeight))
            /// </summary>
            ICriteria CreateCriteria();
    
            /// <summary>
            /// 支持NHibernate语句,查询、更新、删除
            /// eg .CreateCriteria().add(Expression.like("name", "Fritz%")).add( Expression.between("weight", minWeight, maxWeight))
            /// </summary>
            ICriteria CreateCriteria(string entityName);
            
            /// <summary>
            /// 支持 hql,参数,查询、更新、删除\ 注意不是SQL
            /// hibernate 中createQuery与createSQLQuery两者区别是:前者用的hql语句进行查询,后者可以用sql语句查询|
            /// eg .CreateQuery("from Customer c where c.Name.Firstname=:fn and c.Name.Lastname=:ln").SetString("fn", firstname).SetString("ln", lastname)
            /// </summary>
            IQuery CreateQuery(string queryString);
    
            /// <summary>
            /// 支持 SQL、参数,查询、更新、删除  更新语句请加 ExecuteUpdate|
            /// 注意:需使用正确的 数据表结构字段 
            /// eg CreateSQLQuery("select ID from News where id=:f1 and IsTop>5").SetString("f1", firstname) \ 
            /// eg CreateSQLQuery("{Call sp_Login(?,?) }").SetString(0, "admin").SetString(1, "admin");
            /// </summary>
            ISQLQuery CreateSQLQuery(string queryString);
                 
            /// <summary>
            /// 分页查询(sql分页方式) 统计
            /// 注意:需使用正确的 数据表结构字段 
            /// </summary>
            System.Collections.Generic.IList<TModel> GetListForPaging<TModel>(string tableName, int pageNumber, int pageSize, string orderName, string sortOrder, string CommandText, out int Count);
    
            /// <summary>
            /// 分页查询(sql分页方式) 不统计
            /// 注意:需使用正确的 数据表结构字段 
            /// </summary>
            System.Collections.Generic.IList<TModel> GetListForPaging<TModel>(string tableName, int pageNumber, int pageSize, string orderName, string sortOrder, string CommandText);
    
            /// <summary>
            /// 原生SQL关联查询
            /// 注意:需使用正确的 数据表结构字段 
            /// </summary>
            System.Collections.Generic.IList<TModel> GetListForPaging<TModel>(string strQuery);
    
    
            /// <summary>
            /// 原生SQL关联查询
            /// 注意:需使用正确的 数据表结构字段 
            /// </summary>
            System.Data.DataSet GetDataSet(string strQuery);
    
            /// <summary>
            /// 计算总个数(分页)
            /// </summary>
            int GetCount(string tableName, string commandText);
        }
    数据工厂基础接口
    using System;
    using System.Text;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Mvc.Html;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.Entity;
    using System.ComponentModel;
    
    using Job.FluenCore.Ceche;
    using Webdiyer.WebControls.Mvc;
    using DotNet.Common;
    using Job.FluenCore.Common;
    using Job.FluenCore.Common.MVCFormwork;
    
    using Job.FluenCore.Article;
    using Job.FluenCore.Article.Model;
    using Job.FluenCore.Article.Repository;
    using Job.FluenCore.Article.Service;
    
    namespace Job.Web.Controller
    {
        public class NewsController : BaseWebController
        {
            protected readonly INewsService _NewsService;
            protected readonly INewsClassService _NewClassService;
            
            public NewsController(INewsService NewsService, INewsClassService NewClassService)
            {
                this._NewsService = NewsService;
                this._NewClassService = NewClassService;
            }
    
    
            #region web
            [HttpGet]
            public virtual ActionResult Index(int? page, int intPid = 0, int intStatus = 1)
            {
                //int _page = page ?? GetPageIndex();
                //var stus = (NewsClassStatusType)intStatus;
                //var where = PredicateExtensions.True<News>();
                //if (intStatus == 0 || intStatus == 1) { where = where.And(p => p.Status == stus); }
                //if (intPid > 0) { where = where.And(p => p.ParentId == intPid); }
    
                //var list = _NewsService
                //    .Repository()
                //    .GetList(where, GetPageSize(), _page);
    
                //PageData();
    
                return View();
            }
    
            [HttpGet]
            public virtual ActionResult Detail(int id, int intStatus = 1)
            {
                //var data = _NewsService.Repository().GetById(id);
                //data = data ?? new News();
                //PageData();
                return View();
            }
    
            #endregion
    
    
            #region admin
            /* true 显示错误; false 继续 */
            [LoginAllowView]
            [Description("[详细信息]检查是否存在(add,Update必备)")]
            public ActionResult CheckExist()
            {
                var action = QueryString.Request["action"];
                var ID = QueryString.Request["ID"].GetNum(0);
                var val = QueryString.Request[action];
                var b = false;
                if (!val.IsNullOrWhiteSpace())
                {
                    switch (action)
                    {
                        case "ContentUrl":
                            b = _NewsService.Repository().GetMany(p => p.Id != ID && p.ContentUrl == val).Any();
                            break;
                    }
                }
                b = !b;
                return Content(b.ToString().ToLower());
            }
    
    
            [Description("[Index主页]新闻管理")]
            [ViewPage]
            [DefaultPage]
            public ActionResult AdminIndex(int Type = 0)
            {
                var CurrentTypeID = Type;
                var titel = typeof(NewsClassType).GetEnumDictionaryA().GetValue(CurrentTypeID);
                titel = titel ?? "文章";
                ViewBag.Title = titel + "管理";
                ViewBag.CurrentID = CurrentTypeID;
                return View();
            }
    
            [ViewPage]
            [Description("[详细信息]新闻详细信息(add,Update,Detail必备)")]
            public ActionResult AdminDetail()
            {
                ViewBag.IsView = (QueryString.Request["IsView"] == "1") ? 1 : 0;
                ViewBag.CurrentID = QueryString.Request["ID"].GetNum(0);
                return View();
            }
    
            [Description("[Get Json]获取新闻Json")]
            [LigerUIExceptionResult]
            public ActionResult AdminGet(int? id)
            {
                var _id = id ?? 0;
                var data = _NewsService.Repository().Get(o => o.Id == _id);
                data = data ?? new News();
                return this.JsonFormat(data, true, "获取[新闻]");
            }
    
            [Description("[系统]添加动作")]
            [LigerUIExceptionResult]
            public ActionResult AdminAdd()
            {
                News model = new News();
                this.TryUpdateModel(model);
                model.Id = 0;
                model.AddTime = model.AddTime == null ? DateTime.UtcNow : model.AddTime;
                model.EditTime = model.AddTime == null ? DateTime.UtcNow : model.EditTime;
                return AdminSave(model);
            }
    
            [Description("[系统]修改动作")]
            [LigerUIExceptionResult]
            public ActionResult AdminUpdate()
            {
                News model = new News();
                this.TryUpdateModel(model);
                model.AddTime = model.AddTime == null ? DateTime.UtcNow : model.AddTime;
                model.EditTime = model.AddTime == null ? DateTime.UtcNow : model.EditTime;
                return AdminSave(model);
            }
    
            [Description("[Delete]页面删除请求")]
            [LigerUIExceptionResult]
            public ActionResult AdminDelete(int Id)
            {
                var status = true;
                _NewsService.Repository().Delete(Id);
                return this.JsonFormat(null, status, "删除[新闻]");
            }
            
            [Description("[gridRequest请求]获取新闻")]
            [LoginAllowView]
            public ActionResult AdminGetGrid()
            {
                var gridRequest = new LigerUIGridRequest(HttpContext);
                var where = gridRequest.Where;
                var parms = FilterHelper.GetFilterTanslateQuery(ref where);
    
                int Count = _NewsService.Repository().GetMany()
                    .Where(where, parms)
                    .Count();
    
                var data = _NewsService.Repository().GetMany()
                    .Where(where, parms)
                    .OrderBy(gridRequest.SortName + " " + gridRequest.SortOrder)
                    .Skip((gridRequest.PageNumber - 1) * gridRequest.PageSize)
                    .Take(gridRequest.PageSize)
                    .Select(o => new
                    {
                        Id = o.Id,
                        Title = o.Title,
                        TitleColor = o.TitleColor,
                        EditTime = o.EditTime,
                        ContentUrl = o.ContentUrl,
                        ContentStatus = o.ContentStatus,
                        IndexImage = o.IndexImage,
                        IsNoComment = o.IsNoComment,
                        IsRecommend = o.IsRecommend,
                        IsTop = o.IsTop,
                        Visits = o.Visits,
                        Star = o.Star,
                        AddTime = o.AddTime,
                        //TypeId = o.TypeId,
                        ClassId = o.ClassId
                    })
                    .ToList();
    
                var grid = new LigerUIGrid();
                grid.Rows = data;
                grid.Total = Count;
                return this.JsonFormat(grid);
            }
            #endregion
    
            #region heper
    
            [NonAction]
            public ActionResult AdminSave(News model)
            {
                var status = SaveNewsClass(model, model.Id > 1);
                return this.JsonFormat(null, status, "保存[新闻分类]");
            }
    
            public bool SaveNewsClass(News model, bool IsEdit = true)
            {
                if (model == null) { return false; }
    
                model.AddTime = DateTimeHelper.DateTimeYeas(model.AddTime);
                model.EditTime = DateTimeHelper.DateTimeYeas(model.EditTime);
                model.ClassId = model.ClassId < 0 ? QueryString.Request["ClassId"].GetNum(0) : model.ClassId;
    
                if (IsEdit)
                {
                    _NewsService.Repository().Update(model);
                }
                else
                {
                    _NewsService.Repository().Add(model);
                }
                return true;
            }
            #endregion
        }
    }
    新闻资讯操作实例


    同时我们对Linq的扩展也可以流畅的对数据操作。


     

    不过这里也有一些问题需要改进;
    1 目前的动态自定义查询问题,
    让使用者自己可以控制查询字段、条件、显示结果字段、排序等;(特别是在管理后台,jquery + ligerUI 结合使用, 管理者可以按需查询等, )
    如:
    _NewsService.Repository().GetMany()
                    .Where("条件")
                    .OrderBy("id desc")
                    .Take(10)
                    .Skip(10)
                    .Select("New(Id,Title)");

    2 跨表问题(目前可用试图、存储过程处理);

    3 数据验证问题;

    4 结构关系的调整处理。

    未完。。。。 
    先下班了。。。 晚上再写。


  • 相关阅读:
    IDEA一些介绍
    win32控制台程序使用CfileDialog进行文件读取
    判断GPS、网络是否开启
    使用高德地图SDK获取定位信息
    #子线程消息被阻挡
    strlen与sizeof
    C++中路径操作
    20155235 《网络攻防》 实验一 逆向及Bof基础实践说明
    20155235 《信息安全系统设计基础》课程总结
    2017-2018-1 20155235 《信息安全系统设计基础》第十四周学习总结
  • 原文地址:https://www.cnblogs.com/colvinliu/p/Job_FluenCore_Peoject.html
Copyright © 2020-2023  润新知