Compare commits

..
20 Commits
Author SHA1 Message Date
ops 695ba1530e fix(ci): 构建容器名唯一化 + npm 走国内镜像源(避免并发互踩 exit 137) 2026-10-21 06:24:25 +00:00
ops c2f6f42a3c ci: apk 源统一走 aliyun(规避 dl-cdn 时段性被拦卡 60s) 2026-10-21 01:36:56 +00:00
wanghu 2b9389fcb8 附件
build / build-push (push) Successful in 1m55s
2026-09-09 20:30:15 +08:00
wanghu 44d3cd0326 去掉小数点
build / build-push (push) Successful in 40s
2026-09-09 19:01:08 +08:00
wanghu 3ff9b48a19 修改地址
build / build-push (push) Successful in 40s
2026-09-09 17:39:30 +08:00
wanghu 20325bed00 签名
build / build-push (push) Successful in 39s
2026-09-09 16:49:55 +08:00
wanghu 0efde94a74 附件
build / build-push (push) Successful in 1m21s
2026-09-09 16:27:17 +08:00
wanghu 4211b72041 Config.FilePath
build / build-push (push) Successful in 1m24s
2026-09-09 16:04:46 +08:00
wanghu e9e1c1722d uploud
build / build-push (push) Successful in 44s
2026-09-09 15:54:50 +08:00
wanghu 583d06f3d0 pdf
build / build-push (push) Successful in 1m55s
2026-09-09 12:07:17 +08:00
wanghu 83182cb538 插件
build / build-push (push) Failing after 58s
2026-09-09 12:02:05 +08:00
wanghu bbcb865055 插件
build / build-push (push) Failing after 21s
2026-09-09 12:01:45 +08:00
wanghu cf8d6da5b5 提交
build / build-push (push) Successful in 28s
2026-09-09 08:57:41 +08:00
wanghu ae1bd4cdb0 注入
build / build-push (push) Successful in 28s
2026-09-08 21:18:29 +08:00
wanghu f1059fd92b 大小写
build / build-push (push) Successful in 33s
2026-09-08 17:42:25 +08:00
wanghu 5785a2c10e 更新大小写
build / build-push (push) Successful in 28s
2026-09-08 17:29:56 +08:00
wanghu 10c791d034 编辑
build / build-push (push) Successful in 28s
2026-09-08 16:55:39 +08:00
wanghu 04e708c4fe Merge branch 'main' of http://gitea.yunhaixingtong.com/docprint/DocprintService
build / build-push (push) Successful in 1m49s
2026-09-08 15:06:44 +08:00
wanghu d658ab5b6a 服务层列表接口返回 PageVO(配合控制器分页改造) 2026-09-08 15:05:20 +08:00
wanghu ae8327807e 1 2026-09-08 15:05:00 +08:00
24 changed files with 2202 additions and 196 deletions
+17 -8
View File
@@ -26,7 +26,10 @@ jobs:
options: --network infra-infra-net options: --network infra-infra-net
steps: steps:
- name: 安装 git / curl(alpine) - name: 安装 git / curl(alpine)
run: apk add --no-cache git curl run: |
# 统一走 aliyun 源:dl-cdn 时段性被拦会卡满 60s×2(2026-10-20 事故)
sed -i 's#dl-cdn.alpinelinux.org#mirrors.aliyun.com#g' /etc/apk/repositories
apk add --no-cache git curl
- name: 拉取源码(内网无 GitHub,手动 clone 本仓库) - name: 拉取源码(内网无 GitHub,手动 clone 本仓库)
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -53,14 +56,20 @@ jobs:
- name: 构建 publish 产物(dotnet sdk 容器,复用宿主 nuget 缓存) - name: 构建 publish 产物(dotnet sdk 容器,复用宿主 nuget 缓存)
run: | run: |
set -e set -e
docker rm -f dotnetbuild 2>/dev/null || true # 构建容器名必须全局唯一:runner 挂的是宿主 docker.sock,容器名在整个 daemon 上共享
docker run -d --name dotnetbuild \ # 命名空间。固定名会在多仓/多条 workflow 并发时互相 docker rm -f 掉对方正在构建的
# 容器,表现为无编译报错的 exit 137(SIGKILL,2026-10-21 实测复现)。
# GITHUB_RUN_ID 实例级唯一,再拼时间戳兜底。
BUILD_CONTAINER="dotnetbuild-${GITHUB_RUN_ID:-0}-$(date +%s)"
echo "BUILD_CONTAINER=${BUILD_CONTAINER}"
docker rm -f "$BUILD_CONTAINER" 2>/dev/null || true
docker run -d --name "$BUILD_CONTAINER" \
--network infra-infra-net \ --network infra-infra-net \
-v /opt/docker/projman-dotnet-nuget:/root/.nuget/packages \ -v /opt/docker/projman-dotnet-nuget:/root/.nuget/packages \
-w /build mcr.microsoft.com/dotnet/sdk:5.0 sleep 3600 -w /build mcr.microsoft.com/dotnet/sdk:5.0 sleep 3600
docker cp "${PWD}/." dotnetbuild:/build/ docker cp "${PWD}/." "$BUILD_CONTAINER":/build/
docker exec dotnetbuild sh -c "find /build -maxdepth 2 -iname nuget.config -exec sed -i 's|//nexus.yunhaixingtong.com|//nexus:8081|g' {} +" docker exec "$BUILD_CONTAINER" sh -c "find /build -maxdepth 2 -iname nuget.config -exec sed -i 's|//nexus.yunhaixingtong.com|//nexus:8081|g' {} +"
docker exec dotnetbuild sh -c ' docker exec "$BUILD_CONTAINER" sh -c '
set -e set -e
root_sln=$(ls /build/*.sln 2>/dev/null | head -1 || true) root_sln=$(ls /build/*.sln 2>/dev/null | head -1 || true)
root_csproj=$(ls /build/*.csproj 2>/dev/null | head -1 || true) root_csproj=$(ls /build/*.csproj 2>/dev/null | head -1 || true)
@@ -88,8 +97,8 @@ jobs:
echo "publish target: $target" echo "publish target: $target"
' '
mkdir -p "${PWD}/publish" mkdir -p "${PWD}/publish"
docker cp dotnetbuild:/app/. "${PWD}/publish/" docker cp "$BUILD_CONTAINER":/app/. "${PWD}/publish/"
docker rm -f dotnetbuild docker rm -f "$BUILD_CONTAINER"
# 入口脚本:runtimeconfig.json 是可执行项目唯一标志(X.runtimeconfig.json <-> X.dll), # 入口脚本:runtimeconfig.json 是可执行项目唯一标志(X.runtimeconfig.json <-> X.dll),
# 类库 deps.json 无 runtimeconfig 兄弟文件,天然不会误选 # 类库 deps.json 无 runtimeconfig 兄弟文件,天然不会误选
entry=$(ls "${PWD}"/publish/*.runtimeconfig.json | head -1) entry=$(ls "${PWD}"/publish/*.runtimeconfig.json | head -1)
-53
View File
@@ -1,53 +0,0 @@
# DocprintService
> 本仓库由 projman 自动创建并注入 CI(`.gitea/workflows/build.yml`)与 Dockerfile(`docker/Dockerfile`)。
> 推送代码到 main 分支即自动构建镜像并推送到 Nexus,projman 收到通知后建档待部署,由运维在 projman「后端发布」手动点部署。
## 首次推送本地代码到本仓库
> 目标:把本地已有代码推上去,同时**保留** projman 注入的文件(`build.yml`、`Dockerfile`)。
```bash
# 1. 进入本地代码目录
cd /your/local/code/dir
# 2. 初始化 git(目录已有 .git 也安全,git init 幂等无副作用)
git init
git branch -M main
# 3. 先把本地代码提交成一个 commit(关键!不提交直接 merge 会丢失本地代码)
git add -A
git commit -m "init local code"
# 4. 关联远程仓库(幂等:已存在 origin 则改地址,不报错)
git remote add origin http://gitea.yunhaixingtong.com/docprint/DocprintService.git 2>/dev/null \
|| git remote set-url origin http://gitea.yunhaixingtong.com/docprint/DocprintService.git
# 5. 拉取远程信息(只下载,不合并)
git fetch origin
# 6. 合并远程 main(关键步骤)
# 本地与远程是两条独立历史,需允许合并不相关历史。
# 此步会把 projman 注入的 build.yml 与 Dockerfile 合并进你的本地工作区。
git merge origin/main --allow-unrelated-histories -m "merge projman injected files"
# 7. 查看状态(确认 .gitea/workflows/build.yml、docker/Dockerfile 已在本地)
git status
# 8. 提交并推送
git add -A
git commit -m "sync local code" 2>/dev/null || true
git push origin main
```
## 推送后验证
push 触发 CI 构建,前往 Gitea 仓库 → **Actions** 查看日志;构建成功后镜像推送到 Nexus,projman 建档待部署,在 projman 应用详情「后端发布」手动点部署。
## 禁止写法(会导致注入文件被删)
`git reset origin/main` + `git add -A` 会把远程已注入、但你本地没有的文件(`build.yml`、`Dockerfile`)当作"删除"提交并推送,导致这些文件被删、CI 失败。务必用 `git merge` 而非 `git reset`,且 merge 前先提交本地代码。
## 关于 Dockerfile
projman 注入的 Dockerfile 仅在仓库不存在时写入;你自备的 Dockerfile 优先,不会被覆盖。
+3 -1
View File
@@ -8,10 +8,12 @@ FROM mcr.microsoft.com/dotnet/aspnet:${DOTNET_VERSION}
# .NET 5 基础镜像基于 Debian 10(已 EOL),apt 源须切 archive.debian.org 才能装包 # .NET 5 基础镜像基于 Debian 10(已 EOL),apt 源须切 archive.debian.org 才能装包
# curl 供 projman compose healthcheck(test: CMD curl -f ...)使用;tzdata 设上海时区 # curl 供 projman compose healthcheck(test: CMD curl -f ...)使用;tzdata 设上海时区
# wkhtmltopdf 供审批单 PDF 接口(DwonWYSQD)本地转换 HTML(Debian 源为 0.12.5,无需 --enable-local-file-access);
# fonts-wqy-zenhei 提供中文字体(缺失则 PDF 中文显示方块)
RUN sed -i 's|deb.debian.org|archive.debian.org|g; s|security.debian.org|archive.debian.org|g' /etc/apt/sources.list \ RUN sed -i 's|deb.debian.org|archive.debian.org|g; s|security.debian.org|archive.debian.org|g' /etc/apt/sources.list \
&& echo 'Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/99no-valid-until \ && echo 'Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/99no-valid-until \
&& apt-get update \ && apt-get update \
&& apt-get install -y --no-install-recommends curl tzdata \ && apt-get install -y --no-install-recommends curl tzdata wkhtmltopdf fonts-wqy-zenhei \
&& ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone \ && echo "Asia/Shanghai" > /etc/timezone \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
+3
View File
@@ -21,5 +21,8 @@ namespace YhCore.Common.Config
/// <summary>文件访问 URL 前缀(对应 Java FILE_WEB_PATH,由 Startup 注入)</summary> /// <summary>文件访问 URL 前缀(对应 Java FILE_WEB_PATH,由 Startup 注入)</summary>
public static string FileWebPath { get; set; } public static string FileWebPath { get; set; }
/// <summary>wkhtmltopdf 可执行文件路径(审批单 PDF 用;空则用 PATH 中的 wkhtmltopdf,由 Startup 注入)</summary>
public static string WkHtmlToPdfPath { get; set; }
} }
} }
+1 -1
View File
@@ -17,7 +17,7 @@
<ItemGroup> <ItemGroup>
<!-- 平台模型与信封(DException/ErrorCode/BoaUser 等):从 Nexus nuget-group 引用(原为本地源项目引用) --> <!-- 平台模型与信封(DException/ErrorCode/BoaUser 等):从 Nexus nuget-group 引用(原为本地源项目引用) -->
<PackageReference Include="Yh.Platform.Abstractions" Version="1.0.0-preview.1" /> <PackageReference Include="Yh.Platform.Abstractions" Version="1.0.0-preview.2" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+1 -13
View File
@@ -68,22 +68,10 @@ namespace YhCore.Model.Dto
/// <summary>全部字典类别(typeID, typeName),对齐旧 EnumConfig.Dictionary 的 Description。</summary> /// <summary>全部字典类别(typeID, typeName),对齐旧 EnumConfig.Dictionary 的 Description。</summary>
public static readonly (string Id, string Name)[] AllTypes = public static readonly (string Id, string Name)[] AllTypes =
{ {
("DEPT", "部门类别"),
("BXBM", "报修部门"),
("WYFL", "文印分类"), ("WYFL", "文印分类"),
("YZBM", "印制版面"), ("YZBM", "印制版面"),
("YZFL", "印制分类"), ("YZFL", "印制分类"),
("LeaveType", "请假类别"), ("filetype", "文件类型")
("FoodType", "物品类别"),
("Unit", "单位"),
("VenueType", "场馆类别"),
("Vefacilities", "场馆设施"),
("Time", "场馆时间"),
("Repeat", "场馆重复"),
("LevelOrder", "奖次"),
("filetype", "文件类型"),
("doclevel", "公文级别"),
("XKCategory", "选课类型"),
}; };
/// <summary>GetSysDic 的 Type(int) → typeID;越界返回 null。</summary> /// <summary>GetSysDic 的 Type(int) → typeID;越界返回 null。</summary>
+2 -2
View File
@@ -2,7 +2,7 @@ namespace YhCore.Model.Dto
{ {
/// <summary> /// <summary>
/// 文件信息(旧 BestSchool.Entity.FileObj,GetEnclosure/FileSave/FileSaveImage/FileMergeSave 共用)。 /// 文件信息(旧 BestSchool.Entity.FileObj,GetEnclosure/FileSave/FileSaveImage/FileMergeSave 共用)。
/// fileurl 为相对路径(如 /UploadFile/docprint/20260101/{guid}.jpg,与旧库数据格式一致); /// fileurl 为相对路径(如 /upload/docprint/20260101/{guid}.jpg,与旧库数据格式一致);
/// host 为文件访问主机前缀(旧 AuthConfig.weboa_url,新 Config.FileWebPath)。 /// host 为文件访问主机前缀(旧 AuthConfig.weboa_url,新 Config.FileWebPath)。
/// </summary> /// </summary>
public class FileObj public class FileObj
@@ -19,7 +19,7 @@ namespace YhCore.Model.Dto
/// <summary>文件真实名称(含扩展名)。</summary> /// <summary>文件真实名称(含扩展名)。</summary>
public string filename { get; set; } public string filename { get; set; }
/// <summary>相对访问路径(如 /UploadFile/docprint/20260101/{guid}.jpg)。</summary> /// <summary>相对访问路径(如 /upload/docprint/20260101/{guid}.jpg)。</summary>
public string fileurl { get; set; } public string fileurl { get; set; }
/// <summary>文件主机前缀(Config.FileWebPath)。</summary> /// <summary>文件主机前缀(Config.FileWebPath)。</summary>
+149
View File
@@ -0,0 +1,149 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using OfficeOpenXml;
using OfficeOpenXml.Style;
namespace YhCore.Service.Excel
{
/// <summary>
/// EPPlus 多 Sheet 导出(移植自旧 Bsch.Common/DocPrint/ExcelHelper,
/// 保留 RenderToExcelBytes + ExportDynamicSheet:动态列、大标题、自定义列宽、打印优化)。
/// 注意:EPPlus 实际产出 xlsx 字节流,旧系统文件名沿用 .xls,Excel 可正常打开,保持原契约。
/// </summary>
public class ExcelHelper
{
/// <summary>导出多 Sheet Excel 字节流(listdt/listsheetname/listtitle 按下标一一对应)</summary>
public static byte[] RenderToExcelBytes(string fileName, List<DataTable> listdt, List<string> listsheetname, List<string> listtitle, List<double> listcolwidth = null)
{
try
{
var package = new ExcelPackage();
int n = 0;
foreach (var dt in listdt)
{
string sheetname = "Sheet" + (n + 1).ToString();
if (n < listsheetname.Count) sheetname = listsheetname[n];
string title = "";
if (n < listtitle.Count) title = listtitle[n];
var ws1 = package.Workbook.Worksheets.Add(sheetname);
ExportDynamicSheet(ws1, dt, title, "A", listcolwidth);
n++;
}
return package.GetAsByteArray();
}
catch (Exception) { return Array.Empty<byte>(); }
}
#region 动态列 Sheet 导出(通用方法)
/// <summary>
/// 导出 动态列 + 自定义表头 + 自定义页码 + 打印优化
/// </summary>
public static void ExportDynamicSheet(ExcelWorksheet ws, DataTable dt, string sheetTitle, string printMark, List<double> listcolwidth = null)
{
// -------------------------- 行定义 --------------------------
int titleRow = 1; // 大标题行
int headerRow = 2; // 列标题行
int dataStartRow = 3; // 数据开始行
int colCount = dt.Columns.Count;
int rowCount = dt.Rows.Count;
// -------------------------- 1. 自定义大标题(合并行) --------------------------
if (sheetTitle != "")
{
ws.Cells[titleRow, 1].Value = sheetTitle;
ws.Cells[titleRow, 1, titleRow, colCount].Merge = true; // 合并单元格
// 标题样式
var titleStyle = ws.Cells[titleRow, 1].Style;
titleStyle.Font.Size = 12;
titleStyle.Font.Bold = true;
titleStyle.HorizontalAlignment = ExcelHorizontalAlignment.Center;
titleStyle.VerticalAlignment = ExcelVerticalAlignment.Center;
ws.Row(titleRow).Height = 32;
}
else
{
headerRow = 1; // 列标题行
dataStartRow = 2; // 数据开始行
}
// -------------------------- 2. 动态列 自动填充表头 --------------------------
for (int c = 0; c < colCount; c++)
{
ws.Cells[headerRow, c + 1].Value = dt.Columns[c].ColumnName;
}
// -------------------------- 3. 动态列 自动填充数据 --------------------------
for (int r = 0; r < rowCount; r++)
{
for (int c = 0; c < colCount; c++)
{
ws.Cells[dataStartRow + r, c + 1].Value = dt.Rows[r][c];
}
}
// -------------------------- 4. 自动换行 + 行高自适应(关键) --------------------------
var dataRange = ws.Cells[headerRow, 1, dataStartRow + rowCount - 1, colCount];
dataRange.Style.Font.Size = 10;
dataRange.Style.WrapText = true;
dataRange.Style.VerticalAlignment = ExcelVerticalAlignment.Center;
dataRange.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
// 加细边框
dataRange.Style.Border.Top.Style = ExcelBorderStyle.Thin;
dataRange.Style.Border.Bottom.Style = ExcelBorderStyle.Thin;
dataRange.Style.Border.Left.Style = ExcelBorderStyle.Thin;
dataRange.Style.Border.Right.Style = ExcelBorderStyle.Thin;
// 自动计算:所有列平均撑满 A4 宽度
if (listcolwidth != null)
{
int colnum = listcolwidth.Count > colCount ? colCount : listcolwidth.Count;
for (int i = 1; i <= colnum; i++)
{
ws.Column(i).Width = listcolwidth[i - 1];
}
}
else
{
double totalWidth = 58;
double perColWidth = (double)totalWidth / colCount;
for (int i = 1; i <= colCount; i++)
{
ws.Column(i).Width = perColWidth;
}
}
// 行高自适应(EPPlus 4.x 兼容)
for (int r = headerRow; r <= headerRow + rowCount - 1; r++)
{
ws.Row(r).CustomHeight = true;
}
// -------------------------- 5. 表头样式 --------------------------
var headerRange = ws.Cells[headerRow, 1, headerRow, colCount];
headerRange.Style.Font.Bold = true;
ws.Row(headerRow).Height = 25;
// -------------------------- 6. 打印:每页重复标题行 --------------------------
ws.PrinterSettings.RepeatRows = new ExcelAddress($"{headerRow}:{headerRow}");
// -------------------------- 7. 自定义页码(中文样式 + 字体大小) --------------------------
// &P=当前页 &N=总页 12号字体 居中
ws.HeaderFooter.OddFooter.CenteredText = "&14 第 &P 页 / 共 &N 页";
// -------------------------- 8. 打印格式优化 --------------------------
ws.PrinterSettings.PaperSize = ePaperSize.A4;
ws.PrinterSettings.Orientation = eOrientation.Portrait;
ws.PrinterSettings.LeftMargin = 0.4m;
ws.PrinterSettings.RightMargin = 0.4m;
ws.PrinterSettings.TopMargin = 0.8m;
ws.PrinterSettings.BottomMargin = 1.2m;
}
#endregion
}
}
+404
View File
@@ -0,0 +1,404 @@
using System;
using System.Data;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using NPOI.HSSF.UserModel;
using NPOI.SS.Formula.Eval;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
namespace YhCore.Service.Excel
{
/// <summary>
/// 使用 NPOI 操作 Excel,无需 Office COM 组件(移植自旧 Bsch.Common/DocPrint/ExcelRender,
/// 仅保留 6 个 WYSHRole Excel 接口用到的导出/导入成员;控制器用 File() 包装下载)。
/// </summary>
public class ExcelRender
{
static ExcelRender()
{
// ExportDT 列宽按 GB2312 字节长计算:net5 需注册 CodePages 提供程序后才能 GetEncoding(936)
Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
}
#region DataTable 导出为 .xls 字节流
/// <summary>
/// DataTable 导出为 Excel 的 MemoryStream(HSSF .xls;65535 行自动分 sheet;列宽按 GB2312 字节长,上限 50)
/// </summary>
static MemoryStream ExportDT(DataTable dtSource, string strHeaderText)
{
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.CreateSheet() as HSSFSheet;
HSSFCellStyle dateStyle = workbook.CreateCellStyle() as HSSFCellStyle;
HSSFDataFormat format = workbook.CreateDataFormat() as HSSFDataFormat;
dateStyle.DataFormat = format.GetFormat("yyyy-mm-dd");
//取得列宽
int[] arrColWidth = new int[dtSource.Columns.Count];
foreach (DataColumn item in dtSource.Columns)
{
arrColWidth[item.Ordinal] = Encoding.GetEncoding(936).GetBytes(item.ColumnName.ToString()).Length;
}
for (int i = 0; i < dtSource.Rows.Count; i++)
{
for (int j = 0; j < dtSource.Columns.Count; j++)
{
int intTemp = Encoding.GetEncoding(936).GetBytes(dtSource.Rows[i][j].ToString()).Length;
if (intTemp > arrColWidth[j])
{
arrColWidth[j] = intTemp;
if (intTemp >= 50)
arrColWidth[j] = 50;
}
}
}
int rowIndex = 0;
foreach (DataRow row in dtSource.Rows)
{
#region 新建表,填充表头,填充列头,样式
if (rowIndex == 65535 || rowIndex == 0)
{
if (rowIndex != 0)
{
sheet = workbook.CreateSheet() as HSSFSheet;
}
#region 列头及样式
{
HSSFRow headerRow = sheet.CreateRow(0) as HSSFRow;
HSSFCellStyle headStyle = workbook.CreateCellStyle() as HSSFCellStyle;
headStyle.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Center;
HSSFFont font = workbook.CreateFont() as HSSFFont;
font.FontHeightInPoints = 10;
font.Boldweight = 700;
headStyle.SetFont(font);
foreach (DataColumn column in dtSource.Columns)
{
headerRow.CreateCell(column.Ordinal).SetCellValue(column.ColumnName);
headerRow.GetCell(column.Ordinal).CellStyle = headStyle;
//设置列宽
sheet.SetColumnWidth(column.Ordinal, (arrColWidth[column.Ordinal] + 1) * 256);
}
}
#endregion
rowIndex = 1;
}
#endregion
#region 填充内容
HSSFRow dataRow = sheet.CreateRow(rowIndex) as HSSFRow;
foreach (DataColumn column in dtSource.Columns)
{
HSSFCell newCell = dataRow.CreateCell(column.Ordinal) as HSSFCell;
string drValue = row[column].ToString();
switch (column.DataType.ToString())
{
case "System.String": //字符串类型
newCell.SetCellValue(drValue);
break;
case "System.DateTime": //日期类型
DateTime dateV;
DateTime.TryParse(drValue, out dateV);
newCell.SetCellValue(dateV);
newCell.CellStyle = dateStyle; //格式化显示
break;
case "System.Boolean": //布尔型
bool boolV = false;
bool.TryParse(drValue, out boolV);
newCell.SetCellValue(boolV);
break;
case "System.Int16": //整型
case "System.Int32":
case "System.Int64":
case "System.Byte":
int intV = 0;
int.TryParse(drValue, out intV);
newCell.SetCellValue(intV);
break;
case "System.Decimal": //浮点型
case "System.Double":
double doubV = 0;
double.TryParse(drValue, out doubV);
newCell.SetCellValue(doubV);
break;
case "System.DBNull": //空值处理
newCell.SetCellValue("");
break;
default:
newCell.SetCellValue("");
break;
}
}
#endregion
rowIndex++;
}
using (MemoryStream ms = new MemoryStream())
{
workbook.Write(ms);
ms.Flush();
ms.Position = 0;
return ms;
}
}
/// <summary>导出 Excel 字节流(供 Service 层返回 byte[],控制器包 File 下载)</summary>
public static byte[] RenderToExcelBytes(DataTable table, string fileName)
{
try { return ExportDT(table, null).ToArray(); }
catch (Exception) { return Array.Empty<byte>(); }
}
#endregion
#region 读取 excel 到 DataTable
/// <summary>Excel 文档流是否有数据(默认第一个 sheet)</summary>
public static bool HasData(Stream excelFileStream, string fileExt)
{
return HasData(excelFileStream, 0, fileExt);
}
public static bool HasData(Stream excelFileStream, int sheetIndex, string fileExt)
{
using (excelFileStream)
{
IWorkbook workbook = null;
if (fileExt == ".xls")
{
workbook = new HSSFWorkbook(excelFileStream);
}
else if (fileExt == ".xlsx")
{
workbook = new XSSFWorkbook(excelFileStream);
}
{
if (workbook.NumberOfSheets > 0)
{
if (sheetIndex < workbook.NumberOfSheets)
{
ISheet sheet = workbook.GetSheetAt(sheetIndex);
{
return sheet.PhysicalNumberOfRows > 0;
}
}
}
}
}
return false;
}
/// <summary>
/// Excel 文档流转换成 DataTable
/// </summary>
/// <param name="excelFileStream">Excel 文档流</param>
/// <param name="sheetIndex">表索引号,如第一个表为 0</param>
/// <param name="headerRowIndex">标题行索引号,如第一行为 0</param>
public static DataTable RenderFromExcel(Stream excelFileStream, int sheetIndex, int headerRowIndex, string fileExt)
{
DataTable table = null;
using (excelFileStream)
{
IWorkbook workbook = null;
if (fileExt == ".xls")
{
workbook = new HSSFWorkbook(excelFileStream);
}
else if (fileExt == ".xlsx")
{
workbook = new XSSFWorkbook(excelFileStream);
}
ISheet sheet = workbook.GetSheetAt(sheetIndex);
{
table = ImportDt(sheet, headerRowIndex, true);
}
}
return table;
}
/// <summary>将指定 sheet 中的数据导出到 datatable 中(第一行为列头)</summary>
static DataTable ImportDt(ISheet sheet, int HeaderRowIndex, bool needHeader)
{
DataTable table = new DataTable();
IRow headerRow;
int cellCount;
try
{
if (HeaderRowIndex < 0 || !needHeader)
{
headerRow = sheet.GetRow(0);
cellCount = headerRow.LastCellNum;
for (int i = headerRow.FirstCellNum; i <= cellCount; i++)
{
DataColumn column = new DataColumn(Convert.ToString(i));
table.Columns.Add(column);
}
}
else
{
headerRow = sheet.GetRow(HeaderRowIndex);
cellCount = headerRow.LastCellNum;
for (int i = headerRow.FirstCellNum; i < cellCount; i++)
{
if (headerRow.GetCell(i) == null)
{
if (table.Columns.IndexOf(Convert.ToString(i)) > 0)
{
DataColumn column = new DataColumn(Convert.ToString("重复列名" + i));
table.Columns.Add(column);
}
else
{
DataColumn column = new DataColumn(Convert.ToString(i));
table.Columns.Add(column);
}
}
else if (table.Columns.IndexOf(headerRow.GetCell(i).ToString()) > 0)
{
DataColumn column = new DataColumn(Convert.ToString("重复列名" + i));
table.Columns.Add(column);
}
else
{
DataColumn column = new DataColumn(headerRow.GetCell(i).ToString());
table.Columns.Add(column);
}
}
}
int rowCount = sheet.LastRowNum;
for (int i = (HeaderRowIndex + 1); i <= sheet.LastRowNum; i++)
{
try
{
IRow row;
if (sheet.GetRow(i) == null)
{
row = sheet.CreateRow(i);
}
else
{
row = sheet.GetRow(i);
}
DataRow dataRow = table.NewRow();
for (int j = row.FirstCellNum; j < cellCount; j++)
{
try
{
if (row.GetCell(j) != null)
{
switch (row.GetCell(j).CellType)
{
case CellType.String:
string str = row.GetCell(j).StringCellValue;
if (str != null && str.Length > 0)
{
dataRow[j] = str.ToString();
}
else
{
dataRow[j] = null;
}
break;
case CellType.Numeric:
if (DateUtil.IsCellDateFormatted(row.GetCell(j)))
{
dataRow[j] = DateTime.FromOADate(row.GetCell(j).NumericCellValue);
}
else
{
dataRow[j] = Convert.ToDouble(row.GetCell(j).NumericCellValue);
}
break;
case CellType.Boolean:
dataRow[j] = Convert.ToString(row.GetCell(j).BooleanCellValue);
break;
case CellType.Error:
dataRow[j] = ErrorEval.GetText(row.GetCell(j).ErrorCellValue);
break;
case CellType.Formula:
switch (row.GetCell(j).CachedFormulaResultType)
{
case CellType.String:
string strFORMULA = row.GetCell(j).StringCellValue;
if (strFORMULA != null && strFORMULA.Length > 0)
{
dataRow[j] = strFORMULA.ToString();
}
else
{
dataRow[j] = null;
}
break;
case CellType.Numeric:
dataRow[j] = Convert.ToString(row.GetCell(j).NumericCellValue);
break;
case CellType.Boolean:
dataRow[j] = Convert.ToString(row.GetCell(j).BooleanCellValue);
break;
case CellType.Error:
dataRow[j] = ErrorEval.GetText(row.GetCell(j).ErrorCellValue);
break;
default:
dataRow[j] = "";
break;
}
break;
default:
dataRow[j] = "";
break;
}
}
}
catch (Exception)
{
}
}
table.Rows.Add(dataRow);
}
catch (Exception)
{
}
}
}
catch (Exception)
{
}
return table;
}
#endregion
public static byte[] StreamToBytes(Stream stream)
{
byte[] bytes = new byte[stream.Length];
stream.Read(bytes, 0, bytes.Length);
// 设置当前流的位置为流的开始
stream.Seek(0, SeekOrigin.Begin);
return bytes;
}
}
}
+15 -9
View File
@@ -68,10 +68,9 @@ namespace YhCore.Service.Impl
if (file == null || file.Length == 0) if (file == null || file.Length == 0)
throw new DException("上传失败!"); throw new DException("上传失败!");
if (appType.ToLower() == "noticemsg") appType = "notice";
string dtStr = DateTime.Now.ToString("yyyyMMdd"); string dtStr = DateTime.Now.ToString("yyyyMMdd");
string physicalDir = Path.Combine(StorageRoot(), appType, dtStr); string physicalDir = Path.Combine(StorageRoot(), appType, dtStr);
string outFilePath = "/UploadFile/" + appType + "/" + dtStr + "/"; string outFilePath = appType + "/" + dtStr + "/";
if (!Directory.Exists(physicalDir)) if (!Directory.Exists(physicalDir))
Directory.CreateDirectory(physicalDir); Directory.CreateDirectory(physicalDir);
@@ -109,7 +108,7 @@ namespace YhCore.Service.Impl
if (appType.ToLower() == "noticemsg") appType = "notice"; if (appType.ToLower() == "noticemsg") appType = "notice";
string dtStr = DateTime.Now.ToString("yyyyMMdd"); string dtStr = DateTime.Now.ToString("yyyyMMdd");
string physicalDir = Path.Combine(StorageRoot(), appType, dtStr); string physicalDir = Path.Combine(StorageRoot(), appType, dtStr);
string outFilePath = "/UploadFile/" + appType + "/" + dtStr + "/"; string outFilePath = appType + "/" + dtStr + "/";
if (!Directory.Exists(physicalDir)) if (!Directory.Exists(physicalDir))
Directory.CreateDirectory(physicalDir); Directory.CreateDirectory(physicalDir);
@@ -169,7 +168,7 @@ namespace YhCore.Service.Impl
if (appType.ToLower() == "noticemsg") appType = "notice"; if (appType.ToLower() == "noticemsg") appType = "notice";
string dtStr = DateTime.Now.ToString("yyyyMMdd"); string dtStr = DateTime.Now.ToString("yyyyMMdd");
string physicalDir = Path.Combine(StorageRoot(), appType, dtStr); string physicalDir = Path.Combine(StorageRoot(), appType, dtStr);
string outFilePath = "/UploadFile/" + appType + "/" + dtStr + "/"; string outFilePath = appType + "/" + dtStr + "/";
if (!Directory.Exists(physicalDir)) if (!Directory.Exists(physicalDir))
Directory.CreateDirectory(physicalDir); Directory.CreateDirectory(physicalDir);
@@ -214,22 +213,29 @@ namespace YhCore.Service.Impl
if (string.IsNullOrEmpty(fileUrl)) if (string.IsNullOrEmpty(fileUrl))
return ""; return "";
// 相对路径如 /UploadFile/docprint/20260101/{guid}.jpg → Config.FilePath + 子路径 // 新数据:fileurl 以 FILE_PATH 开头(如 /data/upload/docprint/20260101/{guid}.jpg)→ 剥前缀映射回 FILE_PATH。
// 注意必须用未 TrimStart 的原始 fileUrl 比较(FILE_PATH 本身以 / 开头)
string fp = Config.FilePath;
if (!string.IsNullOrEmpty(fp) && fileUrl.StartsWith(fp, StringComparison.OrdinalIgnoreCase))
return Path.Combine(fp, fileUrl.Substring(fp.Length).TrimStart('/').Replace('/', Path.DirectorySeparatorChar));
// 旧数据:相对路径如 /upload/docprint/20260101/{guid}.jpg(不含 FILE_PATH 前缀)→ StorageRoot + 子路径
var relative = fileUrl.TrimStart('/'); var relative = fileUrl.TrimStart('/');
const string prefix = "UploadFile/"; string prefix = Config.FilePath;
if (relative.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) if (relative.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
relative = relative.Substring(prefix.Length); relative = relative.Substring(prefix.Length);
return Path.Combine(StorageRoot(), relative.Replace('/', Path.DirectorySeparatorChar)); return Path.Combine(StorageRoot(), relative.Replace('/', Path.DirectorySeparatorChar));
} }
/// <summary>存储根目录(旧 Server.MapPath("~/UploadFile/");Config.FilePath 缺省时退回进程目录/UploadFile)。</summary> /// <summary>存储根目录(旧 Server.MapPath("~/upload/");Config.FilePath 缺省时退回进程目录/upload)。</summary>
private static string StorageRoot() public static string StorageRoot()
{ {
if (!string.IsNullOrEmpty(Config.FilePath)) if (!string.IsNullOrEmpty(Config.FilePath))
return Config.FilePath; return Config.FilePath;
return Path.Combine(AppContext.BaseDirectory, "UploadFile"); // 此分支仅在 FILE_PATH 未配置时执行(此时 Config.FilePath 为 null,不可再参与拼接)
return Path.Combine(AppContext.BaseDirectory, Config.FilePath);
} }
} }
} }
+42 -26
View File
@@ -7,6 +7,7 @@ using Yh.Platform.Abstractions;
using Yh.SchClass.OpenApi; using Yh.SchClass.OpenApi;
using YhCore.Entity; using YhCore.Entity;
using YhCore.Model.Dto; using YhCore.Model.Dto;
using YhCore.Model.Vo;
using YhCore.Repository; using YhCore.Repository;
using YhCore.Service.Interfaces; using YhCore.Service.Interfaces;
@@ -72,7 +73,7 @@ namespace YhCore.Service.Impl
#region 抄送 #region 抄送
/// <summary>抄送列表(旧 SendRdList:DocStatus 1/11,SendUserID 含本人;旧为教师内连接)。</summary> /// <summary>抄送列表(旧 SendRdList:DocStatus 1/11,SendUserID 含本人;旧为教师内连接)。</summary>
public async Task<List<DocPrintListItem>> SendRdListAsync(string name, string startDate, string endDate, string userId, string schSerId, int pageSize, int pageIndex) public async Task<PageVO<DocPrintListItem>> 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(); var docs = (await _doc.FindAsync(m => m.SchSerID == schSerId)).ToList();
@@ -88,6 +89,7 @@ namespace YhCore.Service.Impl
docs = docs.Where(m => m.TakeDate <= date2).ToList(); docs = docs.Where(m => m.TakeDate <= date2).ToList();
} }
var total = docs.Count(m => m.DocStatus == "1" || m.DocStatus == "11");
docs = docs.Where(m => m.DocStatus == "1" || m.DocStatus == "11") docs = docs.Where(m => m.DocStatus == "1" || m.DocStatus == "11")
.OrderByDescending(m => m.SerID) .OrderByDescending(m => m.SerID)
.Skip(pageSize * pageIndex).Take(pageSize).ToList(); .Skip(pageSize * pageIndex).Take(pageSize).ToList();
@@ -101,7 +103,7 @@ namespace YhCore.Service.Impl
if (!TryGetTea(teaMap, item.UserID, out var tea)) continue; if (!TryGetTea(teaMap, item.UserID, out var tea)) continue;
result.Add(await BuildListItemAsync(item, tea, dictMap, "yyyy-MM-dd")); result.Add(await BuildListItemAsync(item, tea, dictMap, "yyyy-MM-dd"));
} }
return result; return PageVO<DocPrintListItem>.Of(result, total, pageIndex, pageSize);
} }
/// <summary>查看抄送人员详情(旧 SendDetailsList)。</summary> /// <summary>查看抄送人员详情(旧 SendDetailsList)。</summary>
@@ -349,7 +351,7 @@ namespace YhCore.Service.Impl
#region 列表 #region 列表
/// <summary>我的文印列表(旧 MyList;旧 DocTypeName 误用 YZBM 字典,此处保持旧行为)。</summary> /// <summary>我的文印列表(旧 MyList;旧 DocTypeName 误用 YZBM 字典,此处保持旧行为)。</summary>
public async Task<List<DocPrintListItem>> MyListAsync(string name, string status, string startDate, string endDate, string userId, string schSerId, int pageSize, int pageIndex) public async Task<PageVO<DocPrintListItem>> 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(); var docs = (await _doc.FindAsync(m => m.UserID == userId && m.SchSerID == schSerId)).ToList();
@@ -365,6 +367,7 @@ namespace YhCore.Service.Impl
docs = docs.Where(m => m.TakeDate <= date2).ToList(); docs = docs.Where(m => m.TakeDate <= date2).ToList();
} }
var total = docs.Count;
docs = docs.OrderBy(m => m.DocStatus).ThenByDescending(m => m.TakeDate).ThenByDescending(m => m.EditDate) docs = docs.OrderBy(m => m.DocStatus).ThenByDescending(m => m.TakeDate).ThenByDescending(m => m.EditDate)
.Skip(pageSize * pageIndex).Take(pageSize).ToList(); .Skip(pageSize * pageIndex).Take(pageSize).ToList();
@@ -376,14 +379,14 @@ namespace YhCore.Service.Impl
TryGetTea(teaMap, item.UserID, out var tea); // 旧为左连接,缺失补空 TryGetTea(teaMap, item.UserID, out var tea); // 旧为左连接,缺失补空
result.Add(await BuildListItemAsync(item, tea, dictMap, "yyyy-MM-dd HH:mm", "YZBM")); result.Add(await BuildListItemAsync(item, tea, dictMap, "yyyy-MM-dd HH:mm", "YZBM"));
} }
return result; return PageVO<DocPrintListItem>.Of(result, total, pageIndex, pageSize);
} }
/// <summary> /// <summary>
/// 全部文印列表(旧 AllList 原生 SQL 四分类:x=待我审(0) y=我已审(1) z=抄送我(2) m=我发起(3),按优先级去重)。 /// 全部文印列表(旧 AllList 原生 SQL 四分类:x=待我审(0) y=我已审(1) z=抄送我(2) m=我发起(3),按优先级去重)。
/// 旧 Name 过滤的 SQL 拼接存在运算符优先级缺陷,此处按语义实现:内容/教师名/字典名任一命中。 /// 旧 Name 过滤的 SQL 拼接存在运算符优先级缺陷,此处按语义实现:内容/教师名/字典名任一命中。
/// </summary> /// </summary>
public async Task<List<DocPrintListItem>> AllListAsync(string name, string userId, string schSerId, int pageSize, int pageIndex) public async Task<PageVO<DocPrintListItem>> AllListAsync(string name, string userId, string schSerId, int pageSize, int pageIndex)
{ {
int uid = int.Parse(userId); int uid = int.Parse(userId);
@@ -423,6 +426,7 @@ namespace YhCore.Service.Impl
|| DictNameContains(dictMap, "YZFL", t.doc.DPType, name)); || DictNameContains(dictMap, "YZFL", t.doc.DPType, name));
} }
var total = query.Count();
var pageItems = query.OrderByDescending(t => t.doc.SerID).ThenByDescending(t => t.doc.TakeDate) var pageItems = query.OrderByDescending(t => t.doc.SerID).ThenByDescending(t => t.doc.TakeDate)
.Skip(pageSize * pageIndex).Take(pageSize).ToList(); .Skip(pageSize * pageIndex).Take(pageSize).ToList();
@@ -434,16 +438,17 @@ namespace YhCore.Service.Impl
item.ctype = ctype; item.ctype = ctype;
result.Add(item); result.Add(item);
} }
return result; return PageVO<DocPrintListItem>.Of(result, total, pageIndex, pageSize);
} }
/// <summary> /// <summary>
/// 文印审核列表(旧 ShenheList:固定流程 + 指派 + 自由流程三路并集,HashSet 去重)。 /// 文印审核列表(旧 ShenheList:固定流程 + 指派 + 自由流程三路并集,HashSet 去重)。
/// 旧实现的 Name 过滤作用在未被使用的查询上(死代码),此处保持 Name 不生效的旧行为。 /// 旧实现的 Name 过滤作用在未被使用的查询上(死代码),此处保持 Name 不生效的旧行为。
/// </summary> /// </summary>
public async Task<List<DocPrintListItem>> ShenheListAsync(string userId, string schSerId, int pageSize, int pageIndex) public async Task<PageVO<DocPrintListItem>> ShenheListAsync(string userId, string schSerId, int pageSize, int pageIndex)
{ {
var docs = await GetPendingShenheDocsAsync(userId, schSerId, requireFlowIdNonZero: false); var docs = await GetPendingShenheDocsAsync(userId, schSerId, requireFlowIdNonZero: false);
var total = docs.Count;
docs = docs.OrderBy(m => m.DocStatus).ThenByDescending(m => m.TakeDate) docs = docs.OrderBy(m => m.DocStatus).ThenByDescending(m => m.TakeDate)
.Skip(pageSize * pageIndex).Take(pageSize).ToList(); .Skip(pageSize * pageIndex).Take(pageSize).ToList();
@@ -455,13 +460,13 @@ namespace YhCore.Service.Impl
TryGetTea(teaMap, item.UserID, out var tea); TryGetTea(teaMap, item.UserID, out var tea);
result.Add(await BuildListItemAsync(item, tea, dictMap, "yyyy-MM-dd HH:mm")); result.Add(await BuildListItemAsync(item, tea, dictMap, "yyyy-MM-dd HH:mm"));
} }
return result; return PageVO<DocPrintListItem>.Of(result, total, pageIndex, pageSize);
} }
/// <summary> /// <summary>
/// 审批列表(旧 SPList:待审(DS,zt=false)∪ 我已审(YS,zt=true,排除 DS),_disabled 即 zt)。 /// 审批列表(旧 SPList:待审(DS,zt=false)∪ 我已审(YS,zt=true,排除 DS),_disabled 即 zt)。
/// </summary> /// </summary>
public async Task<List<DocPrintListItem>> SPListAsync(string name, string status, string startDate, string endDate, string userId, string schSerId, int pageSize, int pageIndex) public async Task<PageVO<DocPrintListItem>> SPListAsync(string name, string status, string startDate, string endDate, string userId, string schSerId, int pageSize, int pageIndex)
{ {
var userid = int.Parse(userId); var userid = int.Parse(userId);
@@ -493,6 +498,7 @@ namespace YhCore.Service.Impl
query = query.Where(m => m.doc.DocContent != null && m.doc.DocContent.IndexOf(name) >= 0); 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 ordered = query.OrderBy(m => m.doc.DocStatus).ThenByDescending(m => m.doc.EditDate).ToList();
var total = ordered.Count;
var teaMap = await GetTeaMapAsync(schSerId); var teaMap = await GetTeaMapAsync(schSerId);
var dictMap = await GetDictMapAsync(schSerId); var dictMap = await GetDictMapAsync(schSerId);
@@ -504,11 +510,11 @@ namespace YhCore.Service.Impl
item._disabled = zt; // 我已审的记录不可再编辑(覆盖通用 _disabled 语义,与旧一致) item._disabled = zt; // 我已审的记录不可再编辑(覆盖通用 _disabled 语义,与旧一致)
result.Add(item); result.Add(item);
} }
return result; return PageVO<DocPrintListItem>.Of(result, total, pageIndex, pageSize);
} }
/// <summary>文印已审列表(旧 YishenList:本人审核流水关联,排除撤销与已领取)。</summary> /// <summary>文印已审列表(旧 YishenList:本人审核流水关联,排除撤销与已领取)。</summary>
public async Task<List<DocPrintListItem>> YishenListAsync(string name, string userId, string schSerId, int pageSize, int pageIndex) public async Task<PageVO<DocPrintListItem>> YishenListAsync(string name, string userId, string schSerId, int pageSize, int pageIndex)
{ {
int uid = int.Parse(userId); int uid = int.Parse(userId);
var tranctDataIds = (await _flowtranct.FindAsync(b => b.actionuserid == uid && b.datatype == DocPrintConstants.AppCode.ToString() && b.actionstatus != 11)) var tranctDataIds = (await _flowtranct.FindAsync(b => b.actionuserid == uid && b.datatype == DocPrintConstants.AppCode.ToString() && b.actionstatus != 11))
@@ -521,6 +527,7 @@ namespace YhCore.Service.Impl
if (!string.IsNullOrEmpty(name)) if (!string.IsNullOrEmpty(name))
docs = docs.Where(m => m.DocContent != null && m.DocContent.Contains(name)).ToList(); docs = docs.Where(m => m.DocContent != null && m.DocContent.Contains(name)).ToList();
var total = docs.Count;
docs = docs.OrderByDescending(m => m.SerID).ThenByDescending(m => m.TakeDate) docs = docs.OrderByDescending(m => m.SerID).ThenByDescending(m => m.TakeDate)
.Skip(pageSize * pageIndex).Take(pageSize).ToList(); .Skip(pageSize * pageIndex).Take(pageSize).ToList();
@@ -532,11 +539,11 @@ namespace YhCore.Service.Impl
TryGetTea(teaMap, item.UserID, out var tea); TryGetTea(teaMap, item.UserID, out var tea);
result.Add(await BuildListItemAsync(item, tea, dictMap, "yyyy-MM-dd HH:mm")); result.Add(await BuildListItemAsync(item, tea, dictMap, "yyyy-MM-dd HH:mm"));
} }
return result; return PageVO<DocPrintListItem>.Of(result, total, pageIndex, pageSize);
} }
/// <summary>全部文印列表(旧 totalList,管理员视角,按状态/时间过滤)。</summary> /// <summary>全部文印列表(旧 totalList,管理员视角,按状态/时间过滤)。</summary>
public async Task<List<DocPrintListItem>> TotalListAsync(string name, string stateid, string starttime, string endtime, string schSerId, int pageSize, int pageIndex) public async Task<PageVO<DocPrintListItem>> 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(); var docs = (await _doc.FindAsync(m => m.SchSerID == schSerId)).ToList();
@@ -552,6 +559,7 @@ namespace YhCore.Service.Impl
if (!string.IsNullOrEmpty(name)) if (!string.IsNullOrEmpty(name))
docs = docs.Where(m => m.DocContent != null && m.DocContent.IndexOf(name) >= 0).ToList(); docs = docs.Where(m => m.DocContent != null && m.DocContent.IndexOf(name) >= 0).ToList();
var total = docs.Count;
docs = docs.OrderBy(m => m.DocStatus).ThenByDescending(m => m.TakeDate) docs = docs.OrderBy(m => m.DocStatus).ThenByDescending(m => m.TakeDate)
.Skip(pageSize * pageIndex).Take(pageSize).ToList(); .Skip(pageSize * pageIndex).Take(pageSize).ToList();
@@ -563,7 +571,7 @@ namespace YhCore.Service.Impl
TryGetTea(teaMap, item.UserID, out var tea); TryGetTea(teaMap, item.UserID, out var tea);
result.Add(await BuildListItemAsync(item, tea, dictMap, "yyyy-MM-dd")); result.Add(await BuildListItemAsync(item, tea, dictMap, "yyyy-MM-dd"));
} }
return result; return PageVO<DocPrintListItem>.Of(result, total, pageIndex, pageSize);
} }
/// <summary> /// <summary>
@@ -895,7 +903,7 @@ namespace YhCore.Service.Impl
} }
/// <summary>文印详情统计列表(旧 List2:非待审单据按申请时间倒序分页)。</summary> /// <summary>文印详情统计列表(旧 List2:非待审单据按申请时间倒序分页)。</summary>
public async Task<List<DocDetailStatItem>> List2Async(string starttime, string endtime, string schSerId, int pageSize, int pageIndex) public async Task<PageVO<DocDetailStatItem>> List2Async(string starttime, string endtime, string schSerId, int pageSize, int pageIndex)
{ {
var docs = (await _doc.FindAsync(m => m.SchSerID == schSerId && m.DocStatus != "0")).ToList(); var docs = (await _doc.FindAsync(m => m.SchSerID == schSerId && m.DocStatus != "0")).ToList();
if (DateTime.TryParse(starttime, out var date1)) if (DateTime.TryParse(starttime, out var date1))
@@ -933,13 +941,16 @@ namespace YhCore.Service.Impl
}); });
} }
return full.OrderByDescending(a => a.EditDate) var total = full.Count;
return PageVO<DocDetailStatItem>.Of(
full.OrderByDescending(a => a.EditDate)
.Skip(pageSize * pageIndex).Take(pageSize) .Skip(pageSize * pageIndex).Take(pageSize)
.ToList(); .ToList(),
total, pageIndex, pageSize);
} }
/// <summary>文印数量按用户排名(旧 List3)。</summary> /// <summary>文印数量按用户排名(旧 List3)。</summary>
public async Task<List<UserRankItem>> List3Async(string starttime, string endtime, string schSerId, int pageSize, int pageIndex) public async Task<PageVO<UserRankItem>> List3Async(string starttime, string endtime, string schSerId, int pageSize, int pageIndex)
{ {
var docs = (await _doc.FindAsync(m => m.SchSerID == schSerId && m.DocStatus == "1")).ToList(); var docs = (await _doc.FindAsync(m => m.SchSerID == schSerId && m.DocStatus == "1")).ToList();
if (DateTime.TryParse(starttime, out var date1)) if (DateTime.TryParse(starttime, out var date1))
@@ -965,23 +976,27 @@ namespace YhCore.Service.Impl
.OrderByDescending(a => a.num) .OrderByDescending(a => a.num)
.ToList(); .ToList();
return grouped.Skip(pageSize * pageIndex).Take(pageSize).ToList(); var total = grouped.Count;
return PageVO<UserRankItem>.Of(
grouped.Skip(pageSize * pageIndex).Take(pageSize).ToList(),
total, pageIndex, pageSize);
} }
/// <summary>按月份统计明细(旧 List4:指定年月已印好单据,按申请时间倒序分页)。</summary> /// <summary>按月份统计明细(旧 List4:指定年月已印好单据,按申请时间倒序分页)。</summary>
public async Task<List<StatListItem>> List4Async(int year, int month, string schSerId, int pageSize, int pageIndex) public async Task<PageVO<StatListItem>> List4Async(int year, int month, string schSerId, int pageSize, int pageIndex)
{ {
var docs = (await _doc.FindAsync(m => m.SchSerID == schSerId var all = (await _doc.FindAsync(m => m.SchSerID == schSerId
&& m.DocStatus == "1" && m.EditDate.Year == year && m.EditDate.Month == month)) && m.DocStatus == "1" && m.EditDate.Year == year && m.EditDate.Month == month)).ToList();
.OrderByDescending(a => a.EditDate) var total = all.Count;
var docs = all.OrderByDescending(a => a.EditDate)
.Skip(pageSize * pageIndex).Take(pageSize) .Skip(pageSize * pageIndex).Take(pageSize)
.ToList(); .ToList();
return await BuildStatListAsync(docs, schSerId); return PageVO<StatListItem>.Of(await BuildStatListAsync(docs, schSerId), total, pageIndex, pageSize);
} }
/// <summary>按用户统计明细(旧 List5:指定用户已印好单据,按申请时间倒序分页)。</summary> /// <summary>按用户统计明细(旧 List5:指定用户已印好单据,按申请时间倒序分页)。</summary>
public async Task<List<StatListItem>> List5Async(string userid, string starttime, string endtime, string schSerId, int pageSize, int pageIndex) public async Task<PageVO<StatListItem>> 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(); var docs = (await _doc.FindAsync(m => m.SchSerID == schSerId && m.UserID == userid && m.DocStatus == "1")).ToList();
if (DateTime.TryParse(starttime, out var date1)) if (DateTime.TryParse(starttime, out var date1))
@@ -992,11 +1007,12 @@ namespace YhCore.Service.Impl
docs = docs.Where(m => m.EditDate <= date2).ToList(); docs = docs.Where(m => m.EditDate <= date2).ToList();
} }
var total = docs.Count;
docs = docs.OrderByDescending(a => a.EditDate) docs = docs.OrderByDescending(a => a.EditDate)
.Skip(pageSize * pageIndex).Take(pageSize) .Skip(pageSize * pageIndex).Take(pageSize)
.ToList(); .ToList();
return await BuildStatListAsync(docs, schSerId); return PageVO<StatListItem>.Of(await BuildStatListAsync(docs, schSerId), total, pageIndex, pageSize);
} }
#endregion #endregion
File diff suppressed because it is too large Load Diff
@@ -7,8 +7,8 @@ namespace YhCore.Service.Interfaces
{ {
/// <summary> /// <summary>
/// 公共文件服务(移植旧 BestSchool.Service.Common.GetEnclosure/FileSaveEnclosure + /// 公共文件服务(移植旧 BestSchool.Service.Common.GetEnclosure/FileSaveEnclosure +
/// BestSchool.Common.UpLoadFile.FileSave/FileSaveImage/FileChunkSave/FileMergeSave)。 /// BestSchool.Common.upload.FileSave/FileSaveImage/FileChunkSave/FileMergeSave)。
/// 存储约定:物理根目录 = Config.FilePath(≈旧 ~/UploadFile/),相对路径仍存 /UploadFile/{AppType}/{yyyyMMdd}/{guid}{ext} /// 存储约定:物理根目录 = Config.FilePath(≈旧 ~/upload/),相对路径仍存 /upload/{AppType}/{yyyyMMdd}/{guid}{ext}
/// (与旧库数据格式一致,兼容历史记录);host 取 Config.FileWebPath(≈旧 AuthConfig.weboa_url)。 /// (与旧库数据格式一致,兼容历史记录);host 取 Config.FileWebPath(≈旧 AuthConfig.weboa_url)。
/// 旧 ToOfficeHelper.OfficeConvertToPDF(Office COM 转 PDF)新项目缺失,降级:上传不做转 PDF, /// 旧 ToOfficeHelper.OfficeConvertToPDF(Office COM 转 PDF)新项目缺失,降级:上传不做转 PDF,
/// 预览时本地无 .pdf 则降级 officeapps.live.com 在线预览。 /// 预览时本地无 .pdf 则降级 officeapps.live.com 在线预览。
@@ -21,10 +21,10 @@ namespace YhCore.Service.Interfaces
/// <summary>存储附件信息(旧 FileSaveEnclosure:Common_UpFiles 落库,返回 SerID;文件名非法字符替换为 _)。</summary> /// <summary>存储附件信息(旧 FileSaveEnclosure:Common_UpFiles 落库,返回 SerID;文件名非法字符替换为 _)。</summary>
Task<int> SaveEnclosureAsync(FileObj file, string appType); Task<int> SaveEnclosureAsync(FileObj file, string appType);
/// <summary>单文件上传(旧 UpLoadFile.FileSave:扩展名白名单 + 保存 + 返回 FileObj)。</summary> /// <summary>单文件上传(旧 upload.FileSave:扩展名白名单 + 保存 + 返回 FileObj)。</summary>
Task<FileObj> SaveFileAsync(string appType, IFormFile file); Task<FileObj> SaveFileAsync(string appType, IFormFile file);
/// <summary>图片多文件上传(旧 UpLoadFile.FileSaveImage:jpg/gif/png/jpeg)。</summary> /// <summary>图片多文件上传(旧 upload.FileSaveImage:jpg/gif/png/jpeg)。</summary>
Task<List<FileObj>> SaveFileImageAsync(string appType, IFormFileCollection files); Task<List<FileObj>> SaveFileImageAsync(string appType, IFormFileCollection files);
/// <summary>保存分片(旧 FileChunkSave:临时目录 {guid}/ 下按索引存块)。</summary> /// <summary>保存分片(旧 FileChunkSave:临时目录 {guid}/ 下按索引存块)。</summary>
@@ -33,7 +33,7 @@ namespace YhCore.Service.Interfaces
/// <summary>合并分片(旧 FileMergeSave:按块合并 → 正式目录 + FileObj)。</summary> /// <summary>合并分片(旧 FileMergeSave:按块合并 → 正式目录 + FileObj)。</summary>
Task<FileObj> MergeSaveAsync(string appType, string guid, string fileName); Task<FileObj> MergeSaveAsync(string appType, string guid, string fileName);
/// <summary>相对路径 → 物理路径(fileurl 以 /UploadFile/ 开头 → Config.FilePath + 子路径)。</summary> /// <summary>相对路径 → 物理路径(fileurl 以 /upload/ 开头 → Config.FilePath + 子路径)。</summary>
string MapPhysicalPath(string fileUrl); string MapPhysicalPath(string fileUrl);
} }
} }
@@ -1,6 +1,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Collections.Generic; using System.Collections.Generic;
using YhCore.Model.Dto; using YhCore.Model.Dto;
using YhCore.Model.Vo;
namespace YhCore.Service.Interfaces namespace YhCore.Service.Interfaces
{ {
@@ -11,7 +12,7 @@ namespace YhCore.Service.Interfaces
public interface IWYDocPrintService public interface IWYDocPrintService
{ {
// ---- 抄送 ---- // ---- 抄送 ----
Task<List<DocPrintListItem>> SendRdListAsync(string name, string startDate, string endDate, string userId, string schSerId, int pageSize, int pageIndex); Task<PageVO<DocPrintListItem>> SendRdListAsync(string name, string startDate, string endDate, string userId, string schSerId, int pageSize, int pageIndex);
Task<List<SendDetailItem>> SendDetailsListAsync(int serId); Task<List<SendDetailItem>> SendDetailsListAsync(int serId);
Task<string> EditSendAsync(int serId, string senduserid, string mysenduserid, string schSerId, string uid); Task<string> EditSendAsync(int serId, string senduserid, string mysenduserid, string schSerId, string uid);
@@ -25,12 +26,12 @@ namespace YhCore.Service.Interfaces
Task<string> RevokeAsync(FlowTranctDto model); 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<PageVO<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<PageVO<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<PageVO<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<PageVO<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<PageVO<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<PageVO<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<DocPrintDetailItem>> MyListtoOneAsync(int serId, string schSerId);
Task<List<BindFlowOption>> BindflowinfoAsync(string schSerId, int flowType); Task<List<BindFlowOption>> BindflowinfoAsync(string schSerId, int flowType);
@@ -43,9 +44,9 @@ namespace YhCore.Service.Interfaces
// ---- 统计 ---- // ---- 统计 ----
Task<List<MonthStatItem>> List1Async(string starttime, string endtime, string schSerId); 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<PageVO<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<PageVO<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<PageVO<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); Task<PageVO<StatListItem>> List5Async(string userid, string starttime, string endtime, string schSerId, int pageSize, int pageIndex);
} }
} }
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using YhCore.Entity; using YhCore.Entity;
using YhCore.Model.Dto; using YhCore.Model.Dto;
using YhCore.Model.Vo; using YhCore.Model.Vo;
@@ -14,7 +16,9 @@ namespace YhCore.Service.Interfaces
/// 落库 WY_SHRole_SHTea.SHTeaID / WY_SHRole_BSHTea.BSHTeaID 仍为 decimal 用户 id; /// 落库 WY_SHRole_SHTea.SHTeaID / WY_SHRole_BSHTea.BSHTeaID 仍为 decimal 用户 id;
/// 3. 签名存文件系统(DES 加密),非数据库表; /// 3. 签名存文件系统(DES 加密),非数据库表;
/// 4. 旧 ServiceResult 的 Data/OtherData/Page 三段合并为单个 Result 对象(getshenheFlow / getStepInfo / getStepByJE / getdocOftable)。 /// 4. 旧 ServiceResult 的 Data/OtherData/Page 三段合并为单个 Result 对象(getshenheFlow / getStepInfo / getStepByJE / getdocOftable)。
/// 延后:Excel 导入导出(UpfileExcelSHRole/downMBOfSHRole/downUpfileSHRoleFK/ExceltotalListSHRole/WYRDExceldaochu)、审批单 PDF(DwonWYSQD)。 /// Excel 导入导出(UpfileExcelSHRole/downMBOfSHRole/downUpfileSHRoleFK/ExceltotalListSHRole/WYRDExceldaochu)
/// 与审批单 PDF(DwonWYSQD):下载类接口返回 (byte[] Content, string FileName),
/// 控制器按扩展名包装 File();导入反馈表存进程内 IMemoryCache(单实例)。
/// </summary> /// </summary>
public interface IWYSHRoleService public interface IWYSHRoleService
{ {
@@ -50,10 +54,10 @@ namespace YhCore.Service.Interfaces
// ---- 用户记录查看和审核 ---- // ---- 用户记录查看和审核 ----
Task<SHRoleShenheFlowResult> GetshenheFlowAsync(string schSerId, decimal docprintId, int flowid); Task<SHRoleShenheFlowResult> GetshenheFlowAsync(string schSerId, decimal docprintId, int flowid);
Task<List<SHRoleDocPrintListItem>> MyListtoOneSHRoleAsync(int serId, string schSerId); 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<PageVO<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<PageVO<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<PageVO<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<PageVO<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<string> ShenheOkSHRoleAsync(FlowTranctDto model);
// ---- 规格表 / 签名 / 退回 / 金额 ---- // ---- 规格表 / 签名 / 退回 / 金额 ----
@@ -64,7 +68,26 @@ namespace YhCore.Service.Interfaces
Task<string> TuiHuiAsync(string userId, string cont, decimal docId); Task<string> TuiHuiAsync(string userId, string cont, decimal docId);
Task<string> UnTuiHuiAsync(string userId, decimal docId); Task<string> UnTuiHuiAsync(string userId, decimal docId);
Task<string> RegetJEAsync(string schSerId, string userId, List<decimal> listDocPrintId, string type); Task<string> RegetJEAsync(string schSerId, string userId, List<decimal> listDocPrintId, string type);
Task<List<SHRoleRegetJERdItem>> GetRegetJERdAsync(string schSerId, int pageSize, int pageIndex); Task<PageVO<SHRoleRegetJERdItem>> GetRegetJERdAsync(string schSerId, int pageSize, int pageIndex);
Task<int> SetDownFileTimesAsync(int serId); Task<int> SetDownFileTimesAsync(int serId);
// ---- Excel 导入导出 / 审批单 PDF(延后批次补齐)----
/// <summary>下载导入模板(旧 downMBOfSHRole,代码生成)。</summary>
Task<byte[]> DownMBOfSHRoleAsync();
/// <summary>Excel 批量导入审核角色(旧 UpfileExcelSHRole,multipart 第一个文件),返回反馈消息。</summary>
Task<string> UpfileExcelSHRoleAsync(string schSerId, string userId, IFormFile file);
/// <summary>下载最近一次导入的反馈 Excel(旧 downUpfileSHRoleFK)。</summary>
Task<byte[]> DownUpfileSHRoleFKAsync(string userId);
/// <summary>全部文印导出(旧 ExceltotalListSHRole)。</summary>
Task<(byte[] Content, string FileName)> ExceltotalListSHRoleAsync(string schSerId, string name, string stateid, string starttime, string endtime);
/// <summary>文印记录导出(旧 WYRDExceldaochu,flag=page 简版 / 其余完整版)。</summary>
Task<(byte[] Content, string FileName)> WYRDExceldaochuAsync(string schSerId, int flowid, int dwid, string name, string stateid, string starttime, string endtime, string flag);
/// <summary>审批单 PDF 下载(旧 DwonWYSQD,IDS 逗号分隔的 WY_DocPrint.SerID)。</summary>
Task<(byte[] Content, string FileName)> DwonWYSQDAsync(string schSerId, string userId, string ids);
} }
} }
@@ -0,0 +1,123 @@
using System;
namespace YhCore.Service.Pdf
{
/// <summary>数字转中文大写金额(移植自旧 Bsch.Common/DocPrint/NumberToChineseConverter,审批单合计金额用)。</summary>
public class NumberToChineseConverter
{
// 中文大写数字
private static readonly char[] ChineseNumbers = { '零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖' };
// 中文大写单位
private static readonly string[] ChineseUnits = { "分", "角", "元", "拾", "佰", "仟", "万", "拾", "佰", "仟", "亿", "拾", "佰", "仟", "兆" };
/// <summary>
/// 将数字转换为中文大写金额
/// </summary>
/// <param name="number">需要转换的数字(支持整数和小数,建议保留两位小数)</param>
/// <returns>中文大写金额字符串</returns>
public static string ToChineseUppercase(decimal number)
{
// 处理负数
bool isNegative = number < 0;
if (isNegative)
{
number = Math.Abs(number);
}
// 四舍五入保留两位小数
number = Math.Round(number, 2);
long integerPart = (long)number; // 整数部分
int decimalPart = (int)((number - integerPart) * 100); // 小数部分(角分)
// 处理零值
if (integerPart == 0 && decimalPart == 0)
{
return "零元整";
}
string result = string.Empty;
// 处理整数部分
if (integerPart > 0)
{
string integerStr = integerPart.ToString();
int length = integerStr.Length;
// 标记是否需要加零
bool needZero = false;
for (int i = 0; i < length; i++)
{
int digit = int.Parse(integerStr[i].ToString());
int unitIndex = length - i - 1 + 2; // 对应到ChineseUnits的索引(从元开始)
if (digit == 0)
{
needZero = true;
// 处理万、亿等单位,避免漏单位
if (unitIndex == 6 || unitIndex == 10)
{
result += ChineseUnits[unitIndex];
needZero = false;
}
}
else
{
if (needZero)
{
result += ChineseNumbers[0];
needZero = false;
}
result += ChineseNumbers[digit] + ChineseUnits[unitIndex];
}
}
}
// 处理小数部分(角分)
if (decimalPart > 0)
{
int jiao = decimalPart / 10; // 角
int fen = decimalPart % 10; // 分
if (jiao > 0)
{
result += ChineseNumbers[jiao] + ChineseUnits[1];
}
else if (integerPart > 0) // 有整数部分但角为0,需要加零
{
result += ChineseNumbers[0];
}
if (fen > 0)
{
result += ChineseNumbers[fen] + ChineseUnits[0];
}
}
else
{
// 无小数部分,加"整"
result += "整";
}
// 处理负数
if (isNegative)
{
result = "负" + result;
}
return result;
}
// 重载方法,支持double类型
public static string ToChineseUppercase(double number)
{
return ToChineseUppercase((decimal)number);
}
// 重载方法,支持int类型
public static string ToChineseUppercase(int number)
{
return ToChineseUppercase((decimal)number);
}
}
}
+102
View File
@@ -0,0 +1,102 @@
// PDF helper(移植自旧 Bsch.Common/DocPrint/PdfHelper):
// HTML→PDF 由 wkhtmltopdf 外部进程完成(原 iTextSharp XMLWorker 无 Core 版),
// 多页合并用 iTextSharp.LGPLv2.Core;Base64→PNG 直接落盘。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using iTextSharp.text;
using iTextSharp.text.pdf;
using Yh.Platform.Abstractions;
using YhCore.Common.Config;
namespace YhCore.Service.Pdf
{
public class PdfHelper
{
private static string WkHtmlToPdfPath =>
string.IsNullOrEmpty(Config.WkHtmlToPdfPath) ? "wkhtmltopdf" : Config.WkHtmlToPdfPath;
// --enable-local-file-access 为 wkhtmltopdf 0.12.6 新增开关(签名图片用本地绝对路径 <img> 必需);
// 0.12.5 及以下(如 Debian 10 源)不认识该参数会报 Unknown long argument,按版本探测决定是否追加
private static readonly Lazy<bool> NeedsLocalFileAccessFlag = new Lazy<bool>(() =>
{
try
{
using var probe = Process.Start(new ProcessStartInfo(WkHtmlToPdfPath, "--version")
{
RedirectStandardOutput = true,
CreateNoWindow = true,
UseShellExecute = false,
});
if (probe == null) return true;
string output = probe.StandardOutput.ReadToEnd();
probe.WaitForExit(10000);
var m = Regex.Match(output, @"(\d+)\.(\d+)\.(\d+)");
if (!m.Success) return true;
var version = new Version(int.Parse(m.Groups[1].Value), int.Parse(m.Groups[2].Value), int.Parse(m.Groups[3].Value));
return version >= new Version(0, 12, 6);
}
catch
{
return true;
}
});
/// <summary>逐页 HTML→PDF 后合并为单个 PDF 字节流(DwonWYSQD 用)</summary>
public static byte[] ConvertHtmlTextToPdfByList(List<string> htmlText, string HorV = "V")
=> MergePdf(htmlText.Select(HtmlToPdf).ToList());
private static byte[] HtmlToPdf(string html)
{
var tmpIn = Path.GetTempFileName() + ".html";
var tmpOut = Path.GetTempFileName() + ".pdf";
try
{
File.WriteAllText(tmpIn, html, new UTF8Encoding(false));
string localFlag = NeedsLocalFileAccessFlag.Value ? " --enable-local-file-access" : "";
var psi = new ProcessStartInfo(WkHtmlToPdfPath,
$" --encoding utf-8{localFlag} --disable-smart-shrinking \"{tmpIn}\" \"{tmpOut}\"")
{ CreateNoWindow = true, UseShellExecute = false };
using var proc = Process.Start(psi);
if (proc == null || !proc.WaitForExit(60000) || !File.Exists(tmpOut))
throw new DException("审批单 PDF 生成失败,请检查服务器是否已安装 wkhtmltopdf(或配置 wkhtmltopdf_path 指向其可执行文件)!");
return File.ReadAllBytes(tmpOut);
}
finally
{
if (File.Exists(tmpIn)) File.Delete(tmpIn);
if (File.Exists(tmpOut)) File.Delete(tmpOut);
}
}
public static byte[] MergePdf(List<byte[]> pdfBytes)
{
using var ms = new MemoryStream();
var doc = new Document();
var copy = new PdfCopy(doc, ms);
doc.Open();
foreach (var b in pdfBytes)
{
if (b == null || b.Length == 0) continue;
var reader = new PdfReader(b);
for (int i = 1; i <= reader.NumberOfPages; i++)
copy.AddPage(copy.GetImportedPage(reader, i));
copy.FreeReader(reader);
reader.Close();
}
doc.Close();
return ms.ToArray();
}
/// <summary>签名 base64 → PNG 落盘(审批单 HTML 内嵌本地路径图片用)</summary>
public static void ConvertBase64ToPng(string base64String, string outputFilePath)
{
var bytes = Convert.FromBase64String(base64String);
File.WriteAllBytes(outputFilePath, bytes);
}
}
}
+9 -2
View File
@@ -22,9 +22,16 @@
<ItemGroup> <ItemGroup>
<!-- 业务异常 DException / ErrorCode / IErrorCode:从 Nexus nuget-group 引用(原为本地源项目引用) --> <!-- 业务异常 DException / ErrorCode / IErrorCode:从 Nexus nuget-group 引用(原为本地源项目引用) -->
<PackageReference Include="Yh.Platform.Abstractions" Version="1.0.0-preview.1" /> <PackageReference Include="Yh.Platform.Abstractions" Version="1.0.0-preview.2" />
<!-- 教务(SchClass)开放接口 SDK:教师/学校查询(C1 reviewprocess 教师类接口):从 Nexus nuget-group 引用(原为本地源项目引用) --> <!-- 教务(SchClass)开放接口 SDK:教师/学校查询(C1 reviewprocess 教师类接口):从 Nexus nuget-group 引用(原为本地源项目引用) -->
<PackageReference Include="Yh.SchClass.OpenApi" Version="1.0.0-preview.1" /> <PackageReference Include="Yh.SchClass.OpenApi" Version="1.0.0-preview.2" />
</ItemGroup>
<ItemGroup>
<!-- Excel 导入导出(NPOI .xls 读写 / EPPlus 多 Sheet 导出)与 PDF 合并:版本对齐旧 DocPrintCore/Bsch.Common -->
<PackageReference Include="NPOI" Version="2.6.2" />
<PackageReference Include="EPPlus" Version="4.5.3.3" />
<PackageReference Include="iTextSharp.LGPLv2.Core" Version="3.4.20" />
</ItemGroup> </ItemGroup>
</Project> </Project>
@@ -1,12 +1,14 @@
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using Yh.Platform.Abstractions; using Yh.Platform.Abstractions;
using Yh.SchClass.OpenApi;
using Yh.SchClass.OpenApi.Model;
using YhCore.Common; using YhCore.Common;
using YhCore.Common.Config; using YhCore.Common.Config;
using YhCore.Common.UserContext; using YhCore.Common.UserContext;
@@ -28,13 +30,24 @@ namespace YhCore.WebApi.Controllers.Api.Common
{ {
private readonly IDictionaryService _dict; private readonly IDictionaryService _dict;
private readonly IFileService _file; private readonly IFileService _file;
private readonly SchClassOpenApi _sch;
public CommonController(IDictionaryService dict, IFileService file) public CommonController(IDictionaryService dict, IFileService file, SchClassOpenApi sch)
{ {
_dict = dict; _dict = dict;
_file = file; _file = file;
_sch = sch;
} }
[HttpGet("GetSchoolAll")]
public async Task<List<TeaTreeVo>> GetSchoolAll([FromQuery] int type = 0)
{
return await _sch.SchTree.GetTeaTreeAsync(SchSerID(), null);
}
/// <summary>获取指定字典数据(Type 1~10)。</summary> /// <summary>获取指定字典数据(Type 1~10)。</summary>
[HttpGet("GetSysDic")] [HttpGet("GetSysDic")]
public async Task<List<DicOption>> GetSysDic([FromQuery] int type = 0) public async Task<List<DicOption>> GetSysDic([FromQuery] int type = 0)
@@ -222,42 +235,58 @@ namespace YhCore.WebApi.Controllers.Api.Common
/// <summary> /// <summary>
/// 公用附件上传(旧 FileUpload,[NoAuth])。 /// 公用附件上传(旧 FileUpload,[NoAuth])。
/// chunk 参数 → 分片保存返回 {code:1,type:"chunk",msg};fileName 参数 → 合并分片;否则单文件上传。 /// 分片(chunk/chunkIndex + totalChunks&gt;1)→ 分片保存返回 {code:1,type:"chunk",msg};
/// 只带 fileName 不带文件 → 合并分片;带文件 → 单文件直传
/// (前端单文件直传也会带 guid/chunkIndex=0/totalChunks=0/fileName,不得误入合并分支)。
/// guid/chunk/fileName 兼容 query 与 form 两种形态(旧版读 Query)。
/// 落库 Common_UpFiles(TopicType=AppCode),返回 {fileurl,host,fileid,name,type}。 /// 落库 Common_UpFiles(TopicType=AppCode),返回 {fileurl,host,fileid,name,type}。
/// </summary> /// </summary>
[HttpPost("FileUpload")] [HttpPost("FileUpload")]
public async Task<object> Upload() public async Task<object> Upload()
{ {
string apptype = "docprint"; // 旧 WebHelper.AppType 固定 docprint string apptype = "docprint"; // 旧 WebHelper.AppType 固定 docprint
string guid = Request.Form["guid"]; string guid = FormOrQuery("guid");
string chunk = Request.Form["chunk"]; string chunk = FormOrQuery("chunk"); // 旧契约分片序号
string fileName = Request.Form["fileName"]; string chunkIndex = FormOrQuery("chunkIndex"); // 前端现用分片序号
string totalChunks = FormOrQuery("totalChunks");
string fileName = FormOrQuery("fileName");
if (!string.IsNullOrEmpty(chunk)) var file = Request.Form.Files.FirstOrDefault(f => f.Name == "file") ?? Request.Form.Files.FirstOrDefault();
bool multiChunk = int.TryParse(totalChunks, out var total) && total > 1;
bool isChunkReq = file != null
&& (!string.IsNullOrEmpty(chunk) || (!string.IsNullOrEmpty(chunkIndex) && multiChunk));
if (isChunkReq)
{ {
var chunkFile = Request.Form.Files.FirstOrDefault(f => f.Name == "file") ?? Request.Form.Files.FirstOrDefault(); await _file.SaveChunkAsync(guid, string.IsNullOrEmpty(chunk) ? chunkIndex : chunk, file);
await _file.SaveChunkAsync(guid, chunk, chunkFile);
return new { code = 1, type = "chunk", msg = "分片上传成功!" }; return new { code = 1, type = "chunk", msg = "分片上传成功!" };
} }
FileObj url; FileObj url;
string errMsg = "上传失败!"; if (!string.IsNullOrEmpty(fileName) && file == null)
if (string.IsNullOrEmpty(fileName))
{ {
url = await _file.SaveFileAsync(apptype, Request.Form.Files.FirstOrDefault()); url = await _file.MergeSaveAsync(apptype, guid, fileName); // 合并分片
} }
else else
{ {
url = await _file.MergeSaveAsync(apptype, guid, fileName); url = await _file.SaveFileAsync(apptype, file); // 单文件直传
} }
if (url == null) if (url == null)
throw new DException(errMsg); throw new DException("上传失败!");
// 旧 WebHelper.AppCode == "1102" ? "1002" : AppCode → docprint 固定 1003 // 旧 WebHelper.AppCode == "1102" ? "1002" : AppCode → docprint 固定 1003
int serid = await _file.SaveEnclosureAsync(url, DocPrintConstants.AppCode.ToString()); int serid = await _file.SaveEnclosureAsync(url, DocPrintConstants.AppCode.ToString());
return new { fileurl = url.fileurl, host = url.host, fileid = serid, name = url.filename, type = url.filetype }; return new { fileurl = url.fileurl, host = url.host, fileid = serid, name = url.filename, type = url.filetype };
} }
/// <summary>取表单/URL 参数(旧版读 Request["x"] 同时覆盖 Query+Form)。</summary>
private string FormOrQuery(string name)
{
var v = Request.Form[name].ToString();
if (!string.IsNullOrEmpty(v)) return v;
return Request.Query[name].ToString();
}
/// <summary>公用图片上传(旧 FileUploadImage,[NoAuth];多图,返回数组)。</summary> /// <summary>公用图片上传(旧 FileUploadImage,[NoAuth];多图,返回数组)。</summary>
[HttpPost("FileUploadImage")] [HttpPost("FileUploadImage")]
public async Task<object> UploadImg() public async Task<object> UploadImg()
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -16,8 +17,7 @@ namespace YhCore.WebApi.Controllers.Api.DocPrint
{ {
/// <summary> /// <summary>
/// 审核角色 + 流程配置控制器(对应旧 Areas/DocPrint/WYSHRoleController,路由 api/docprint/wyshrole)。 /// 审核角色 + 流程配置控制器(对应旧 Areas/DocPrint/WYSHRoleController,路由 api/docprint/wyshrole)。
/// 45 action 中本批落地 39 个;Excel 导入导出(ExceltotalListSHRole/WYRDExceldaochu/UpfileExcelSHRole/ /// 45 action 全部落地(Excel 导入导出 5 个走 NPOI/EPPlus,审批单 PDF 走 wkhtmltopdf)。
/// downMBOfSHRole/downUpfileSHRoleFK)与审批单 PDF(DwonWYSQD)依赖 NPOI/PdfHelper,延后迁移。
/// POST 动作一律 [FromForm]:前端 api/index.js 固定 x-www-form-urlencoded 提交([FromForm] 会 415); /// POST 动作一律 [FromForm]:前端 api/index.js 固定 x-www-form-urlencoded 提交([FromForm] 会 415);
/// 老契约"表单字段塞 JSON"的动作(WYSHRole/SHTeaTxt/listXxxTxt/data/docdata/docnumdata) /// 老契约"表单字段塞 JSON"的动作(WYSHRole/SHTeaTxt/listXxxTxt/data/docdata/docnumdata)
/// 以 [FromForm] string 接收后手动反序列化。 /// 以 [FromForm] string 接收后手动反序列化。
@@ -118,7 +118,7 @@ namespace YhCore.WebApi.Controllers.Api.DocPrint
#region 审核角色流程 #region 审核角色流程
/// <summary>审核流程列表(旧 ListFlow)。</summary> /// <summary>审核流程列表(旧 ListFlow;分页返回 PageVO,Content 内对象字段与旧 Data 对齐)。</summary>
[HttpGet("ListFlow")] [HttpGet("ListFlow")]
public async Task<PageVO<SHRoleFlowListItem>> ListFlow( public async Task<PageVO<SHRoleFlowListItem>> ListFlow(
[FromQuery] string name = null, [FromQuery] string name = null,
@@ -297,8 +297,74 @@ namespace YhCore.WebApi.Controllers.Api.DocPrint
return await _role.ShenheOkSHRoleAsync(model); return await _role.ShenheOkSHRoleAsync(model);
} }
// 延后:ExceltotalListSHRole / WYRDExceldaochu / UpfileExcelSHRole / downMBOfSHRole / downUpfileSHRoleFK(Excel,NPOI) #region Excel 导入导出 / 审批单 PDF
// 延后:DwonWYSQD(审批单 PDF,PdfHelper + Server.MapPath)
/// <summary>全部文印导出(旧 ExceltotalListSHRole,NPOI .xls)。</summary>
[HttpGet("ExceltotalListSHRole")]
public async Task<IActionResult> ExceltotalListSHRole(
[FromQuery] string Name,
[FromQuery] string stateid,
[FromQuery] string starttime,
[FromQuery] string endtime)
{
var (content, fileName) = await _role.ExceltotalListSHRoleAsync(SchSerID(), Name, stateid, starttime, endtime);
return File(content, ContentType(fileName), fileName);
}
/// <summary>文印记录导出(旧 WYRDExceldaochu,flag=page 简版 / 完整版,EPPlus)。</summary>
[HttpGet("WYRDExceldaochu")]
public async Task<IActionResult> WYRDExceldaochu(
[FromQuery] int flowid,
[FromQuery] int DWid,
[FromQuery] string Name,
[FromQuery] string stateid,
[FromQuery] string starttime,
[FromQuery] string endtime,
[FromQuery] string flag)
{
var (content, fileName) = await _role.WYRDExceldaochuAsync(SchSerID(), flowid, DWid, Name, stateid, starttime, endtime, flag);
return File(content, ContentType(fileName), fileName);
}
/// <summary>Excel 批量导入审核角色(旧 UpfileExcelSHRole,multipart 文件字段名不敏感取第一个)。</summary>
[HttpPost("UpfileExcelSHRole")]
public async Task<string> UpfileExcelSHRole()
{
var file = Request.Form.Files.Count > 0 ? Request.Form.Files[0] : null;
return await _role.UpfileExcelSHRoleAsync(SchSerID(), UserId(), file);
}
/// <summary>下载导入模板(旧 downMBOfSHRole,代码生成)。</summary>
[HttpGet("downMBOfSHRole")]
public async Task<IActionResult> DownMBOfSHRole()
{
var content = await _role.DownMBOfSHRoleAsync();
return File(content, "application/vnd.ms-excel", "批量导入审核角色.xls");
}
/// <summary>下载最近一次导入的反馈 Excel(旧 downUpfileSHRoleFK)。</summary>
[HttpGet("downUpfileSHRoleFK")]
public async Task<IActionResult> DownUpfileSHRoleFK()
{
var content = await _role.DownUpfileSHRoleFKAsync(UserId());
return File(content, "application/vnd.ms-excel", "批量导入审核角色反馈.xls");
}
/// <summary>审批单 PDF 下载(旧 DwonWYSQD,IDS 逗号分隔的 WY_DocPrint.SerID;依赖服务器安装 wkhtmltopdf)。</summary>
[HttpGet("DwonWYSQD")]
public async Task<IActionResult> DwonWYSQD([FromQuery] string IDS)
{
var (content, fileName) = await _role.DwonWYSQDAsync(SchSerID(), UserId(), IDS);
return File(content, "application/pdf", fileName);
}
/// <summary>按扩展名取下载 Content-Type(对齐 CommonController.GetContentType 约定)。</summary>
private static string ContentType(string fileName)
=> fileName != null && fileName.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase)
? "application/pdf"
: "application/vnd.ms-excel";
#endregion
#endregion #endregion
@@ -100,9 +100,8 @@ namespace YhCore.WebApi.Controllers.Api.DocPrint
[HttpPost("EditDictionaryType")] [HttpPost("EditDictionaryType")]
public async Task EditDictionaryType([FromForm] Common_Dictionary model) public async Task EditDictionaryType([FromForm] Common_Dictionary model)
{ {
if (model == null || model.SerID <= 0 || string.IsNullOrWhiteSpace(model.typeID) if (model == null || model.SerID <= 0 || string.IsNullOrWhiteSpace(model.Name))
|| string.IsNullOrWhiteSpace(model.typeName) || string.IsNullOrWhiteSpace(model.Name)) throw new DException("参数错误:SerID、Name 均不能为空(注意表单字段名拼写,TypeId≠TypelD)");
throw new DException("参数错误:SerID、typeID、typeName、Name 均不能为空(注意表单字段名拼写,TypeId≠TypelD)");
model.Schserid = SchSerID(); model.Schserid = SchSerID();
await _dict.EditDicAsync(model); await _dict.EditDicAsync(model);
} }
+17 -3
View File
@@ -122,6 +122,10 @@ namespace YhCore.WebApi
// Redis 缓存(RedisUtil 单例,连接串取 Redis:ConnectionString) // Redis 缓存(RedisUtil 单例,连接串取 Redis:ConnectionString)
services.AddRedisCache(Configuration); services.AddRedisCache(Configuration);
// 进程内内存缓存:WYSHRole Excel 导入的反馈表(updateSHRole_{userId},单实例;
// 多实例部署需改 Redis 支撑的分布式缓存)
services.AddMemoryCache();
// ===== 会话状态(Session)===== // ===== 会话状态(Session)=====
// 对应 Java HttpSession / session.invalidate();旧 DocPrintCore 同款配置。 // 对应 Java HttpSession / session.invalidate();旧 DocPrintCore 同款配置。
// 注意:YhAuth 的登录态走自定义短 Cookie(SessionTicketDataFormat,票据存进程内存), // 注意:YhAuth 的登录态走自定义短 Cookie(SessionTicketDataFormat,票据存进程内存),
@@ -171,9 +175,19 @@ namespace YhCore.WebApi
.Bind(Configuration.GetSection("Swagger")); .Bind(Configuration.GetSection("Swagger"));
services.AddOptions<YhCore.Common.Config.FileConfig>() services.AddOptions<YhCore.Common.Config.FileConfig>()
.Bind(Configuration); .Bind(Configuration);
// 静态门面(对齐 Java Config):文件路径由配置注入 // 静态门面(对齐 Java Config):文件路径由配置注入。
YhCore.Common.Config.Config.FilePath = Configuration["FILE_PATH"]; // FILE_PATH 必须以 / 结尾(旧约定:FileService 多处 FilePath + "{appType}/..." 直接拼接,缺斜杠会拼出坏路径);
YhCore.Common.Config.Config.FileWebPath = Configuration["FILE_WEB_PATH"]; // FILE_WEB_PATH 不带尾 /(拼接目标 fileurl 均以 / 开头,带尾斜杠会产生 //)
var configFilePath = Configuration["FILE_PATH"];
YhCore.Common.Config.Config.FilePath = string.IsNullOrEmpty(configFilePath)
? configFilePath
: configFilePath.TrimEnd('/').Replace('\\', '/') + "/";
var configFileWebPath = Configuration["FILE_WEB_PATH"];
YhCore.Common.Config.Config.FileWebPath = string.IsNullOrEmpty(configFileWebPath)
? configFileWebPath
: configFileWebPath.TrimEnd('/');
// 审批单 PDF:wkhtmltopdf 可执行文件路径(空则用 PATH)
YhCore.Common.Config.Config.WkHtmlToPdfPath = Configuration["wkhtmltopdf_path"];
// 接口文档:对应 Java SpringDoc,按渠道分三组(api / mobiapi / oapi) // 接口文档:对应 Java SpringDoc,按渠道分三组(api / mobiapi / oapi)
services.AddSwaggerGen(c => services.AddSwaggerGen(c =>
+5 -5
View File
@@ -36,11 +36,11 @@
<ItemGroup> <ItemGroup>
<!-- 平台基础(Abstractions/AppAuth/Auth/OpenApi + 教务 SDK):从 Nexus nuget-group 引用(原为本地源项目引用) --> <!-- 平台基础(Abstractions/AppAuth/Auth/OpenApi + 教务 SDK):从 Nexus nuget-group 引用(原为本地源项目引用) -->
<PackageReference Include="Yh.Platform.Abstractions" Version="1.0.0-preview.1" /> <PackageReference Include="Yh.Platform.Abstractions" Version="1.0.0-preview.2" />
<PackageReference Include="Yh.Platform.AppAuth" Version="1.0.0-preview.1" /> <PackageReference Include="Yh.Platform.AppAuth" Version="1.0.0-preview.2" />
<PackageReference Include="Yh.Platform.Auth" Version="1.0.0-preview.1" /> <PackageReference Include="Yh.Platform.Auth" Version="1.0.0-preview.2" />
<PackageReference Include="Yh.Platform.OpenApi" Version="1.0.0-preview.1" /> <PackageReference Include="Yh.Platform.OpenApi" Version="1.0.0-preview.2" />
<PackageReference Include="Yh.SchClass.OpenApi" Version="1.0.0-preview.1" /> <PackageReference Include="Yh.SchClass.OpenApi" Version="1.0.0-preview.2" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+4 -1
View File
@@ -4,12 +4,15 @@
"AutoCreateMissingTables": true "AutoCreateMissingTables": true
}, },
"ConnectionStrings": { "ConnectionStrings": {
"SqlServer": "Server=localhost;Database=EduCloudDocPrint;User Id=sa;Password=Password01!;MultipleActiveResultSets=true", "SqlServer": "Server=localhost;Database=EduCloudDocPrint2;User Id=sa;Password=Password01!;MultipleActiveResultSets=true",
"Mysql": "Server=localhost;Port=3306;Database=EduCloudDocPrint;User Id=root;Password=123123;CharSet=utf8mb4" "Mysql": "Server=localhost;Port=3306;Database=EduCloudDocPrint;User Id=root;Password=123123;CharSet=utf8mb4"
}, },
"Redis": { "Redis": {
"ConnectionString": "localhost:6379,defaultDatabase=0" "ConnectionString": "localhost:6379,defaultDatabase=0"
}, },
"FILE_PATH": "/data/upload/",
"FILE_WEB_PATH": "",
"wkhtmltopdf_path": "",
"yh": { "yh": {
"auth": { "auth": {
"Enabled": true, "Enabled": true,