1.概述
所谓操作日志,就是某人对指定模块的指定对象进行操作的记录,在某种情况下,可能会有显示本次操作对哪些数据字段进行啦了动,或者是操作前后该模型的数据比对情况。针对于这类需求,想要查看某条操作记录具体改动项信息的时候,对于前端来讲,他不需要知道这个对象的具体类型及各个字段的意义的汉字描述,他们只需要循环遍历,将所有的数据拼到一个div容器里即可。后台该如何去设计才能做到最简化的操作那?第一种情况可以用枚举的形式,来标记每种模型的类型,在每次记录日志的时候,可以将当前枚举也记录进去(数据库/nosql),这样的话,在查看每种记录变动的详情时,就可以根据每种类型封装不同的拼接数据的策略,最终以统一的格式返回给前端。但是,这种操作起来并不是很理想,没实现一种模块的操作都要实现对应的表单项拼接策略,显然以后维护起来并不是十分的完美。那么,能不能通过反射的形式来根绝具体的模型名称动态的获取模型类型,并根据具体的类型和json数据来形成对象,从而完成表单项的构建那??答案是:可以的(c#中可以根据Type type = Type.GetType(FullTypeName);类实现)。下面我们就先实现一下获取当前操作模型具体操作操作记录。
2.需求分析及实现
1.前端需要展示成这样:
2.后台代码设置
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace ConsoleApp1.OpearorLog.Entity 9 { 10 public class ColumnRenark : Attribute 11 { 12 /// <summary> 13 /// 字段名称 14 /// </summary> 15 public string ColumnName { get; set; } 16 /// <summary> 17 /// 字段类型 18 /// </summary> 19 public ColumnType ColumnType { get; set; } 20 21 22 public ColumnRenark() { } 23 } 24 public enum ColumnType 25 { 26 [Description("基础类型(int/string...)")] 27 Basic = 0, 28 [Description("类类型")] 29 Class = 1, 30 [Description("字典类型")] 31 Dic = 2, 32 [Description("列表类型")] 33 Lst = 3, 34 35 36 } 37 }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApp1.OpearorLog.Entity { /// <summary> ///行记录 /// </summary> public class RowRecord { /// <summary> /// 字段语义 /// </summary> public string Key { get; set; } /// <summary> /// 字段对应值 /// </summary> public string Value { get; set; } /// <summary> /// 所属层级 /// </summary> public int Lev { get; set; } /// <summary> /// 包含的子项 /// </summary> public List<List<RowRecord>> Child { get; set; } public RowRecord() { Child = new List<List<RowRecord>>(); } } }
1 using Newtonsoft.Json; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Runtime.InteropServices; 6 using System.Text; 7 using System.Threading.Tasks; 8 9 namespace ConsoleApp1 10 { 11 public class OperatorLogContext 12 { 13 #region attr 14 private List<RowRecord> RowRecords { get; set; } 15 private object Obj { get; set; } 16 public string FullTypeName { get; set; } 17 public string JsonStr { get; set; } 18 #endregion 19 20 public OperatorLogContext(string fullTypeName,string jsonStr) 21 { 22 FullTypeName = fullTypeName; 23 JsonStr = jsonStr; 24 RowRecords = new List<RowRecord>(); 25 Type type = Type.GetType(FullTypeName); 26 Obj = JsonConvert.DeserializeObject(JsonStr,type); 27 } 28 public OperatorLogContext() 29 { 30 Type type = Type.GetType(FullTypeName); 31 Obj = JsonConvert.DeserializeObject(JsonStr, type); 32 } 33 /// <summary> 34 /// 构建表单项 35 /// </summary> 36 /// <returns></returns> 37 public List<RowRecord> BuildForm() 38 { 39 return Build(Obj); 40 } 41 #region private 42 private List<RowRecord> Build(object obj, RowRecord parentRow = null, List<RowRecord> childRow = null, int lev = 0) 43 { 44 //1.当前类型 45 Type currentType = obj.GetType(); 46 //2.获取当下模型的所有属性字段 47 var props = currentType.GetProperties(); 48 foreach (var prop in props) 49 { 50 RowRecord rowRecord = new RowRecord(); 51 rowRecord.Lev = lev; //设置当前树形级别 52 //3.获取当下属性字段下的所有自定义属性 53 var attr = prop.CustomAttributes.FirstOrDefault(o => o.AttributeType == typeof(ColumnRenark)); 54 if (attr == null) 55 { 56 continue; 57 } 58 else 59 { 60 foreach (var named in attr.NamedArguments) 61 { 62 if (named.MemberName == "ColumnName") 63 { 64 rowRecord.Key = named.TypedValue.Value.ToString(); 65 } 66 else if (named.MemberName == "ColumnType") 67 { 68 ColumnType columnType = (ColumnType)Enum.Parse(typeof(ColumnType), named.TypedValue.Value.ToString()); 69 switch (columnType) 70 { 71 case ColumnType.Basic: 72 rowRecord.Value = prop.GetValue(obj).ToString(); 73 break; 74 case ColumnType.Class: 75 Build(prop.GetValue(obj), rowRecord, new List<RowRecord>()); 76 break; 77 case ColumnType.Lst: 78 var currentLst = (IEnumerable<object>)prop.GetValue(obj); 79 80 foreach (var opt in currentLst) 81 { 82 83 Build(opt, rowRecord, new List<RowRecord>()); 84 } 85 break; 86 } 87 } 88 } 89 } 90 91 if (parentRow != null) 92 { 93 rowRecord.Lev = parentRow.Lev + 1; 94 childRow.Add(rowRecord); 95 } 96 else 97 { 98 RowRecords.Add(rowRecord); 99 } 100 } 101 if (childRow != null) 102 { 103 parentRow.Child.Add(childRow); 104 } 105 return RowRecords; 106 } 107 #endregion 108 109 } 110 }
using Newtonsoft.Json; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Reflection; using System.Text; namespace ConsoleApp1 { class Program { static void Main(string[] args) { User user = new User(); user.Age = 20; user.Name = "lsh"; user.Book = new Book() { Name = "c#从入门指南", Desc = "书籍描述" }; user.Hobby = new List<Hobby>() { new Hobby() { Name = "读书", Rate = "89%" }, new Hobby() { Name = "旅游", Rate = "33%" } }; string json = JsonConvert.SerializeObject(user); var lst = new OperatorLogContext("ConsoleApp1.User", json).BuildForm(); Console.ReadKey(); } } public class User { [ColumnRenark(ColumnName = "姓名", ColumnType = ColumnType.Basic)] public string Name { get; set; } [ColumnRenark(ColumnName = "年龄", ColumnType = ColumnType.Basic)] public int Age { get; set; } [ColumnRenark(ColumnName = "书籍", ColumnType = ColumnType.Class)] public Book Book { get; set; } [ColumnRenark(ColumnName = "爱好", ColumnType = ColumnType.Lst)] public List<Hobby> Hobby { get; set; } } public class Hobby { [ColumnRenark(ColumnName = "爱好名", ColumnType = ColumnType.Basic)] public string Name { get; set; } [ColumnRenark(ColumnName = "比率", ColumnType = ColumnType.Basic)] public string Rate { get; set; } } /// <summary> /// 行元素 /// </summary> public class RowRecord { public string Key { get; set; } public string Value { get; set; } public int Lev { get; set; } public List<List<RowRecord>> Child { get; set; } public RowRecord() { Child = new List<List<RowRecord>>(); } } public class Book { [ColumnRenark(ColumnName = "书名", ColumnType = ColumnType.Basic)] public string Name { get; set; } [ColumnRenark(ColumnName = "书描述", ColumnType = ColumnType.Basic)] public string Desc { get; set; } } /// <summary> /// 字段标记属性 /// </summary> public class ColumnRenark : Attribute { /// <summary> /// 字段名称 /// </summary> public string ColumnName { get; set; } /// <summary> /// 字段类型 /// </summary> public ColumnType ColumnType { get; set; } public ColumnRenark() { } } public enum ColumnType { [Description("基础类型(int/string...)")] Basic = 0, [Description("类类型")] Class = 1, [Description("字典类型")] Dic = 2, [Description("列表类型")] Lst = 3, } }
3.测试结果