using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Yh.Platform.Abstractions; using Yh.SchClass.OpenApi; using YhCore.Entity; using YhCore.Model.Dto; using YhCore.Repository; using YhCore.Service.Interfaces; namespace YhCore.Service.Impl { /// /// 文印核心业务服务(移植旧 BestSchool.DocPrint.WY_list 的 CRUD/列表/审核/统计/指派部分)。 /// 与旧实现差异(迁移降级说明): /// 1. 旧 SendMessage/SendUnread/Backlog(短信、已读未读、待办中心)在新项目缺失,全部降级为纯落库; /// 2. 教师/头像改走教务 SDK(Yh.SchClass.OpenApi),教师标识用 teaId(TeaSerId); /// 3. 旧返回体的 OtherData(userCenterUrl 等)无新槽位,仅 MyListtoOne 的 tempresultfile 并入 data; /// 4. 旧 SPList/ShenheList/AllList 的多段 Union 查询改为内存 HashSet 去重实现,语义一致。 /// public class WYDocPrintService : IWYDocPrintService { private readonly IRepository _doc; private readonly IRepository _dic; private readonly IRepository _flowinfo; private readonly IRepository _flowstep; private readonly IRepository _flowtranct; private readonly IRepository _groupuser; private readonly IRepository _freegroup; private readonly IRepository _freeflowinfo; private readonly IRepository _zhipai; private readonly IRepository _senduser; private readonly IRepository _files; private readonly YHDbContext _db; private readonly SchClassOpenApi _sch; private static readonly string[] DictTypes = { "WYFL", "YZBM", "YZFL" }; private static readonly string[] OfficeExts = { ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx" }; public WYDocPrintService( IRepository doc, IRepository dic, IRepository flowinfo, IRepository flowstep, IRepository flowtranct, IRepository groupuser, IRepository freegroup, IRepository freeflowinfo, IRepository zhipai, IRepository senduser, IRepository files, YHDbContext db, SchClassOpenApi sch) { _doc = doc; _dic = dic; _flowinfo = flowinfo; _flowstep = flowstep; _flowtranct = flowtranct; _groupuser = groupuser; _freegroup = freegroup; _freeflowinfo = freeflowinfo; _zhipai = zhipai; _senduser = senduser; _files = files; _db = db; _sch = sch; } #region 抄送 /// 抄送列表(旧 SendRdList:DocStatus 1/11,SendUserID 含本人;旧为教师内连接)。 public async Task> SendRdListAsync(string name, string startDate, string endDate, string userId, string schSerId, int pageSize, int pageIndex) { var docs = (await _doc.FindAsync(m => m.SchSerID == schSerId)).ToList(); if (!string.IsNullOrEmpty(name)) docs = docs.Where(m => m.DocContent != null && m.DocContent.Contains(name)).ToList(); if (!string.IsNullOrEmpty(userId)) docs = docs.Where(m => m.SendUserID != null && m.SendUserID.IndexOf(userId) > -1).ToList(); if (DateTime.TryParse(startDate, out var date1)) docs = docs.Where(m => m.TakeDate >= date1).ToList(); if (DateTime.TryParse(endDate, out var endDateValue)) { var date2 = endDateValue.AddDays(1).AddMilliseconds(-1); // 防止跨天 docs = docs.Where(m => m.TakeDate <= date2).ToList(); } docs = docs.Where(m => m.DocStatus == "1" || m.DocStatus == "11") .OrderByDescending(m => m.SerID) .Skip(pageSize * pageIndex).Take(pageSize).ToList(); var teaMap = await GetTeaMapAsync(schSerId); var dictMap = await GetDictMapAsync(schSerId); var result = new List(); foreach (var item in docs) { // 旧实现为教师内连接:UserID 不在校内教师列表的记录被丢弃 if (!TryGetTea(teaMap, item.UserID, out var tea)) continue; result.Add(await BuildListItemAsync(item, tea, dictMap, "yyyy-MM-dd")); } return result; } /// 查看抄送人员详情(旧 SendDetailsList)。 public async Task> SendDetailsListAsync(int serId) { var result = new List(); var model = await _doc.FirstOrDefaultAsync(m => m.SerID == serId); if (model?.SendUserID == null) return result; var ids = model.SendUserID.Split(',') .Where(a => !string.IsNullOrEmpty(a) && int.TryParse(a, out _)) .Select(int.Parse).ToArray(); if (ids.Length == 0) return result; var teas = await _sch.TeaInfo.GetArrTeaInfoAsync(string.Join(",", ids)); var teaMap = teas.Where(t => int.TryParse(t.TeaSerId, out _)) .GroupBy(t => int.Parse(t.TeaSerId)) .ToDictionary(g => g.Key, g => g.First()); foreach (var id in ids) { teaMap.TryGetValue(id, out var tea); result.Add(new SendDetailItem { TeaName = tea?.Name ?? "", TeaPhoto = tea?.Photo ?? "", }); } return result; } /// 修改抄送人(旧 EditSend:SetChaoSend + 回写 WY_DocPrint.SendUserID)。 public async Task EditSendAsync(int serId, string senduserid, string mysenduserid, string schSerId, string uid) { await SetChaoSendAsync(int.Parse(uid), schSerId, mysenduserid); var model = await _doc.FirstOrDefaultAsync(m => m.SerID == serId); if (model != null) { model.SendUserID = senduserid; await _doc.SaveChangesAsync(); } return "修改成功!"; } #endregion #region 附件 /// 获取附件数据源(旧 filelist)。 public async Task> FileListAsync(string ids) { var result = new List(); if (string.IsNullOrEmpty(ids)) return result; var idArr = ids.Split(',') .Where(a => !string.IsNullOrEmpty(a) && int.TryParse(a, out _)) .Select(int.Parse).ToArray(); if (idArr.Length == 0) return result; var files = await _files.FindAsync(a => idArr.Contains(a.SerID)); return files.Select(a => new DocPrintFileItem { SerID = a.SerID, FileTrueName = a.FileTrueName, FileName = a.FileName, FileSize = a.FileSize, FileType = a.FileType, DownTimes = a.DownTimes ?? 0, }).ToList(); } #endregion #region 文印 CRUD /// /// 添加文印(旧 Create):落库 + 首步骤定位 + 附件 TopicID 回写 + 指派 upsert + 自由流程挂链 + 抄送。 /// 旧短信/待办/已读未读推送降级为纯落库;旧 OtherData(IsNextStepMyShenhe) 新响应无槽位,不再返回。 /// public async Task CreateAsync(WYDocPrintSaveDto model) { var info = new WY_DocPrint { DocType = model.DocType, DocStyle = model.DocStyle, DPType = model.DPType, DocShare = model.DocShare, DocFix = model.DocFix, DocNum = model.DocNum, DocContent = model.DocContent, UserID = model.UserID, DocTitle = model.DocTitle, EditDate = DateTime.Now, TakeDate = model.TakeDate, DocStatus = "0", UpFile = model.UpFile, explain = model.explain, SchSerID = model.SchSerID, flowid = model.flowid, freestepid = model.freestepid, SendUserID = model.SendUserID, FreeUserID = model.FreeUserID, zhipai = model.zhipai, }; await _doc.AddAsync(info); await _doc.SaveChangesAsync(); if ((model.flowid ?? 0) > 0) { // 固定流程:定位首步骤(prestep == 0);旧短信/已读未读推送已降级 info.DocStep = (await _flowstep.FindAsync(a => a.flowid == model.flowid && a.prestep == 0)) .Select(a => a.SerID).FirstOrDefault(); await _doc.SaveChangesAsync(); } else if ((model.freestepid ?? 0) > 0) { // 自由流程:无附加落库(旧短信/已读未读推送已降级) } else { // 无流程直接完成 info.DocStatus = "1"; await _doc.SaveChangesAsync(); } // 附件:UpFile 逗号分隔 SerID 回写 TopicID await BindFilesTopicAsync(model.UpFile, (int)info.SerID); // 指派 upsert await UserFlowZhipaiAsync(int.Parse(model.UserID), model.flowid ?? 0, info.DocStep, (int)info.SerID, model.zhipai, model.SchSerID, "Add"); // 自定义流程挂链 + 抄送 if ((info.freestepid ?? 0) > 0) await SetPrimarySteptIDAsync((int)info.SerID, info.freestepid ?? 0); await SetChaoSendAsync(int.Parse(info.UserID), info.SchSerID, info.SendUserID); return (int)info.SerID; } /// /// 修改文印(旧 Edit):字段覆盖 + 状态复位 0 + 附件/指派/自由流程/抄送处理。 /// 旧实现附件差异比对因 UpFile 已先被覆盖而成为死代码(新增附件不回写 TopicID),此处保持同样行为。 /// public async Task EditAsync(WYDocPrintSaveDto model) { var info = await _doc.FirstOrDefaultAsync(x => x.SerID == model.SerID); if (info == null) throw new DException("未找到该文印记录!"); info.DocType = model.DocType; info.DocStyle = model.DocStyle; info.DPType = model.DPType; info.DocShare = model.DocShare; info.DocFix = model.DocFix; info.DocNum = model.DocNum; info.DocContent = model.DocContent; info.UserID = model.UserID; info.DocTitle = model.DocTitle; info.EditDate = DateTime.Now; info.TakeDate = model.TakeDate; info.DocStatus = "0"; // 旧实现最终回写 join(oldFids),即过滤空串后的新附件 id 串 info.UpFile = string.Join(",", ParseIds(model.UpFile)); info.explain = model.explain; info.SchSerID = model.SchSerID; info.flowid = model.flowid; info.freestepid = model.freestepid; info.SendUserID = model.SendUserID; info.FreeUserID = model.FreeUserID; info.zhipai = model.zhipai; if ((model.flowid ?? 0) > 0) { info.DocStep = (await _flowstep.FindAsync(a => a.flowid == model.flowid && a.prestep == 0)) .Select(a => a.SerID).FirstOrDefault(); info.freestepid = null; } await _doc.SaveChangesAsync(); // 指派 upsert await UserFlowZhipaiAsync(int.Parse(info.UserID), info.flowid ?? 0, info.DocStep, (int)info.SerID, info.zhipai, info.SchSerID, "Add"); // 自定义流程挂链 + 抄送 if ((model.freestepid ?? 0) > 0) await SetPrimarySteptIDAsync((int)info.SerID, model.freestepid ?? 0); await SetChaoSendAsync(int.Parse(info.UserID), info.SchSerID, info.SendUserID); return (int)info.SerID; } /// 删除文印(旧 Delete:按 ids 批量物理删除;旧待办中心清理已降级)。 public async Task DeleteAsync(string ids) { if (string.IsNullOrWhiteSpace(ids)) throw new DException("ids is not null"); var idArr = ids.Split(',') .Where(a => !string.IsNullOrEmpty(a) && decimal.TryParse(a, out _)) .Select(decimal.Parse).ToArray(); var objlist = await _doc.FindAsync(m => idArr.Contains(m.SerID)); foreach (var item in objlist) { await _doc.RemoveAsync(item); } await _doc.SaveChangesAsync(); } /// 撤销(旧 Revoke:写撤销流水 + 更新单据状态;旧待办/短信/已读未读已降级)。 public async Task RevokeAsync(FlowTranctDto model) { if (string.IsNullOrEmpty(model.remark) && model.actionstatus == 3) model.remark = "撤销"; var tranct = new Common_flowtranctInfo { dataid = model.dataid, stepid = model.stepid, actionuserid = model.actionuserid, actionstatus = model.actionstatus, actiondate = DateTime.Now, remark = model.remark, undertaker = model.undertaker, Pid = model.Pid, DoneUserID = model.DoneUserID, filespath = model.filespath, datatype = DocPrintConstants.AppCode.ToString(), }; await _flowtranct.AddAsync(tranct); await _flowtranct.SaveChangesAsync(); var docModel = await _doc.FirstOrDefaultAsync(m => m.SerID == model.dataid); if (docModel != null) { docModel.DocStatus = model.actionstatus.ToString(); await _doc.SaveChangesAsync(); } return "撤销成功!"; } #endregion #region 列表 /// 我的文印列表(旧 MyList;旧 DocTypeName 误用 YZBM 字典,此处保持旧行为)。 public async Task> MyListAsync(string name, string status, string startDate, string endDate, string userId, string schSerId, int pageSize, int pageIndex) { var docs = (await _doc.FindAsync(m => m.UserID == userId && m.SchSerID == schSerId)).ToList(); if (!string.IsNullOrEmpty(name)) docs = docs.Where(m => m.DocContent != null && m.DocContent.Contains(name)).ToList(); if (!string.IsNullOrEmpty(status)) docs = docs.Where(m => m.DocStatus == status).ToList(); if (DateTime.TryParse(startDate, out var date1)) docs = docs.Where(m => m.TakeDate >= date1).ToList(); if (DateTime.TryParse(endDate, out var endDateValue)) { var date2 = endDateValue.AddDays(1).AddMilliseconds(-1); docs = docs.Where(m => m.TakeDate <= date2).ToList(); } docs = docs.OrderBy(m => m.DocStatus).ThenByDescending(m => m.TakeDate).ThenByDescending(m => m.EditDate) .Skip(pageSize * pageIndex).Take(pageSize).ToList(); var teaMap = await GetTeaMapAsync(schSerId); var dictMap = await GetDictMapAsync(schSerId); var result = new List(); foreach (var item in docs) { TryGetTea(teaMap, item.UserID, out var tea); // 旧为左连接,缺失补空 result.Add(await BuildListItemAsync(item, tea, dictMap, "yyyy-MM-dd HH:mm", "YZBM")); } return result; } /// /// 全部文印列表(旧 AllList 原生 SQL 四分类:x=待我审(0) y=我已审(1) z=抄送我(2) m=我发起(3),按优先级去重)。 /// 旧 Name 过滤的 SQL 拼接存在运算符优先级缺陷,此处按语义实现:内容/教师名/字典名任一命中。 /// public async Task> AllListAsync(string name, string userId, string schSerId, int pageSize, int pageIndex) { int uid = int.Parse(userId); var docs = await _doc.FindAsync(m => m.SchSerID == schSerId); var fixedGroupIds = (await _groupuser.FindAsync(b => b.userid == uid)) .Select(b => b.usergroup).Where(g => g.HasValue).Select(g => g.Value).ToList(); var freeGroupIds = (await _freegroup.FindAsync(b => b.userid == uid)) .Select(b => b.usergroup).Where(g => g.HasValue).Select(g => g.Value).ToList(); var tranctDataIds = (await _flowtranct.FindAsync(a => a.actionuserid == uid && a.datatype == DocPrintConstants.AppCode.ToString())) .Select(a => a.dataid).ToHashSet(); bool IsPending(WY_DocPrint a) => a.DocStatus == "0" && ((a.flowid != null && a.zhipai == null && a.DocStep.HasValue && fixedGroupIds.Contains(a.DocStep.Value)) || (a.flowid != null && a.zhipai != null && a.zhipai.Contains(userId)) || (a.freestepid > 0 && freeGroupIds.Contains(a.freestepid ?? 0))); var taken = new HashSet(); var combined = new List<(WY_DocPrint doc, int ctype)>(); foreach (var a in docs.Where(a => IsPending(a))) { combined.Add((a, 0)); taken.Add(a.SerID); } foreach (var a in docs.Where(a => a.DocStatus != "3" && tranctDataIds.Contains((int)a.SerID) && !taken.Contains(a.SerID))) { combined.Add((a, 1)); taken.Add(a.SerID); } foreach (var a in docs.Where(a => a.DocStatus == "1" && a.SendUserID != null && a.SendUserID.Contains(userId) && !taken.Contains(a.SerID))) { combined.Add((a, 2)); taken.Add(a.SerID); } foreach (var a in docs.Where(a => a.UserID == userId && !taken.Contains(a.SerID))) { combined.Add((a, 3)); taken.Add(a.SerID); } var teaMap = await GetTeaMapAsync(schSerId); var dictMap = await GetDictMapAsync(schSerId); IEnumerable<(WY_DocPrint doc, int ctype)> query = combined; if (!string.IsNullOrEmpty(name)) { query = query.Where(t => (t.doc.DocContent != null && t.doc.DocContent.IndexOf(name) >= 0) || (t.doc.UserID != null && int.TryParse(t.doc.UserID, out var touid) && teaMap.TryGetValue(touid, out var tt) && tt.Name != null && tt.Name.IndexOf(name) >= 0) || DictNameContains(dictMap, "WYFL", t.doc.DocType, name) || DictNameContains(dictMap, "YZBM", t.doc.DocStyle, name) || DictNameContains(dictMap, "YZFL", t.doc.DPType, name)); } var pageItems = query.OrderByDescending(t => t.doc.SerID).ThenByDescending(t => t.doc.TakeDate) .Skip(pageSize * pageIndex).Take(pageSize).ToList(); var result = new List(); foreach (var (doc, ctype) in pageItems) { TryGetTea(teaMap, doc.UserID, out var tea); var item = await BuildListItemAsync(doc, tea, dictMap, "yyyy-MM-dd HH:mm"); item.ctype = ctype; result.Add(item); } return result; } /// /// 文印审核列表(旧 ShenheList:固定流程 + 指派 + 自由流程三路并集,HashSet 去重)。 /// 旧实现的 Name 过滤作用在未被使用的查询上(死代码),此处保持 Name 不生效的旧行为。 /// public async Task> ShenheListAsync(string userId, string schSerId, int pageSize, int pageIndex) { var docs = await GetPendingShenheDocsAsync(userId, schSerId, requireFlowIdNonZero: false); docs = docs.OrderBy(m => m.DocStatus).ThenByDescending(m => m.TakeDate) .Skip(pageSize * pageIndex).Take(pageSize).ToList(); var teaMap = await GetTeaMapAsync(schSerId); var dictMap = await GetDictMapAsync(schSerId); var result = new List(); foreach (var item in docs) { TryGetTea(teaMap, item.UserID, out var tea); result.Add(await BuildListItemAsync(item, tea, dictMap, "yyyy-MM-dd HH:mm")); } return result; } /// /// 审批列表(旧 SPList:待审(DS,zt=false)∪ 我已审(YS,zt=true,排除 DS),_disabled 即 zt)。 /// public async Task> SPListAsync(string name, string status, string startDate, string endDate, string userId, string schSerId, int pageSize, int pageIndex) { var userid = int.Parse(userId); // DS:待审集合(固定流程 flowid!=0 + 指派 + 自由流程) var dsDocs = await GetPendingShenheDocsAsync(userId, schSerId, requireFlowIdNonZero: true); var dsIds = dsDocs.Select(a => a.SerID).ToHashSet(); // YS:本人已审(actionstatus != 0),排除 DS var ysDataIds = (await _flowtranct.FindAsync(b => b.actionuserid == userid && b.datatype == DocPrintConstants.AppCode.ToString() && b.actionstatus != 0)) .Select(b => b.dataid).ToHashSet(); var ysDocs = (await _doc.FindAsync(s => s.SchSerID == schSerId && s.DocStatus != null)) .Where(s => !dsIds.Contains(s.SerID) && ysDataIds.Contains((int)s.SerID)) .ToList(); IEnumerable<(WY_DocPrint doc, bool zt)> query = dsDocs.Select(a => (a, false)) .Concat(ysDocs.Select(a => (a, true))); if (DateTime.TryParse(startDate, out var date1)) query = query.Where(m => m.doc.TakeDate >= date1); if (DateTime.TryParse(endDate, out var endDateValue)) { var date2 = endDateValue.AddDays(1).AddMilliseconds(-1); query = query.Where(m => m.doc.TakeDate <= date2); } if (!string.IsNullOrEmpty(status)) query = query.Where(m => m.doc.DocStatus == status); if (!string.IsNullOrEmpty(name)) query = query.Where(m => m.doc.DocContent != null && m.doc.DocContent.IndexOf(name) >= 0); var ordered = query.OrderBy(m => m.doc.DocStatus).ThenByDescending(m => m.doc.EditDate).ToList(); var teaMap = await GetTeaMapAsync(schSerId); var dictMap = await GetDictMapAsync(schSerId); var result = new List(); foreach (var (doc, zt) in ordered.Skip(pageSize * pageIndex).Take(pageSize)) { TryGetTea(teaMap, doc.UserID, out var tea); var item = await BuildListItemAsync(doc, tea, dictMap, "yyyy-MM-dd HH:mm"); item._disabled = zt; // 我已审的记录不可再编辑(覆盖通用 _disabled 语义,与旧一致) result.Add(item); } return result; } /// 文印已审列表(旧 YishenList:本人审核流水关联,排除撤销与已领取)。 public async Task> YishenListAsync(string name, string userId, string schSerId, int pageSize, int pageIndex) { int uid = int.Parse(userId); var tranctDataIds = (await _flowtranct.FindAsync(b => b.actionuserid == uid && b.datatype == DocPrintConstants.AppCode.ToString() && b.actionstatus != 11)) .Select(b => b.dataid).ToHashSet(); var docs = (await _doc.FindAsync(m => m.SchSerID == schSerId && m.DocStatus != "3")) .Where(m => tranctDataIds.Contains((int)m.SerID)) .ToList(); if (!string.IsNullOrEmpty(name)) docs = docs.Where(m => m.DocContent != null && m.DocContent.Contains(name)).ToList(); docs = docs.OrderByDescending(m => m.SerID).ThenByDescending(m => m.TakeDate) .Skip(pageSize * pageIndex).Take(pageSize).ToList(); var teaMap = await GetTeaMapAsync(schSerId); var dictMap = await GetDictMapAsync(schSerId); var result = new List(); foreach (var item in docs) { TryGetTea(teaMap, item.UserID, out var tea); result.Add(await BuildListItemAsync(item, tea, dictMap, "yyyy-MM-dd HH:mm")); } return result; } /// 全部文印列表(旧 totalList,管理员视角,按状态/时间过滤)。 public async Task> TotalListAsync(string name, string stateid, string starttime, string endtime, string schSerId, int pageSize, int pageIndex) { var docs = (await _doc.FindAsync(m => m.SchSerID == schSerId)).ToList(); if (DateTime.TryParse(starttime, out var date1)) docs = docs.Where(m => m.TakeDate >= date1).ToList(); if (DateTime.TryParse(endtime, out var endDateValue)) { var date2 = endDateValue.AddDays(1).AddMilliseconds(-1); docs = docs.Where(m => m.TakeDate <= date2).ToList(); } if (!string.IsNullOrEmpty(stateid)) docs = docs.Where(m => m.DocStatus == stateid).ToList(); if (!string.IsNullOrEmpty(name)) docs = docs.Where(m => m.DocContent != null && m.DocContent.IndexOf(name) >= 0).ToList(); docs = docs.OrderBy(m => m.DocStatus).ThenByDescending(m => m.TakeDate) .Skip(pageSize * pageIndex).Take(pageSize).ToList(); var teaMap = await GetTeaMapAsync(schSerId); var dictMap = await GetDictMapAsync(schSerId); var result = new List(); foreach (var item in docs) { TryGetTea(teaMap, item.UserID, out var tea); result.Add(await BuildListItemAsync(item, tea, dictMap, "yyyy-MM-dd")); } return result; } /// /// 获取当前一条文印详情(旧 MyListtoOne:单元素列表 + 附件明细; /// 旧 OtherData.tempresultfile 并入条目 tempresultfile,userCenterUrl/teaPhoto 头像不再返回)。 /// public async Task> MyListtoOneAsync(int serId, string schSerId) { var result = new List(); var doc = await _doc.FirstOrDefaultAsync(m => m.SerID == serId); if (doc == null) return result; var teaMap = await GetTeaMapAsync(schSerId); var dictMap = await GetDictMapAsync(schSerId); if (!TryGetTea(teaMap, doc.UserID, out var tea)) return result; // 旧为教师内连接 var tranctAny = await _flowtranct.AnyAsync(a => a.dataid == serId && a.datatype == DocPrintConstants.AppCode.ToString()); var item = new DocPrintDetailItem { SerID = (int)doc.SerID, DocContent = doc.DocContent, DocType = doc.DocType, DocTypeName = DictName(dictMap, "WYFL", doc.DocType), DocStyle = doc.DocStyle, DocStyleName = DictName(dictMap, "YZBM", doc.DocStyle), DPType = doc.DPType, DPTypeName = DictName(dictMap, "YZFL", doc.DPType), EditDate = doc.EditDate.ToString("yyyy-MM-dd HH:mm"), TakeDate = doc.TakeDate?.ToString("yyyy-MM-dd"), UserID = doc.UserID, teaname = tea.Name, TeaPhoto = tea.Photo, UpFile = doc.UpFile, DocTitle = doc.DocTitle, DocStatus = doc.DocStatus, DocStatusName = await GetStatusByCodeAsync(doc.DocStatus, doc.DocStep ?? 0, doc.zhipai, doc.freestepid, teaMap), DocStep = doc.DocStep?.ToString() ?? "", explain = doc.explain, flowid = doc.flowid?.ToString() ?? "", DocShare = doc.DocShare, DocFix = doc.DocFix, DocNum = doc.DocNum, _disabled = !(doc.DocStatus == "0" && !tranctAny), // 旧 GetEditDeleteStatus FreeUserID = doc.FreeUserID, SendUserID = doc.SendUserID, freestepid = doc.freestepid, zhipai = doc.zhipai, rid = doc.rid, reid = doc.reid, grade = doc.grade, Examtype = doc.Examtype, tempresultfile = new List(), }; // 附件明细:office 文件替换为 .pdf 预览名并拼接文件 Web 前缀 var fids = ParseIds(doc.UpFile); if (fids.Count > 0) { var fileUrl = YhCore.Common.Config.Config.FileWebPath ?? ""; var files = await _files.FindAsync(a => fids.Contains(a.SerID)); item.tempresultfile = files.Select(a => new DocPrintFileItem { SerID = a.SerID, FileTrueName = a.FileTrueName, FileName = fileUrl + (OfficeExts.Contains(a.FileType) ? a.FileName?.Replace(a.FileType, ".pdf") : a.FileName), FileSize = a.FileSize, FileType = a.FileType, DownTimes = a.DownTimes ?? 0, }).ToList(); } result.Add(item); return result; } /// 绑定流程(旧 Bindflowinfo:本校启用流程下拉)。 public async Task> BindflowinfoAsync(string schSerId, int flowType) { var list = await _flowinfo.FindAsync(a => a.flowType == flowType && a.schSerID == schSerId && a.enable == 1); return list.OrderBy(m => m.SerID) .Select(item => new BindFlowOption { SerID = item.SerID.ToString(), flowname = item.flowname, }).ToList(); } #endregion #region 审核动作 /// /// 审核确认(旧 shenheok):事务内写流水 + 推进固定/自由流程或终态; /// 重复审核 / 已处理单据按旧语义抛业务异常;旧短信/待办/已读未读已降级。 /// public async Task ShenheOkAsync(FlowTranctDto model) { using var tx = await _db.Database.BeginTransactionAsync(); try { var rdmodel = await _doc.FirstOrDefaultAsync(m => m.SerID == model.dataid); if (rdmodel == null) throw new DException("未找到该文印记录!"); if (rdmodel.DocStatus != "0") { var statusName = rdmodel.DocStatus switch { "1" => "已印好", "2" => "已拒绝", "3" => "已撤销", "11" => "已领取", _ => "", }; throw new DException(statusName); } var audited = await _flowtranct.AnyAsync(a => a.dataid == model.dataid && a.datatype == DocPrintConstants.AppCode.ToString() && a.stepid == model.stepid); if (audited) throw new DException("已审核"); if (string.IsNullOrEmpty(model.remark)) model.remark = model.actionstatus == 1 ? "同意" : "不同意"; rdmodel.zhipai = model.undertaker; var tranct = new Common_flowtranctInfo { dataid = model.dataid, stepid = model.stepid, actionuserid = model.actionuserid, actionstatus = model.actionstatus, actiondate = DateTime.Now, remark = model.remark, undertaker = model.undertaker, Pid = model.Pid, datatype = DocPrintConstants.AppCode.ToString(), }; await _flowtranct.AddAsync(tranct); await _flowtranct.SaveChangesAsync(); if (model.actionstatus == 1) { if (rdmodel.DocStep == null || rdmodel.DocStep == 0) { // 自由流程:按 laststep 推进下一步 var stepid = (await _freeflowinfo.FindAsync(a => a.laststep == model.stepid)) .Select(a => a.SerID).FirstOrDefault(); if (stepid > 0) { rdmodel.freestepid = stepid; await _doc.SaveChangesAsync(); } else { // 无下一步:流程结束(旧抄送短信推送已降级) rdmodel.DocStatus = model.actionstatus.ToString(); await _doc.SaveChangesAsync(); } } else { // 固有流程:按 prestep 推进下一步 if (!int.TryParse(model.Pid, out var flowid)) throw new DException("审核失败:Pid 参数错误"); var steps = await _flowstep.FindAsync(a => a.flowid == flowid && a.prestep == model.stepid); if (steps.Count > 0) { rdmodel.DocStep = steps.Select(a => a.SerID).First(); await _doc.SaveChangesAsync(); } else { rdmodel.DocStatus = model.actionstatus.ToString(); await _doc.SaveChangesAsync(); } } } else { // 拒绝:直接终态 rdmodel.DocStatus = model.actionstatus.ToString(); await _doc.SaveChangesAsync(); } await tx.CommitAsync(); return "已审核"; } catch { await tx.RollbackAsync(); throw; } } /// /// 领取确认(旧 AffirmApplyforOk):写 actionstatus=11 流水 + 单据置为已领取(11)。 /// 旧代码查重用 datatype=="1010" 而写入用 AppCode(1003),导致查重永远为空; /// 此处统一为 1003 修正重复领取校验。 /// public async Task AffirmApplyforOkAsync(FlowTranctDto model) { var itt = await _doc.FirstOrDefaultAsync(m => m.SerID == model.dataid); if (itt == null) throw new DException("未找到该文印记录!"); var claimed = await _flowtranct.AnyAsync(a => a.dataid == model.dataid && a.datatype == DocPrintConstants.AppCode.ToString() && a.actionstatus == 11); if (claimed) throw new DException("已领取,不能重复提交!"); var info = new Common_flowtranctInfo { remark = string.IsNullOrEmpty(model.remark) ? "已领取" : model.remark, stepid = model.stepid, dataid = model.dataid, actionuserid = model.actionuserid, actionstatus = 11, datatype = DocPrintConstants.AppCode.ToString(), actiondate = DateTime.Now, }; await _flowtranct.AddAsync(info); itt.DocStatus = "11"; await _flowtranct.SaveChangesAsync(); await _doc.SaveChangesAsync(); return "已通知!"; } #endregion #region 指派 /// 查询指派(旧 GetUserZhipai:取最近一次指派记录)。 public async Task GetUserZhipaiAsync(int userid, string schSerId, int? flowid, int? flowstepid, string type) { if ((flowid ?? 0) <= 0) return null; var zhipai = (await _zhipai.FindAsync(a => a.UserID == userid && a.SchSerID == schSerId && a.FlowID == flowid && a.FlowStepID == flowstepid && a.type == type)) .OrderByDescending(a => a.MSerID) .FirstOrDefault()?.zhipai; return zhipai; } /// /// 指派 upsert(旧 UserFlowZhipai:flowid>0 时按六元组查重,有则更新无则新增;异常按旧实现吞掉不回滚主流程)。 /// private async Task UserFlowZhipaiAsync(int uid, int flowid, int? flowstepid, int dataid, string zhipai, string schSerId, string type) { try { if (flowid <= 0) return; var list = await _zhipai.FirstOrDefaultAsync(a => a.UserID == uid && a.MSerID == dataid && a.SchSerID == schSerId && a.FlowID == flowid && a.FlowStepID == flowstepid && a.type == type); if (list == null) { await _zhipai.AddAsync(new Common_UserFlowZhipai { UserID = uid, MSerID = dataid, FlowID = flowid, FlowStepID = flowstepid, type = type, zhipai = zhipai, SchSerID = schSerId, }); } else { list.FlowID = flowid; list.FlowStepID = flowstepid; list.type = type; list.zhipai = zhipai; list.SchSerID = schSerId; } await _zhipai.SaveChangesAsync(); } catch { // 旧实现仅记日志不抛出,保持一致 } } #endregion #region 统计 /// 文印数量按月统计(旧 List1:按年月分组 + MonthDiff 补零;起止时间任一为空时不补零)。 public async Task> List1Async(string starttime, string endtime, string schSerId) { var docs = await _doc.FindAsync(m => m.SchSerID == schSerId); if (DateTime.TryParse(starttime, out var date1)) docs = docs.Where(m => m.EditDate >= date1).ToList(); if (DateTime.TryParse(endtime, out var endDateValue)) { var date2 = endDateValue.AddDays(1).AddMilliseconds(-1); docs = docs.Where(m => m.EditDate <= date2).ToList(); } var grouped = docs.OrderBy(m => m.SerID) .GroupBy(m => new { m.EditDate.Year, m.EditDate.Month }) .Select(g => new MonthStatItem { dateyear = g.Key.Year, datemonth = g.Key.Month, num = g.Count(), DocNum = g.Sum(a => a.DocNum), }) .OrderBy(a => a.dateyear) .ToList(); // 月份区间补零(旧 MonthDiff 跨年补零;参数缺失时跳过,避免旧实现 MinValue 起点的异常输出) if (DateTime.TryParse(starttime, out var s) && DateTime.TryParse(endtime, out var e)) { var months = MonthDiff(s, e.AddDays(1).AddMilliseconds(-1)); var result = new List(); foreach (var m in months) { var year = int.Parse(m.Split(',')[0]); var month = int.Parse(m.Split(',')[1]); var hit = grouped.FirstOrDefault(r => r.dateyear == year && r.datemonth == month); result.Add(new MonthStatItem { dateyear = year, datemonth = month, num = hit?.num ?? 0, DocNum = hit?.DocNum ?? 0 }); } return result; } return grouped; } /// 文印详情统计列表(旧 List2:非待审单据按申请时间倒序分页)。 public async Task> List2Async(string starttime, string endtime, string schSerId, int pageSize, int pageIndex) { var docs = (await _doc.FindAsync(m => m.SchSerID == schSerId && m.DocStatus != "0")).ToList(); if (DateTime.TryParse(starttime, out var date1)) docs = docs.Where(m => m.EditDate >= date1).ToList(); if (DateTime.TryParse(endtime, out var endDateValue)) { var date2 = endDateValue.AddDays(1).AddMilliseconds(-1); docs = docs.Where(m => m.EditDate <= date2).ToList(); } var teaMap = await GetTeaMapAsync(schSerId); var dictMap = await GetDictMapAsync(schSerId); var full = new List(); foreach (var m in docs.Where(m => TryGetTea(teaMap, m.UserID, out _)).OrderBy(m => m.SerID)) { TryGetTea(teaMap, m.UserID, out var tea); full.Add(new DocDetailStatItem { UserID = m.UserID, teaname = tea.Name, DocContent = m.DocContent, DocType = m.DocType, DocTypeName = DictName(dictMap, "WYFL", m.DocType), DocStyle = m.DocStyle, DocStyleName = DictName(dictMap, "YZBM", m.DocStyle), DPType = m.DPType, DPTypeName = DictName(dictMap, "YZFL", m.DPType), EditDate = m.EditDate.ToString("yyyy-MM-dd"), UpFile = m.UpFile, DocTitle = m.DocTitle, DocNum = m.DocNum, DocStatus = m.DocStatus, DocStatusName = await GetStatusByCodeAsync(m.DocStatus, m.DocStep ?? 0, m.zhipai, m.freestepid, teaMap), }); } return full.OrderByDescending(a => a.EditDate) .Skip(pageSize * pageIndex).Take(pageSize) .ToList(); } /// 文印数量按用户排名(旧 List3)。 public async Task> List3Async(string starttime, string endtime, string schSerId, int pageSize, int pageIndex) { var docs = (await _doc.FindAsync(m => m.SchSerID == schSerId && m.DocStatus == "1")).ToList(); if (DateTime.TryParse(starttime, out var date1)) docs = docs.Where(m => m.EditDate >= date1).ToList(); if (DateTime.TryParse(endtime, out var endDateValue)) { var date2 = endDateValue.AddDays(1).AddMilliseconds(-1); docs = docs.Where(m => m.EditDate <= date2).ToList(); } var teaMap = await GetTeaMapAsync(schSerId); var grouped = docs.OrderBy(m => m.SerID) .Where(m => TryGetTea(teaMap, m.UserID, out _)) // 旧为教师内连接 .GroupBy(m => new { m.UserID, Name = GetTeaName(teaMap, m.UserID) }) .Select(g => new UserRankItem { UserID = g.Key.UserID, teaname = g.Key.Name, num = g.Count(), DocNum = g.Sum(a => a.DocNum), }) .OrderByDescending(a => a.num) .ToList(); return grouped.Skip(pageSize * pageIndex).Take(pageSize).ToList(); } /// 按月份统计明细(旧 List4:指定年月已印好单据,按申请时间倒序分页)。 public async Task> List4Async(int year, int month, string schSerId, int pageSize, int pageIndex) { var docs = (await _doc.FindAsync(m => m.SchSerID == schSerId && m.DocStatus == "1" && m.EditDate.Year == year && m.EditDate.Month == month)) .OrderByDescending(a => a.EditDate) .Skip(pageSize * pageIndex).Take(pageSize) .ToList(); return await BuildStatListAsync(docs, schSerId); } /// 按用户统计明细(旧 List5:指定用户已印好单据,按申请时间倒序分页)。 public async Task> List5Async(string userid, string starttime, string endtime, string schSerId, int pageSize, int pageIndex) { var docs = (await _doc.FindAsync(m => m.SchSerID == schSerId && m.UserID == userid && m.DocStatus == "1")).ToList(); if (DateTime.TryParse(starttime, out var date1)) docs = docs.Where(m => m.EditDate >= date1).ToList(); if (DateTime.TryParse(endtime, out var endDateValue)) { var date2 = endDateValue.AddDays(1).AddMilliseconds(-1); docs = docs.Where(m => m.EditDate <= date2).ToList(); } docs = docs.OrderByDescending(a => a.EditDate) .Skip(pageSize * pageIndex).Take(pageSize) .ToList(); return await BuildStatListAsync(docs, schSerId); } #endregion #region 内部辅助:查询集合 /// /// 待审单据集合(旧 SPList/ShenheList/GetShnheCount 的三路 Union,HashSet 去重): /// 固定流程组命中 / 指派包含本人 / 自由流程组命中。 /// requireFlowIdNonZero:旧 SPList 固定分支带 flowid != 0 条件,ShenheList 仅 flowid != null。 /// private async Task> GetPendingShenheDocsAsync(string userId, string schSerId, bool requireFlowIdNonZero) { var userid = int.Parse(userId); var docs = await _doc.FindAsync(a => a.SchSerID == schSerId && a.DocStatus == "0"); var fixedGroupIds = (await _groupuser.FindAsync(b => b.userid == userid)) .Select(b => b.usergroup).Where(g => g.HasValue).Select(g => g.Value).ToList(); var freeGroupIds = (await _freegroup.FindAsync(b => b.userid == userid)) .Select(b => b.usergroup).Where(g => g.HasValue).Select(g => g.Value).ToList(); var fixedFlowOk = requireFlowIdNonZero ? new Func(a => a.flowid != null && a.flowid != 0) : new Func(a => a.flowid != null); var result = new HashSet(); var list = new List(); foreach (var a in docs) { bool match = false; // 固定流程:DocStep 命中本人组(旧 zhipai == null 分支) if (fixedFlowOk(a) && a.zhipai == null && a.DocStep.HasValue && fixedGroupIds.Contains(a.DocStep.Value)) match = true; // 指派:zhipai 含本人 if (!match && a.DocStatus == "0" && a.zhipai != null && a.flowid != null && a.zhipai.Contains(userId) && a.SchSerID == schSerId) match = true; // 自由流程:freestepid 命中本人自由组 if (!match && a.SchSerID == schSerId && a.freestepid > 0 && freeGroupIds.Contains(a.freestepid ?? 0)) match = true; if (match && result.Add(a.SerID)) { list.Add(a); } } return list; } #endregion #region 内部辅助:教师 / 字典 private async Task> GetTeaMapAsync(string schSerId) { var teas = await _sch.TeaInfo.GetSchTeaInfoAsync(schSerId, null); return teas .Where(t => int.TryParse(t.TeaSerId, out _)) .GroupBy(t => int.Parse(t.TeaSerId)) .ToDictionary(g => g.Key, g => (g.First().Name, g.First().Photo)); } private static bool TryGetTea(Dictionary teaMap, string userId, out (string Name, string Photo) tea) { tea = default; return int.TryParse(userId, out var uid) && teaMap.TryGetValue(uid, out tea); } private static string GetTeaName(Dictionary teaMap, string userId) => TryGetTea(teaMap, userId, out var tea) ? tea.Name : ""; /// 字典名缓存(旧 Get_DictionaryOnly:typeID+CodeID 定位,本校或通用,按 Enable 升序取首个)。 private async Task> GetDictMapAsync(string schSerId) { var list = await _dic.FindAsync(a => a.typeID == "WYFL" || a.typeID == "YZBM" || a.typeID == "YZFL"); return list .Where(a => a.Schserid == schSerId || string.IsNullOrEmpty(a.Schserid)) .GroupBy(a => a.typeID + "|" + a.CodeID) .ToDictionary(g => g.Key, g => g.OrderBy(x => x.Enable).Select(x => x.Name).FirstOrDefault() ?? ""); } private static string DictName(Dictionary dictMap, string type, string code) => code != null && dictMap.TryGetValue(type + "|" + code, out var name) ? name : ""; private static bool DictNameContains(Dictionary dictMap, string type, string code, string name) { var dictName = DictName(dictMap, type, code); return dictName != null && dictName.IndexOf(name) >= 0; } private static List ParseIds(string commaIds) { if (string.IsNullOrEmpty(commaIds)) return new List(); return commaIds.Split(',') .Where(a => !string.IsNullOrEmpty(a) && int.TryParse(a, out _)) .Select(int.Parse).ToList(); } #endregion #region 内部辅助:状态 / 列表项 /// 状态文案(旧 GetStatusByCode:0=待审人名+审核中,1=印刷中,2=审核未通过,3=已撤销,11=已印刷;teaMap 为空时走 SDK 兜底)。 private async Task GetStatusByCodeAsync(string statusid, int stepid, string zhipai, int? freestepid, Dictionary teaMap) { if (statusid == "0") { if (stepid > 0) { // 固有流程 var userids = (await _groupuser.FindAsync(a => a.usergroup == stepid)) .Select(a => a.userid ?? 0).Where(u => u > 0).Distinct().ToList(); if (!string.IsNullOrEmpty(zhipai)) { var ids = zhipai.Split(',').Where(s => int.TryParse(s, out _)).Select(int.Parse).ToList(); userids = userids.Where(u => ids.Contains(u)).ToList(); } var names = await GetTeaNamesAsync(userids, teaMap); return string.Join(",", names) + " 审核中"; } if ((freestepid ?? 0) > 0) { // 自由流程 var userids = (await _freegroup.FindAsync(a => a.usergroup == freestepid)) .Select(a => a.userid ?? 0).Where(u => u > 0).Distinct().ToList(); var names = await GetTeaNamesAsync(userids, teaMap); return string.Join(",", names) + " 审核中"; } return ""; } if (statusid == "1") return "印刷中"; if (statusid == "2") return "审核未通过"; if (statusid == "3") return "已撤销"; if (statusid == "11") return "已印刷"; return ""; } /// 按 id 顺序取教师名:校内映射优先(teaMap 可为 null),缺失的批量走 SDK 兜底。 private async Task> GetTeaNamesAsync(List ids, Dictionary teaMap) { var map = teaMap ?? new Dictionary(); var missing = ids.Where(i => !map.ContainsKey(i)).Distinct().ToList(); var fallback = new Dictionary(); if (missing.Count > 0) { var teas = await _sch.TeaInfo.GetArrTeaInfoAsync(string.Join(",", missing)); fallback = teas.Where(t => int.TryParse(t.TeaSerId, out _)) .GroupBy(t => int.Parse(t.TeaSerId)) .ToDictionary(g => g.Key, g => g.First().Name ?? ""); } return ids.Select(i => map.TryGetValue(i, out var t) ? t.Name : fallback.TryGetValue(i, out var n) ? n : "").ToList(); } /// 是否禁编辑(旧 GetEditDeleteStatus:待审且无任何审核流水才可编辑)。 private Task GetEditDeleteStatusAsync(int dateid, string status) { if (status == "0") return _flowtranct.AnyAsync(a => a.dataid == dateid && a.datatype == DocPrintConstants.AppCode.ToString()); return Task.FromResult(true); } private async Task BuildListItemAsync(WY_DocPrint item, (string Name, string Photo) tea, Dictionary dictMap, string editDateFormat, string docTypeDictType = "WYFL") { return new DocPrintListItem { SerID = (int)item.SerID, DocContent = item.DocContent, DocType = item.DocType, DocTypeName = DictName(dictMap, docTypeDictType, item.DocType), DocStyle = item.DocStyle, DocStyleName = DictName(dictMap, "YZBM", item.DocStyle), DPType = item.DPType, DPTypeName = DictName(dictMap, "YZFL", item.DPType), EditDate = item.EditDate.ToString(editDateFormat), TakeDate = item.TakeDate?.ToString("yyyy-MM-dd"), UserID = item.UserID, teaname = tea.Name, TeaPhoto = tea.Photo, DocShare = item.DocShare, UpFile = item.UpFile, DocTitle = item.DocTitle, DocStatus = item.DocStatus, DocStatusName = await GetStatusByCodeAsync(item.DocStatus, item.DocStep ?? 0, item.zhipai, item.freestepid, null), DocStep = item.DocStep, flowid = item.flowid, _disabled = await GetEditDeleteStatusAsync((int)item.SerID, item.DocStatus), FreeUserID = item.FreeUserID, SendUserID = item.SendUserID, freestepid = item.freestepid, explain = item.explain, }; } /// 统计明细条目(旧 List4/List5 投影,editstatus 对应旧 Common.GetEditDeleteStatus)。 private async Task> BuildStatListAsync(List docs, string schSerId) { var teaMap = await GetTeaMapAsync(schSerId); var dictMap = await GetDictMapAsync(schSerId); var firstStepIds = (await _flowstep.FindAsync(m => m.prestep == 0)) .Select(m => m.SerID).ToHashSet(); var result = new List(); foreach (var item in docs) { TryGetTea(teaMap, item.UserID, out var tea); bool editstatus = true; if (item.DocStatus != "1" && item.DocStatus != "2" && item.DocStep.HasValue) editstatus = !firstStepIds.Contains(item.DocStep.Value); // 一审可编辑 → false result.Add(new StatListItem { SerID = (int)item.SerID, DocContent = item.DocContent, DocType = item.DocType, DocTypeName = DictName(dictMap, "WYFL", item.DocType), DocStyle = item.DocStyle, DocStyleName = DictName(dictMap, "YZBM", item.DocStyle), DPType = item.DPType, DPTypeName = DictName(dictMap, "YZFL", item.DPType), EditDate = item.EditDate.ToString("yyyy-MM-dd"), UserID = item.UserID, teaname = tea.Name, UpFile = item.UpFile, DocTitle = item.DocTitle, DocStatus = item.DocStatus, DocStatusName = await GetStatusByCodeAsync(item.DocStatus, item.DocStep ?? 0, item.zhipai, item.freestepid, null), editstatus = editstatus, DocStep = item.DocStep, flowid = item.flowid, }); } return result; } #endregion #region 内部辅助:流程挂链 / 抄送 / 附件 /// 自由流程挂链(旧 ApproveFlow.SetPrimarySteptID:从 stepid 起沿 laststep 链回写 dataid)。 private async Task SetPrimarySteptIDAsync(int dataid, int stepid) { var node = await _freeflowinfo.FirstOrDefaultAsync(a => a.SerID == stepid); if (node != null) { node.dataid = dataid; await _freeflowinfo.SaveChangesAsync(); } var nextSer = stepid; while (true) { var next = await _freeflowinfo.FirstOrDefaultAsync(a => a.laststep == nextSer); if (next == null) break; next.dataid = dataid; await _freeflowinfo.SaveChangesAsync(); nextSer = next.SerID; } } /// 抄送人落库(旧 ApproveFlow.SetChaoSend:按用户+flowType 全删再插)。 private async Task SetChaoSendAsync(int uid, string schSerId, string senduserid) { var olds = await _senduser.FindAsync(a => a.flowType == DocPrintConstants.AppCode && a.userid == uid); foreach (var old in olds) { await _senduser.RemoveAsync(old); } await _senduser.SaveChangesAsync(); await _senduser.AddAsync(new Common_SendUserinfo { userid = uid, flowType = DocPrintConstants.AppCode, Senduserid = senduserid, schserid = schSerId, createTime = DateTime.Now, }); await _senduser.SaveChangesAsync(); } /// 附件 TopicID 回写(旧 Create 的 Attach + SetModifiedProperty("TopicID"))。 private async Task BindFilesTopicAsync(string upFile, int topicId) { if (string.IsNullOrEmpty(upFile)) return; var fids = ParseIds(upFile); foreach (var fid in fids) { var file = await _files.FirstOrDefaultAsync(x => x.SerID == fid); if (file != null) { file.TopicID = topicId; await _files.UpdateAsync(file); } } await _files.SaveChangesAsync(); } /// 月份间隔(旧 MonthDiff:跨年分段补零)。 private static List MonthDiff(DateTime start, DateTime end) { var months = new List(); int sMonth = start.Month; int sYear = start.Year; int eMonth = end.Month; int eYear = end.Year; if (end.Year > start.Year) { var tempStart = sMonth; while (tempStart <= 12) { months.Add(sYear + "," + tempStart); tempStart++; } var tempEnd = 1; while (tempEnd <= eMonth) { months.Add(eYear + "," + tempEnd); tempEnd++; } } else { var tempStart = sMonth; var tempEnd = eMonth; while (tempStart <= tempEnd) { months.Add(eYear + "," + tempStart); tempStart++; } } return months; } #endregion } }