实际需求:业务上的一个需求,数据库表A中的B字段存放的是该条数据的一些标签,标签存在两级【即一级标签和二级标签】, 现在要是实现将这些标签统计到报表中,一级标签作为表头,二级标签作为填充值。

由于之前的报表每增加一个列都需要去数据库表中增加这个字段名称,然后代码中写统计逻辑,这样才能实现, 这样做的好处是只需要后端就可以完成报表的一个字段的增加,不需要等待前端去增加这了列名了, 后来的低代码就是这样的逻辑,可以简单的编写sql,去实现统计逻辑。实现快速开发,节省开发时间。

回归正题,由于每个数据的标签是不确定的,那就导致了每个数据要统计的列是不一样的,这里的做法是将全部的标签都进行增加统计【用户不需要的话还有个功能取消掉某列的统计就可以了】,当用户新增一个标签的时候,只要去查看报表,检测到这个标签的名称不在列统计当中,就会自动的去新增一条列统计字段,每条数据循环的时候要将给其赋值,有对应的二级标签那就展示二级标签名称,没有的话那也要新增一个列,但是数据填写空字符串。【这一步主要是防止在下载报表的时候报错,以防列数对应不上而报错】。

有两个核心的点,第一个:统计的列名为什么还要在数据库表中再存一份? 其实也可以将要统计的列名放入代码中写死,同样可以实现报表数据的展示,但是所有的客户都会展示这么多的列统计,没有办法支持各个客户对统计列的筛选【这个就是原因】。

第二个:代码中的列数据如何和数据库表中的列名对应上的? 数据库表中存入统计列的列中文名和列英文名, 在代码中一个字段的定义肯定是英文的,这样就可以知道如何赋值了,【列中文名是给客户展示的】。

这里是说了一下实现报表的列自适应原理, 至于报表的操作直接去nuget管理中下载NPOI库,对于报表的一些操作什么的可以直接网查,封装起来使用就好了。

下边附上实现的代码,供大家参考:

自己封装的NPOI帮助类, 大家可以根据自己的实际需要去封装:

点击查看代码
public class NPOIHelper
{
/// <summary>
/// 1. 创建workbook
/// 1.1创建详细信息DocumentSummaryInformation和SummaryInformation
/// 2. 创建工作簿CreateSheet
/// 3. 表头设置
/// 4. 生成字节数组
/// </summary>
/// <param name="excelSheets"></param>
public static byte[] CreateExcel<T>(Dictionary<string, List<T>> excelSheets)
{
HSSFWorkbook workbook = new HSSFWorkbook(); //创建workbook
SetSummaryInformation(workbook);
foreach (var origin in excelSheets)
{
CreateSheet(workbook, origin.Value, origin.Key); //创建工作簿
}
byte[] data = null; //生成字节数组
using (MemoryStream ms = new MemoryStream())
{
workbook.Write(ms);
ms.Flush();
ms.Position = 0;
data = ms.ToArray();
}
return data;
} /// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="K">扩展列的值,定保跟进历史</typeparam>
/// <param name="excelSheets"></param>
/// <param name="extendKV">key是扩展列的列名</param>
/// <returns></returns>
public static byte[] CreateExcelExtend<T, K>(Dictionary<string, List<T>> excelSheets, Dictionary<string, int> extendKV)
{
HSSFWorkbook workbook = new HSSFWorkbook(); //创建workbook
SetSummaryInformation(workbook);
foreach (var origin in excelSheets)
{
CreateSheetExtend<T, K>(workbook, origin.Value, extendKV, origin.Key); //创建工作簿
}
byte[] data = null; //生成字节数组
using (MemoryStream ms = new MemoryStream())
{
workbook.Write(ms);
ms.Flush();
ms.Position = 0;
data = ms.ToArray();
}
return data;
} /// <summary>
/// 创建excel
/// </summary>
/// <returns></returns>
public static IWorkbook CreateExcelOnly()
{
HSSFWorkbook workbook = new HSSFWorkbook(); //创建workbook
SetSummaryInformation(workbook);
return workbook;
} /// <summary>
/// 追加页签
/// </summary>
/// <typeparam name="T">数据实体类型</typeparam>
/// <param name="workbook">excel</param>
/// <param name="sheetVlaue"></param>
/// <param name="sheetName"></param>
/// <returns></returns>
public static IWorkbook AppendSheet<T>(IWorkbook workbook, IEnumerable<T> sheetVlaue, string sheetName = null)
{
CreateSheet(workbook, sheetVlaue, sheetName);
return workbook;
} /// <summary>
/// 获取excel字节
/// </summary>
/// <param name="workbook">excel</param>
/// <returns></returns>
public static byte[] GetExcelData(IWorkbook workbook)
{
byte[] data = null; //生成字节数组
using (MemoryStream ms = new MemoryStream())
{
workbook.Write(ms);
ms.Flush();
ms.Position = 0;
data = ms.ToArray();
} return data;
} /// <summary>
/// 1. 创建workbook
/// 1.1创建详细信息DocumentSummaryInformation和SummaryInformation
/// 2. 创建工作簿CreateSheet
/// 3. 表头设置
/// 4. 生成字节数组
/// </summary>
/// <param name="excelSheets"></param>
public static byte[] AppendExcel<T>(IWorkbook workbook, Dictionary<string, List<T>> excelSheets)
{
foreach (var origin in excelSheets)
{
CreateSheet(workbook, origin.Value, origin.Key); //创建工作簿
} byte[] data = null; //生成字节数组
using (MemoryStream ms = new MemoryStream())
{
workbook.Write(ms);
ms.Flush();
ms.Position = 0;
data = ms.ToArray();
} return data;
} /// <summary>
/// 详细信息
/// </summary>
/// <param name="workbook"></param>
public static void SetSummaryInformation(HSSFWorkbook workbook)
{
DocumentSummaryInformation dsi = PropertySetFactory.CreateDocumentSummaryInformation(); //创建详细信息:属性、来源等
dsi.Company = "xxxxxx有限公司";
dsi.Category = "xx科技";
dsi.Manager = "xx科技";
workbook.DocumentSummaryInformation = dsi;
SummaryInformation si = PropertySetFactory.CreateSummaryInformation();
si.Subject = "xx科技";
si.Title = "xx科技";
si.ApplicationName = "xx科技";
si.Author = "xx科技";
si.LastAuthor = "xx科技";
si.Comments = "xx科技";
si.CreateDateTime = DateTime.Now.AddMonths(-2);
//创建好的对象赋给hssfWorkbook,这样才能保证这些信息被写入文件
workbook.SummaryInformation = si;
} /// <summary>
/// 创建工作簿
/// </summary>
/// <param name="workbook"></param>
/// <param name="rowList"></param>
/// <param name="sheetName"></param>
public static void CreateSheet<T>(IWorkbook workbook, IEnumerable<T> rowList, string sheetName = null)
{
var newname = NewSheetName(workbook, sheetName);
ISheet sheet = workbook.CreateSheet(newname);//创建工作簿
Type tp = typeof(T);
var proInfos = tp.GetProperties();//获得实体对象的属性集合
SetRowHead(sheet, proInfos); if (rowList == null || rowList.Count() == 0)
{
return;
}
int i = 1;
foreach (var row in rowList)
{
CreateRow(row, proInfos, sheet, i++);
} } /// <summary>
/// 创建工作簿
/// </summary>
/// <param name="workbook"></param>
/// <param name="rowList"></param>
/// <param name="sheetName"></param>
/// <param name="dics"></param>
public static void CreateSheetClue<T>(IWorkbook workbook, IEnumerable<T> rowList, Dictionary<string, string> dics, string sheetName = null)
{
var newname = NewSheetName(workbook, sheetName);
ISheet sheet = workbook.CreateSheet(newname);//创建工作簿
Type tp = typeof(T);
var proInfos = tp.GetProperties();//获得实体对象的属性集合
SetRowHeadClue(sheet, dics, proInfos); if (rowList == null || rowList.Count() == 0)
{
return;
}
int i = 1;
foreach (var row in rowList)
{
CreateRowClue(row, sheet, i++, dics);
} } /// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="K">扩展列的值,跟进历史</typeparam>
/// <param name="workbook"></param>
/// <param name="rowList"></param>
/// <param name="extendKV">key为扩展列的列名</param>
/// <param name="sheetName">工作簿的名称</param>
private static void CreateSheetExtend<T, K>(IWorkbook workbook, IEnumerable<T> rowList, Dictionary<string, int> extendKV, string sheetName = null)
{
var newname = NewSheetName(workbook, sheetName);
ISheet sheet = workbook.CreateSheet(newname);//创建工作簿
Type tp = typeof(T);
var proInfos = tp.GetProperties();//获得实体对象的属性集合
SetRowHeadExtend(sheet, proInfos, extendKV);
if (rowList == null || rowList.Count() == 0)
{
return;
}
int i = 1;
Type ktp = typeof(K);//扩展列的属性
var kProInfos = ktp.GetProperties();
var extendCount = extendKV == null ? 0 : extendKV.Count();//扩展列的总数量
foreach (var row in rowList)
{
CreateRowExtend<T, K>(row, proInfos, sheet, i++, kProInfos, extendCount);
} } /// <summary>
/// 获取新页签名防止重复
/// </summary>
/// <param name="workbook"></param>
/// <param name="sheetName"></param>
/// <returns></returns>
private static string NewSheetName(IWorkbook workbook, string sheetName = null)
{
if (string.IsNullOrWhiteSpace(sheetName))
{
sheetName = "Sheet";
} if (workbook.NumberOfSheets == 0)
{
return sheetName;
} List<string> nameList = new List<string>();
for (int i = 0; i < workbook.NumberOfSheets; i++)
{
nameList.Add(workbook.GetSheetName(i));
} var newname = sheetName;
int endidx = 0;
while (nameList.Contains(newname, StringComparer.OrdinalIgnoreCase))
{
newname = newname + endidx;
} return newname;
} /// <summary>
/// 创建单元格
/// </summary>
/// <param name="origin"></param>
/// <param name="proInfos"></param>
/// <param name="sheet"></param>
/// <param name="rowIndex"></param>
private static void CreateRow<T>(T origin, PropertyInfo[] proInfos, ISheet sheet, int rowIndex)
{
IRow row = sheet.CreateRow(rowIndex);//创建行
for (int j = 0; j < proInfos.Length; j++)
{
var cell = row.CreateCell(j);
var proInfo = proInfos[j];
//CellStyleAlignment(cell, proInfo);//设置水平对齐方式
object val = proInfo.GetValue(origin);//获取该属性的Value值
if (val == null)
{
continue;
}
var cellValue = val == null ? "" : val.ToString();
cell.SetCellValue(cellValue);//创建单元格、并且赋值
}
} /// <summary>
/// 创建单元格
/// </summary>
/// <param name="origin"></param>
/// <param name="sheet"></param>
/// <param name="rowIndex"></param>
private static void CreateRowClue<T>(T origin, ISheet sheet, int rowIndex, Dictionary<string, string> dics)
{
IRow row = sheet.CreateRow(rowIndex);//创建行
int i = 0;
Dictionary<string, string> pairs = JsonConvert.DeserializeObject<Dictionary<string, string>>(origin.ToJson()); foreach (var item in dics)
{
var cell = row.CreateCell(i);
string result = item.Key.Substring(0, 1).ToLower() + item.Key.Substring(1, item.Key.Length - 1);
object val = pairs[$"{result}"];
var cellValue = val == null ? "" : val.ToString();
cell.SetCellValue(cellValue);//创建单元格、并且赋值
i++;
}
} /// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="K"></typeparam>
/// <param name="origin"></param>
/// <param name="proInfos"></param>
/// <param name="sheet"></param>
/// <param name="rowIndex"></param>
/// <param name="kProInfos">扩展列的属性</param>
/// <param name="extendCount">扩展列的总数量</param>
private static void CreateRowExtend<T, K>(T origin, PropertyInfo[] proInfos, ISheet sheet, int rowIndex, PropertyInfo[] kProInfos, int extendCount)
{
IRow row = sheet.CreateRow(rowIndex);//创建行
for (int j = 0; j < proInfos.Length; j++)
{
var proInfo = proInfos[j];
object val = proInfo.GetValue(origin);//获取该属性的Value值
if (val != null && typeof(List<K>) == val.GetType())
{
SetExtendCellValue<K>(row, val, kProInfos, j, extendCount); //扩展列的类型
continue;
}
var cellValue = val == null ? "" : val.ToString();
var cell = row.CreateCell(j);
cell.SetCellValue(cellValue);//创建单元格、并且赋值
} } private static void SetExtendCellValue<K>(IRow row, object val, PropertyInfo[] kProInfos, int j, int extendCount)
{
if (val == null || extendCount == 0)
{
return;//没有值,则不创建单元格
}
var kk = (List<K>)val;
foreach (var kkk in kk)
{
for (int i = 0; i < kProInfos.Length; i++, j++)
{
var cellK = row.CreateCell(j);
var proInfoK = kProInfos[i];
var valK = proInfoK.GetValue(kkk);
var cellValueK = valK == null ? "" : valK.ToString();
cellK.SetCellValue(cellValueK);//创建单元格、并且赋值
} }
//不满足数量的也不创建单元格
} /// <summary>
/// 设置单元格的对齐方式
/// </summary>
private static void CellStyleAlignment(ICell cell, PropertyInfo proInfo)
{
if (proInfo.Name == "FollowUpHistory" || proInfo.Name == "MaintainFollowUpHistory")
{
cell.CellStyle.Alignment = HorizontalAlignment.Fill;//水平对齐方式:填充
} //该方法设置填充,耗时较长
//Type attType = typeof(HorizontalAlignmentAttribute);
//var attr = Attribute.GetCustomAttribute(proInfo, attType);
//if (attr == null)
//{
// return;
//}
//var haAttr = (HorizontalAlignmentAttribute)attr;
//if (haAttr==null)
//{
// return;
//}
//if (haAttr.Fill)
//{
// cell.CellStyle.Alignment = HorizontalAlignment.Fill;//水平对齐方式:填充
//}
}
/// <summary>
/// 设置表头
/// </summary>
/// <param name="sheet"></param>
/// <param name="proInfos"></param>
private static void SetRowHead(ISheet sheet, PropertyInfo[] proInfos)
{
IRow rowHead = sheet.CreateRow(0);
for (int k = 0; k < proInfos.Length; k++)
{
var proInfo = proInfos[k];
string name = proInfo.Name; //获取T的字段名称 //string d=proInfo.Attributes.Description
string des = ((DescriptionAttribute)Attribute.GetCustomAttribute(proInfo, typeof(DescriptionAttribute))).Description; //获取T的字段名称的描述
rowHead.CreateCell(k).SetCellValue(des);//创建单元格、并且赋值
} } /// <summary>
/// 设置表头
/// </summary>
/// <param name="sheet"></param>
/// <param name="proInfos"></param>
private static void SetRowHeadClue(ISheet sheet, Dictionary<string, string> dics, PropertyInfo[] proInfos)
{
IRow rowHead = sheet.CreateRow(0);
int i = 0;
foreach (var item in dics)
{
string name = item.Key; //获取T的字段名称
string des = item.Value; //获取T的字段名称的描述
rowHead.CreateCell(i).SetCellValue(des);//创建单元格、并且赋值
i++;
}
//for (int k = 0; k < dics.Count; k++)
//{
// var proInfo = proInfos[k];
// string name = dics[k].Key; //获取T的字段名称 // //string d=proInfo.Attributes.Description
// string des = ((DescriptionAttribute)Attribute.GetCustomAttribute(proInfo, typeof(DescriptionAttribute))).Description; //获取T的字段名称的描述
// rowHead.CreateCell(k).SetCellValue(des);//创建单元格、并且赋值
//} } /// <summary>
///
/// </summary>
/// <param name="sheet"></param>
/// <param name="proInfos"></param>
/// <param name="extendProInfos">扩展列</param>
private static void SetRowHeadExtend(ISheet sheet, PropertyInfo[] proInfos, Dictionary<string, int> extendProInfos)
{
IRow rowHead = sheet.CreateRow(0);
for (int k = 0; k < proInfos.Length; k++)
{
var proInfo = proInfos[k];
string name = proInfo.Name; //获取T的字段名称
var attr = Attribute.GetCustomAttribute(proInfo, typeof(DescriptionAttribute));
if (attr == null)
{
continue;//没有DescriptionAttribute标签,则不生成列
}
var desAttr = (DescriptionAttribute)attr;
if (desAttr == null)
{
continue;//没有DescriptionAttribute标签,则不生成列
}
string des = desAttr.Description; //获取T的字段名称的描述
rowHead.CreateCell(k).SetCellValue(des);//创建单元格、并且赋值
} //增加扩展列(随着extendProInfos的大小动态生成,列名为extendProInfos的key)
if (extendProInfos == null || !extendProInfos.Any())
{
return;//不包含扩展列
}
var index = rowHead.Count();
foreach (var kv in extendProInfos)
{
if (string.IsNullOrWhiteSpace(kv.Key))
{
continue;
}
rowHead.CreateCell(index).SetCellValue(kv.Key);
index += 1;
}
}
}

下边是下载报表字段对应的代码:

点击查看代码
    List<Dictionary<string, string>> CompanyAccidentDatas = new List<Dictionary<string, string>>();

