Compare commits
20
Commits
74ba80ddf9
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
695ba1530e | ||
|
|
c2f6f42a3c | ||
|
|
2b9389fcb8 | ||
|
|
44d3cd0326 | ||
|
|
3ff9b48a19 | ||
|
|
20325bed00 | ||
|
|
0efde94a74 | ||
|
|
4211b72041 | ||
|
|
e9e1c1722d | ||
|
|
583d06f3d0 | ||
|
|
83182cb538 | ||
|
|
bbcb865055 | ||
|
|
cf8d6da5b5 | ||
|
|
ae1bd4cdb0 | ||
|
|
f1059fd92b | ||
|
|
5785a2c10e | ||
|
|
10c791d034 | ||
|
|
04e708c4fe | ||
|
|
d658ab5b6a | ||
|
|
ae8327807e |
@@ -26,7 +26,10 @@ jobs:
|
||||
options: --network infra-infra-net
|
||||
steps:
|
||||
- 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 本仓库)
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -53,14 +56,20 @@ jobs:
|
||||
- name: 构建 publish 产物(dotnet sdk 容器,复用宿主 nuget 缓存)
|
||||
run: |
|
||||
set -e
|
||||
docker rm -f dotnetbuild 2>/dev/null || true
|
||||
docker run -d --name dotnetbuild \
|
||||
# 构建容器名必须全局唯一:runner 挂的是宿主 docker.sock,容器名在整个 daemon 上共享
|
||||
# 命名空间。固定名会在多仓/多条 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 \
|
||||
-v /opt/docker/projman-dotnet-nuget:/root/.nuget/packages \
|
||||
-w /build mcr.microsoft.com/dotnet/sdk:5.0 sleep 3600
|
||||
docker cp "${PWD}/." dotnetbuild:/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 dotnetbuild sh -c '
|
||||
docker cp "${PWD}/." "$BUILD_CONTAINER":/build/
|
||||
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 "$BUILD_CONTAINER" sh -c '
|
||||
set -e
|
||||
root_sln=$(ls /build/*.sln 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"
|
||||
'
|
||||
mkdir -p "${PWD}/publish"
|
||||
docker cp dotnetbuild:/app/. "${PWD}/publish/"
|
||||
docker rm -f dotnetbuild
|
||||
docker cp "$BUILD_CONTAINER":/app/. "${PWD}/publish/"
|
||||
docker rm -f "$BUILD_CONTAINER"
|
||||
# 入口脚本:runtimeconfig.json 是可执行项目唯一标志(X.runtimeconfig.json <-> X.dll),
|
||||
# 类库 deps.json 无 runtimeconfig 兄弟文件,天然不会误选
|
||||
entry=$(ls "${PWD}"/publish/*.runtimeconfig.json | head -1)
|
||||
|
||||
@@ -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
@@ -8,10 +8,12 @@ FROM mcr.microsoft.com/dotnet/aspnet:${DOTNET_VERSION}
|
||||
|
||||
# .NET 5 基础镜像基于 Debian 10(已 EOL),apt 源须切 archive.debian.org 才能装包
|
||||
# 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 \
|
||||
&& echo 'Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/99no-valid-until \
|
||||
&& 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 \
|
||||
&& echo "Asia/Shanghai" > /etc/timezone \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
@@ -21,5 +21,8 @@ namespace YhCore.Common.Config
|
||||
|
||||
/// <summary>文件访问 URL 前缀(对应 Java FILE_WEB_PATH,由 Startup 注入)</summary>
|
||||
public static string FileWebPath { get; set; }
|
||||
|
||||
/// <summary>wkhtmltopdf 可执行文件路径(审批单 PDF 用;空则用 PATH 中的 wkhtmltopdf,由 Startup 注入)</summary>
|
||||
public static string WkHtmlToPdfPath { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<!-- 平台模型与信封(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>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -68,22 +68,10 @@ namespace YhCore.Model.Dto
|
||||
/// <summary>全部字典类别(typeID, typeName),对齐旧 EnumConfig.Dictionary 的 Description。</summary>
|
||||
public static readonly (string Id, string Name)[] AllTypes =
|
||||
{
|
||||
("DEPT", "部门类别"),
|
||||
("BXBM", "报修部门"),
|
||||
("WYFL", "文印分类"),
|
||||
("YZBM", "印制版面"),
|
||||
("YZFL", "印制分类"),
|
||||
("LeaveType", "请假类别"),
|
||||
("FoodType", "物品类别"),
|
||||
("Unit", "单位"),
|
||||
("VenueType", "场馆类别"),
|
||||
("Vefacilities", "场馆设施"),
|
||||
("Time", "场馆时间"),
|
||||
("Repeat", "场馆重复"),
|
||||
("LevelOrder", "奖次"),
|
||||
("filetype", "文件类型"),
|
||||
("doclevel", "公文级别"),
|
||||
("XKCategory", "选课类型"),
|
||||
("filetype", "文件类型")
|
||||
};
|
||||
|
||||
/// <summary>GetSysDic 的 Type(int) → typeID;越界返回 null。</summary>
|
||||
|
||||
@@ -2,7 +2,7 @@ namespace YhCore.Model.Dto
|
||||
{
|
||||
/// <summary>
|
||||
/// 文件信息(旧 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)。
|
||||
/// </summary>
|
||||
public class FileObj
|
||||
@@ -19,7 +19,7 @@ namespace YhCore.Model.Dto
|
||||
/// <summary>文件真实名称(含扩展名)。</summary>
|
||||
public string filename { get; set; }
|
||||
|
||||
/// <summary>相对访问路径(如 /UploadFile/docprint/20260101/{guid}.jpg)。</summary>
|
||||
/// <summary>相对访问路径(如 /upload/docprint/20260101/{guid}.jpg)。</summary>
|
||||
public string fileurl { get; set; }
|
||||
|
||||
/// <summary>文件主机前缀(Config.FileWebPath)。</summary>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,10 +68,9 @@ namespace YhCore.Service.Impl
|
||||
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 + "/";
|
||||
string outFilePath = appType + "/" + dtStr + "/";
|
||||
|
||||
if (!Directory.Exists(physicalDir))
|
||||
Directory.CreateDirectory(physicalDir);
|
||||
@@ -109,7 +108,7 @@ namespace YhCore.Service.Impl
|
||||
if (appType.ToLower() == "noticemsg") appType = "notice";
|
||||
string dtStr = DateTime.Now.ToString("yyyyMMdd");
|
||||
string physicalDir = Path.Combine(StorageRoot(), appType, dtStr);
|
||||
string outFilePath = "/UploadFile/" + appType + "/" + dtStr + "/";
|
||||
string outFilePath = appType + "/" + dtStr + "/";
|
||||
|
||||
if (!Directory.Exists(physicalDir))
|
||||
Directory.CreateDirectory(physicalDir);
|
||||
@@ -169,7 +168,7 @@ namespace YhCore.Service.Impl
|
||||
if (appType.ToLower() == "noticemsg") appType = "notice";
|
||||
string dtStr = DateTime.Now.ToString("yyyyMMdd");
|
||||
string physicalDir = Path.Combine(StorageRoot(), appType, dtStr);
|
||||
string outFilePath = "/UploadFile/" + appType + "/" + dtStr + "/";
|
||||
string outFilePath = appType + "/" + dtStr + "/";
|
||||
if (!Directory.Exists(physicalDir))
|
||||
Directory.CreateDirectory(physicalDir);
|
||||
|
||||
@@ -214,22 +213,29 @@ namespace YhCore.Service.Impl
|
||||
if (string.IsNullOrEmpty(fileUrl))
|
||||
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('/');
|
||||
const string prefix = "UploadFile/";
|
||||
string prefix = Config.FilePath;
|
||||
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()
|
||||
/// <summary>存储根目录(旧 Server.MapPath("~/upload/");Config.FilePath 缺省时退回进程目录/upload)。</summary>
|
||||
public static string StorageRoot()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Config.FilePath))
|
||||
return Config.FilePath;
|
||||
|
||||
return Path.Combine(AppContext.BaseDirectory, "UploadFile");
|
||||
// 此分支仅在 FILE_PATH 未配置时执行(此时 Config.FilePath 为 null,不可再参与拼接)
|
||||
return Path.Combine(AppContext.BaseDirectory, Config.FilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using Yh.Platform.Abstractions;
|
||||
using Yh.SchClass.OpenApi;
|
||||
using YhCore.Entity;
|
||||
using YhCore.Model.Dto;
|
||||
using YhCore.Model.Vo;
|
||||
using YhCore.Repository;
|
||||
using YhCore.Service.Interfaces;
|
||||
|
||||
@@ -72,7 +73,7 @@ namespace YhCore.Service.Impl
|
||||
#region 抄送
|
||||
|
||||
/// <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();
|
||||
|
||||
@@ -88,6 +89,7 @@ namespace YhCore.Service.Impl
|
||||
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")
|
||||
.OrderByDescending(m => m.SerID)
|
||||
.Skip(pageSize * pageIndex).Take(pageSize).ToList();
|
||||
@@ -101,7 +103,7 @@ namespace YhCore.Service.Impl
|
||||
if (!TryGetTea(teaMap, item.UserID, out var tea)) continue;
|
||||
result.Add(await BuildListItemAsync(item, tea, dictMap, "yyyy-MM-dd"));
|
||||
}
|
||||
return result;
|
||||
return PageVO<DocPrintListItem>.Of(result, total, pageIndex, pageSize);
|
||||
}
|
||||
|
||||
/// <summary>查看抄送人员详情(旧 SendDetailsList)。</summary>
|
||||
@@ -349,7 +351,7 @@ namespace YhCore.Service.Impl
|
||||
#region 列表
|
||||
|
||||
/// <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();
|
||||
|
||||
@@ -365,6 +367,7 @@ namespace YhCore.Service.Impl
|
||||
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)
|
||||
.Skip(pageSize * pageIndex).Take(pageSize).ToList();
|
||||
|
||||
@@ -376,14 +379,14 @@ namespace YhCore.Service.Impl
|
||||
TryGetTea(teaMap, item.UserID, out var tea); // 旧为左连接,缺失补空
|
||||
result.Add(await BuildListItemAsync(item, tea, dictMap, "yyyy-MM-dd HH:mm", "YZBM"));
|
||||
}
|
||||
return result;
|
||||
return PageVO<DocPrintListItem>.Of(result, total, pageIndex, pageSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 全部文印列表(旧 AllList 原生 SQL 四分类:x=待我审(0) y=我已审(1) z=抄送我(2) m=我发起(3),按优先级去重)。
|
||||
/// 旧 Name 过滤的 SQL 拼接存在运算符优先级缺陷,此处按语义实现:内容/教师名/字典名任一命中。
|
||||
/// </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);
|
||||
|
||||
@@ -423,6 +426,7 @@ namespace YhCore.Service.Impl
|
||||
|| DictNameContains(dictMap, "YZFL", t.doc.DPType, name));
|
||||
}
|
||||
|
||||
var total = query.Count();
|
||||
var pageItems = query.OrderByDescending(t => t.doc.SerID).ThenByDescending(t => t.doc.TakeDate)
|
||||
.Skip(pageSize * pageIndex).Take(pageSize).ToList();
|
||||
|
||||
@@ -434,16 +438,17 @@ namespace YhCore.Service.Impl
|
||||
item.ctype = ctype;
|
||||
result.Add(item);
|
||||
}
|
||||
return result;
|
||||
return PageVO<DocPrintListItem>.Of(result, total, pageIndex, pageSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 文印审核列表(旧 ShenheList:固定流程 + 指派 + 自由流程三路并集,HashSet 去重)。
|
||||
/// 旧实现的 Name 过滤作用在未被使用的查询上(死代码),此处保持 Name 不生效的旧行为。
|
||||
/// </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 total = docs.Count;
|
||||
docs = docs.OrderBy(m => m.DocStatus).ThenByDescending(m => m.TakeDate)
|
||||
.Skip(pageSize * pageIndex).Take(pageSize).ToList();
|
||||
|
||||
@@ -455,13 +460,13 @@ namespace YhCore.Service.Impl
|
||||
TryGetTea(teaMap, item.UserID, out var tea);
|
||||
result.Add(await BuildListItemAsync(item, tea, dictMap, "yyyy-MM-dd HH:mm"));
|
||||
}
|
||||
return result;
|
||||
return PageVO<DocPrintListItem>.Of(result, total, pageIndex, pageSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 审批列表(旧 SPList:待审(DS,zt=false)∪ 我已审(YS,zt=true,排除 DS),_disabled 即 zt)。
|
||||
/// </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);
|
||||
|
||||
@@ -493,6 +498,7 @@ namespace YhCore.Service.Impl
|
||||
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 total = ordered.Count;
|
||||
|
||||
var teaMap = await GetTeaMapAsync(schSerId);
|
||||
var dictMap = await GetDictMapAsync(schSerId);
|
||||
@@ -504,11 +510,11 @@ namespace YhCore.Service.Impl
|
||||
item._disabled = zt; // 我已审的记录不可再编辑(覆盖通用 _disabled 语义,与旧一致)
|
||||
result.Add(item);
|
||||
}
|
||||
return result;
|
||||
return PageVO<DocPrintListItem>.Of(result, total, pageIndex, pageSize);
|
||||
}
|
||||
|
||||
/// <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);
|
||||
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))
|
||||
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)
|
||||
.Skip(pageSize * pageIndex).Take(pageSize).ToList();
|
||||
|
||||
@@ -532,11 +539,11 @@ namespace YhCore.Service.Impl
|
||||
TryGetTea(teaMap, item.UserID, out var tea);
|
||||
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>
|
||||
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();
|
||||
|
||||
@@ -552,6 +559,7 @@ namespace YhCore.Service.Impl
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
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)
|
||||
.Skip(pageSize * pageIndex).Take(pageSize).ToList();
|
||||
|
||||
@@ -563,7 +571,7 @@ namespace YhCore.Service.Impl
|
||||
TryGetTea(teaMap, item.UserID, out var tea);
|
||||
result.Add(await BuildListItemAsync(item, tea, dictMap, "yyyy-MM-dd"));
|
||||
}
|
||||
return result;
|
||||
return PageVO<DocPrintListItem>.Of(result, total, pageIndex, pageSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -895,7 +903,7 @@ namespace YhCore.Service.Impl
|
||||
}
|
||||
|
||||
/// <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();
|
||||
if (DateTime.TryParse(starttime, out var date1))
|
||||
@@ -933,13 +941,16 @@ namespace YhCore.Service.Impl
|
||||
});
|
||||
}
|
||||
|
||||
return full.OrderByDescending(a => a.EditDate)
|
||||
.Skip(pageSize * pageIndex).Take(pageSize)
|
||||
.ToList();
|
||||
var total = full.Count;
|
||||
return PageVO<DocDetailStatItem>.Of(
|
||||
full.OrderByDescending(a => a.EditDate)
|
||||
.Skip(pageSize * pageIndex).Take(pageSize)
|
||||
.ToList(),
|
||||
total, pageIndex, pageSize);
|
||||
}
|
||||
|
||||
/// <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();
|
||||
if (DateTime.TryParse(starttime, out var date1))
|
||||
@@ -965,23 +976,27 @@ namespace YhCore.Service.Impl
|
||||
.OrderByDescending(a => a.num)
|
||||
.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>
|
||||
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
|
||||
&& m.DocStatus == "1" && m.EditDate.Year == year && m.EditDate.Month == month))
|
||||
.OrderByDescending(a => a.EditDate)
|
||||
var all = (await _doc.FindAsync(m => m.SchSerID == schSerId
|
||||
&& m.DocStatus == "1" && m.EditDate.Year == year && m.EditDate.Month == month)).ToList();
|
||||
var total = all.Count;
|
||||
var docs = all.OrderByDescending(a => a.EditDate)
|
||||
.Skip(pageSize * pageIndex).Take(pageSize)
|
||||
.ToList();
|
||||
|
||||
return await BuildStatListAsync(docs, schSerId);
|
||||
return PageVO<StatListItem>.Of(await BuildStatListAsync(docs, schSerId), total, pageIndex, pageSize);
|
||||
}
|
||||
|
||||
/// <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();
|
||||
if (DateTime.TryParse(starttime, out var date1))
|
||||
@@ -992,11 +1007,12 @@ namespace YhCore.Service.Impl
|
||||
docs = docs.Where(m => m.EditDate <= date2).ToList();
|
||||
}
|
||||
|
||||
var total = docs.Count;
|
||||
docs = docs.OrderByDescending(a => a.EditDate)
|
||||
.Skip(pageSize * pageIndex).Take(pageSize)
|
||||
.ToList();
|
||||
|
||||
return await BuildStatListAsync(docs, schSerId);
|
||||
return PageVO<StatListItem>.Of(await BuildStatListAsync(docs, schSerId), total, pageIndex, pageSize);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,8 @@ 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}
|
||||
/// BestSchool.Common.upload.FileSave/FileSaveImage/FileChunkSave/FileMergeSave)。
|
||||
/// 存储约定:物理根目录 = Config.FilePath(≈旧 ~/upload/),相对路径仍存 /upload/{AppType}/{yyyyMMdd}/{guid}{ext}
|
||||
/// (与旧库数据格式一致,兼容历史记录);host 取 Config.FileWebPath(≈旧 AuthConfig.weboa_url)。
|
||||
/// 旧 ToOfficeHelper.OfficeConvertToPDF(Office COM 转 PDF)新项目缺失,降级:上传不做转 PDF,
|
||||
/// 预览时本地无 .pdf 则降级 officeapps.live.com 在线预览。
|
||||
@@ -21,10 +21,10 @@ namespace YhCore.Service.Interfaces
|
||||
/// <summary>存储附件信息(旧 FileSaveEnclosure:Common_UpFiles 落库,返回 SerID;文件名非法字符替换为 _)。</summary>
|
||||
Task<int> SaveEnclosureAsync(FileObj file, string appType);
|
||||
|
||||
/// <summary>单文件上传(旧 UpLoadFile.FileSave:扩展名白名单 + 保存 + 返回 FileObj)。</summary>
|
||||
/// <summary>单文件上传(旧 upload.FileSave:扩展名白名单 + 保存 + 返回 FileObj)。</summary>
|
||||
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);
|
||||
|
||||
/// <summary>保存分片(旧 FileChunkSave:临时目录 {guid}/ 下按索引存块)。</summary>
|
||||
@@ -33,7 +33,7 @@ namespace YhCore.Service.Interfaces
|
||||
/// <summary>合并分片(旧 FileMergeSave:按块合并 → 正式目录 + FileObj)。</summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using YhCore.Model.Dto;
|
||||
using YhCore.Model.Vo;
|
||||
|
||||
namespace YhCore.Service.Interfaces
|
||||
{
|
||||
@@ -11,7 +12,7 @@ namespace YhCore.Service.Interfaces
|
||||
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<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<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<PageVO<DocPrintListItem>> MyListAsync(string name, string status, string startDate, string endDate, string userId, string schSerId, int pageSize, int pageIndex);
|
||||
Task<PageVO<DocPrintListItem>> AllListAsync(string name, string userId, string schSerId, int pageSize, int pageIndex);
|
||||
Task<PageVO<DocPrintListItem>> ShenheListAsync(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<PageVO<DocPrintListItem>> YishenListAsync(string name, string userId, 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<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<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);
|
||||
Task<PageVO<DocDetailStatItem>> List2Async(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<PageVO<StatListItem>> List4Async(int year, int month, 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.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using YhCore.Entity;
|
||||
using YhCore.Model.Dto;
|
||||
using YhCore.Model.Vo;
|
||||
@@ -14,7 +16,9 @@ namespace YhCore.Service.Interfaces
|
||||
/// 落库 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)。
|
||||
/// Excel 导入导出(UpfileExcelSHRole/downMBOfSHRole/downUpfileSHRoleFK/ExceltotalListSHRole/WYRDExceldaochu)
|
||||
/// 与审批单 PDF(DwonWYSQD):下载类接口返回 (byte[] Content, string FileName),
|
||||
/// 控制器按扩展名包装 File();导入反馈表存进程内 IMemoryCache(单实例)。
|
||||
/// </summary>
|
||||
public interface IWYSHRoleService
|
||||
{
|
||||
@@ -50,10 +54,10 @@ namespace YhCore.Service.Interfaces
|
||||
// ---- 用户记录查看和审核 ----
|
||||
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<PageVO<SHRoleDocPrintListItem>> MyListSHRoleAsync(string name, string status, string startDate, string endDate, int pageSize, int pageIndex, string userId, 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<PageVO<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>> 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);
|
||||
|
||||
// ---- 规格表 / 签名 / 退回 / 金额 ----
|
||||
@@ -64,7 +68,26 @@ namespace YhCore.Service.Interfaces
|
||||
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<PageVO<SHRoleRegetJERdItem>> GetRegetJERdAsync(string schSerId, int pageSize, int pageIndex);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,9 +22,16 @@
|
||||
|
||||
<ItemGroup>
|
||||
<!-- 业务异常 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 引用(原为本地源项目引用) -->
|
||||
<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>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Yh.Platform.Abstractions;
|
||||
using Yh.SchClass.OpenApi;
|
||||
using Yh.SchClass.OpenApi.Model;
|
||||
using YhCore.Common;
|
||||
using YhCore.Common.Config;
|
||||
using YhCore.Common.UserContext;
|
||||
@@ -28,13 +30,24 @@ namespace YhCore.WebApi.Controllers.Api.Common
|
||||
{
|
||||
private readonly IDictionaryService _dict;
|
||||
private readonly IFileService _file;
|
||||
private readonly SchClassOpenApi _sch;
|
||||
|
||||
public CommonController(IDictionaryService dict, IFileService file)
|
||||
public CommonController(IDictionaryService dict, IFileService file, SchClassOpenApi sch)
|
||||
{
|
||||
_dict = dict;
|
||||
_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>
|
||||
[HttpGet("GetSysDic")]
|
||||
public async Task<List<DicOption>> GetSysDic([FromQuery] int type = 0)
|
||||
@@ -222,42 +235,58 @@ namespace YhCore.WebApi.Controllers.Api.Common
|
||||
|
||||
/// <summary>
|
||||
/// 公用附件上传(旧 FileUpload,[NoAuth])。
|
||||
/// chunk 参数 → 分片保存返回 {code:1,type:"chunk",msg};fileName 参数 → 合并分片;否则单文件上传。
|
||||
/// 分片(chunk/chunkIndex + totalChunks>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}。
|
||||
/// </summary>
|
||||
[HttpPost("FileUpload")]
|
||||
public async Task<object> Upload()
|
||||
{
|
||||
string apptype = "docprint"; // 旧 WebHelper.AppType 固定 docprint
|
||||
string guid = Request.Form["guid"];
|
||||
string chunk = Request.Form["chunk"];
|
||||
string fileName = Request.Form["fileName"];
|
||||
string guid = FormOrQuery("guid");
|
||||
string chunk = FormOrQuery("chunk"); // 旧契约分片序号
|
||||
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, chunk, chunkFile);
|
||||
await _file.SaveChunkAsync(guid, string.IsNullOrEmpty(chunk) ? chunkIndex : chunk, file);
|
||||
return new { code = 1, type = "chunk", msg = "分片上传成功!" };
|
||||
}
|
||||
|
||||
FileObj url;
|
||||
string errMsg = "上传失败!";
|
||||
if (string.IsNullOrEmpty(fileName))
|
||||
if (!string.IsNullOrEmpty(fileName) && file == null)
|
||||
{
|
||||
url = await _file.SaveFileAsync(apptype, Request.Form.Files.FirstOrDefault());
|
||||
url = await _file.MergeSaveAsync(apptype, guid, fileName); // 合并分片
|
||||
}
|
||||
else
|
||||
{
|
||||
url = await _file.MergeSaveAsync(apptype, guid, fileName);
|
||||
url = await _file.SaveFileAsync(apptype, file); // 单文件直传
|
||||
}
|
||||
if (url == null)
|
||||
throw new DException(errMsg);
|
||||
throw new DException("上传失败!");
|
||||
|
||||
// 旧 WebHelper.AppCode == "1102" ? "1002" : AppCode → docprint 固定 1003
|
||||
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 };
|
||||
}
|
||||
|
||||
/// <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>
|
||||
[HttpPost("FileUploadImage")]
|
||||
public async Task<object> UploadImg()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@@ -16,8 +17,7 @@ namespace YhCore.WebApi.Controllers.Api.DocPrint
|
||||
{
|
||||
/// <summary>
|
||||
/// 审核角色 + 流程配置控制器(对应旧 Areas/DocPrint/WYSHRoleController,路由 api/docprint/wyshrole)。
|
||||
/// 45 action 中本批落地 39 个;Excel 导入导出(ExceltotalListSHRole/WYRDExceldaochu/UpfileExcelSHRole/
|
||||
/// downMBOfSHRole/downUpfileSHRoleFK)与审批单 PDF(DwonWYSQD)依赖 NPOI/PdfHelper,延后迁移。
|
||||
/// 45 action 全部落地(Excel 导入导出 5 个走 NPOI/EPPlus,审批单 PDF 走 wkhtmltopdf)。
|
||||
/// POST 动作一律 [FromForm]:前端 api/index.js 固定 x-www-form-urlencoded 提交([FromForm] 会 415);
|
||||
/// 老契约"表单字段塞 JSON"的动作(WYSHRole/SHTeaTxt/listXxxTxt/data/docdata/docnumdata)
|
||||
/// 以 [FromForm] string 接收后手动反序列化。
|
||||
@@ -118,7 +118,7 @@ namespace YhCore.WebApi.Controllers.Api.DocPrint
|
||||
|
||||
#region 审核角色流程
|
||||
|
||||
/// <summary>审核流程列表(旧 ListFlow)。</summary>
|
||||
/// <summary>审核流程列表(旧 ListFlow;分页返回 PageVO,Content 内对象字段与旧 Data 对齐)。</summary>
|
||||
[HttpGet("ListFlow")]
|
||||
public async Task<PageVO<SHRoleFlowListItem>> ListFlow(
|
||||
[FromQuery] string name = null,
|
||||
@@ -297,8 +297,74 @@ namespace YhCore.WebApi.Controllers.Api.DocPrint
|
||||
return await _role.ShenheOkSHRoleAsync(model);
|
||||
}
|
||||
|
||||
// 延后:ExceltotalListSHRole / WYRDExceldaochu / UpfileExcelSHRole / downMBOfSHRole / downUpfileSHRoleFK(Excel,NPOI)
|
||||
// 延后:DwonWYSQD(审批单 PDF,PdfHelper + Server.MapPath)
|
||||
#region Excel 导入导出 / 审批单 PDF
|
||||
|
||||
/// <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
|
||||
|
||||
|
||||
@@ -100,9 +100,8 @@ namespace YhCore.WebApi.Controllers.Api.DocPrint
|
||||
[HttpPost("EditDictionaryType")]
|
||||
public async Task EditDictionaryType([FromForm] Common_Dictionary model)
|
||||
{
|
||||
if (model == null || model.SerID <= 0 || string.IsNullOrWhiteSpace(model.typeID)
|
||||
|| string.IsNullOrWhiteSpace(model.typeName) || string.IsNullOrWhiteSpace(model.Name))
|
||||
throw new DException("参数错误:SerID、typeID、typeName、Name 均不能为空(注意表单字段名拼写,TypeId≠TypelD)");
|
||||
if (model == null || model.SerID <= 0 || string.IsNullOrWhiteSpace(model.Name))
|
||||
throw new DException("参数错误:SerID、Name 均不能为空(注意表单字段名拼写,TypeId≠TypelD)");
|
||||
model.Schserid = SchSerID();
|
||||
await _dict.EditDicAsync(model);
|
||||
}
|
||||
|
||||
@@ -122,6 +122,10 @@ namespace YhCore.WebApi
|
||||
// Redis 缓存(RedisUtil 单例,连接串取 Redis:ConnectionString)
|
||||
services.AddRedisCache(Configuration);
|
||||
|
||||
// 进程内内存缓存:WYSHRole Excel 导入的反馈表(updateSHRole_{userId},单实例;
|
||||
// 多实例部署需改 Redis 支撑的分布式缓存)
|
||||
services.AddMemoryCache();
|
||||
|
||||
// ===== 会话状态(Session)=====
|
||||
// 对应 Java HttpSession / session.invalidate();旧 DocPrintCore 同款配置。
|
||||
// 注意:YhAuth 的登录态走自定义短 Cookie(SessionTicketDataFormat,票据存进程内存),
|
||||
@@ -171,9 +175,19 @@ namespace YhCore.WebApi
|
||||
.Bind(Configuration.GetSection("Swagger"));
|
||||
services.AddOptions<YhCore.Common.Config.FileConfig>()
|
||||
.Bind(Configuration);
|
||||
// 静态门面(对齐 Java Config):文件路径由配置注入
|
||||
YhCore.Common.Config.Config.FilePath = Configuration["FILE_PATH"];
|
||||
YhCore.Common.Config.Config.FileWebPath = Configuration["FILE_WEB_PATH"];
|
||||
// 静态门面(对齐 Java Config):文件路径由配置注入。
|
||||
// FILE_PATH 必须以 / 结尾(旧约定:FileService 多处 FilePath + "{appType}/..." 直接拼接,缺斜杠会拼出坏路径);
|
||||
// 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)
|
||||
services.AddSwaggerGen(c =>
|
||||
|
||||
@@ -36,11 +36,11 @@
|
||||
|
||||
<ItemGroup>
|
||||
<!-- 平台基础(Abstractions/AppAuth/Auth/OpenApi + 教务 SDK):从 Nexus nuget-group 引用(原为本地源项目引用) -->
|
||||
<PackageReference Include="Yh.Platform.Abstractions" Version="1.0.0-preview.1" />
|
||||
<PackageReference Include="Yh.Platform.AppAuth" Version="1.0.0-preview.1" />
|
||||
<PackageReference Include="Yh.Platform.Auth" Version="1.0.0-preview.1" />
|
||||
<PackageReference Include="Yh.Platform.OpenApi" Version="1.0.0-preview.1" />
|
||||
<PackageReference Include="Yh.SchClass.OpenApi" 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.2" />
|
||||
<PackageReference Include="Yh.Platform.Auth" Version="1.0.0-preview.2" />
|
||||
<PackageReference Include="Yh.Platform.OpenApi" Version="1.0.0-preview.2" />
|
||||
<PackageReference Include="Yh.SchClass.OpenApi" Version="1.0.0-preview.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -4,12 +4,15 @@
|
||||
"AutoCreateMissingTables": true
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"Redis": {
|
||||
"ConnectionString": "localhost:6379,defaultDatabase=0"
|
||||
},
|
||||
"FILE_PATH": "/data/upload/",
|
||||
"FILE_WEB_PATH": "",
|
||||
"wkhtmltopdf_path": "",
|
||||
"yh": {
|
||||
"auth": {
|
||||
"Enabled": true,
|
||||
|
||||
Reference in New Issue
Block a user