fix: 考试收集完成,待测试

This commit is contained in:
lyndonliu 2024-03-20 11:19:46 +08:00
parent 7da88993af
commit 9c53c18db9
7 changed files with 128 additions and 78 deletions

View File

@ -20,21 +20,29 @@ public class ExamManager : DomainService, IExamManager
{
private readonly ISqlSugarClient Db;
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)
{
Db = db;
_blobContainer = blobContainer;
}
[AutoTran]
public async Task ExamStudentGather(ExamStudentGatherEto eto)
{
var guid = GuidGenerator.Create().ToString("N");
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 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 data = JsonConvert.DeserializeObject<TemplateJsonModel_DataArr>(s.QueData);
@ -45,6 +53,8 @@ public class ExamManager : DomainService, IExamManager
PartId = s.PartId,
PageIndex = s.PageIndex,
QueData = data.queData,
ImgUrl = s.ImgUrl,
Sort = s.Order,
};
}).OrderBy(s => s.PartId).ThenBy(s => s.PageIndex).ToList();
@ -80,7 +90,89 @@ public class ExamManager : DomainService, IExamManager
IsObjectiveQuestion = true,
GroupNo = guid,
}).ToListAsync();
var gotoCount = 0;
dotPenOriginalImg:
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
{
Id = YitIdHelper.NextId(),
@ -96,31 +188,6 @@ public class ExamManager : DomainService, IExamManager
IsExcess = s.IsExcess,
}).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 zgt = new List<Tuple<string, string, bool>>(); // 纸张Id, 题号, 是否跨页
var pageSerials = new List<string>(); // 需要计算的页
@ -205,8 +272,6 @@ public class ExamManager : DomainService, IExamManager
var paper = paperInfo.FirstOrDefault(w => w.PaperId == paperId);
var paperJobPage = templateIds.FindIndex(s => s == paper.TemplateId) + 1;
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();
foreach (var que in ques)
@ -239,10 +304,10 @@ public class ExamManager : DomainService, IExamManager
queLatts.AddRange(queLattsOnNextPaper);
}
var jobDtl = dtls.FirstOrDefault(w => w.SectionId == paper.PartId && w.QuestionNo == queNo);
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);
using (var canvas = new SKCanvas(queBitmap))
@ -260,57 +325,30 @@ public class ExamManager : DomainService, IExamManager
canvas.DrawPoints(skPointMode, points, skPaint);
}
stuAnswer = $"questionAnswer/{eto.JobId}/{studentId}/{paperJobPage}/{paper.PartId}-{queNo}.png";
JobExtension.UploadFileToCloud(stuAnswer, queBitmap.Encode(SKEncodedImageFormat.Png, 100).ToArray());
stuAnswer = $"queAnswer/{eto.ExamSubjectId}/{penSerial}/{paper.Sort}/{queNo}.png";
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);
if (dtl != null)
{
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)
await DbBiz.UseTranAsync(async () =>
{
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.Updateable<SubjectiveMarkingResult>()
.SetColumns(s => s.IsDeleted == true)
.Where(w => w.ExamSubjectId == eto.ExamSubjectId && w.StudentExamNum == penSerial)
.Where(w => w.ExamSubjectId == eto.ExamSubjectId && w.StudentExamNum == penSerial && w.IsDeleted == false)
.ExecuteCommandAsync();
// 新增
await DbBiz.Insertable(kgtDtls).ExecuteCommandAsync();
await DbBiz.Insertable(zgtDtls).ExecuteCommandAsync();
});
}
public async Task ExamAnnotate(ExamAnnotateEto eto)

View File

@ -12,6 +12,6 @@ public class ExamAppService : DolphinAppService
public async Task Test()
{
await _examManager.ExamStudentGather(new() { SchoolId = 1 });
await _examManager.ExamStudentGather(new() { SchoolId = 1, StudentExamNum = "BP2-3G3-07K-C0", TemplateId = "[]" });
}
}

View File

@ -16,7 +16,7 @@
<ItemGroup>
<PackageReference Include="CSRedisCore" Version="3.8.802" />
<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.NativeAssets.Linux.NoDependencies" Version="2.88.6" />
<PackageReference Include="Volo.Abp.BlobStoring.Aliyun" Version="8.0.2" />

View File

@ -24,6 +24,8 @@ public class PaperQueData
public string PaperId { get; set; }
public long PartId { 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 class TemplateJsonModel_QueData

View File

@ -5,6 +5,16 @@ namespace Dolphin.ExamPictureCut.Extensions;
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)
{
return MM * 96 / 25.4F;

View File

@ -32,6 +32,6 @@
"Endpoint": "https://oss-cn-chengdu.aliyuncs.com",
"RegionId": "oss-cn-chengdu",
"ContainerName": "mk-xk-test",
"CustomHostUrl": "//mk-xk-test.23544.com"
"Host": "//mk-xk-test.23544.com"
}
}

View File

@ -32,6 +32,6 @@
"Endpoint": "https://oss-cn-chengdu.aliyuncs.com",
"RegionId": "oss-cn-chengdu",
"ContainerName": "mk-xk",
"CustomHostUrl": "//mk-xk.23544.com"
"Host": "//mk-xk.23544.com"
}
}