    CompanyAccidentDatas = companyAccident.Data.CompanyAccidentDatas;//自己要填充的数据源
List<ExpandoObject> dynamiccompanyAccidents = new List<ExpandoObject>(); foreach (var x in CompanyAccidentDatas)
{
dynamic dynamiccompanyAccident = new ExpandoObject();
foreach (var item in x)
{
((IDictionary<string, object>)dynamiccompanyAccident)[item.Key] = item.Value;
}
dynamiccompanyAccidents.Add(dynamiccompanyAccident);
}
if (dynamiccompanyAccidents != null && dynamiccompanyAccidents.Count > 0)
{
string token = _httpContext.HttpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", "");
var jwtToken = new JwtSecurityTokenHandler().ReadJwtToken(token);
string Employeed = await Task.Run(() =>
{
return jwtToken.Claims.FirstOrDefault(x => x.Type == "employeeId")?.Value;
});
long employeeId = Convert.ToInt64(Employeed);
List<LxbTableHeader> tableHeaders = new List<LxbTableHeader>();
using (var dbContext = new ClueContext(CompId))
{
tableHeaders = dbContext.LxbTableHeader.Where(x => x.IsDel == 0 && x.CompId == CompId && x.EmployeeId == employeeId && x.TableName == "xx报表" && x.IsShow == true).OrderBy(x => x.Sort).ToList();
}
Dictionary<string, string> dics = new Dictionary<string, string>();
if (tableHeaders != null && tableHeaders.Count > 0)
{
tableHeaders.ForEach(item =>
{
dics.Add(item.ColumnEName, item.ColumnCName); });
}
NPOIHelper.CreateSheetClue(workbook, dynamiccompanyAccidents, dics, "报表xx");
}
}

