fix: 考试收集完成,待测试
This commit is contained in:
parent
7da88993af
commit
9c53c18db9
|
|
@ -20,21 +20,29 @@ public class ExamManager : DomainService, IExamManager
|
||||||
{
|
{
|
||||||
private readonly ISqlSugarClient Db;
|
private readonly ISqlSugarClient Db;
|
||||||
private readonly IBlobContainer _blobContainer;
|
private readonly IBlobContainer _blobContainer;
|
||||||
|
private readonly SKPaint skPaint = new SKPaint
|
||||||
|
{
|
||||||
|
Color = SKColors.Black,
|
||||||
|
IsAntialias = true, // 抗锯齿
|
||||||
|
Style = SKPaintStyle.Stroke,
|
||||||
|
StrokeWidth = 1,
|
||||||
|
StrokeCap = SKStrokeCap.Round,
|
||||||
|
};
|
||||||
public ExamManager(ISqlSugarClient db, IBlobContainer blobContainer)
|
public ExamManager(ISqlSugarClient db, IBlobContainer blobContainer)
|
||||||
{
|
{
|
||||||
Db = db;
|
Db = db;
|
||||||
_blobContainer = blobContainer;
|
_blobContainer = blobContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
[AutoTran]
|
|
||||||
public async Task ExamStudentGather(ExamStudentGatherEto eto)
|
public async Task ExamStudentGather(ExamStudentGatherEto eto)
|
||||||
{
|
{
|
||||||
var guid = GuidGenerator.Create().ToString("N");
|
|
||||||
var penSerial = eto.StudentExamNum;
|
var penSerial = eto.StudentExamNum;
|
||||||
|
Logger.LogInformation("{ExamSubjectId} {penSerial} 开始收集...", eto.ExamSubjectId, penSerial);
|
||||||
|
var guid = GuidGenerator.Create().ToString("N");
|
||||||
|
|
||||||
var templateIds = JsonConvert.DeserializeObject<List<string>>(eto.TemplateId);
|
var templateIds = JsonConvert.DeserializeObject<List<string>>(eto.TemplateId);
|
||||||
var templates = await Db.Queryable<GroupBookPaperTemplate>().Where(w => templateIds.Contains(w.Id))
|
var templates = await Db.Queryable<GroupBookPaperTemplate>().Where(w => templateIds.Contains(w.Id))
|
||||||
.Select(s => new { s.Id, s.PaperId, s.PartId, s.PageIndex, s.ImgUrl, s.QueData }).ToListAsync();
|
.Select(s => new { s.Id, s.PaperId, s.PartId, s.PageIndex, s.ImgUrl, s.QueData, s.Order }).ToListAsync();
|
||||||
var paperInfo = templates.Select(s =>
|
var paperInfo = templates.Select(s =>
|
||||||
{
|
{
|
||||||
var data = JsonConvert.DeserializeObject<TemplateJsonModel_DataArr>(s.QueData);
|
var data = JsonConvert.DeserializeObject<TemplateJsonModel_DataArr>(s.QueData);
|
||||||
|
|
@ -45,6 +53,8 @@ public class ExamManager : DomainService, IExamManager
|
||||||
PartId = s.PartId,
|
PartId = s.PartId,
|
||||||
PageIndex = s.PageIndex,
|
PageIndex = s.PageIndex,
|
||||||
QueData = data.queData,
|
QueData = data.queData,
|
||||||
|
ImgUrl = s.ImgUrl,
|
||||||
|
Sort = s.Order,
|
||||||
};
|
};
|
||||||
}).OrderBy(s => s.PartId).ThenBy(s => s.PageIndex).ToList();
|
}).OrderBy(s => s.PartId).ThenBy(s => s.PageIndex).ToList();
|
||||||
|
|
||||||
|
|
@ -80,7 +90,89 @@ public class ExamManager : DomainService, IExamManager
|
||||||
IsObjectiveQuestion = true,
|
IsObjectiveQuestion = true,
|
||||||
GroupNo = guid,
|
GroupNo = guid,
|
||||||
}).ToListAsync();
|
}).ToListAsync();
|
||||||
|
|
||||||
|
var gotoCount = 0;
|
||||||
|
dotPenOriginalImg:
|
||||||
var zgtSettingDtls = await DbBiz.Queryable<MarkingSettingSubjective>().Where(w => w.ExamSubjectId == eto.ExamSubjectId).ToListAsync();
|
var zgtSettingDtls = await DbBiz.Queryable<MarkingSettingSubjective>().Where(w => w.ExamSubjectId == eto.ExamSubjectId).ToListAsync();
|
||||||
|
|
||||||
|
// 割原题
|
||||||
|
if (!zgtSettingDtls.Any(s => s.DotPenOriginalImg.IsNotNullOrEmpty()))
|
||||||
|
{
|
||||||
|
if (gotoCount >= 1)
|
||||||
|
{
|
||||||
|
Logger.LogError("割原题超出次数限制,收集终止");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var redisLockKey = "LockKey:" + eto.ExamSubjectId;
|
||||||
|
var redisLock = await RedisHelper.GetAsync(redisLockKey);
|
||||||
|
if (string.IsNullOrEmpty(redisLock))
|
||||||
|
{
|
||||||
|
await RedisHelper.SetAsync(redisLockKey, "1");
|
||||||
|
foreach (var paper in paperInfo)
|
||||||
|
{
|
||||||
|
var imgStream = await paper.ImgUrl.GetStreamAsync();
|
||||||
|
var bitmap = SKBitmap.Decode(imgStream);
|
||||||
|
|
||||||
|
var nextPaper = paperInfo.FirstOrDefault(w => w.PartId == paper.PartId && w.PageIndex == paper.PageIndex + 1);
|
||||||
|
|
||||||
|
foreach (var que in paper.QueData)
|
||||||
|
{
|
||||||
|
if (que.type != "2") continue;
|
||||||
|
if (que.options == null || que.options.Count == 0) continue;
|
||||||
|
|
||||||
|
var area = que.options[0].AnswerArea;
|
||||||
|
var areaTop = area.pxTop;
|
||||||
|
var areaHeight = area.pxHeight;
|
||||||
|
var sourceRect = new SKRect(0, areaTop, bitmap.Width, areaTop + areaHeight);
|
||||||
|
|
||||||
|
var height = areaHeight;
|
||||||
|
|
||||||
|
SKBitmap nextImgBitmap = null;
|
||||||
|
SKRect nextSourceRect = new();
|
||||||
|
var queOnNextPaper = nextPaper?.QueData.FirstOrDefault(w => w.no == que.no);
|
||||||
|
if (queOnNextPaper?.options?.Count >= 1)
|
||||||
|
{
|
||||||
|
var nextImgStream = await nextPaper.ImgUrl.GetStreamAsync();
|
||||||
|
nextImgBitmap = SKBitmap.Decode(nextImgStream);
|
||||||
|
|
||||||
|
var nextArea = queOnNextPaper.options[0].AnswerArea;
|
||||||
|
var nextAreaTop = nextArea.pxTop;
|
||||||
|
var nextAreaHeight = nextArea.pxHeight;
|
||||||
|
height += nextAreaHeight;
|
||||||
|
|
||||||
|
nextSourceRect = new SKRect(0, nextAreaTop, bitmap.Width, nextAreaTop + nextAreaHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var newBitmap = new SKBitmap(bitmap.Width, height.SSWR(), SKColorType.Rgba8888, SKAlphaType.Premul))
|
||||||
|
{
|
||||||
|
using (var canvas = new SKCanvas(newBitmap))
|
||||||
|
{
|
||||||
|
canvas.DrawBitmap(bitmap, sourceRect, new SKRect(0, 0, bitmap.Width, areaHeight));
|
||||||
|
if (nextImgBitmap != null)
|
||||||
|
canvas.DrawBitmap(nextImgBitmap, nextSourceRect, new SKRect(0, areaHeight, bitmap.Width, height));
|
||||||
|
}
|
||||||
|
|
||||||
|
var dtl = zgtSettingDtls.FirstOrDefault(w => w.QuestionNum == que.no);
|
||||||
|
dtl.DotPenOriginalImg = $"que/{eto.ExamSubjectId}/{paper.Sort}/{que.no}.png";
|
||||||
|
await _blobContainer.SaveAsync(dtl.DotPenOriginalImg, newBitmap.Encode(SKEncodedImageFormat.Png, 100).ToArray(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await Db.Updateable(zgtSettingDtls).UpdateColumns(s => s.DotPenOriginalImg).Where(w => w.DotPenOriginalImg.IsNotNullOrEmpty()).ExecuteCommandAsync();
|
||||||
|
await RedisHelper.SetAsync(redisLockKey, "0");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (await RedisHelper.GetAsync(redisLockKey) == "1")
|
||||||
|
{
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
}
|
||||||
|
gotoCount++;
|
||||||
|
goto dotPenOriginalImg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var zgtDtls = zgtSettingDtls.Select(s => new SubjectiveMarkingResult
|
var zgtDtls = zgtSettingDtls.Select(s => new SubjectiveMarkingResult
|
||||||
{
|
{
|
||||||
Id = YitIdHelper.NextId(),
|
Id = YitIdHelper.NextId(),
|
||||||
|
|
@ -96,31 +188,6 @@ public class ExamManager : DomainService, IExamManager
|
||||||
IsExcess = s.IsExcess,
|
IsExcess = s.IsExcess,
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
|
||||||
// 割原题
|
|
||||||
if (!zgtSettingDtls.Any(s => s.DotPenOriginalImg.IsNotNullOrEmpty()))
|
|
||||||
{
|
|
||||||
var redisLockKey = "LockKey:" + eto.ExamSubjectId;
|
|
||||||
var redisLock = await RedisHelper.GetAsync(redisLockKey);
|
|
||||||
if (string.IsNullOrEmpty(redisLock))
|
|
||||||
{
|
|
||||||
await RedisHelper.SetAsync(redisLockKey, "1");
|
|
||||||
foreach (var tmp in templates)
|
|
||||||
{
|
|
||||||
var imgStream = await tmp.ImgUrl.GetStreamAsync();
|
|
||||||
var bitmap = SKBitmap.Decode(imgStream);
|
|
||||||
|
|
||||||
}
|
|
||||||
await RedisHelper.SetAsync(redisLockKey, "0");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
while (await RedisHelper.GetAsync(redisLockKey) == "1")
|
|
||||||
{
|
|
||||||
Thread.Sleep(500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var kgt = new List<Tuple<string, string>>();
|
var kgt = new List<Tuple<string, string>>();
|
||||||
var zgt = new List<Tuple<string, string, bool>>(); // 纸张Id, 题号, 是否跨页
|
var zgt = new List<Tuple<string, string, bool>>(); // 纸张Id, 题号, 是否跨页
|
||||||
var pageSerials = new List<string>(); // 需要计算的页
|
var pageSerials = new List<string>(); // 需要计算的页
|
||||||
|
|
@ -205,8 +272,6 @@ public class ExamManager : DomainService, IExamManager
|
||||||
var paper = paperInfo.FirstOrDefault(w => w.PaperId == paperId);
|
var paper = paperInfo.FirstOrDefault(w => w.PaperId == paperId);
|
||||||
var paperJobPage = templateIds.FindIndex(s => s == paper.TemplateId) + 1;
|
var paperJobPage = templateIds.FindIndex(s => s == paper.TemplateId) + 1;
|
||||||
var paperLatts = lattices.Where(w => w.PageSerial == paperId).ToList();
|
var paperLatts = lattices.Where(w => w.PageSerial == paperId).ToList();
|
||||||
var paperPicture = $"questionAnswer/{eto.JobId}/{studentId}/{paperJobPage}/{paper.PartId}.png";
|
|
||||||
var questionPaperPicture = string.Empty;
|
|
||||||
|
|
||||||
var ques = zgt.Where(w => w.Item1 == paperId).ToList();
|
var ques = zgt.Where(w => w.Item1 == paperId).ToList();
|
||||||
foreach (var que in ques)
|
foreach (var que in ques)
|
||||||
|
|
@ -239,10 +304,10 @@ public class ExamManager : DomainService, IExamManager
|
||||||
queLatts.AddRange(queLattsOnNextPaper);
|
queLatts.AddRange(queLattsOnNextPaper);
|
||||||
}
|
}
|
||||||
|
|
||||||
var jobDtl = dtls.FirstOrDefault(w => w.SectionId == paper.PartId && w.QuestionNo == queNo);
|
|
||||||
var stuAnswer = "";
|
var stuAnswer = "";
|
||||||
|
|
||||||
var imgStream = await $"{App.Configuration["Aliyun:OssJob:Host"]}/{jobDtl.QuestionPicture}".GetStreamAsync();
|
var queImgUrl = zgtSettingDtls.FirstOrDefault(w => w.QuestionNum == queNo).DotPenOriginalImg;
|
||||||
|
var imgStream = await _blobContainer.GetAsync(queImgUrl);
|
||||||
var queBitmap = SKBitmap.Decode(imgStream);
|
var queBitmap = SKBitmap.Decode(imgStream);
|
||||||
|
|
||||||
using (var canvas = new SKCanvas(queBitmap))
|
using (var canvas = new SKCanvas(queBitmap))
|
||||||
|
|
@ -260,57 +325,30 @@ public class ExamManager : DomainService, IExamManager
|
||||||
|
|
||||||
canvas.DrawPoints(skPointMode, points, skPaint);
|
canvas.DrawPoints(skPointMode, points, skPaint);
|
||||||
}
|
}
|
||||||
stuAnswer = $"questionAnswer/{eto.JobId}/{studentId}/{paperJobPage}/{paper.PartId}-{queNo}.png";
|
stuAnswer = $"queAnswer/{eto.ExamSubjectId}/{penSerial}/{paper.Sort}/{queNo}.png";
|
||||||
JobExtension.UploadFileToCloud(stuAnswer, queBitmap.Encode(SKEncodedImageFormat.Png, 100).ToArray());
|
await _blobContainer.SaveAsync(stuAnswer, queBitmap.Encode(SKEncodedImageFormat.Png, 100).ToArray(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
questionPaperPicture = jobDtl.QuestionPaperPicture;
|
|
||||||
jobDtl.StudentAnswers = stuAnswer;
|
|
||||||
|
|
||||||
var dtl = zgtDtls.FirstOrDefault(w => w.QuestionNum == queNo);
|
var dtl = zgtDtls.FirstOrDefault(w => w.QuestionNum == queNo);
|
||||||
if (dtl != null)
|
if (dtl != null)
|
||||||
{
|
{
|
||||||
dtl.StudentAnswer = stuAnswer;
|
dtl.StudentAnswer = stuAnswer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 大图处理(拼接答案)
|
|
||||||
var paperZgtDtls = dtls.Where(w => w.SectionId == paper.PartId && w.QuestionType == JobQuestionTypeEnum.主观题 && w.QuestionPaperPicture == questionPaperPicture)
|
|
||||||
.OrderBy(s => SqlFunc.ToInt32(s.QuestionNo)).ToList();
|
|
||||||
var bitmaps = new List<SKBitmap>();
|
|
||||||
foreach (var zgtDtl in paperZgtDtls)
|
|
||||||
{
|
|
||||||
var studentAnswer = zgtDtl.StudentAnswers;
|
|
||||||
var url = studentAnswer.IsNotNullOrEmpty() ? studentAnswer : zgtDtl.QuestionPicture;
|
|
||||||
var imgStream = await $"{App.Configuration["Aliyun:OssJob:Host"]}/{url}".GetStreamAsync();
|
|
||||||
var bitmap = SKBitmap.Decode(imgStream);
|
|
||||||
bitmaps.Add(bitmap);
|
|
||||||
}
|
|
||||||
var width = bitmaps.FirstOrDefault()?.Width ?? 0;
|
|
||||||
var height = bitmaps.Sum(s => s.Height);
|
|
||||||
using (var paperBitmap = new SKBitmap(width, height, SKColorType.Rgba8888, SKAlphaType.Premul))
|
|
||||||
{
|
|
||||||
using (var canvas = new SKCanvas(paperBitmap))
|
|
||||||
{
|
|
||||||
var top = 0;
|
|
||||||
foreach (var bitmap in bitmaps)
|
|
||||||
{
|
|
||||||
canvas.DrawBitmap(bitmap, 0, top);
|
|
||||||
top += bitmap.Height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JobExtension.UploadFileToCloud(paperPicture, paperBitmap.Encode(SKEncodedImageFormat.Png, 100).ToArray());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// 删除
|
|
||||||
await DbBiz.Deleteable<MkExamResult>().Where(w => w.ExamSubjectId == eto.ExamSubjectId && w.StudentNo == penSerial).ExecuteCommandAsync();
|
await DbBiz.UseTranAsync(async () =>
|
||||||
await DbBiz.Updateable<SubjectiveMarkingResult>()
|
{
|
||||||
.SetColumns(s => s.IsDeleted == true)
|
// 删除
|
||||||
.Where(w => w.ExamSubjectId == eto.ExamSubjectId && w.StudentExamNum == penSerial)
|
await DbBiz.Deleteable<MkExamResult>().Where(w => w.ExamSubjectId == eto.ExamSubjectId && w.StudentNo == penSerial).ExecuteCommandAsync();
|
||||||
.ExecuteCommandAsync();
|
await DbBiz.Updateable<SubjectiveMarkingResult>()
|
||||||
// 新增
|
.SetColumns(s => s.IsDeleted == true)
|
||||||
await DbBiz.Insertable(kgtDtls).ExecuteCommandAsync();
|
.Where(w => w.ExamSubjectId == eto.ExamSubjectId && w.StudentExamNum == penSerial && w.IsDeleted == false)
|
||||||
await DbBiz.Insertable(zgtDtls).ExecuteCommandAsync();
|
.ExecuteCommandAsync();
|
||||||
|
// 新增
|
||||||
|
await DbBiz.Insertable(kgtDtls).ExecuteCommandAsync();
|
||||||
|
await DbBiz.Insertable(zgtDtls).ExecuteCommandAsync();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ExamAnnotate(ExamAnnotateEto eto)
|
public async Task ExamAnnotate(ExamAnnotateEto eto)
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,6 @@ public class ExamAppService : DolphinAppService
|
||||||
|
|
||||||
public async Task Test()
|
public async Task Test()
|
||||||
{
|
{
|
||||||
await _examManager.ExamStudentGather(new() { SchoolId = 1 });
|
await _examManager.ExamStudentGather(new() { SchoolId = 1, StudentExamNum = "BP2-3G3-07K-C0", TemplateId = "[]" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CSRedisCore" Version="3.8.802" />
|
<PackageReference Include="CSRedisCore" Version="3.8.802" />
|
||||||
<PackageReference Include="Flurl.Http" Version="4.0.2" />
|
<PackageReference Include="Flurl.Http" Version="4.0.2" />
|
||||||
<PackageReference Include="NoFurion" Version="0.0.6" />
|
<PackageReference Include="NoFurion" Version="0.0.7" />
|
||||||
<PackageReference Include="SkiaSharp" Version="2.88.6" />
|
<PackageReference Include="SkiaSharp" Version="2.88.6" />
|
||||||
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.6" />
|
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.6" />
|
||||||
<PackageReference Include="Volo.Abp.BlobStoring.Aliyun" Version="8.0.2" />
|
<PackageReference Include="Volo.Abp.BlobStoring.Aliyun" Version="8.0.2" />
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ public class PaperQueData
|
||||||
public string PaperId { get; set; }
|
public string PaperId { get; set; }
|
||||||
public long PartId { get; set; }
|
public long PartId { get; set; }
|
||||||
public int PageIndex { get; set; }
|
public int PageIndex { get; set; }
|
||||||
|
public string ImgUrl { get; set; }
|
||||||
|
public int Sort { get; set; }
|
||||||
public List<TemplateJsonModel_QueData> QueData { get; set; }
|
public List<TemplateJsonModel_QueData> QueData { get; set; }
|
||||||
}
|
}
|
||||||
public class TemplateJsonModel_QueData
|
public class TemplateJsonModel_QueData
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,16 @@ namespace Dolphin.ExamPictureCut.Extensions;
|
||||||
|
|
||||||
public static class RectExt
|
public static class RectExt
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 四舍五入
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="px"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static int SSWR(this float px)
|
||||||
|
{
|
||||||
|
return (int)Math.Round(px, 0, MidpointRounding.AwayFromZero);
|
||||||
|
}
|
||||||
|
|
||||||
public static float MMToPX(this float MM)
|
public static float MMToPX(this float MM)
|
||||||
{
|
{
|
||||||
return MM * 96 / 25.4F;
|
return MM * 96 / 25.4F;
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,6 @@
|
||||||
"Endpoint": "https://oss-cn-chengdu.aliyuncs.com",
|
"Endpoint": "https://oss-cn-chengdu.aliyuncs.com",
|
||||||
"RegionId": "oss-cn-chengdu",
|
"RegionId": "oss-cn-chengdu",
|
||||||
"ContainerName": "mk-xk-test",
|
"ContainerName": "mk-xk-test",
|
||||||
"CustomHostUrl": "//mk-xk-test.23544.com"
|
"Host": "//mk-xk-test.23544.com"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,6 @@
|
||||||
"Endpoint": "https://oss-cn-chengdu.aliyuncs.com",
|
"Endpoint": "https://oss-cn-chengdu.aliyuncs.com",
|
||||||
"RegionId": "oss-cn-chengdu",
|
"RegionId": "oss-cn-chengdu",
|
||||||
"ContainerName": "mk-xk",
|
"ContainerName": "mk-xk",
|
||||||
"CustomHostUrl": "//mk-xk.23544.com"
|
"Host": "//mk-xk.23544.com"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue