This commit is contained in:
wanghu
2026-09-02 10:23:24 +08:00
parent 5a414b14d2
commit ce436cbb32
117 changed files with 13768 additions and 0 deletions
@@ -0,0 +1,171 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Yh.Platform.Abstractions;
using YhCore.Entity;
using YhCore.Model.Dto;
using YhCore.Repository;
using YhCore.Service.Interfaces;
namespace YhCore.Service.Impl
{
/// <summary>
/// 公共字典服务实现(移植旧 BestSchool.Service.GetSysDict)。
/// </summary>
public class DictionaryService : IDictionaryService
{
private readonly IRepository<Common_Dictionary> _dic;
public DictionaryService(IRepository<Common_Dictionary> dic)
{
_dic = dic;
}
public async Task<List<DicOption>> GetDictListAsync(string typeId, string schId)
{
var has = await _dic.AnyAsync(d => d.typeID == typeId);
if (!has)
{
throw new DException("尚未添加发文部门,请联系管理员添加!");
}
var list = await _dic.FindAsync(d =>
d.typeID == typeId &&
(string.IsNullOrEmpty(schId) || d.Schserid == schId) &&
d.Enable == 1);
return list
.OrderBy(d => DictionaryTypeHelper.ParseSort(d.Des))
.Select(d => new DicOption { K = d.CodeID, V = d.Name })
.ToList();
}
public Task<List<DicOption>> GetDicTypeListAsync()
{
var list = DictionaryTypeHelper.AllTypes
.Where(t => t.Id != "DEPT")
.Select(t => new DicOption { K = t.Id, V = t.Name })
.ToList();
return Task.FromResult(list);
}
public async Task<List<DicManageItem>> GetDictMangeListAsync(string typeId, string schId)
{
var has = await _dic.AnyAsync(d => d.typeID == typeId);
if (!has)
{
throw new DException("字典类型错误,未找到该字典类型");
}
var list = await _dic.FindAsync(d =>
d.typeID == typeId &&
(string.IsNullOrEmpty(schId) || d.Schserid == schId));
return list
.OrderBy(d => string.IsNullOrEmpty(d.Des) ? 999 : DictionaryTypeHelper.ParseSort(d.Des))
.Select(d => new DicManageItem
{
SerID = d.SerID,
CodeID = d.CodeID,
Name = d.Name,
Des = d.Des,
Enable = d.Enable
})
.ToList();
}
public async Task AddDicAsync(Common_Dictionary model)
{
if (model == null) throw new DException("参数错误!");
if (string.IsNullOrWhiteSpace(model.Name)) throw new DException("输入字符串格式不正确!");
var listed = await _dic.AnyAsync(b =>
b.typeID == model.typeID &&
(b.Schserid == model.Schserid || b.Schserid == null || b.Schserid == "") &&
b.Name == model.Name && b.Enable != 3);
if (listed || await IsDicnameAsync(model.typeID, model.Name))
{
throw new DException("已存在该分类,请重新填写!");
}
var candidates = await _dic.FindAsync(x =>
x.typeID == model.typeID && x.Enable != 3 &&
(x.Schserid == model.Schserid || x.Schserid == null || x.Schserid == ""));
if (candidates.Count == 0)
{
model.Sort = 1;
model.Des = "1";
}
else
{
var maxSort = candidates.Max(c => c.Sort ?? (int)c.SerID);
model.Sort = maxSort + 1;
model.Des = model.Sort.ToString();
}
model.Enable = 1;
model.CodeID = model.typeID + model.Sort.ToString();
await _dic.AddAsync(model);
await _dic.SaveChangesAsync();
}
public async Task UpdateDicAsync(Common_Dictionary model)
{
if (model == null) throw new DException("参数错误!");
var dup = await _dic.AnyAsync(m =>
m.SerID != model.SerID && m.Name == model.Name &&
(m.Schserid == model.Schserid || m.Schserid == null) && m.Enable != 3);
if (dup || await IsDicnameAsync(model.typeID, model.Name))
{
throw new DException("已存在该分类,请重新填写!");
}
var obj = await _dic.FirstOrDefaultAsync(x => x.SerID == model.SerID);
if (obj == null) throw new DException("未找到要更新的分类!");
obj.Name = model.Name;
obj.Des = model.Des;
obj.Sort = model.Sort;
obj.Enable = model.Enable;
await _dic.UpdateAsync(obj);
await _dic.SaveChangesAsync();
}
public async Task EditDicAsync(Common_Dictionary model)
{
if (model == null) throw new DException("参数错误!");
var current = await _dic.FirstOrDefaultAsync(m => m.SerID == model.SerID);
if (current == null) throw new DException("未找到要编辑的分类!");
var dup = await _dic.AnyAsync(m =>
m.SerID != model.SerID && m.Enable != 3 && m.typeID == current.typeID &&
m.Name == model.Name && (m.Schserid == model.Schserid || m.Schserid == null));
if (dup)
{
throw new DException("已存在该分类,请重新填写!");
}
current.Name = model.Name;
current.Code = model.Code;
current.Path = model.Path;
await _dic.UpdateAsync(current);
await _dic.SaveChangesAsync();
}
/// <summary>判断全局(Schserid 为空)是否已存在同名类别(对齐旧 IsDicname)。</summary>
private async Task<bool> IsDicnameAsync(string typeId, string dicName)
{
var names = await _dic.FindAsync(b => b.typeID == typeId && b.Schserid == null);
return names.Select(a => a.Name).Distinct().Contains(dicName);
}
}
}
@@ -0,0 +1,338 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Yh.Platform.Abstractions;
using YhCore.Entity;
using YhCore.Model.Dto;
using YhCore.Repository;
using YhCore.Service.Interfaces;
namespace YhCore.Service.Impl
{
/// <summary>
/// 文印分类 / 印制版面 / 公共类别服务实现
/// (移植旧 BestSchool.DocPrint.WY_list 分类部分 + WY_Classify)。
/// </summary>
public class DocPrintClassifyService : IDocPrintClassifyService
{
private readonly IRepository<Common_Dictionary> _dic;
private readonly IRepository<Common_DictionaryOrd> _ord;
private readonly IRepository<WY_DocPrint> _doc;
private readonly IRepository<Common_Groupuser> _groupuser;
private readonly IRepository<Common_freeGroupuser> _freegroup;
private readonly IRepository<WY_SchZyBit> _zybit;
public DocPrintClassifyService(
IRepository<Common_Dictionary> dic,
IRepository<Common_DictionaryOrd> ord,
IRepository<WY_DocPrint> doc,
IRepository<Common_Groupuser> groupuser,
IRepository<Common_freeGroupuser> freegroup,
IRepository<WY_SchZyBit> zybit)
{
_dic = dic;
_ord = ord;
_doc = doc;
_groupuser = groupuser;
_freegroup = freegroup;
_zybit = zybit;
}
#region 文印分类 / 印制版面
public async Task AddDicAsync(Common_Dictionary model)
{
if (model == null) throw new DException("参数错误!");
var listed = await _dic.AnyAsync(b =>
b.typeID == model.typeID && b.Name == model.Name && b.Schserid == model.Schserid);
if (listed) throw new DException("该分类已经存在!");
var max = (await _dic.FindAsync(x => x.typeID == model.typeID))
.OrderByDescending(x => x.SerID).FirstOrDefault();
model.Sort = max != null ? (max.Sort ?? 0) + 1 : 1;
model.Enable = 1;
model.CodeID = model.typeID + model.Sort;
model.typeName = "印制分类";
await _dic.AddAsync(model);
await _dic.SaveChangesAsync();
}
public async Task AddYZBMAsync(Common_Dictionary model)
{
if (model == null) throw new DException("参数错误!");
var listed = await _dic.AnyAsync(b =>
b.typeID == model.typeID && b.Name == model.Name && b.Schserid == model.Schserid);
if (listed) throw new DException("该版面已经存在!");
var max = (await _dic.FindAsync(x => x.typeID == model.typeID))
.OrderByDescending(x => x.SerID).FirstOrDefault();
model.Sort = max != null ? (max.Sort ?? 0) + 1 : 1;
model.Enable = 1;
model.CodeID = model.typeID + model.Sort;
model.typeName = "印制版面";
await _dic.AddAsync(model);
await _dic.SaveChangesAsync();
}
public async Task<List<CategoryItem>> CategoryListAsync(string schSerId, string name)
=> await QueryClassifyListAsync("印制分类", schSerId, name);
public async Task<List<CategoryItem>> YZBMListAsync(string schSerId, string name)
=> await QueryClassifyListAsync("印制版面", schSerId, name);
private async Task<List<CategoryItem>> QueryClassifyListAsync(string typeName, string schSerId, string name)
{
var query = await _dic.FindAsync(m => m.typeName == typeName);
if (!string.IsNullOrEmpty(schSerId))
{
query = query.Where(m => m.Schserid == schSerId || m.Schserid == null || m.Schserid == "").ToList();
}
if (!string.IsNullOrEmpty(name))
{
query = query.Where(m => m.Name != null && m.Name.IndexOf(name) >= 0).ToList();
}
return query
.OrderBy(x => string.IsNullOrEmpty(x.Des) ? 999 : DictionaryTypeHelper.ParseSort(x.Des))
.Select(item => new CategoryItem
{
SerID = item.SerID,
CodeID = item.CodeID,
Name = item.Name,
Des = item.Des,
Enable = item.Enable,
Sort = item.Sort,
SchSerID = item.Schserid,
UserSchSerID = schSerId,
_disabled = string.IsNullOrEmpty(item.Schserid)
})
.ToList();
}
public async Task DeleteDicAsync(decimal[] ids)
{
if (ids == null || ids.Length == 0) throw new DException("ids is not null");
var items = await _dic.FindAsync(m => ids.Contains(m.SerID));
if (items.Count == 0) throw new DException("未找到要删除的分类!");
foreach (var item in items)
{
var used = await _doc.AnyAsync(m => m.DPType == item.CodeID);
if (used) throw new DException("该类别下有使用记录无法删除!");
}
foreach (var item in items)
{
await _dic.RemoveAsync(item);
}
await _dic.SaveChangesAsync();
}
#endregion
#region 公共类别
public async Task<List<DictionaryTypeItem>> DictionaryTypeListAsync(string schSerId, string name, bool showAll, string typeId)
{
var resultList = await _dic.FindAsync(m => m.typeID == typeId && m.Enable == 1);
if (!string.IsNullOrEmpty(schSerId))
{
resultList = resultList.Where(m => m.Schserid == schSerId || m.Schserid == null || m.Schserid == "").ToList();
}
if (!string.IsNullOrEmpty(name))
{
resultList = resultList.Where(m => m.Name != null && m.Name.IndexOf(name) >= 0).ToList();
}
var ordList = await _ord.FindAsync(a => a.SchSerID == schSerId);
var items = resultList.Select(item => new DictionaryTypeItem
{
Code = item.Code,
SerID = item.SerID,
CodeID = item.CodeID,
Name = item.Name,
Des = item.Des,
Enable = item.Enable,
Sort = item.Sort,
SchSerID = item.Schserid,
UserSchSerID = schSerId,
_disabled = string.IsNullOrEmpty(item.Schserid),
Status = false,
Open = ordList.FirstOrDefault(a => a.DicSerID == (int)item.SerID)?.Open ?? true,
Ord = ordList.FirstOrDefault(a => a.DicSerID == (int)item.SerID)?.Ord
?? (string.IsNullOrEmpty(item.Des) ? 999 : DictionaryTypeHelper.ParseSort(item.Des)),
Path = item.Path
})
.OrderBy(a => a.Ord)
.ToList();
if (!showAll)
{
items = items.Where(a => a.Open).ToList();
}
return items;
}
public async Task DeleteDictionaryTypeAsync(string[] ids)
{
if (ids == null || ids.Length == 0) throw new DException("ids is not null");
var items = await _dic.FindAsync(m => ids.Contains(m.SerID.ToString()));
foreach (var item in items)
{
item.Enable = 3;
}
await _dic.SaveChangesAsync();
}
public async Task SetDictionaryOrdAsync(string schSerId, int dicSerId, bool show)
{
var obj = await _ord.FirstOrDefaultAsync(m => m.DicSerID == dicSerId && m.SchSerID == schSerId);
if (obj == null)
{
await _ord.AddAsync(new Common_DictionaryOrd
{
DicSerID = dicSerId,
Open = show,
SchSerID = schSerId
});
}
else
{
obj.Open = show;
await _ord.UpdateAsync(obj);
}
await _ord.SaveChangesAsync();
}
public async Task OrderDicAsync(string ids, string schSerId)
{
if (string.IsNullOrEmpty(ids)) throw new DException("ids is not null");
var ssids = ids.Split(',').Select(a => int.Parse(a)).ToArray();
var list = await _ord.FindAsync(m => m.SchSerID == schSerId);
var i = 0;
foreach (var id in ssids)
{
var obj = list.FirstOrDefault(a => a.DicSerID == id && a.SchSerID == schSerId);
if (obj == null)
{
await _ord.AddAsync(new Common_DictionaryOrd
{
DicSerID = id,
Open = true,
SchSerID = schSerId,
Ord = i
});
}
else
{
obj.Ord = i;
await _ord.UpdateAsync(obj);
}
i++;
}
await _ord.SaveChangesAsync();
}
#endregion
#region 文印资源开关 / 结果链接 / 待审角标
/// <summary>
/// 结果链接:旧实现调用资源平台 ziyuan 的 /SaveResource/Batch 生成分享链接,
/// 新项目未接入资源平台,按「未配置 ziyuan」的旧分支降级返回 false。
/// </summary>
public Task<object> GetResultUrlAsync(int dataId, int uid, int schId, string schSerId)
{
return Task.FromResult<object>(false);
}
public async Task<bool> GetSchZyBitAsync(string schSerId)
{
// 旧实现仅在 ziyuan 配置存在时返回库值、否则 false;新项目未接入 ziyuan,直接返回库内开关值。
var bit = await _zybit.FirstOrDefaultAsync(a => a.SchSerID == schSerId);
return bit?.ziyuanbit ?? true;
}
public async Task SetSchZyBitAsync(string schSerId, bool zyBit)
{
var obj = await _zybit.FirstOrDefaultAsync(a => a.SchSerID == schSerId);
if (obj == null)
{
await _zybit.AddAsync(new WY_SchZyBit { SchSerID = schSerId, ziyuanbit = zyBit });
}
else
{
obj.ziyuanbit = zyBit;
await _zybit.UpdateAsync(obj);
}
await _zybit.SaveChangesAsync();
}
public async Task<int> GetShnheCountAsync(string userId, string schSerId)
{
if (!int.TryParse(userId, out var userid))
{
return 0;
}
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 result = new HashSet<decimal>();
foreach (var a in docs)
{
bool match = false;
// 固定流程:DocStep 命中本人组
if (a.flowid != null && a.zhipai == null && a.DocStep.HasValue && fixedGroupIds.Contains(a.DocStep.Value))
{
match = true;
}
// 指派:zhipai 含本人
if (!match && a.zhipai != null && a.flowid != null && a.zhipai.Contains(userId))
{
match = true;
}
// 自由流程:freestepid 命中本人自由组
if (!match && a.freestepid.HasValue && a.freestepid.Value > 0 && freeGroupIds.Contains(a.freestepid.Value))
{
match = true;
}
if (match)
{
result.Add(a.SerID);
}
}
return result.Count;
}
#endregion
}
}
@@ -0,0 +1,213 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Yh.SchClass.OpenApi;
using YhCore.Entity;
using YhCore.Model.Dto;
using YhCore.Repository;
using YhCore.Service.Interfaces;
namespace YhCore.Service.Impl
{
/// <summary>
/// 文印统计看板服务(移植旧 BestSchool.DocPrint.DocPrintStatistics,4 接口,ExportRecord 延后)。
/// 旧 [NoAuth] 无用户上下文,schId 由前端传入;新库 SchSerID 已是 int 字符串,直接 schId.ToString() 匹配,
/// 不再依赖旧 GetSchSerID(int) 的 HTTP 转换。roleId 参数旧实现零引用,已删除。
/// 旧 StatusArray = {"1","11"}(印刷中/已印刷)。
/// </summary>
public class DocPrintStatisticsService : IDocPrintStatisticsService
{
private static readonly string[] StatusArray = { "1", "11" };
private readonly IRepository<WY_DocPrint> _doc;
private readonly IRepository<Common_Dictionary> _dic;
private readonly SchClassOpenApi _sch;
public DocPrintStatisticsService(
IRepository<WY_DocPrint> doc,
IRepository<Common_Dictionary> dic,
SchClassOpenApi sch)
{
_doc = doc;
_dic = dic;
_sch = sch;
}
public async Task<List<DocPrintCountItem>> GetDocPrintCountAsync(int schId, DateTime? startDate = null, DateTime? endDate = null)
{
var schSerId = schId.ToString();
var list = (await _doc.FindAsync(a =>
a.SchSerID == schSerId && StatusArray.Contains(a.DocStatus))).ToList();
if (startDate != null)
list = list.Where(a => a.EditDate >= startDate).ToList();
if (endDate != null)
list = list.Where(a => a.EditDate <= endDate).ToList();
var count = list.Count;
var xhzsl = list.Select(a => a.DocNum).DefaultIfEmpty(0).Sum();
var doctypeId = list.GroupBy(a => a.DPType)
.Select(a => new { type = a.Key, sum = a.Sum(s => s.DocNum) })
.OrderByDescending(a => a.sum).FirstOrDefault()?.type ?? "";
string yzfl = "";
if (!string.IsNullOrEmpty(doctypeId))
{
yzfl = (await _dic.FindAsync(g =>
g.typeID == "YZFL" && g.CodeID == doctypeId &&
(g.Schserid == schSerId || g.Schserid == null)))
.OrderBy(g => g.Enable).Select(g => g.Name).FirstOrDefault() ?? "";
}
return new List<DocPrintCountItem>
{
new DocPrintCountItem { k = "文印数量(条)", v = count },
new DocPrintCountItem { k = "消耗总数(张)", v = xhzsl },
new DocPrintCountItem { k = "最多消耗类型", v = yzfl },
};
}
public async Task<List<PieChartSeries>> GetPieChartAsync(int schId, DateTime? startDate = null, DateTime? endDate = null)
{
var schSerId = schId.ToString();
var list = (await _doc.FindAsync(a =>
a.SchSerID == schSerId && StatusArray.Contains(a.DocStatus))).ToList();
if (startDate != null)
list = list.Where(a => a.EditDate >= startDate).ToList();
if (endDate != null)
list = list.Where(a => a.EditDate <= endDate).ToList();
var yzfl = (await _dic.FindAsync(g =>
g.typeID == "YZFL" && (g.Schserid == schSerId || g.Schserid == null)))
.OrderBy(g => g.Enable).ToList();
var yslx = list.GroupBy(a => a.DPType)
.Select(a => new { k = a.Key, v = a.Sum(s => s.DocNum) }).ToList()
.Select(a => new PieChartKv { k = yzfl.FirstOrDefault(s => s.CodeID == a.k)?.Name, v = a.v }).ToList();
var ysxh = list.GroupBy(a => a.DPType)
.Select(a => new { k = a.Key, v = a.Count() }).ToList()
.Select(a => new PieChartKv { k = yzfl.FirstOrDefault(s => s.CodeID == a.k)?.Name, v = a.v }).ToList();
return new List<PieChartSeries>
{
new PieChartSeries { title = "印刷类型", list = yslx },
new PieChartSeries { title = "印刷消耗", list = ysxh },
};
}
public async Task<List<DocPrintTypeItem>> GetDocPrintTypeAsync(int schId)
{
var schSerId = schId.ToString();
var list = await _dic.FindAsync(g =>
g.typeID == "YZFL" && g.Enable == 1 && (g.Schserid == schSerId || g.Schserid == null));
return list.OrderBy(g => g.Des)
.Select(a => new DocPrintTypeItem { CodeID = a.CodeID, Name = a.Name })
.ToList();
}
public async Task<DocPrintListResult> GetDocPrintListAsync(int schId, string docType, string likeName, int pageIndex, int pageSize, DateTime? startDate = null, DateTime? endDate = null)
{
var schSerId = schId.ToString();
if (pageSize <= 0) pageSize = 10;
var list = (await _doc.FindAsync(a =>
a.SchSerID == schSerId && StatusArray.Contains(a.DocStatus))).ToList();
if (!string.IsNullOrEmpty(docType) && docType != "ALL")
list = list.Where(a => a.DPType == docType).ToList();
if (startDate != null)
list = list.Where(a => a.TakeDate >= startDate).ToList();
if (endDate != null)
list = list.Where(a => a.TakeDate <= endDate).ToList();
if (!string.IsNullOrEmpty(likeName))
{
var teaMap = await GetTeaMapAsync(schSerId);
var userids = teaMap.Where(a => a.Value.Name.Contains(likeName))
.Select(a => a.Key.ToString()).ToArray();
list = list.Where(m =>
(m.DocTitle != null && m.DocTitle.Contains(likeName)) ||
(m.DocContent != null && m.DocContent.Contains(likeName)) ||
(m.UserID != null && userids.Contains(m.UserID))).ToList();
}
var totle = list.Count;
if (totle == 0)
return new DocPrintListResult { tlist = new List<DocPrintStatItem>(), totle = totle };
// 旧 1 基分页 → 0 基
pageIndex -= 1;
if (pageIndex < 0) pageIndex = 0;
var yzflList = (await _dic.FindAsync(g =>
g.typeID == "YZFL" && (g.Schserid == schSerId || g.Schserid == null)))
.OrderBy(g => g.Des)
.Select(a => new DocPrintTypeItem { CodeID = a.CodeID, Name = a.Name }).ToList();
var yzbmList = (await _dic.FindAsync(g =>
g.typeID == "YZBM" && (g.Schserid == schSerId || g.Schserid == null)))
.OrderBy(g => g.Des)
.Select(a => new DocPrintTypeItem { CodeID = a.CodeID, Name = a.Name }).ToList();
var teaMap2 = await GetTeaMapAsync(schSerId);
var tlist = list.OrderByDescending(a => a.TakeDate)
.Skip(pageIndex * pageSize).Take(pageSize)
.Select(item => new DocPrintStatItem
{
SerID = (int)item.SerID,
DocContent = item.DocContent,
DocType = item.DPType,
DocNum = item.DocNum,
DocTypeName = yzflList.FirstOrDefault(a => a.CodeID == item.DPType)?.Name,
DocStyle = item.DocStyle,
DocStyleName = yzbmList.FirstOrDefault(a => a.CodeID == item.DocStyle)?.Name,
EditDate = item.EditDate.ToString("yyyy-MM-dd HH:mm"),
TakeDate = item.TakeDate?.ToString("yyyy-MM-dd"),
UserID = item.UserID,
teaname = GetTeaName(teaMap2, item.UserID),
UpFile = item.UpFile,
DocTitle = item.DocTitle,
DocStatus = item.DocStatus,
DocStatusName = item.DocStatus == "1" ? "印刷中" : "已印刷",
DocStep = item.DocStep,
flowid = item.flowid,
FreeUserID = item.FreeUserID,
SendUserID = item.SendUserID,
freestepid = item.freestepid,
DocShare = item.DocShare,
explain = item.explain,
})
.ToList();
return new DocPrintListResult { tlist = tlist, totle = totle };
}
#region 辅助
private async Task<Dictionary<int, (string Name, string Photo)>> 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 string GetTeaName(Dictionary<int, (string Name, string Photo)> teaMap, string userId)
{
if (!int.TryParse(userId, out var uid)) return "";
return teaMap.TryGetValue(uid, out var tea) ? tea.Name : "";
}
#endregion
}
}
+235
View File
@@ -0,0 +1,235 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Yh.Platform.Abstractions;
using YhCore.Common.Config;
using YhCore.Entity;
using YhCore.Model.Dto;
using YhCore.Repository;
using YhCore.Service.Interfaces;
namespace YhCore.Service.Impl
{
/// <summary>
/// 公共文件服务实现(见 IFileService 注释)。
/// 降级:Office→PDF(ToOfficeHelper)新项目缺失,上传不转 PDF;预览兜底 officeapps.live.com。
/// </summary>
public class FileService : IFileService
{
private readonly IRepository<Common_UpFiles> _upfiles;
public FileService(IRepository<Common_UpFiles> upfiles)
{
_upfiles = upfiles;
}
public async Task<FileObj> GetEnclosureAsync(int id)
{
var model = await _upfiles.FirstOrDefaultAsync(x => x.SerID == id);
if (model == null)
throw new DException("文件ID错误!");
return new FileObj
{
fileurl = model.FileName,
filename = model.FileTrueName,
filetype = model.FileType,
filesize = model.FileSize ?? 0,
};
}
public async Task<int> SaveEnclosureAsync(FileObj file, string appType)
{
// 替换文件名中的特殊字符(旧 FileSaveEnclosure)
string safeName = file.filename ?? "";
foreach (var c in Path.GetInvalidFileNameChars())
safeName = safeName.Replace(c, '_');
safeName = safeName.Replace(",", "_").Replace(",", "_");
var model = new Common_UpFiles
{
FileName = file.fileurl,
FileSize = file.filesize,
FileTrueName = safeName,
FileType = file.filetype,
DateAndTime = DateTime.Now,
TopicType = appType,
};
await _upfiles.AddAsync(model);
await _upfiles.SaveChangesAsync();
return model.SerID;
}
public async Task<FileObj> SaveFileAsync(string appType, IFormFile file)
{
if (file == null || file.Length == 0)
throw new DException("上传失败!");
if (appType.ToLower() == "noticemsg") appType = "notice";
string dtStr = DateTime.Now.ToString("yyyyMMdd");
string physicalDir = Path.Combine(StorageRoot(), appType, dtStr);
string outFilePath = "/UploadFile/" + appType + "/" + dtStr + "/";
if (!Directory.Exists(physicalDir))
Directory.CreateDirectory(physicalDir);
string guid = Guid.NewGuid().ToString();
string fileName = Path.GetFileName(file.FileName);
string suffixName = Path.GetExtension(file.FileName);
string[] suffArry = { "jpg", "gif", "png", "xls", "doc", "txt", "docx", "xlsx", "zip", "rar", "pdf", "ppt", "pptx" };
suffArry = suffArry.Select(x => "." + x).ToArray();
bool isSuffType = suffArry.Any(x => x == suffixName.ToLower());
if (!isSuffType)
throw new DException("不能上传" + suffixName + "格式的文件");
int fileSize = (int)file.Length;
string savePath = Path.Combine(physicalDir, guid + suffixName);
using (var stream = new FileStream(savePath, FileMode.Create))
{
await file.CopyToAsync(stream);
}
// 旧 ToOfficeHelper.OfficeConvertToPDF(Office COM)新项目缺失 → 降级不转 PDF
return new FileObj
{
fileurl = outFilePath + guid + suffixName,
filename = fileName,
filetype = suffixName,
filesize = fileSize,
host = Config.FileWebPath,
};
}
public async Task<List<FileObj>> SaveFileImageAsync(string appType, IFormFileCollection files)
{
if (appType.ToLower() == "noticemsg") appType = "notice";
string dtStr = DateTime.Now.ToString("yyyyMMdd");
string physicalDir = Path.Combine(StorageRoot(), appType, dtStr);
string outFilePath = "/UploadFile/" + appType + "/" + dtStr + "/";
if (!Directory.Exists(physicalDir))
Directory.CreateDirectory(physicalDir);
string[] suffArry = { "jpg", "gif", "png", "jpeg" };
suffArry = suffArry.Select(x => "." + x).ToArray();
var list = new List<FileObj>();
foreach (var file in files)
{
if (file == null || file.Length == 0) continue;
string suffixName = Path.GetExtension(file.FileName);
if (!suffArry.Any(x => x == suffixName.ToLower())) continue;
string guid = Guid.NewGuid().ToString();
string fileName = Path.GetFileName(file.FileName);
int fileSize = (int)file.Length;
string savePath = Path.Combine(physicalDir, guid + suffixName);
using (var stream = new FileStream(savePath, FileMode.Create))
{
await file.CopyToAsync(stream);
}
list.Add(new FileObj
{
fileurl = outFilePath + guid + suffixName,
filename = fileName,
filetype = suffixName,
filesize = fileSize,
host = Config.FileWebPath,
});
}
return list;
}
public async Task SaveChunkAsync(string guid, string chunkIndex, IFormFile chunk)
{
if (string.IsNullOrEmpty(guid) || chunk == null || chunk.Length == 0)
throw new DException("上传失败!");
var dir = Path.Combine(StorageRoot(), guid);
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
string filePath = Path.Combine(dir, chunkIndex);
using (var stream = new FileStream(filePath, FileMode.Create))
{
await chunk.CopyToAsync(stream);
}
}
public async Task<FileObj> MergeSaveAsync(string appType, string guid, string fileName)
{
if (string.IsNullOrEmpty(guid) || string.IsNullOrEmpty(fileName))
throw new DException("上传失败!");
if (appType.ToLower() == "noticemsg") appType = "notice";
string dtStr = DateTime.Now.ToString("yyyyMMdd");
string physicalDir = Path.Combine(StorageRoot(), appType, dtStr);
string outFilePath = "/UploadFile/" + appType + "/" + dtStr + "/";
if (!Directory.Exists(physicalDir))
Directory.CreateDirectory(physicalDir);
var uploadDir = Path.Combine(StorageRoot());
var dir = Path.Combine(uploadDir, guid);
if (!Directory.Exists(dir))
throw new DException("上传失败!");
var files = Directory.GetFiles(dir);
string fileRealName = Path.GetFileName(fileName);
string suffixName = Path.GetExtension(fileRealName);
string fileGuid = Guid.NewGuid().ToString();
string outUrl = outFilePath + fileGuid + suffixName;
string finalPath = Path.Combine(physicalDir, fileGuid + suffixName);
using (var fs = new FileStream(finalPath, FileMode.Create))
{
foreach (var part in files.OrderBy(x => x.Length).ThenBy(x => x))
{
var bytes = await File.ReadAllBytesAsync(part);
fs.Write(bytes, 0, bytes.Length);
File.Delete(part);
}
}
int fileSize = (int)(new FileInfo(finalPath).Length / 1024 / 1024); // 旧实现以 MB 存储,保持一致
Directory.Delete(dir);
// 旧 ToOfficeHelper.OfficeConvertToPDF 缺失 → 降级不转 PDF
return new FileObj
{
fileurl = outUrl,
filename = fileRealName,
filetype = suffixName,
filesize = fileSize,
host = Config.FileWebPath,
};
}
public string MapPhysicalPath(string fileUrl)
{
if (string.IsNullOrEmpty(fileUrl))
return "";
// 相对路径如 /UploadFile/docprint/20260101/{guid}.jpg → Config.FilePath + 子路径
var relative = fileUrl.TrimStart('/');
const string prefix = "UploadFile/";
if (relative.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
relative = relative.Substring(prefix.Length);
return Path.Combine(StorageRoot(), relative.Replace('/', Path.DirectorySeparatorChar));
}
/// <summary>存储根目录(旧 Server.MapPath("~/UploadFile/");Config.FilePath 缺省时退回进程目录/UploadFile)。</summary>
private static string StorageRoot()
{
if (!string.IsNullOrEmpty(Config.FilePath))
return Config.FilePath;
return Path.Combine(AppContext.BaseDirectory, "UploadFile");
}
}
}
@@ -0,0 +1,595 @@
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
{
/// <summary>
/// 自由审批流服务(移植旧 BestSchool.Service.ApproveFlow)。
/// 教师查询改走教务 SDK(Yh.SchClass.OpenApi),教师标识用 teaId(TeaSerId)对齐新教务。
/// </summary>
public class FreeApproveService : IFreeApproveService
{
private readonly IRepository<Common_freeflowinfo> _freeflowinfo;
private readonly IRepository<Common_freeGroupuser> _freegroup;
private readonly IRepository<Common_flowtranctInfo> _flowtranct;
private readonly IRepository<Common_flowstep> _flowstep;
private readonly IRepository<Common_Groupuser> _groupuser;
private readonly IRepository<Common_UserFlowZhipai> _userflowzhipai;
private readonly YHDbContext _db;
private readonly SchClassOpenApi _sch;
public FreeApproveService(
IRepository<Common_freeflowinfo> freeflowinfo,
IRepository<Common_freeGroupuser> freegroup,
IRepository<Common_flowtranctInfo> flowtranct,
IRepository<Common_flowstep> flowstep,
IRepository<Common_Groupuser> groupuser,
IRepository<Common_UserFlowZhipai> userflowzhipai,
YHDbContext db,
SchClassOpenApi sch)
{
_freeflowinfo = freeflowinfo;
_freegroup = freegroup;
_flowtranct = flowtranct;
_flowstep = flowstep;
_groupuser = groupuser;
_userflowzhipai = userflowzhipai;
_db = db;
_sch = sch;
}
public async Task<List<FreeFlowNode>> GetFleeFlowAsync(int userId, int? dataid)
{
int appid = DocPrintConstants.AppCode;
if ((dataid ?? 0) == 0)
{
var all = await _freeflowinfo.FindAsync(a => a.flowType == appid && a.userid == userId);
dataid = all.Count == 0 ? 0 : all.Max(a => a.dataid ?? 0);
}
if ((dataid ?? 0) <= 0) return new List<FreeFlowNode>();
var nodes = (await _freeflowinfo.FindAsync(a => a.flowType == appid && a.userid == userId && a.dataid == dataid))
.OrderBy(a => a.SerID).ToList();
return await BuildFreeFlowNodesAsync(nodes);
}
public async Task<int> EditFleeFlowAsync(int userId, string schSerId, int? dataid, string flowUids, bool isFlowNull)
{
if (string.IsNullOrEmpty(flowUids))
{
if (!isFlowNull)
throw new DException("请选择审核步骤人员!");
flowUids = "";
}
var flowUidArr = flowUids.Split('_');
int appid = DocPrintConstants.AppCode;
int firstStepId = 0;
using var tx = await _db.Database.BeginTransactionAsync();
try
{
if ((dataid ?? 0) > 0)
{
var old = await _freeflowinfo.FindAsync(a => a.dataid == dataid && a.flowType == appid && a.userid == userId);
foreach (var item in old)
{
await _freeflowinfo.RemoveAsync(item.SerID);
}
await _freeflowinfo.SaveChangesAsync();
}
var upSeptid = 0;
var date = DateTime.Now;
foreach (var fw in flowUidArr)
{
var free = new Common_freeflowinfo
{
laststep = upSeptid,
userid = userId,
createTime = date,
dataid = dataid,
schserid = schSerId,
flowType = appid,
};
await _freeflowinfo.AddAsync(free);
await _freeflowinfo.SaveChangesAsync();
if (upSeptid == 0)
firstStepId = free.SerID;
upSeptid = free.SerID;
foreach (var guserid in fw.Split(','))
{
await _freegroup.AddAsync(new Common_freeGroupuser
{
flowid = upSeptid,
SchSerID = schSerId,
usergroup = upSeptid,
userid = string.IsNullOrEmpty(guserid) ? (int?)null : int.Parse(guserid),
});
}
await _freegroup.SaveChangesAsync();
}
await tx.CommitAsync();
return firstStepId;
}
catch
{
await tx.RollbackAsync();
throw;
}
}
public async Task<string> AddShenheRemarkAsync(int userId, int? dataid, string remark, int freestepid)
{
var info = new Common_flowtranctInfo
{
actionstatus = -1,
remark = remark,
dataid = dataid ?? 0,
stepid = freestepid,
datatype = DocPrintConstants.AppCode.ToString(),
actiondate = DateTime.Now,
actionuserid = userId,
};
await _flowtranct.AddAsync(info);
await _flowtranct.SaveChangesAsync();
return "备注成功!";
}
public async Task<List<FreeFlowNode>> GetAfterFleeFlowAsync(int? dataid, int freestepid)
{
int appid = DocPrintConstants.AppCode;
if ((dataid ?? 0) <= 0) return new List<FreeFlowNode>();
var nodes = (await _freeflowinfo.FindAsync(a => a.flowType == appid && a.SerID > freestepid && a.dataid == dataid))
.OrderBy(a => a.SerID).ToList();
return await BuildFreeFlowNodesAsync(nodes);
}
public async Task<int> EditAfterFleeFlowAsync(int? dataid, int freestepid, string flowUids)
{
int appid = DocPrintConstants.AppCode;
// 删除 freestepid 之后的剩余步骤
var nextSteps = await _freeflowinfo.FindAsync(a => a.dataid == dataid && a.SerID > freestepid);
if (nextSteps.Count > 0)
{
foreach (var item in nextSteps)
{
await _freeflowinfo.RemoveAsync(item.SerID);
}
await _freeflowinfo.SaveChangesAsync();
}
if (string.IsNullOrEmpty(flowUids))
return 0;
var flowUidArr = flowUids.Split('_');
var laststept = await _freeflowinfo.FirstOrDefaultAsync(a => a.SerID == freestepid);
if (laststept == null)
throw new DException("未找到自由流程步骤!");
var upSeptid = freestepid;
using var tx = await _db.Database.BeginTransactionAsync();
try
{
var date = DateTime.Now;
foreach (var fw in flowUidArr)
{
var free = new Common_freeflowinfo
{
laststep = upSeptid,
userid = laststept.userid,
createTime = date,
dataid = dataid,
schserid = laststept.schserid,
flowType = appid,
};
await _freeflowinfo.AddAsync(free);
await _freeflowinfo.SaveChangesAsync();
upSeptid = free.SerID;
foreach (var guserid in fw.Split(','))
{
await _freegroup.AddAsync(new Common_freeGroupuser
{
flowid = upSeptid,
SchSerID = laststept.schserid,
usergroup = upSeptid,
userid = string.IsNullOrEmpty(guserid) ? (int?)null : int.Parse(guserid),
});
}
await _freegroup.SaveChangesAsync();
}
await tx.CommitAsync();
return upSeptid;
}
catch
{
await tx.RollbackAsync();
throw;
}
}
public async Task<List<FreeShenheInfo>> ShenheFlowAsync(int uid, string ctime, int dataid, int? freestepid, int? flowid)
{
int flowType = DocPrintConstants.AppCode;
string appCode = DocPrintConstants.AppCode.ToString();
if ((freestepid ?? 0) > 0)
return await BuildFreeShenheFlowAsync(uid, ctime, dataid, freestepid, flowType, appCode);
if ((flowid ?? 0) > 0)
return await BuildFixShenheFlowAsync(uid, ctime, dataid, flowid, appCode);
return await BuildOnlyTranctShenheFlowAsync(uid, ctime, dataid, appCode);
}
public async Task<List<ChaoSendUser>> ChaoSendListAsync(string uids)
{
var result = new List<ChaoSendUser>();
if (string.IsNullOrEmpty(uids) || uids == "undefined")
return result;
var userids = uids.Split(',')
.Where(a => !string.IsNullOrEmpty(a) && int.TryParse(a, out _))
.Select(int.Parse).ToArray();
if (userids.Length == 0)
return result;
var teas = await _sch.TeaInfo.GetArrTeaInfoAsync(string.Join(",", userids));
return teas.Select(a => new ChaoSendUser
{
uid = ParseTeaId(a.TeaSerId),
photo = a.Photo,
name = a.Name,
status = false,
}).ToList();
}
#region 审核流程回显
private async Task<List<FreeShenheInfo>> BuildFreeShenheFlowAsync(int uid, string ctime, int dataid, int? freestepid, int flowType, string appCode)
{
var freeflow = (await _freeflowinfo.FindAsync(a => a.dataid == dataid && a.flowType == flowType))
.OrderBy(a => a.SerID).ToList();
var freeflowids = freeflow.Select(a => a.SerID).ToArray();
var freeflowGroup = (await _freegroup.FindAsync(a => freeflowids.Contains(a.usergroup ?? 0))).ToList();
var freeshenhe = await _flowtranct.FindAsync(a => a.dataid == dataid && a.datatype == appCode);
var frees = new List<FreeShenheInfo>();
var firstFlow = freeflow.FirstOrDefault();
frees.Add(new FreeShenheInfo
{
steptname = await GetUserNameAsync(firstFlow?.userid?.ToString() ?? ""),
steptstatus = "发起申请",
photo = await GetUserPhotoAsync(firstFlow?.userid?.ToString() ?? ""),
actiondate = firstFlow?.createTime?.ToString("yyyy-MM-dd HH:mm") ?? "",
});
if (freeshenhe.Count(a => a.actionstatus > 2) == 0)
{
foreach (var item in freeflow)
{
var act = new FreeShenheInfo();
var isShok = freeshenhe.FirstOrDefault(a => a.stepid == item.SerID);
if (isShok != null)
{
act.steptname = await ChaoSendNameAsync(isShok.actionuserid?.ToString());
act.photo = await GetUserPhotoAsync(isShok.actionuserid?.ToString());
act.steptstatus = ActionstatusName(isShok.actionstatus);
act.actionstatus = isShok.actionstatus.ToString();
act.Remark = isShok.remark;
act.actiondate = isShok.actiondate?.ToString("yyyy-MM-dd HH:mm") ?? "";
if (isShok.actionstatus == 2)
{
frees.Add(act);
break;
}
}
else
{
var userids = ToUserIds(freeflowGroup.Where(a => a.usergroup == item.SerID).Select(a => a.userid));
act.steptname = await ChaoSendNameAsync(string.Join(",", userids));
act.photo = userids.Length == 1 ? await GetUserPhotoAsync(userids[0].ToString()) : "/static/image/site/moreperpeo.png";
act.steptstatus = "待审核";
act.actionstatus = "0";
}
frees.Add(act);
}
}
else
{
foreach (var item in freeshenhe)
{
frees.Add(new FreeShenheInfo
{
steptname = await ChaoSendNameAsync((item.actionuserid ?? uid).ToString()),
photo = await GetUserPhotoAsync((item.actionuserid ?? uid).ToString()),
steptstatus = ActionstatusName(item.actionstatus),
actionstatus = item.actionstatus.ToString(),
Remark = item.remark,
actiondate = item.actiondate?.ToString("yyyy-MM-dd HH:mm") ?? "",
});
}
var zuihouyibu = freeshenhe.Where(a => a.actionstatus >= 2).OrderByDescending(a => a.SerID).FirstOrDefault();
if (zuihouyibu != null && zuihouyibu.actionstatus == 4)
{
var isShok = freeshenhe.FirstOrDefault(a => a.SerID > zuihouyibu.SerID);
if (isShok == null)
{
var userids = ToUserIds(freeflowGroup.Where(a => a.usergroup == freestepid).Select(a => a.userid));
frees.Add(new FreeShenheInfo
{
steptname = await ChaoSendNameAsync(string.Join(",", userids)),
photo = userids.Length == 1 ? await GetUserPhotoAsync(userids[0].ToString()) : "/static/image/site/moreperpeo.png",
steptstatus = "待审核",
actionstatus = "0",
});
}
}
else if (zuihouyibu != null && zuihouyibu.actionstatus == 6)
{
foreach (var item in freeflow)
{
var isShok = freeshenhe.FirstOrDefault(a => a.stepid == item.SerID && a.SerID > zuihouyibu.SerID);
if (isShok == null)
{
var userids = ToUserIds(freeflowGroup.Where(a => a.usergroup == item.SerID).Select(a => a.userid));
frees.Add(new FreeShenheInfo
{
steptname = await ChaoSendNameAsync(string.Join(",", userids)),
photo = userids.Length == 1 ? await GetUserPhotoAsync(userids[0].ToString()) : "/static/image/site/moreperpeo.png",
steptstatus = "待审核",
actionstatus = "0",
});
}
}
}
}
return frees;
}
private async Task<List<FreeShenheInfo>> BuildFixShenheFlowAsync(int uid, string ctime, int dataid, int? flowid, string appCode)
{
var fixflow = (await _flowstep.FindAsync(a => a.flowid == flowid)).OrderBy(a => a.SerID).ToList();
var fixflowids = fixflow.Select(a => a.SerID).ToArray();
var fixflowGroup = (await _groupuser.FindAsync(a => fixflowids.Contains(a.usergroup ?? 0))).ToList();
var fixshenhe = await _flowtranct.FindAsync(a => a.dataid == dataid && a.datatype == appCode);
var frees = new List<FreeShenheInfo>();
frees.Add(new FreeShenheInfo
{
steptname = await GetUserNameAsync(uid.ToString()),
steptstatus = "发起申请",
actiondate = ctime,
photo = await GetUserPhotoAsync(uid.ToString()),
});
if (fixshenhe.Count(a => a.actionstatus > 2) == 0)
{
foreach (var item in fixflow)
{
var act = new FreeShenheInfo();
var isShok = fixshenhe.FirstOrDefault(a => a.stepid == item.SerID);
if (isShok != null)
{
act.steptname = await ChaoSendNameAsync(isShok.actionuserid?.ToString());
act.photo = await GetUserPhotoAsync(isShok.actionuserid?.ToString());
act.steptstatus = ActionstatusName(isShok.actionstatus);
act.actionstatus = isShok.actionstatus.ToString();
act.Remark = isShok.remark;
act.actiondate = isShok.actiondate?.ToString("yyyy-MM-dd HH:mm") ?? "";
if (isShok.actionstatus == 2)
{
frees.Add(act);
break;
}
}
else
{
var userids = ToUserIds(fixflowGroup.Where(a => a.usergroup == item.SerID).Select(a => a.userid));
var userzhipai = await _userflowzhipai.FirstOrDefaultAsync(
a => a.MSerID == dataid && a.FlowID == flowid && a.FlowStepID == item.SerID);
if (!string.IsNullOrEmpty(userzhipai?.zhipai))
{
userids = userzhipai.zhipai.Split(',')
.Where(s => int.TryParse(s, out _))
.Select(int.Parse).Distinct().ToArray();
}
act.steptname = await ChaoSendNameAsync(string.Join(",", userids));
act.photo = userids.Length == 1 ? await GetUserPhotoAsync(userids[0].ToString()) : "/static/image/site/moreperpeo.png";
act.steptstatus = "待审核";
act.actionstatus = "0";
}
frees.Add(act);
}
}
else
{
foreach (var item in fixshenhe)
{
frees.Add(new FreeShenheInfo
{
steptname = await ChaoSendNameAsync((item.actionuserid ?? uid).ToString()),
photo = await GetUserPhotoAsync((item.actionuserid ?? uid).ToString()),
steptstatus = ActionstatusName(item.actionstatus),
actionstatus = item.actionstatus.ToString(),
Remark = item.remark,
actiondate = item.actiondate?.ToString("yyyy-MM-dd HH:mm") ?? "",
});
}
var zuihouyibu = fixshenhe.Where(a => a.actionstatus >= 2).OrderByDescending(a => a.SerID).FirstOrDefault();
if (zuihouyibu != null && zuihouyibu.actionstatus == 4)
{
var isShok = fixshenhe.FirstOrDefault(a => a.SerID > zuihouyibu.SerID);
if (isShok == null)
{
var lastStepId = fixflowids.OrderByDescending(p => p).FirstOrDefault();
var userids = ToUserIds(fixflowGroup.Where(a => a.usergroup == lastStepId).Select(a => a.userid));
frees.Add(new FreeShenheInfo
{
steptname = await ChaoSendNameAsync(string.Join(",", userids)),
photo = userids.Length == 1 ? await GetUserPhotoAsync(userids[0].ToString()) : "/static/image/site/moreperpeo.png",
steptstatus = "待审核",
actionstatus = "0",
});
}
}
else if (zuihouyibu != null && zuihouyibu.actionstatus == 6)
{
foreach (var item in fixflow)
{
var isShok = fixshenhe.FirstOrDefault(a => a.stepid == item.SerID && a.SerID > zuihouyibu.SerID);
if (isShok == null)
{
var userids = ToUserIds(fixflowGroup.Where(a => a.usergroup == item.SerID).Select(a => a.userid));
frees.Add(new FreeShenheInfo
{
steptname = await ChaoSendNameAsync(string.Join(",", userids)),
photo = userids.Length == 1 ? await GetUserPhotoAsync(userids[0].ToString()) : "/static/image/site/moreperpeo.png",
steptstatus = "待审核",
actionstatus = "0",
});
}
}
}
}
return frees;
}
private async Task<List<FreeShenheInfo>> BuildOnlyTranctShenheFlowAsync(int uid, string ctime, int dataid, string appCode)
{
var shenhe = await _flowtranct.FindAsync(a => a.dataid == dataid && a.datatype == appCode);
var frees = new List<FreeShenheInfo>();
frees.Add(new FreeShenheInfo
{
steptname = await GetUserNameAsync(uid.ToString()),
steptstatus = "发起申请",
actiondate = ctime,
photo = await GetUserPhotoAsync(uid.ToString()),
});
if (shenhe.Count(a => a.actionstatus > 2) > 0)
{
foreach (var item in shenhe)
{
frees.Add(new FreeShenheInfo
{
steptname = await ChaoSendNameAsync(item.actionuserid?.ToString()),
photo = await GetUserPhotoAsync(item.actionuserid?.ToString()),
steptstatus = ActionstatusName(item.actionstatus),
actionstatus = item.actionstatus.ToString(),
Remark = item.remark,
actiondate = item.actiondate?.ToString("yyyy-MM-dd HH:mm") ?? "",
});
}
}
return frees;
}
#endregion
#region 辅助
private async Task<List<FreeFlowNode>> BuildFreeFlowNodesAsync(List<Common_freeflowinfo> nodes)
{
var result = new List<FreeFlowNode>();
foreach (var node in nodes)
{
var userIds = (await _freegroup.FindAsync(n => n.flowid == node.SerID))
.Select(n => n.userid).Where(u => u.HasValue).Select(u => u.Value).ToList();
var users = new List<FreeFlowUser>();
if (userIds.Count > 0)
{
var teas = await _sch.TeaInfo.GetArrTeaInfoAsync(string.Join(",", userIds));
users = teas.Select(t => new FreeFlowUser
{
userid = ParseTeaId(t.TeaSerId),
name = t.Name,
photo = t.Photo,
}).ToList();
}
result.Add(new FreeFlowNode { user = users });
}
return result;
}
private static int ParseTeaId(string teaSerId)
=> int.TryParse(teaSerId, out var id) ? id : 0;
private static int[] ToUserIds(IEnumerable<int?> source)
=> source.Select(u => u ?? 0).ToArray();
private async Task<string> GetUserNameAsync(string teaId)
{
if (!int.TryParse(teaId, out var id)) return "";
var tea = await _sch.TeaInfo.GetTeaInfoAsync(id);
return tea?.Name ?? "";
}
private async Task<string> GetUserPhotoAsync(string teaId)
{
if (!int.TryParse(teaId, out var id)) return "";
var tea = await _sch.TeaInfo.GetTeaInfoAsync(id);
return tea?.Photo ?? "";
}
private async Task<string> ChaoSendNameAsync(string uids)
{
if (string.IsNullOrEmpty(uids) || uids == "undefined") return "";
var ids = uids.Split(',')
.Where(s => int.TryParse(s, out _)).Select(int.Parse).Distinct().ToArray();
if (ids.Length == 0) return "";
var teas = await _sch.TeaInfo.GetArrTeaInfoAsync(string.Join(",", ids));
return string.Join(",", teas.Select(t => t.Name).Where(n => !string.IsNullOrEmpty(n)));
}
private static string ActionstatusName(int actionstatus)
{
return actionstatus == -1 ? "备注"
: actionstatus == 0 ? "发起"
: actionstatus == 1 ? "已同意"
: actionstatus == 2 ? "拒绝"
: actionstatus == 3 ? "已撤销"
: actionstatus == 4 ? "撤销发起"
: actionstatus == 5 ? "通过(撤销被拒绝)"
: actionstatus == 6 ? "发起修改申请"
: actionstatus == 7 ? "已领取"
: actionstatus == 8 ? "已维修"
: actionstatus == 9 ? "已归还"
: actionstatus == 10 ? "已领取"
: actionstatus == 11 ? "印刷中"
: "其他";
}
#endregion
}
}
+18
View File
@@ -0,0 +1,18 @@
using YhCore.Entity;
using YhCore.Model.Vo;
using YhCore.Service.Interfaces;
namespace YhCore.Service.Impl
{
/// <summary>
/// 日志查询服务占位实现(框架期保证接口可运行)。
/// TODO:接入 Repository 层后实现真实查询(按 schId + systemScope 过滤、keyword 模糊匹配、分页)。
/// </summary>
public class LogServiceStub : ILogService
{
public PageVO<SysOperationLog> Search(int schId, string keyword, bool systemScope, int page, int size)
{
return PageVO<SysOperationLog>.Empty(page, size);
}
}
}
@@ -0,0 +1,530 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Yh.Platform.Abstractions;
using Yh.SchClass.OpenApi;
using Yh.SchClass.OpenApi.Model;
using YhCore.Common.UserContext;
using YhCore.Entity;
using YhCore.Model.Dto;
using YhCore.Model.Vo;
using YhCore.Repository;
using YhCore.Service.Interfaces;
namespace YhCore.Service.Impl
{
/// <summary>
/// 审核流程管理服务(移植旧 BestSchool.Service.Reviewprocess + UserFlowRole)。
/// 教师查询改走教务 SDK(Yh.SchClass.OpenApi),缺失 groupId 的局版接口降级为当前学校。
/// </summary>
public class ReviewProcessService : IReviewProcessService
{
private readonly IRepository<Common_flowinfo> _flowinfo;
private readonly IRepository<Common_flowstep> _flowstep;
private readonly IRepository<Common_Groupuser> _groupuser;
private readonly IRepository<Common_freeflowinfo> _freeflowinfo;
private readonly IRepository<Common_freeGroupuser> _freegroup;
private readonly IRepository<Common_SendUserinfo> _senduser;
private readonly IRepository<Common_UpFiles> _upfiles;
private readonly SchClassOpenApi _sch;
public ReviewProcessService(
IRepository<Common_flowinfo> flowinfo,
IRepository<Common_flowstep> flowstep,
IRepository<Common_Groupuser> groupuser,
IRepository<Common_freeflowinfo> freeflowinfo,
IRepository<Common_freeGroupuser> freegroup,
IRepository<Common_SendUserinfo> senduser,
IRepository<Common_UpFiles> upfiles,
SchClassOpenApi sch)
{
_flowinfo = flowinfo;
_flowstep = flowstep;
_groupuser = groupuser;
_freeflowinfo = freeflowinfo;
_freegroup = freegroup;
_senduser = senduser;
_upfiles = upfiles;
_sch = sch;
}
#region 流程与步骤
public Task<PageVO<FlowListItem>> List2Async(string name, int pageSize, int pageIndex, int flowType, bool edit, string schSerId)
=> ListAsync(name, pageSize, pageIndex, flowType, edit, schSerId);
public async Task<PageVO<FlowListItem>> ListAsync(string name, int pageSize, int pageIndex, int flowType, bool edit, string schSerId)
{
var resultList = await _flowinfo.FindAsync(m => m.flowType == flowType && m.schSerID == schSerId);
if (pageSize == 100 && pageIndex == 0)
{
if (!edit)
resultList = resultList.Where(a => a.enable == 1).ToList();
var stepFlowIds = (await _flowstep.FindAsync(s => true))
.Select(s => s.flowid).Where(f => f.HasValue).Select(f => f.Value).Distinct().ToHashSet();
resultList = resultList.Where(a => stepFlowIds.Contains(a.SerID)).ToList();
}
else
{
if (!edit)
resultList = resultList.Where(a => a.enable != 3).ToList();
}
if (!string.IsNullOrEmpty(name))
{
resultList = resultList.Where(m => m.flowname != null && m.flowname.IndexOf(name) >= 0).ToList();
}
var ordered = resultList.OrderBy(m => m.SerID).ToList();
var items = ordered.Skip(pageSize * pageIndex).Take(pageSize)
.Select(item => new FlowListItem
{
SerID = item.SerID.ToString(),
flowname = item.flowname,
deptid = item.deptid,
typeid = item.typeid,
enable = item.enable,
enablename = item.enable == 1 ? "是" : "否",
miaoshu = item.flowdes,
deitdate = item.deitdate.ToString("yyyy-MM-dd"),
})
.ToList();
return PageVO<FlowListItem>.Of(items, ordered.Count, pageIndex, pageSize);
}
public async Task<PageVO<StepListItem>> StepListAsync(string name, int serid)
{
var steps = await _flowstep.FindAsync(a => a.flowid == serid);
if (!string.IsNullOrEmpty(name))
{
steps = steps.Where(m => m.StepName != null && m.StepName.IndexOf(name) >= 0).ToList();
}
var ordered = steps.OrderBy(m => m.SerID).ToList();
var items = new List<StepListItem>();
foreach (var item in ordered)
{
items.Add(new StepListItem
{
SerID = item.SerID.ToString(),
StepName = item.StepName,
flowid = item.flowid?.ToString(),
prestep = item.prestep,
TeaName = await GetTeaNamesByStepAsync(item.SerID),
Teaid = await GetTeaIdsByStepAsync(item.SerID),
istrue = steps.Any(a => a.prestep == item.SerID),
});
}
return PageVO<StepListItem>.Of(items, ordered.Count, 0, ordered.Count == 0 ? 20 : ordered.Count);
}
public async Task CreateAsync(Common_flowinfo model)
{
if (model == null) throw new DException("参数错误!");
var flowname = model.flowname?.Trim();
var dup = await _flowinfo.AnyAsync(a =>
a.schSerID == model.schSerID && a.flowType == model.flowType &&
a.flowname != null && a.flowname.Trim() == flowname && a.enable != 3);
if (dup) throw new DException("流程名不能重名!");
var info = new Common_flowinfo
{
flowname = model.flowname,
deptid = model.deptid,
typeid = model.typeid,
flowdes = model.flowdes,
enable = model.enable,
flowType = model.flowType,
deitdate = DateTime.Now,
schSerID = model.schSerID,
};
await _flowinfo.AddAsync(info);
await _flowinfo.SaveChangesAsync();
}
public async Task EditAsync(Common_flowinfo model)
{
if (model == null) throw new DException("参数错误!");
var flowname = model.flowname?.Trim();
var dup = (await _flowinfo.FindAsync(a =>
a.schSerID == model.schSerID && a.flowType == DocPrintConstants.AppCode &&
a.flowname != null && a.flowname.Trim() == flowname && a.enable != 3)).Count;
if (dup > 1) throw new DException("流程名不能重名!");
var info = await _flowinfo.FirstOrDefaultAsync(a => a.SerID == model.SerID);
if (info == null) throw new DException("未找到要修改的流程!");
info.flowname = model.flowname;
info.deptid = model.deptid;
info.typeid = model.typeid;
info.flowdes = model.flowdes;
info.enable = model.enable;
await _flowinfo.UpdateAsync(info);
await _flowinfo.SaveChangesAsync();
}
public async Task StepCreateAsync(Common_flowstep model)
{
if (model == null) throw new DException("参数错误!");
var info = new Common_flowstep
{
StepName = model.StepName,
prestep = model.prestep,
flowid = model.flowid,
ordernum = 0,
};
await _flowstep.AddAsync(info);
await _flowstep.SaveChangesAsync();
if (string.IsNullOrEmpty(model.SchSerID))
{
throw new DException("添加失败!");
}
await AddGroupUsersAsync(info.SerID, model.flowid, model.SchSerID);
}
public async Task StepEditAsync(Common_flowstep model)
{
if (model == null) throw new DException("参数错误!");
var info = await _flowstep.FirstOrDefaultAsync(a => a.SerID == model.SerID);
if (info == null) throw new DException("未找到要修改的步骤!");
info.flowid = model.flowid;
info.StepName = model.StepName;
info.prestep = model.prestep;
await _flowstep.UpdateAsync(info);
// 重写该步骤的人员绑定(先清后加)
var existing = await _groupuser.FindAsync(a => a.usergroup == info.SerID);
foreach (var itt in existing)
{
await _groupuser.RemoveAsync(itt);
}
if (string.IsNullOrEmpty(model.SchSerID))
{
await _groupuser.SaveChangesAsync();
throw new DException("修改失败!");
}
await AddGroupUsersAsync(info.SerID, info.flowid, model.SchSerID);
}
private async Task AddGroupUsersAsync(int stepSerId, int? flowId, string userIds)
{
foreach (var idStr in userIds.Split(','))
{
if (!int.TryParse(idStr.Trim(), out var userId)) continue;
await _groupuser.AddAsync(new Common_Groupuser
{
usergroup = stepSerId,
flowid = flowId,
userid = userId,
IsOk = 0,
});
}
await _groupuser.SaveChangesAsync();
}
public async Task DeleteAsync(string ids)
{
if (string.IsNullOrWhiteSpace(ids)) throw new DException("ids is not null");
var lids = ids.Split(',').Select(a => int.Parse(a)).ToArray();
var flows = await _flowinfo.FindAsync(a => lids.Contains(a.SerID));
foreach (var item in flows)
{
item.enable = 3;
}
await _flowinfo.SaveChangesAsync();
}
public async Task StepDeleteAsync(decimal[] ids)
{
if (ids == null || ids.Length == 0) throw new DException("ids is not null");
var intIds = ids.Select(d => (int)d).ToArray();
var objlist = await _flowstep.FindAsync(m => intIds.Contains(m.SerID));
foreach (var item in objlist)
{
await _flowstep.RemoveAsync(item);
}
await _flowstep.SaveChangesAsync();
}
public async Task<List<BindStepItem>> BindstepListAsync(int serid)
{
var steps = await _flowstep.FindAsync(a => a.flowid == serid);
return steps.OrderBy(a => a.prestep)
.Select(a => new BindStepItem { SerID = a.SerID, StepName = a.StepName })
.ToList();
}
public async Task<List<BindGroupuserItem>> BindGroupuserListAsync(int flowid, int prestep)
{
var steps = await _flowstep.FindAsync(a => a.flowid == flowid && a.prestep == prestep);
var result = new List<BindGroupuserItem>();
foreach (var step in steps)
{
var users = await _groupuser.FindAsync(b => b.usergroup == step.SerID);
foreach (var u in users)
{
result.Add(new BindGroupuserItem
{
StepId = step.SerID,
userid = u.userid,
StepName = step.StepName,
TeaName = await GetTeaNameAsync(u.userid?.ToString()),
});
}
}
return result;
}
public async Task<List<BindFlowItem>> BindflowinfoAsync(string schSerId, int flowType)
{
var flows = await _flowinfo.FindAsync(a => a.flowType == flowType && a.schSerID == schSerId && a.enable == 1);
var result = new List<BindFlowItem>();
foreach (var item in flows.OrderBy(m => m.SerID))
{
var chaoSend = new List<ChaoSendItem>();
if (!string.IsNullOrEmpty(item.ChaoSend))
{
foreach (var uid in item.ChaoSend.Split(','))
{
if (string.IsNullOrEmpty(uid)) continue;
chaoSend.Add(new ChaoSendItem
{
uid = uid,
name = await GetTeaNameAsync(uid),
photo = await GetTeaPhotoAsync(uid),
});
}
}
result.Add(new BindFlowItem
{
SerID = item.SerID.ToString(),
flowname = item.flowname,
ChaoSendArr = chaoSend,
});
}
return result;
}
#endregion
#region 教师 / 人员
public async Task<List<TeaOption>> GetTeaListAsync(string schSerId)
{
var teas = await _sch.TeaInfo.GetSchTeaInfoAsync(schSerId, null);
return teas.Select(a => new TeaOption { value = ParseTeaId(a.TeaSerId), label = a.Name }).ToList();
}
public Task<List<TeaOption>> GetTeaListSupAsync(string schSerId)
=> GetTeaListAsync(schSerId);
public async Task<PageVO<TeaInfoVo>> GetUserinfoAsync(string schSerId, string name, int statetype, int pageSize, int pageIndex)
{
var teas = await _sch.TeaInfo.GetSchTeaInfoAsync(schSerId, null);
if (!string.IsNullOrEmpty(name))
{
teas = teas.Where(m => m.Name != null && m.Name.IndexOf(name) >= 0).ToList();
}
if (statetype > -1)
{
teas = teas.Where(m => m.IsLeadingMiddle == statetype).ToList();
}
var ordered = teas.OrderBy(m => ParseTeaId(m.TeaSerId)).ToList();
var items = ordered.Skip(pageSize * pageIndex).Take(pageSize).ToList();
return PageVO<TeaInfoVo>.Of(items, ordered.Count, pageIndex, pageSize);
}
public async Task<string[]> GetNoLeadingMiddleInfoAsync(string schSerId)
{
var teas = await _sch.TeaInfo.GetSchTeaInfoAsync(schSerId, null);
return teas.Where(m => m.IsLeadingMiddle != 1)
.Select(m => m.TeaSerId)
.Where(id => !string.IsNullOrEmpty(id))
.ToArray();
}
public Task<string[]> GetNoLeadingMiddleInfoSupAsync(string schSerId)
=> GetNoLeadingMiddleInfoAsync(schSerId);
public Task ChangeLeadingMiddleStatusAsync(int state, string userTeaSerIdStr)
{
// 旧实现调用 OATea/EditIntheBearingStatus 写教师状态;教务 SDK 未提供该写接口,降级拒绝。
throw new DException("该功能依赖教师状态写接口,教务 SDK 暂未提供,无法修改中层领导状态!");
}
public async Task<List<FileListItem>> FilelistAsync(string ids)
{
if (string.IsNullOrWhiteSpace(ids)) return new List<FileListItem>();
var idStrings = ids.Split(',');
var all = await _upfiles.FindAsync(a => true);
return all.Where(a => idStrings.Contains(a.SerID.ToString()))
.Select(a => new FileListItem
{
SerID = a.SerID,
FileTrueName = string.IsNullOrEmpty(a.FileTrueName) ? a.FileTrueName : a.FileTrueName.Split('.')[0],
FileName = a.FileName,
FileSize = a.FileSize,
FileType = a.FileType,
})
.ToList();
}
#endregion
#region 流程判断 / 用户
public async Task<ShenheFlowResult> IsShenheflowAsync(string schSerId, int userId)
{
var stepFlowIds = (await _flowstep.FindAsync(s => true))
.Select(s => s.flowid).Where(f => f.HasValue).Select(f => f.Value).Distinct().ToHashSet();
var flowlist = await _flowinfo.FindAsync(a =>
a.schSerID == schSerId && a.flowType == DocPrintConstants.AppCode && a.enable == 1);
var hasFixed = flowlist.Any(a => stepFlowIds.Contains(a.SerID));
var result = new ShenheFlowResult { flowflag = hasFixed, freeflowlist = new List<FreeFlowItem>() };
if (!hasFixed)
{
var freeflowlist = await _freeflowinfo.FindAsync(a => a.userid == userId && a.flowType == DocPrintConstants.AppCode);
foreach (var a in freeflowlist)
{
result.freeflowlist.Add(new FreeFlowItem
{
shenheuserid = a.shenheuserid,
teamnamestr = await GetTeaNamesStrAsync(a.shenheuserid),
});
}
}
return result;
}
public async Task<List<SendUserItem>> IsSendUserAsync(string schSerId, int userId)
{
var result = new List<SendUserItem>();
var senduserlist = await _senduser.FirstOrDefaultAsync(a => a.userid == userId && a.flowType == DocPrintConstants.AppCode);
if (senduserlist == null || string.IsNullOrEmpty(senduserlist.Senduserid))
{
return result;
}
var userids = senduserlist.Senduserid.Split(',')
.Where(a => !string.IsNullOrEmpty(a) && a != "undefined" && int.TryParse(a, out _))
.Select(int.Parse).ToArray();
if (userids.Length == 0) return result;
var teas = await _sch.TeaInfo.GetArrTeaInfoAsync(string.Join(",", userids));
return teas.Select(a => new SendUserItem
{
uid = ParseTeaId(a.TeaSerId),
photo = a.Photo,
name = a.Name,
status = false,
}).ToList();
}
public Task<CurrentUserInfo> GetUserAsync()
{
var user = UserContextHolder.Current;
return Task.FromResult(new CurrentUserInfo
{
Id = user?.Id ?? 0,
UserName = user?.UserName,
Account = user?.Account,
SchoolId = user?.SchoolId,
SchoolName = user?.SchoolName,
});
}
public async Task<int> GetUserFlowRoleAsync(int userId)
{
var fixFlowIds = (await _flowinfo.FindAsync(f => f.enable == 1 && f.flowType == DocPrintConstants.AppCode))
.Select(f => f.SerID).ToHashSet();
var fixFlow = await _groupuser.AnyAsync(a => a.userid == userId && a.flowid.HasValue && fixFlowIds.Contains(a.flowid.Value));
var freeFlowIds = (await _freeflowinfo.FindAsync(f => f.flowType == DocPrintConstants.AppCode))
.Select(f => f.SerID).ToHashSet();
var freeFlow = await _freegroup.AnyAsync(a => a.userid == userId && a.flowid.HasValue && freeFlowIds.Contains(a.flowid.Value));
return (fixFlow || freeFlow) ? 1 : 0;
}
#endregion
#region 教师名称/头像辅助
private static int ParseTeaId(string teaSerId)
=> int.TryParse(teaSerId, out var id) ? id : 0;
private async Task<string> GetTeaNameAsync(string teaId)
{
if (!int.TryParse(teaId, out var id)) return "";
var tea = await _sch.TeaInfo.GetTeaInfoAsync(id);
return tea?.Name ?? "";
}
private async Task<string> GetTeaPhotoAsync(string teaId)
{
if (!int.TryParse(teaId, out var id)) return "";
var tea = await _sch.TeaInfo.GetTeaInfoAsync(id);
return tea?.Photo ?? "";
}
private async Task<string> GetTeaIdsByStepAsync(int stepSerId)
{
var ids = (await _groupuser.FindAsync(a => a.usergroup == stepSerId))
.Select(a => a.userid).Where(u => u.HasValue).Select(u => u.Value).ToList();
return string.Join(",", ids);
}
private async Task<string> GetTeaNamesByStepAsync(int stepSerId)
{
var ids = (await _groupuser.FindAsync(a => a.usergroup == stepSerId))
.Select(a => a.userid).Where(u => u.HasValue).Select(u => u.Value).ToList();
return await GetTeaNamesByIdsAsync(ids);
}
private async Task<string> GetTeaNamesByIdsAsync(IEnumerable<int> ids)
{
var list = ids.Distinct().ToList();
if (list.Count == 0) return "";
var teas = await _sch.TeaInfo.GetArrTeaInfoAsync(string.Join(",", list));
return string.Join(",", teas.Select(t => t.Name).Where(n => !string.IsNullOrEmpty(n)));
}
private async Task<string> GetTeaNamesStrAsync(string commaIds)
{
if (string.IsNullOrEmpty(commaIds)) return "";
var ids = commaIds.Split(',')
.Where(s => int.TryParse(s, out _)).Select(int.Parse).Distinct().ToList();
if (ids.Count == 0) return "";
return await GetTeaNamesByIdsAsync(ids);
}
#endregion
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,31 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using YhCore.Entity;
using YhCore.Model.Dto;
namespace YhCore.Service.Interfaces
{
/// <summary>
/// 公共字典服务(对应旧 BestSchool.Service.GetSysDict)。
/// </summary>
public interface IDictionaryService
{
/// <summary>指定字典下拉框列表({k=CodeID, v=Name},Enable=1)。</summary>
Task<List<DicOption>> GetDictListAsync(string typeId, string schId);
/// <summary>字典类别列表(排除 DEPT)。</summary>
Task<List<DicOption>> GetDicTypeListAsync();
/// <summary>字典管理列表(含已删除,供超管管理)。</summary>
Task<List<DicManageItem>> GetDictMangeListAsync(string typeId, string schId);
/// <summary>添加字典(typeID/typeName/Name/Schserid 由调用方填好,此处计算 Sort/Des/Enable/CodeID)。</summary>
Task AddDicAsync(Common_Dictionary model);
/// <summary>更新字典(Name/Des/Sort/Enable)。</summary>
Task UpdateDicAsync(Common_Dictionary model);
/// <summary>编辑字典名称(Name/Code/Path)。</summary>
Task EditDicAsync(Common_Dictionary model);
}
}
@@ -0,0 +1,52 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using YhCore.Entity;
using YhCore.Model.Dto;
namespace YhCore.Service.Interfaces
{
/// <summary>
/// 文印分类 / 印制版面 / 公共类别服务(对应旧 BestSchool.DocPrint.WY_list 分类部分 + WY_Classify)。
/// </summary>
public interface IDocPrintClassifyService
{
/// <summary>添加文印分类(typeName 固定「印制分类」)。</summary>
Task AddDicAsync(Common_Dictionary model);
/// <summary>文印分类列表。</summary>
Task<List<CategoryItem>> CategoryListAsync(string schSerId, string name);
/// <summary>添加印制版面(typeName 固定「印制版面」)。</summary>
Task AddYZBMAsync(Common_Dictionary model);
/// <summary>印制版面列表。</summary>
Task<List<CategoryItem>> YZBMListAsync(string schSerId, string name);
/// <summary>删除文印分类/版面(有使用记录则拒绝)。</summary>
Task DeleteDicAsync(decimal[] ids);
/// <summary>公共类别列表(含排序/显隐)。</summary>
Task<List<DictionaryTypeItem>> DictionaryTypeListAsync(string schSerId, string name, bool showAll, string typeId);
/// <summary>删除公共类别(软删除 Enable=3)。</summary>
Task DeleteDictionaryTypeAsync(string[] ids);
/// <summary>公共类别显隐。</summary>
Task SetDictionaryOrdAsync(string schSerId, int dicSerId, bool show);
/// <summary>公共类别排序。</summary>
Task OrderDicAsync(string ids, string schSerId);
/// <summary>结果链接(依赖资源平台 ziyuan,新项目未接入,降级返回 false)。</summary>
Task<object> GetResultUrlAsync(int dataId, int uid, int schId, string schSerId);
/// <summary>学校文印资源开关。</summary>
Task<bool> GetSchZyBitAsync(string schSerId);
/// <summary>设置学校文印资源开关。</summary>
Task SetSchZyBitAsync(string schSerId, bool zyBit);
/// <summary>待审数量角标。</summary>
Task<int> GetShnheCountAsync(string userId, string schSerId);
}
}
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using YhCore.Model.Dto;
namespace YhCore.Service.Interfaces
{
/// <summary>
/// 文印统计看板服务(移植旧 BestSchool.DocPrint.DocPrintStatistics,5 接口)。
/// 旧 [NoAuth] 无用户上下文,schId 由前端传入;新库 SchSerID 已是 int 字符串,直接 schId.ToString() 匹配。
/// roleId 参数旧实现零引用,已删除。ExportRecord(Excel 导出,NPOI)延后。
/// </summary>
public interface IDocPrintStatisticsService
{
Task<List<DocPrintCountItem>> GetDocPrintCountAsync(int schId, DateTime? startDate = null, DateTime? endDate = null);
Task<List<PieChartSeries>> GetPieChartAsync(int schId, DateTime? startDate = null, DateTime? endDate = null);
Task<List<DocPrintTypeItem>> GetDocPrintTypeAsync(int schId);
Task<DocPrintListResult> GetDocPrintListAsync(int schId, string docType, string likeName, int pageIndex, int pageSize, DateTime? startDate = null, DateTime? endDate = null);
}
}
@@ -0,0 +1,39 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using YhCore.Model.Dto;
namespace YhCore.Service.Interfaces
{
/// <summary>
/// 公共文件服务(移植旧 BestSchool.Service.Common.GetEnclosure/FileSaveEnclosure +
/// BestSchool.Common.UpLoadFile.FileSave/FileSaveImage/FileChunkSave/FileMergeSave)。
/// 存储约定:物理根目录 = Config.FilePath(≈旧 ~/UploadFile/),相对路径仍存 /UploadFile/{AppType}/{yyyyMMdd}/{guid}{ext}
/// (与旧库数据格式一致,兼容历史记录);host 取 Config.FileWebPath(≈旧 AuthConfig.weboa_url)。
/// 旧 ToOfficeHelper.OfficeConvertToPDF(Office COM 转 PDF)新项目缺失,降级:上传不做转 PDF,
/// 预览时本地无 .pdf 则降级 officeapps.live.com 在线预览。
/// </summary>
public interface IFileService
{
/// <summary>按附件 id 取文件信息(旧 GetEnclosure:Common_UpFiles → FileObj)。</summary>
Task<FileObj> GetEnclosureAsync(int id);
/// <summary>存储附件信息(旧 FileSaveEnclosure:Common_UpFiles 落库,返回 SerID;文件名非法字符替换为 _)。</summary>
Task<int> SaveEnclosureAsync(FileObj file, string appType);
/// <summary>单文件上传(旧 UpLoadFile.FileSave:扩展名白名单 + 保存 + 返回 FileObj)。</summary>
Task<FileObj> SaveFileAsync(string appType, IFormFile file);
/// <summary>图片多文件上传(旧 UpLoadFile.FileSaveImage:jpg/gif/png/jpeg)。</summary>
Task<List<FileObj>> SaveFileImageAsync(string appType, IFormFileCollection files);
/// <summary>保存分片(旧 FileChunkSave:临时目录 {guid}/ 下按索引存块)。</summary>
Task SaveChunkAsync(string guid, string chunkIndex, IFormFile chunk);
/// <summary>合并分片(旧 FileMergeSave:按块合并 → 正式目录 + FileObj)。</summary>
Task<FileObj> MergeSaveAsync(string appType, string guid, string fileName);
/// <summary>相对路径 → 物理路径(fileurl 以 /UploadFile/ 开头 → Config.FilePath + 子路径)。</summary>
string MapPhysicalPath(string fileUrl);
}
}
@@ -0,0 +1,34 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using YhCore.Model.Dto;
namespace YhCore.Service.Interfaces
{
/// <summary>
/// 自由审批流服务(移植旧 BestSchool.Service.ApproveFlow)。
/// 教师查询走教务 SDK(Yh.SchClass.OpenApi)。
/// </summary>
public interface IFreeApproveService
{
/// <summary>获取自己的自由流程节点(dataid 为空时取最大 dataid)。</summary>
Task<List<FreeFlowNode>> GetFleeFlowAsync(int userId, int? dataid);
/// <summary>保存自由流程审核人,返回首步骤 SerID。</summary>
Task<int> EditFleeFlowAsync(int userId, string schSerId, int? dataid, string flowUids, bool isFlowNull);
/// <summary>添加审核意见(写 flowtranctInfo,actionstatus=-1)。</summary>
Task<string> AddShenheRemarkAsync(int userId, int? dataid, string remark, int freestepid);
/// <summary>获取 freestepid 之后的自由流程节点。</summary>
Task<List<FreeFlowNode>> GetAfterFleeFlowAsync(int? dataid, int freestepid);
/// <summary>编辑 freestepid 之后的自由流程,返回末步骤 SerID。</summary>
Task<int> EditAfterFleeFlowAsync(int? dataid, int freestepid, string flowUids);
/// <summary>查看发起人 + 审核人步骤(自由 / 固定 / 仅记录)。</summary>
Task<List<FreeShenheInfo>> ShenheFlowAsync(int uid, string ctime, int dataid, int? freestepid, int? flowid);
/// <summary>抄送人列表(按 uids 查用户)。</summary>
Task<List<ChaoSendUser>> ChaoSendListAsync(string uids);
}
}
@@ -0,0 +1,21 @@
using YhCore.Entity;
using YhCore.Model.Vo;
namespace YhCore.Service.Interfaces
{
/// <summary>
/// 日志查询服务(对应 Java 版 ILogService)。
/// </summary>
public interface ILogService
{
/// <summary>
/// 分页搜索系统审计日志。
/// </summary>
/// <param name="schId">学校 ID</param>
/// <param name="keyword">关键字(可为空)</param>
/// <param name="systemScope">true=系统操作日志(systemSearch);false=普通操作日志(search)</param>
/// <param name="page">页码,从 0 开始</param>
/// <param name="size">每页大小</param>
PageVO<SysOperationLog> Search(int schId, string keyword, bool systemScope, int page, int size);
}
}
@@ -0,0 +1,84 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Yh.SchClass.OpenApi.Model;
using YhCore.Entity;
using YhCore.Model.Dto;
using YhCore.Model.Vo;
namespace YhCore.Service.Interfaces
{
/// <summary>
/// 审核流程管理服务(对应旧 BestSchool.Service.Reviewprocess + UserFlowRole)。
/// </summary>
public interface IReviewProcessService
{
/// <summary>审核流程列表(分页)。</summary>
Task<PageVO<FlowListItem>> ListAsync(string name, int pageSize, int pageIndex, int flowType, bool edit, string schSerId);
/// <summary>审核流程列表【局版】(新框架无 groupId,降级为与 List 同源)。</summary>
Task<PageVO<FlowListItem>> List2Async(string name, int pageSize, int pageIndex, int flowType, bool edit, string schSerId);
/// <summary>步骤流程列表。</summary>
Task<PageVO<StepListItem>> StepListAsync(string name, int serid);
/// <summary>添加审核流程。</summary>
Task CreateAsync(Common_flowinfo model);
/// <summary>修改审核流程。</summary>
Task EditAsync(Common_flowinfo model);
/// <summary>添加步骤流程。</summary>
Task StepCreateAsync(Common_flowstep model);
/// <summary>修改步骤流程。</summary>
Task StepEditAsync(Common_flowstep model);
/// <summary>删除审核流程(软删除 enable=3)。</summary>
Task DeleteAsync(string ids);
/// <summary>删除步骤。</summary>
Task StepDeleteAsync(decimal[] ids);
/// <summary>流程绑定的步骤列表。</summary>
Task<List<BindStepItem>> BindstepListAsync(int serid);
/// <summary>绑定步骤的人员。</summary>
Task<List<BindGroupuserItem>> BindGroupuserListAsync(int flowid, int prestep);
/// <summary>绑定流程(流程 + 抄送人)。</summary>
Task<List<BindFlowItem>> BindflowinfoAsync(string schSerId, int flowType);
/// <summary>当前学校教师列表。</summary>
Task<List<TeaOption>> GetTeaListAsync(string schSerId);
/// <summary>学校 + 教育局教师列表(降级为当前学校)。</summary>
Task<List<TeaOption>> GetTeaListSupAsync(string schSerId);
/// <summary>教师信息分页。</summary>
Task<PageVO<TeaInfoVo>> GetUserinfoAsync(string schSerId, string name, int statetype, int pageSize, int pageIndex);
/// <summary>非中层领导人员(teaId 数组)。</summary>
Task<string[]> GetNoLeadingMiddleInfoAsync(string schSerId);
/// <summary>非中层领导人员【局版】(降级为当前学校)。</summary>
Task<string[]> GetNoLeadingMiddleInfoSupAsync(string schSerId);
/// <summary>设置/取消中层领导(教务 SDK 未提供写接口,暂不支持)。</summary>
Task ChangeLeadingMiddleStatusAsync(int state, string userTeaSerIdStr);
/// <summary>附件数据源。</summary>
Task<List<FileListItem>> FilelistAsync(string ids);
/// <summary>该校审核流程是固定还是自由。</summary>
Task<ShenheFlowResult> IsShenheflowAsync(string schSerId, int userId);
/// <summary>绑定抄送默认人。</summary>
Task<List<SendUserItem>> IsSendUserAsync(string schSerId, int userId);
/// <summary>当前用户基础信息。</summary>
Task<CurrentUserInfo> GetUserAsync();
/// <summary>用户流程角色(1 有固定/自由流程,0 无)。</summary>
Task<int> GetUserFlowRoleAsync(int userId);
}
}
@@ -0,0 +1,51 @@
using System.Threading.Tasks;
using System.Collections.Generic;
using YhCore.Model.Dto;
namespace YhCore.Service.Interfaces
{
/// <summary>
/// 文印核心业务服务(移植旧 BestSchool.DocPrint.WY_list 除分类/字典外的部分:
/// 抄送、附件、文印 CRUD、各类列表、审核动作、统计、指派)。
/// </summary>
public interface IWYDocPrintService
{
// ---- 抄送 ----
Task<List<DocPrintListItem>> SendRdListAsync(string name, string startDate, string endDate, string userId, string schSerId, int pageSize, int pageIndex);
Task<List<SendDetailItem>> SendDetailsListAsync(int serId);
Task<string> EditSendAsync(int serId, string senduserid, string mysenduserid, string schSerId, string uid);
// ---- 附件 ----
Task<List<DocPrintFileItem>> FileListAsync(string ids);
// ---- 文印 CRUD ----
Task<int> CreateAsync(WYDocPrintSaveDto model);
Task<int> EditAsync(WYDocPrintSaveDto model);
Task DeleteAsync(string ids);
Task<string> RevokeAsync(FlowTranctDto model);
// ---- 列表 ----
Task<List<DocPrintListItem>> MyListAsync(string name, string status, string startDate, string endDate, string userId, string schSerId, int pageSize, int pageIndex);
Task<List<DocPrintListItem>> AllListAsync(string name, string userId, string schSerId, int pageSize, int pageIndex);
Task<List<DocPrintListItem>> ShenheListAsync(string userId, string schSerId, int pageSize, int pageIndex);
Task<List<DocPrintListItem>> SPListAsync(string name, string status, string startDate, string endDate, string userId, string schSerId, int pageSize, int pageIndex);
Task<List<DocPrintListItem>> YishenListAsync(string name, string userId, string schSerId, int pageSize, int pageIndex);
Task<List<DocPrintListItem>> TotalListAsync(string name, string stateid, string starttime, string endtime, string schSerId, int pageSize, int pageIndex);
Task<List<DocPrintDetailItem>> MyListtoOneAsync(int serId, string schSerId);
Task<List<BindFlowOption>> BindflowinfoAsync(string schSerId, int flowType);
// ---- 审核动作 ----
Task<string> ShenheOkAsync(FlowTranctDto model);
Task<string> AffirmApplyforOkAsync(FlowTranctDto model);
// ---- 指派 ----
Task<string> GetUserZhipaiAsync(int userid, string schSerId, int? flowid, int? flowstepid, string type);
// ---- 统计 ----
Task<List<MonthStatItem>> List1Async(string starttime, string endtime, string schSerId);
Task<List<DocDetailStatItem>> List2Async(string starttime, string endtime, string schSerId, int pageSize, int pageIndex);
Task<List<UserRankItem>> List3Async(string starttime, string endtime, string schSerId, int pageSize, int pageIndex);
Task<List<StatListItem>> List4Async(int year, int month, string schSerId, int pageSize, int pageIndex);
Task<List<StatListItem>> List5Async(string userid, string starttime, string endtime, string schSerId, int pageSize, int pageIndex);
}
}
@@ -0,0 +1,70 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using YhCore.Entity;
using YhCore.Model.Dto;
using YhCore.Model.Vo;
namespace YhCore.Service.Interfaces
{
/// <summary>
/// 审核角色业务服务(移植旧 BestSchool.DocPrint.WY_SHRoleP,45 接口)。
/// 降级说明:
/// 1. 旧 SendMessage/SendUnread/Backlog(短信、已读未读、待办中心)新项目缺失,全部降级为纯落库;
/// 2. 教师/头像改走教务 SDK(Yh.SchClass.OpenApi),教师标识用 int(TeaSerId 解析),
/// 落库 WY_SHRole_SHTea.SHTeaID / WY_SHRole_BSHTea.BSHTeaID 仍为 decimal 用户 id;
/// 3. 签名存文件系统(DES 加密),非数据库表;
/// 4. 旧 ServiceResult 的 Data/OtherData/Page 三段合并为单个 Result 对象(getshenheFlow / getStepInfo / getStepByJE / getdocOftable)。
/// 延后:Excel 导入导出(UpfileExcelSHRole/downMBOfSHRole/downUpfileSHRoleFK/ExceltotalListSHRole/WYRDExceldaochu)、审批单 PDF(DwonWYSQD)。
/// </summary>
public interface IWYSHRoleService
{
// ---- 审核角色管理 ----
Task<string> GetIsSHRoleAsync(string schSerId);
Task<string> SaveIsSHRoleAsync(string schSerId, string val);
Task<List<WYSHRoleExt>> GetSHRoleAsync(string schSerId);
Task<string> UpdateSHRoleNameAsync(string schSerId, string userId, decimal serId, string roleName);
Task<string> EditSHRoleAsync(string schSerId, string userId, WYSHRoleExt roleInfo);
Task<string> EditSHTeaRowAsync(string schSerId, string userId, WY_SHRole_SHTea data);
Task<string> DelSHTeaRowAsync(string schSerId, decimal serId);
Task<string> EditSHTeaAsync(string schSerId, string userId, decimal shRoleId, List<WY_SHRole_SHTea> listSHTea);
Task<string> EditBSHTeaAsync(string schSerId, string userId, decimal shRoleId, List<WY_SHRole_BSHTea> listBSHTea);
Task<string> DelSHRoleAsync(string schSerId, List<decimal?> listSHRoleId);
// ---- 审核角色流程 ----
Task<PageVO<SHRoleFlowListItem>> ListFlowAsync(string name, int pageSize, int pageIndex, string schSerId, int flowType, bool edit);
Task<string> EditFlowAsync(Common_flowinfo model);
Task<string> StepCreateOfSHRoleAsync(Common_flowstep model);
Task<SHRoleStepInfo> GetStepInfoAsync(string schSerId, decimal flowid);
Task<string> StepDeleteAsync(List<int> ids);
Task<string> UpdateStepNameAsync(string schSerId, int serId, int flowid, string stepName, int cond, string isshowJE, string isshowBtn);
Task<string> AddStepSHRoleAsync(string schSerId, int flowid, int stepid, decimal shRoleid);
Task<string> DelStepSHRoleAsync(string schSerId, int stepid, decimal shRoleid);
Task<SHRoleStepByJEResult> GetStepByJEAsync(string schSerId, string userId, int flowid, int je);
// ---- 文印申请页面 ----
Task<List<WYFlowForUser>> GetLCOfUserAsync(string schSerId, string userId, string userName);
Task<List<SendUserItem>> IsSendUserSHRoleAsync(string schSerId, int userId, int flowid);
Task<List<Common_Dictionary>> GetDicInfoAsync(string schSerId, string typeId);
Task<int> EditDocPrintAsync(string schSerId, string userId, WYSHRoleEditDocPrintDto model);
// ---- 用户记录查看和审核 ----
Task<SHRoleShenheFlowResult> GetshenheFlowAsync(string schSerId, decimal docprintId, int flowid);
Task<List<SHRoleDocPrintListItem>> MyListtoOneSHRoleAsync(int serId, string schSerId);
Task<List<SHRoleDocPrintListItem>> MyListSHRoleAsync(string name, string status, string startDate, string endDate, int pageSize, int pageIndex, string userId, string schSerId);
Task<List<SHRoleDocPrintListItem>> TotalListSHRoleAsync(int flowid, int dwid, string name, string stateid, string starttime, string endtime, int pageSize, int pageIndex, string schSerId);
Task<List<SHRoleDocPrintListItem>> SendRdListSHRoleAsync(int flowid, int dwid, string name, string status, string startDate, string endDate, string userId, string schSerId, int pageSize, int pageIndex);
Task<List<SHRoleDocPrintListItem>> SPListSHRoleAsync(int flowid, int dwid, string name, string status, string startDate, string endDate, string userId, string userName, string schSerId, int pageSize, int pageIndex);
Task<string> ShenheOkSHRoleAsync(FlowTranctDto model);
// ---- 规格表 / 签名 / 退回 / 金额 ----
Task<SHRoleDocOfTableResult> GetDocOftableAsync(string schSerId, decimal docPrintId);
Task<string> SaveSignatureAsync(string schSerId, string userId, string pic);
Task<string> GetSignatureAsync(string schSerId, string userId);
Task<string> DelSignatureAsync(string schSerId, string userId);
Task<string> TuiHuiAsync(string userId, string cont, decimal docId);
Task<string> UnTuiHuiAsync(string userId, decimal docId);
Task<string> RegetJEAsync(string schSerId, string userId, List<decimal> listDocPrintId, string type);
Task<List<SHRoleRegetJERdItem>> GetRegetJERdAsync(string schSerId, int pageSize, int pageIndex);
Task<int> SetDownFileTimesAsync(int serId);
}
}
+34
View File
@@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<RootNamespace>YhCore.Service</RootNamespace>
<AssemblyName>YhCore.Service</AssemblyName>
<LangVersion>9.0</LangVersion>
<Nullable>disable</Nullable>
</PropertyGroup>
<ItemGroup>
<!-- 文件上传:IFileService 使用 IFormFile / IFormFileCollection(ASP.NET Core 共享框架) -->
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\YhCore.Entity\YhCore.Entity.csproj" />
<ProjectReference Include="..\YhCore.Model\YhCore.Model.csproj" />
<ProjectReference Include="..\YhCore.Repository\YhCore.Repository.csproj" />
<ProjectReference Include="..\YhCore.Common\YhCore.Common.csproj" />
</ItemGroup>
<ItemGroup>
<!-- 业务异常 DException / ErrorCode / IErrorCode -->
<Reference Include="Yh.Platform.Abstractions">
<HintPath>..\..\..\Library\Yh.Platform.Abstractions.dll</HintPath>
</Reference>
<!-- 教务(SchClass)开放接口 SDK:教师/学校查询(C1 reviewprocess 教师类接口) -->
<Reference Include="Yh.SchClass.OpenApi">
<HintPath>..\..\..\Library\Yh.SchClass.OpenApi.dll</HintPath>
</Reference>
</ItemGroup>
</Project>