下边是获取一条数据标记的标签

点击查看代码
if (tempClue != null && !string.IsNullOrEmpty(tempClue.Label))
{
List<string> tempLabels = tempClue.Label.Split(',').ToList();
List<string> secondLabelIdsStr = secondLabelIds.Select(g => g.ToString()).ToList();
List<string> interLabelIdsStr = secondLabelIdsStr.Intersect(tempLabels).ToList();
if (interLabelIdsStr != null && interLabelIdsStr.Count > 0)
{
List<int> interLabelIds = interLabelIdsStr.Select(g => Convert.ToInt32(g)).ToList(); interLabelIds.ForEach(async g =>
{
LxbLabelConfig tempsecond = labels.Where(x => x.Id == g).FirstOrDefault();
LxbLabelConfig tempfirst = labels.Where(x => x.Id == tempsecond.GroupId).FirstOrDefault();
LxbTableHeader tempTable = new LxbTableHeader();
using (var dbContext = new ClueContext(CompId))
{
tempTable = dbContext.LxbTableHeader.Where(x => x.ColumnCName == tempfirst.LabelName && x.CompId == CompId && x.EmployeeId == employeeId && x.IsDel == 0).FirstOrDefault();
}
if (tempTable != null)
{
if (dic.Keys.Contains(tempTable.ColumnEName))
{
dic[tempTable.ColumnEName] = $"{dic[tempTable.ColumnEName]},{tempsecond.LabelName}";
}
else
dic.Add(tempTable.ColumnEName, tempsecond.LabelName);
}
}); }
} if (customLabels != null && customLabels.Count > 0)
{
customLabels.ForEach(async g =>
{ LxbTableHeader tempTable = new LxbTableHeader();
using (var dbContext = new ClueContext(CompId))
{
tempTable = dbContext.LxbTableHeader.Where(x => x.ColumnCName == g.LabelName && x.CompId == CompId && x.EmployeeId == employeeId && x.IsDel == 0).FirstOrDefault();
} if (tempTable != null)
{
if (!dic.Keys.Contains(tempTable.ColumnEName))
dic.Add(tempTable.ColumnEName, "");
} });
}

几个主要的逻辑代码,这里已经附上,有疑问的可以私信探讨,共同进步ヽ()人()ノ

C# .netcore NPOI库 实现报表的列自适应删减的更多相关文章

  1. C# 使用 NPOI 库读写 Excel 文件

    NPOI 是开源的 POI 项目的.NET版,可以用来读写Excel,Word,PPT文件.在处理Excel文件上,NPOI 可以同时兼容 xls 和 xlsx.官网提供了一份 Examples,给出 ...

  2. C#项目中操作Excel文件——使用NPOI库

    转载自:http://blog.csdn.net/dcrmg/article/details/52356236# 感谢-牧野- 实际C#项目中经常会涉及到需要对本地Excel文件进行操作,特别是一些包 ...

  3. NPOI库读写Excel文件

    //首先Nuget安装NPOI库using System; using System.Data; using System.IO; using NPOI.HSSF.UserModel; using N ...

  4. 用awk检查报表的列数

    用awk检查报表的列数 前提当然是报表都有相同数量的列 less yourfile|awk ‘{print NF;exit;}’ NF是awk的内置变量,表示当前记录里域的个数,不难看出,这个命令实际 ...

  5. 【MM系列】SAP库龄报表逻辑理解

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[MM系列]SAP库龄报表逻辑理解   第一篇 ...

  6. 用NPOI操作EXCEL-锁定列CreateFreezePane()

    public void ExportPermissionRoleData(string search, int roleStatus) { var workbook = new HSSFWorkboo ...

  7. 【MM系列】SAP SAP库龄报表逻辑理解

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[MM系列]SAP SAP库龄报表逻辑理解   ...

  8. [CSS布局]3列布局:左右两列固定宽度、中间列自适应满宽

    一种常见的3列布局,左右两列固定宽度.中间列自适应满宽.整个网页不出现横向滚动条 纯CSS实现 效果图: 代码: <!DOCTYPE html> <html lang="e ...

  9. DataGridView列自适应宽度

    来源:http://www.cnblogs.com/wolf-sun/p/3480104.html 在做winform项目中,数据控件DataGridView的使用多多少少是会用到的,如果不设置它的属 ...

  10. css 两列自适应布局的4种思路

    前面的话 前面已经介绍过css 两列布局中单列定宽单列自适应布局的6种思路的两列布局,而两列自适应布局是指一列由内容撑开,另一列撑满剩余宽度的布局方式.本文将从float.table.flex和gri ...

随机推荐

  1. Electronics投稿指南

    原地址: https://m.peipusci.com/news/10593.html Electronics的自引率先增后减,2023年度为10.3%.

  2. 哈哈哈,我就说未来要研发无人的AI潜艇嘛 —— 说啥来啥 —— AI驱动的无人潜艇

    相关: 沉默5个月后,美国对华发出挑战书,万没想到,中方打法早就变了

  3. pip install --user 使用方法和注意事项——python中安装module库到用户packages路径中

    pip install --user   是python中安装module库到用户packages路径中的方法. 参考: https://blog.csdn.net/The_Time_Runner/a ...

  4. Visual Studio 个人配置和插件

    主题和字体 一般为黑色深色主题,看起来比较舒服. 字体使用Fira Code,好处就是它把 =>和!=换成更加熟悉的表示.就比如以下.缺点就是习惯之后,看别人的代码就不习惯. 插件 当然是首推R ...

  5. 突破单点瓶颈、挑战海量离线任务,Apache Dolphinscheduler在生鲜电商领域的落地实践

    ​ 点亮 ️ Star · 照亮开源之路 GitHub:https://github.com/apache/dolphinscheduler 精彩回顾 近期,食行生鲜的数据平台工程师单葛尧在社区线上 ...

  6. SMU 2024 spring 天梯赛2

    SMU 2024 spring 天梯赛2 7-1 计算指数 - SMU 2024 spring 天梯赛2 (pintia.cn) #include <bits/stdc++.h> usin ...

  7. SMU Spring 2023 Trial Contest Round 1

    A. Prepend and Append 用ans记录n的值,然后双指针从前后判断是否一个为0一个为1,是的话则ans-2,否则退出循环即可. #include<bits/stdc++.h&g ...

  8. Notes for uc/OS-III User Guide

    1. Architecture F2-1(1) The application code consists of project or product files. For convenience, ...

  9. Linux 进程编程入门

    关于进程和线程的关系,之前一口君写过这几篇文章,大家可以参考下. 本文从头带着大家一起学习Linux进程 <搞懂进程组.会话.控制终端关系,才能明白守护进程干嘛的?> <[粉丝问答6 ...

  10. 如何使用4G模块通过MQTT协议传输温湿度数据到onenet

    本次实验是采用SIM7600CE 4G cat4 模块进行操作的,本模块支持GNSS定位功能.也可以采用别的4G模块,只要支持TCP传输就行.本模块支持的AT命令相当强大,拥有TCP&UDP命 ...