fix: 考试内容收集
This commit is contained in:
parent
dd397cecdf
commit
7da88993af
|
|
@ -1,10 +1,13 @@
|
|||
using Dolphin.ExamPictureCut.Localization;
|
||||
using Dolphin.ExamPictureCut.Localization;
|
||||
using SqlSugar;
|
||||
using Volo.Abp.Application.Services;
|
||||
|
||||
namespace Dolphin.ExamPictureCut;
|
||||
|
||||
public abstract class DolphinAppService : ApplicationService
|
||||
{
|
||||
protected ISqlSugarClient Db => LazyServiceProvider.LazyGetRequiredService<ISqlSugarClient>();
|
||||
|
||||
protected DolphinAppService()
|
||||
{
|
||||
LocalizationResource = typeof(DolphinResource);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
using Dolphin.ExamPictureCut.Exams.Dto;
|
||||
using Volo.Abp.DependencyInjection;
|
||||
using Volo.Abp.EventBus.Distributed;
|
||||
|
||||
namespace Dolphin.ExamPictureCut.Exams;
|
||||
|
||||
/// <summary>
|
||||
/// 考试批阅后割图
|
||||
/// </summary>
|
||||
public class ExamAnnotateEventHandler : IDistributedEventHandler<ExamAnnotateEto>//, ITransientDependency
|
||||
{
|
||||
private readonly IExamManager _examManager;
|
||||
public ExamAnnotateEventHandler(IExamManager examManager)
|
||||
{
|
||||
_examManager = examManager;
|
||||
}
|
||||
|
||||
public async Task HandleEventAsync(ExamAnnotateEto eventData)
|
||||
{
|
||||
await _examManager.ExamAnnotate(eventData);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
using Dolphin.ExamPictureCut.Exams.Dto;
|
||||
using Volo.Abp.DependencyInjection;
|
||||
using Volo.Abp.EventBus.Distributed;
|
||||
|
||||
namespace Dolphin.ExamPictureCut.Exams;
|
||||
|
||||
/// <summary>
|
||||
/// 考试收集
|
||||
/// </summary>
|
||||
public class ExamGatherEventHandler : IDistributedEventHandler<ExamStudentGatherEto>, ITransientDependency
|
||||
{
|
||||
private readonly IExamManager _examManager;
|
||||
public ExamGatherEventHandler(IExamManager examManager)
|
||||
{
|
||||
_examManager = examManager;
|
||||
}
|
||||
|
||||
public async Task HandleEventAsync(ExamStudentGatherEto eventData)
|
||||
{
|
||||
await _examManager.ExamStudentGather(eventData);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,334 @@
|
|||
using Dolphin.ExamPictureCut.Domains.Basic;
|
||||
using Dolphin.ExamPictureCut.Domains.Biz;
|
||||
using Dolphin.ExamPictureCut.Domains.Quest;
|
||||
using Dolphin.ExamPictureCut.Exams.Dto;
|
||||
using Dolphin.ExamPictureCut.Extensions;
|
||||
using Flurl.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using NoFurion;
|
||||
using NoFurion.SqlSugar;
|
||||
using SkiaSharp;
|
||||
using SqlSugar;
|
||||
using Volo.Abp.BlobStoring;
|
||||
using Volo.Abp.Domain.Services;
|
||||
using Yitter.IdGenerator;
|
||||
|
||||
namespace Dolphin.ExamPictureCut.Exams;
|
||||
|
||||
public class ExamManager : DomainService, IExamManager
|
||||
{
|
||||
private readonly ISqlSugarClient Db;
|
||||
private readonly IBlobContainer _blobContainer;
|
||||
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;
|
||||
|
||||
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();
|
||||
var paperInfo = templates.Select(s =>
|
||||
{
|
||||
var data = JsonConvert.DeserializeObject<TemplateJsonModel_DataArr>(s.QueData);
|
||||
return new PaperQueData
|
||||
{
|
||||
TemplateId = s.Id,
|
||||
PaperId = s.PaperId,
|
||||
PartId = s.PartId,
|
||||
PageIndex = s.PageIndex,
|
||||
QueData = data.queData,
|
||||
};
|
||||
}).OrderBy(s => s.PartId).ThenBy(s => s.PageIndex).ToList();
|
||||
|
||||
var paperIds = templates.Select(s => s.PaperId).ToList();
|
||||
|
||||
// 获取点阵数据
|
||||
var lattices = await Db.Queryable<PenOfflineData>()
|
||||
.Where(w => w.PenSerial == penSerial && paperIds.Contains(w.PageSerial) && w.logType == LogType.作业)
|
||||
.Select(s => new PenOfflineData
|
||||
{
|
||||
PageSerial = s.PageSerial,
|
||||
CX = s.CX,
|
||||
CY = s.CY,
|
||||
Time = s.Time,
|
||||
strokeIndex = s.strokeIndex,
|
||||
}).ToListAsync();
|
||||
if (lattices.Count == 0)
|
||||
{
|
||||
Logger.LogInformation("{ExamSubjectId} {penSerial} 无点阵数据", eto.ExamSubjectId, penSerial);
|
||||
return;
|
||||
}
|
||||
|
||||
var DbBiz = await GetTenantDb(eto.SchoolId);
|
||||
var kgtDtls = await DbBiz.Queryable<MarkingSettingObjective>()
|
||||
.Where(w => w.ExamSubjectId == eto.ExamSubjectId)
|
||||
.Select(s => new MkExamResult
|
||||
{
|
||||
Id = YitIdHelper.NextId(),
|
||||
StudentNo = penSerial,
|
||||
ExamId = eto.ExamId,
|
||||
ExamSubjectId = eto.ExamSubjectId,
|
||||
QuestionNumber = s.QuestionNum,
|
||||
IsObjectiveQuestion = true,
|
||||
GroupNo = guid,
|
||||
}).ToListAsync();
|
||||
var zgtSettingDtls = await DbBiz.Queryable<MarkingSettingSubjective>().Where(w => w.ExamSubjectId == eto.ExamSubjectId).ToListAsync();
|
||||
var zgtDtls = zgtSettingDtls.Select(s => new SubjectiveMarkingResult
|
||||
{
|
||||
Id = YitIdHelper.NextId(),
|
||||
ExamSubjectId = eto.ExamSubjectId,
|
||||
ExamSubjectSchoolId = eto.ExamSubjectSchoolId,
|
||||
StudentExamNum = penSerial,
|
||||
QuestionNum = s.QuestionNum,
|
||||
TotalScore = s.Score,
|
||||
SubQuestionCount = s.SubQuestionCount,
|
||||
SubQuestionDetail = s.SubQuestionDetail,
|
||||
GroupNo = guid,
|
||||
BigQuestionNum = s.BigQuestionNum,
|
||||
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>(); // 需要计算的页
|
||||
foreach (var paper in paperInfo)
|
||||
{
|
||||
var paperLatts = lattices.Where(w => w.PageSerial == paper.PaperId).ToList();
|
||||
foreach (var que in paper.QueData)
|
||||
{
|
||||
if (que.type == "1") // 客观题
|
||||
{
|
||||
if (que.options.Any() && que.options.Any(opt => paperLatts.Any(s => RectExt.IsRectContainsLattice(opt.AnswerArea, s))))
|
||||
{
|
||||
pageSerials.Add(paper.PaperId);
|
||||
kgt.Add(new(paper.PaperId, que.no));
|
||||
}
|
||||
}
|
||||
else if (que.type == "2") // 主观题
|
||||
{
|
||||
if (que.options.Any() && paperLatts.Any(s => RectExt.IsRectContainsLattice(que.options[0].AnswerArea, s)))
|
||||
{
|
||||
pageSerials.Add(paper.PaperId);
|
||||
var ky = false;
|
||||
var quePaperId = paper.PaperId;
|
||||
var queOthPaper = paperInfo.FirstOrDefault(w => w.PartId == paper.PartId && w.PageIndex != paper.PageIndex && w.QueData.Any(s => s.no == que.no));
|
||||
if (queOthPaper != null)
|
||||
{
|
||||
ky = true;
|
||||
if (paper.PageIndex > queOthPaper.PageIndex)
|
||||
{
|
||||
pageSerials.Add(queOthPaper.PaperId);
|
||||
quePaperId = queOthPaper.PaperId;
|
||||
}
|
||||
}
|
||||
if (!zgt.Any(s => s.Item1 == quePaperId && s.Item2 == que.no))
|
||||
zgt.Add(new(quePaperId, que.no, ky));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var kgtPapers = kgt.GroupBy(s => s.Item1).Select(s => s.Key).ToList(); // 客观题处理
|
||||
foreach (var paperId in kgtPapers)
|
||||
{
|
||||
var paper = paperInfo.FirstOrDefault(w => w.PaperId == paperId);
|
||||
var paperLatts = lattices.Where(w => w.PageSerial == paperId).ToList();
|
||||
|
||||
var queNos = kgt.Where(w => w.Item1 == paperId).Select(s => s.Item2).ToList();
|
||||
foreach (var queNo in queNos)
|
||||
{
|
||||
var queInfo = paper.QueData.FirstOrDefault(w => w.no == queNo);
|
||||
var queLatts = paperLatts.Where(w => queInfo.options.Any(s => RectExt.IsRectContainsLattice(s.AnswerArea, w))
|
||||
|| queInfo.resetPoint.Any(s => RectExt.IsRectContainsLattice(s, w))).ToList();
|
||||
|
||||
long? resetTime = null;
|
||||
if (queInfo.resetPoint.Any())
|
||||
{
|
||||
var resetLatts = queLatts.Where(w => RectExt.IsRectContainsLattice(queInfo.resetPoint[0], w)).ToList();
|
||||
resetTime = resetLatts.Any() ? resetLatts.Max(w => w.Time) : null;
|
||||
}
|
||||
|
||||
var stuAnswer = "";
|
||||
// 遍历每个选项
|
||||
foreach (var option in queInfo.options)
|
||||
{
|
||||
if (!option.point.Any()) continue; // 该选项无坐标数据
|
||||
var choose = queLatts.WhereIF(resetTime.HasValue, w => w.Time > resetTime).Where(a => RectExt.IsRectContainsLattice(option.point[0], a));
|
||||
if (!choose.Any()) continue;
|
||||
stuAnswer += option.option;
|
||||
}
|
||||
|
||||
var dtl = kgtDtls.FirstOrDefault(w => w.QuestionNumber == queNo);
|
||||
if (dtl != null)
|
||||
{
|
||||
dtl.QuestionValue = stuAnswer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var zgtPapers = zgt.GroupBy(s => s.Item1).Select(s => s.Key).ToList(); // 主观题处理
|
||||
foreach (var paperId in zgtPapers)
|
||||
{
|
||||
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)
|
||||
{
|
||||
var queNo = que.Item2;
|
||||
var queInfo = paper.QueData.FirstOrDefault(w => w.no == queNo);
|
||||
var answerArea = queInfo.options[0].AnswerArea;
|
||||
var queLatts = paperLatts.Where(w => RectExt.IsRectContainsLattice(answerArea, w))
|
||||
.Select(s => new SubjectiveLatt()
|
||||
{
|
||||
Stroke = s.strokeIndex,
|
||||
X = s.CX.AUToPX(),
|
||||
Y = s.CY.AUToPX() - answerArea.pxTop,
|
||||
Time = s.Time,
|
||||
}).ToList();
|
||||
|
||||
if (que.Item3) // 跨页
|
||||
{
|
||||
var queOnNextPaper = paperInfo.FirstOrDefault(w => w.PartId == paper.PartId && w.PageIndex == paper.PageIndex + 1 && w.QueData.Any(s => s.no == queNo));
|
||||
var queInfoOnNextPaper = queOnNextPaper.QueData.FirstOrDefault(w => w.no == queNo);
|
||||
var answerAreaOnNextPaper = queInfoOnNextPaper.options[0].AnswerArea;
|
||||
var queLattsOnNextPaper = lattices.Where(w => w.PageSerial == queOnNextPaper.PaperId && RectExt.IsRectContainsLattice(answerAreaOnNextPaper, w))
|
||||
.Select(s => new SubjectiveLatt()
|
||||
{
|
||||
Stroke = s.strokeIndex,
|
||||
X = s.CX.AUToPX(),
|
||||
Y = s.CY.AUToPX() + answerArea.pxHeight - answerAreaOnNextPaper.pxTop,
|
||||
Time = s.Time,
|
||||
}).ToList();
|
||||
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 queBitmap = SKBitmap.Decode(imgStream);
|
||||
|
||||
using (var canvas = new SKCanvas(queBitmap))
|
||||
{
|
||||
// 一笔一笔的画上去
|
||||
var strokeIndexs = queLatts.GroupBy(g => g.Stroke).Select(s => s.Key).ToList();
|
||||
foreach (var stroke in strokeIndexs)
|
||||
{
|
||||
var points = queLatts.Where(w => w.Stroke == stroke).OrderBy(s => s.Time).Select(s => new SKPoint(s.X, s.Y)).ToArray();
|
||||
var skPointMode = SKPointMode.Polygon;
|
||||
if (points.Length == 1)
|
||||
skPointMode = SKPointMode.Points;
|
||||
else if (points.Length == 2)
|
||||
skPointMode = SKPointMode.Lines;
|
||||
|
||||
canvas.DrawPoints(skPointMode, points, skPaint);
|
||||
}
|
||||
stuAnswer = $"questionAnswer/{eto.JobId}/{studentId}/{paperJobPage}/{paper.PartId}-{queNo}.png";
|
||||
JobExtension.UploadFileToCloud(stuAnswer, queBitmap.Encode(SKEncodedImageFormat.Png, 100).ToArray());
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
.ExecuteCommandAsync();
|
||||
// 新增
|
||||
await DbBiz.Insertable(kgtDtls).ExecuteCommandAsync();
|
||||
await DbBiz.Insertable(zgtDtls).ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
public async Task ExamAnnotate(ExamAnnotateEto eto)
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<SqlSugarClient> GetTenantDb(long tenantCode)
|
||||
{
|
||||
var tenant = await Db.Queryable<Tenant>().Where(w => w.TenantCode == tenantCode).FirstAsync();
|
||||
ExceptionExt.ThrowIf(tenant == null, $"{nameof(tenant)} is null with ${tenantCode}");
|
||||
|
||||
var config = new SqlSugarConfig();
|
||||
return new SqlSugarClient(new ConnectionConfig()
|
||||
{
|
||||
DbType = DbType.MySql,
|
||||
ConnectionString = tenant.ConnectionString,
|
||||
IsAutoCloseConnection = true,
|
||||
ConfigureExternalServices = config.ExtService,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
using Dolphin.ExamPictureCut.Exams;
|
||||
|
||||
namespace Dolphin.ExamPictureCut.Services;
|
||||
|
||||
public class ExamAppService : DolphinAppService
|
||||
{
|
||||
private readonly IExamManager _examManager;
|
||||
public ExamAppService(IExamManager examManager)
|
||||
{
|
||||
_examManager = examManager;
|
||||
}
|
||||
|
||||
public async Task Test()
|
||||
{
|
||||
await _examManager.ExamStudentGather(new() { SchoolId = 1 });
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
namespace Dolphin.ExamPictureCut.Constants;
|
||||
|
||||
public class DbConsts
|
||||
{
|
||||
public const string marking_basic = "marking_basic";
|
||||
public const string marking_biz = "marking_biz";
|
||||
public const string penoffline = "penoffline";
|
||||
}
|
||||
|
||||
public class SysConsts
|
||||
{
|
||||
public const string AppName = "ExamPictureCut";
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
namespace Dolphin.ExamPictureCut;
|
||||
|
||||
public class DbConsts
|
||||
{
|
||||
public const string basic = "basic";
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
|
|
@ -14,7 +14,18 @@
|
|||
</ItemGroup>
|
||||
|
||||
<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="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" />
|
||||
<PackageReference Include="Volo.Abp.EventBus.RabbitMQ" Version="8.0.2" />
|
||||
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Options\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
using Dolphin.ExamPictureCut.Localization;
|
||||
using Dolphin.ExamPictureCut.Localization;
|
||||
using NoFurion.Extensions;
|
||||
using Volo.Abp.Application;
|
||||
using Volo.Abp.BlobStoring;
|
||||
using Volo.Abp.BlobStoring.Aliyun;
|
||||
using Volo.Abp.EventBus.RabbitMq;
|
||||
using Volo.Abp.Localization;
|
||||
using Volo.Abp.Localization.ExceptionHandling;
|
||||
using Volo.Abp.Modularity;
|
||||
|
|
@ -11,12 +14,22 @@ namespace Dolphin.ExamPictureCut;
|
|||
|
||||
[DependsOn(
|
||||
typeof(AbpLocalizationModule),
|
||||
typeof(AbpDddApplicationContractsModule)
|
||||
typeof(AbpDddApplicationContractsModule),
|
||||
typeof(AbpBlobStoringAliyunModule),
|
||||
typeof(AbpEventBusRabbitMqModule)
|
||||
)]
|
||||
public class DolphinExamPictureCutCoreModule : AbpModule
|
||||
{
|
||||
public override void ConfigureServices(ServiceConfigurationContext context)
|
||||
{
|
||||
Configure<AbpBlobStoringOptions>(options =>
|
||||
{
|
||||
options.Containers.ConfigureDefault(container =>
|
||||
{
|
||||
container.UseAliyun(aliyun => { });
|
||||
});
|
||||
});
|
||||
|
||||
Configure<AbpVirtualFileSystemOptions>(options =>
|
||||
{
|
||||
options.FileSets.AddEmbedded<DolphinExamPictureCutCoreModule>();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,120 @@
|
|||
using Dolphin.ExamPictureCut.Constants;
|
||||
using SqlSugar;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Dolphin.ExamPictureCut.Domains.Basic;
|
||||
|
||||
[Table(nameof(GroupBook)), Tenant(DbConsts.marking_basic)]
|
||||
public class GroupBook
|
||||
{
|
||||
[SugarColumn(IsPrimaryKey = true, ColumnName = "id", Length = 32)]
|
||||
public string Id { get; set; }
|
||||
|
||||
[SugarColumn(ColumnName = "book_id")]
|
||||
public long BookId { get; set; }
|
||||
[SugarColumn(ColumnName = "book_name", IsNullable = true)]
|
||||
public string BookName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 页面方向 0 竖向 1 横向
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "orientation")]
|
||||
public int Orientation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 页面大小 A3 = 8,A4 = 9,A4Small = 10,A5 = 11,B4 = 12, B5 = 13,
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "page_size")]
|
||||
public int PaperSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属科目
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "course_name", IsNullable = true)]
|
||||
public string CourseName { get; set; }
|
||||
|
||||
[SugarColumn(ColumnName = "remark", IsNullable = true)]
|
||||
public string Remark { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否试卷
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "is_paper")]
|
||||
public bool IsPaper { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否归档
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "is_save")]
|
||||
public bool IsSave { get; set; }
|
||||
|
||||
[SugarColumn(ColumnName = "download_url", IsNullable = true)]
|
||||
public string DownloadUrl { get; set; }
|
||||
/// <summary>
|
||||
/// 生成状态 0 未生成 1 正在生成 2 生成成功 3生成失败
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "state")]
|
||||
public int State { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 学段
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "stage", IsNullable = true)]
|
||||
public string Stage { get; set; }
|
||||
/// <summary>
|
||||
/// 学段Id
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "stage_id")]
|
||||
public long StageId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 年级
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "grade_name", IsNullable = true)]
|
||||
public string GradeName { get; set; }
|
||||
/// <summary>
|
||||
/// 科目
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "subject_name", IsNullable = true)]
|
||||
public string SubjectName { get; set; }
|
||||
[SugarColumn(ColumnName = "subject_id")]
|
||||
public int SubjectId { get; set; }
|
||||
/// <summary>
|
||||
/// 年份
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "year")]
|
||||
public int Year { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模板制作状态
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "template_make_status")]
|
||||
public TemplateMakeStatusEnum TemplateMakeStatus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 归类Id
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "catalog_id", IsNullable = true)]
|
||||
public string CatalogId { get; set; }
|
||||
/// <summary>
|
||||
/// 归类名称
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "catalog_name", IsNullable = true)]
|
||||
public string CatalogName { get; set; }
|
||||
|
||||
[SugarColumn(ColumnName = "create_time", IsOnlyIgnoreUpdate = true, InsertServerTime = true)]
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
||||
[SugarColumn(ColumnName = "update_time", InsertServerTime = true, UpdateServerTime = true)]
|
||||
public DateTime UpdateTime { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 模板制作状态
|
||||
/// </summary>
|
||||
public enum TemplateMakeStatusEnum
|
||||
{
|
||||
未制作 = 0,
|
||||
制作中 = 1,
|
||||
已完成 = 2,
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
using Dolphin.ExamPictureCut.Constants;
|
||||
using SqlSugar;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Dolphin.ExamPictureCut.Domains.Basic;
|
||||
|
||||
[Table(nameof(GroupBookPaperTemplate)), Tenant(DbConsts.marking_basic)]
|
||||
public class GroupBookPaperTemplate
|
||||
{
|
||||
[SugarColumn(IsPrimaryKey = true, ColumnName = "id", Length = 32)]
|
||||
public string Id { get; set; }
|
||||
|
||||
[SugarColumn(ColumnName = "book_id")]
|
||||
public long BookId { get; set; }
|
||||
[SugarColumn(ColumnName = "book_name", IsNullable = true)]
|
||||
public string BookName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 章
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "chapter_id")]
|
||||
public long ChapterId { get; set; }
|
||||
[SugarColumn(ColumnName = "chapter_name", IsNullable = true)]
|
||||
public string ChapterName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 节
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "part_id")]
|
||||
public long PartId { get; set; }
|
||||
[SugarColumn(ColumnName = "part_name", IsNullable = true)]
|
||||
public string PartName { get; set; }
|
||||
|
||||
[SugarColumn(ColumnName = "page_index")]
|
||||
public int PageIndex { get; set; }
|
||||
|
||||
[SugarColumn(ColumnName = "paper_id", IsNullable = true)]
|
||||
public string PaperId { get; set; }
|
||||
|
||||
[SugarColumn(ColumnName = "img_url", IsNullable = true)]
|
||||
public string ImgUrl { get; set; }
|
||||
|
||||
[SugarColumn(ColumnName = "que_data", IsNullable = true, ColumnDataType = "text")]
|
||||
public string QueData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模板制作状态
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "template_make_status")]
|
||||
public TemplateMakeStatusEnum TemplateMakeStatus { get; set; }
|
||||
|
||||
[SugarColumn(ColumnName = "order")]
|
||||
public int Order { get; set; }
|
||||
|
||||
[SugarColumn(ColumnName = "create_time", IsOnlyIgnoreUpdate = true, InsertServerTime = true)]
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
||||
[SugarColumn(ColumnName = "update_time", InsertServerTime = true, UpdateServerTime = true)]
|
||||
public DateTime UpdateTime { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
using Dolphin.ExamPictureCut.Constants;
|
||||
using SqlSugar;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Volo.Abp;
|
||||
|
||||
namespace Dolphin.ExamPictureCut.Domains.Basic;
|
||||
|
||||
[Table(nameof(Tenant)), Tenant(DbConsts.marking_basic)]
|
||||
public class Tenant : ISoftDelete
|
||||
{
|
||||
[SugarColumn(IsIdentity = true, IsPrimaryKey = true)]
|
||||
public long Id { get; set; }
|
||||
public string IpAddr { get; set; }
|
||||
public string Database { get; set; }
|
||||
public string Dbuser { get; set; }
|
||||
public string Password { get; set; }
|
||||
public string Port { get; set; }
|
||||
public long TenantCode { get; set; }
|
||||
public string TenantName { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public string ConnectionString => $"Server={IpAddr};Port={Port};Database={Database};Uid={Dbuser};Pwd={Password};";
|
||||
|
||||
public bool IsEnable { get; set; }
|
||||
public bool IsDeleted { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Dolphin.ExamPictureCut.Domains.Biz;
|
||||
|
||||
[Table(nameof(MarkingSettingObjective))]
|
||||
public class MarkingSettingObjective
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public long ExamSubjectId { get; set; }
|
||||
public string QuestionNum { get; set; }
|
||||
public float Score { get; set; }
|
||||
public string ObjectiveAnswer { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
namespace Dolphin.ExamPictureCut.Domains.Biz;
|
||||
|
||||
public class MarkingSettingSubjective
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public long ExamSubjectId { get; set; }
|
||||
public string QuestionNum { get; set; }
|
||||
public float Score { get; set; }
|
||||
public int SubQuestionCount { get; set; }
|
||||
public string SubQuestionDetail { get; set; }
|
||||
public string BigQuestionNum { get; set; }
|
||||
public bool IsExcess { get; set; }
|
||||
public string DotPenOriginalImg { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
using SqlSugar;
|
||||
|
||||
namespace Dolphin.ExamPictureCut.Domains.Biz;
|
||||
|
||||
[SugarTable("MK_ExamResult")]
|
||||
public class MkExamResult
|
||||
{
|
||||
[SugarColumn(IsPrimaryKey = true, ColumnName = "ID_bigint")]
|
||||
public long Id { get; set; }
|
||||
|
||||
[SugarColumn(ColumnName = "StudentNo_nvarchar")]
|
||||
public string StudentNo { get; set; }
|
||||
|
||||
[SugarColumn(ColumnName = "ExamId_bigint")]
|
||||
public long ExamId { get; set; }
|
||||
|
||||
public long ExamSubjectId { get; set; }
|
||||
|
||||
[SugarColumn(ColumnName = "QuestionNumber_int")]
|
||||
public string QuestionNumber { get; set; }
|
||||
|
||||
[SugarColumn(ColumnName = "SmallQuestion_int")]
|
||||
public int SmallQuestion { get; set; }
|
||||
|
||||
[SugarColumn(ColumnName = "IsObjectiveQuestions_bit")]
|
||||
public bool IsObjectiveQuestion { get; set; }
|
||||
|
||||
[SugarColumn(ColumnName = "QuestionValue_nvarchar")]
|
||||
public string QuestionValue { get; set; }
|
||||
|
||||
[SugarColumn(ColumnName = "QuestionNumberSlaveIndex_int")]
|
||||
public int QuestionNumberSlaveIndex { get; set; }
|
||||
|
||||
[SugarColumn(ColumnName = "GroupNo_nvarchar")]
|
||||
public string GroupNo { get; set; }
|
||||
|
||||
public bool IsSync { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
using Volo.Abp;
|
||||
|
||||
namespace Dolphin.ExamPictureCut.Domains.Biz;
|
||||
|
||||
public class SubjectiveMarkingResult : ISoftDelete
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public long ExamSubjectId { get; set; }
|
||||
public long ExamSubjectSchoolId { get; set; }
|
||||
public string StudentExamNum { get; set; }
|
||||
public string QuestionNum { get; set; }
|
||||
public float Score { get; set; }
|
||||
public float TotalScore { get; set; }
|
||||
public int SubQuestionCount { get; set; }
|
||||
public string SubQuestionDetail { get; set; } = "[]";
|
||||
public string StudentAnswer { get; set; }
|
||||
public string GroupNo { get; set; }
|
||||
public bool IsAssign { get; set; }
|
||||
public bool IsRating { get; set; }
|
||||
public string CommentImgUrl { get; set; } = "[]";
|
||||
public string BigQuestionNum { get; set; }
|
||||
public bool IsExcess { get; set; }
|
||||
public bool IsDeleted { get; set; }
|
||||
public DateTime CreateDate { get; set; }
|
||||
public DateTime UpdateDate { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
using Dolphin.ExamPictureCut.Constants;
|
||||
using Dolphin.ExamPictureCut.Domains.Basic;
|
||||
using SqlSugar;
|
||||
|
||||
namespace Dolphin.ExamPictureCut.Domains.Quest;
|
||||
|
||||
/// <summary>
|
||||
/// 点阵笔离线数据
|
||||
/// </summary>
|
||||
[Tenant(DbConsts.penoffline)]
|
||||
public class PenOfflineData
|
||||
{
|
||||
/// <summary>
|
||||
/// 笔id
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDataType = "symbol")]
|
||||
public string PenSerial { get; set; }
|
||||
/// <summary>
|
||||
/// 纸张id
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDataType = "symbol")]
|
||||
public string PageSerial { get; set; } = "no";
|
||||
public int CX { get; set; }
|
||||
public int CY { get; set; }
|
||||
/// <summary>
|
||||
/// 时间戳
|
||||
/// </summary>
|
||||
public long Time { get; set; }
|
||||
/// <summary>
|
||||
/// 笔数据类型
|
||||
/// </summary>
|
||||
public PenDataType DataType { get; set; }
|
||||
/// <summary>
|
||||
/// 笔画
|
||||
/// </summary>
|
||||
public long strokeIndex { get; set; }
|
||||
/// <summary>
|
||||
/// 记录类型
|
||||
/// </summary>
|
||||
public LogType logType { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 点阵笔数据类型
|
||||
/// </summary>
|
||||
public enum PenDataType
|
||||
{
|
||||
落笔,
|
||||
移动,
|
||||
抬笔,
|
||||
连接,
|
||||
断开连接
|
||||
}
|
||||
/// <summary>
|
||||
/// 记录类型
|
||||
/// </summary>
|
||||
public enum LogType
|
||||
{
|
||||
作业,
|
||||
互动课堂
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using Volo.Abp.EventBus;
|
||||
|
||||
namespace Dolphin.ExamPictureCut.Exams.Dto;
|
||||
|
||||
[EventName("Dolphin.ExamAnnotateEto")]
|
||||
public class ExamAnnotateEto
|
||||
{
|
||||
public string ExamId { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
using Volo.Abp.EventBus;
|
||||
|
||||
namespace Dolphin.ExamPictureCut.Exams.Dto;
|
||||
|
||||
[EventName("Dolphin.ExamStudentGatherEto")]
|
||||
public class ExamStudentGatherEto
|
||||
{
|
||||
/// <summary>
|
||||
/// 考试Id
|
||||
/// </summary>
|
||||
public long ExamId { get; set; }
|
||||
/// <summary>
|
||||
/// 考试科目Id
|
||||
/// </summary>
|
||||
public long ExamSubjectId { get; set; }
|
||||
/// <summary>
|
||||
/// 所属学校
|
||||
/// </summary>
|
||||
public long SchoolId { get; set; }
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public long ExamSubjectSchoolId { get; set; }
|
||||
/// <summary>
|
||||
/// 模板id
|
||||
/// </summary>
|
||||
public string TemplateId { get; set; }
|
||||
/// <summary>
|
||||
/// 所属学生
|
||||
/// </summary>
|
||||
public string StudentExamNum { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
namespace Dolphin.ExamPictureCut.Exams.Dto;
|
||||
|
||||
public class SubjectiveLatt
|
||||
{
|
||||
public long Stroke { get; set; }
|
||||
public float X { get; set; }
|
||||
public float Y { get; set; }
|
||||
public long Time { get; set; }
|
||||
}
|
||||
public class TemplateJsonModel_DataArr
|
||||
{
|
||||
public string id { get; set; }
|
||||
public string jobName { get; set; }
|
||||
public string imgUrl { get; set; }
|
||||
public string paperId { get; set; }
|
||||
public long bookId { get; set; }
|
||||
public long chapterId { get; set; }
|
||||
public long partId { get; set; }
|
||||
public List<TemplateJsonModel_QueData> queData { get; set; }
|
||||
}
|
||||
public class PaperQueData
|
||||
{
|
||||
public string TemplateId { get; set; }
|
||||
public string PaperId { get; set; }
|
||||
public long PartId { get; set; }
|
||||
public int PageIndex { get; set; }
|
||||
public List<TemplateJsonModel_QueData> QueData { get; set; }
|
||||
}
|
||||
public class TemplateJsonModel_QueData
|
||||
{
|
||||
/// <summary>
|
||||
/// 题号
|
||||
/// </summary>
|
||||
public string no { get; set; }
|
||||
/// <summary>
|
||||
/// 题型
|
||||
/// </summary>
|
||||
public string type { get; set; }
|
||||
/// <summary>
|
||||
/// 选项
|
||||
/// </summary>
|
||||
public List<TemplateJsonModel_Option> options { get; set; }
|
||||
/// <summary>
|
||||
/// 答案
|
||||
/// </summary>
|
||||
public List<TemplateJsonModel_Answer> answer { get; set; }
|
||||
/// <summary>
|
||||
/// 关联学科网知识点
|
||||
/// </summary>
|
||||
public List<TemplateJsonModel_Know> xkKnows { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 重置按钮
|
||||
/// </summary>
|
||||
public List<TemplateJsonModel_Rect> resetPoint { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 选项
|
||||
/// </summary>
|
||||
public class TemplateJsonModel_Option
|
||||
{
|
||||
/// <summary>
|
||||
/// 选项代号
|
||||
/// </summary>
|
||||
public string option { get; set; }
|
||||
/// <summary>
|
||||
/// ID
|
||||
/// </summary>
|
||||
public long index { get; set; }
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool active { get; set; }
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public List<TemplateJsonModel_Rect> point { get; set; }
|
||||
/// <summary>
|
||||
/// 第一个答案区域
|
||||
/// </summary>
|
||||
public TemplateJsonModel_Rect AnswerArea { get { return point.FirstOrDefault() ?? new(); } }
|
||||
}
|
||||
/// <summary>
|
||||
/// 选项-坐标
|
||||
/// </summary>
|
||||
public class TemplateJsonModel_Rect
|
||||
{
|
||||
public float pxWidth { get; set; }
|
||||
public float pxHeight { get; set; }
|
||||
public float pxTop { get; set; }
|
||||
public float pxLeft { get; set; }
|
||||
public int angle { get; set; }
|
||||
public string type { get; set; }
|
||||
public long bindId { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// 答案
|
||||
/// </summary>
|
||||
public class TemplateJsonModel_Answer
|
||||
{
|
||||
/// <summary>
|
||||
/// 答案
|
||||
/// </summary>
|
||||
public string name { get; set; }
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool active { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// 知识点
|
||||
/// </summary>
|
||||
public class TemplateJsonModel_Know
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public long id { get; set; }
|
||||
/// <summary>
|
||||
/// 知识点名称
|
||||
/// </summary>
|
||||
public string knowName { get; set; }
|
||||
/// <summary>
|
||||
/// 知识点描述
|
||||
/// </summary>
|
||||
public string knowDescription { get; set; }
|
||||
/// <summary>
|
||||
/// 上级ID
|
||||
/// </summary>
|
||||
public long parentId { get; set; }
|
||||
/// <summary>
|
||||
/// 上级知识点名称
|
||||
/// </summary>
|
||||
public string parentKnowName { get; set; }
|
||||
/// <summary>
|
||||
/// 科目
|
||||
/// </summary>
|
||||
public int subjectId { get; set; }
|
||||
/// <summary>
|
||||
/// 科目名称
|
||||
/// </summary>
|
||||
public string subjectName { get; set; }
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int categoryId { get; set; }
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string categoryName { get; set; }
|
||||
/// <summary>
|
||||
/// 难度
|
||||
/// </summary>
|
||||
public int difficulty { get; set; }
|
||||
/// <summary>
|
||||
/// 是否选中
|
||||
/// </summary>
|
||||
public bool Checked { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
using Dolphin.ExamPictureCut.Exams.Dto;
|
||||
using Volo.Abp.Domain.Services;
|
||||
|
||||
namespace Dolphin.ExamPictureCut.Exams;
|
||||
|
||||
public interface IExamManager : IDomainService
|
||||
{
|
||||
Task ExamStudentGather(ExamStudentGatherEto eto);
|
||||
Task ExamAnnotate(ExamAnnotateEto eto);
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
using Dolphin.ExamPictureCut.Constants;
|
||||
using System.Timers;
|
||||
using Yitter.IdGenerator;
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
namespace Dolphin.ExamPictureCut.Extensions;
|
||||
|
||||
public class IdExt
|
||||
{
|
||||
public static IdGeneratorOptions GetIdGeneratorOptions(string uniqueValue)
|
||||
{
|
||||
byte workerIdBitLength = 8;
|
||||
var maxWorkId = Math.Pow(2, workerIdBitLength) - 1; //63
|
||||
var workIdKey = $"{uniqueValue}idgen:workid";
|
||||
|
||||
var workId = GetNextWorkId();
|
||||
|
||||
while (!RedisHelper.SetNx($"{workIdKey}:{workId}", SysConsts.AppName))
|
||||
{
|
||||
// workId 已被占用,获取下一个workId
|
||||
workId = GetNextWorkId();
|
||||
};
|
||||
|
||||
// 设置5分钟过期
|
||||
RedisHelper.Expire($"{workIdKey}:{workId}", 60 * 5);
|
||||
|
||||
// 设置定时器,每4分钟更新一次过期时间
|
||||
SetTimer(4, (s, e) =>
|
||||
{
|
||||
RedisHelper.Expire($"{workIdKey}:{workId}", 60 * 5);
|
||||
});
|
||||
|
||||
// WorkerIdBitLength + SeqBitLength 不超过 22
|
||||
return new IdGeneratorOptions
|
||||
{
|
||||
WorkerIdBitLength = workerIdBitLength,
|
||||
//SeqBitLength = 6, // 数值越高,性能越好,但是Id也越长
|
||||
WorkerId = (ushort)workId
|
||||
};
|
||||
|
||||
long GetNextWorkId()
|
||||
{
|
||||
var workId = RedisHelper.IncrBy(workIdKey);
|
||||
if (workId > maxWorkId)
|
||||
{
|
||||
// 大于了最大可用WorkId,重置workId,并获取
|
||||
RedisHelper.Set(workIdKey, -1);
|
||||
workId = RedisHelper.IncrBy(workIdKey);
|
||||
}
|
||||
|
||||
return workId;
|
||||
}
|
||||
}
|
||||
|
||||
private static Timer _timer;
|
||||
private static void SetTimer(int mins, ElapsedEventHandler eh)
|
||||
{
|
||||
// 创建一个 Timer 实例,并设置其相关属性
|
||||
_timer = new Timer(TimeSpan.FromMinutes(mins).TotalMilliseconds); // 4 分钟
|
||||
_timer.Elapsed += eh;
|
||||
_timer.AutoReset = true; // 设置 Timer 实例能否多次触发
|
||||
_timer.Enabled = true; // 启动 Timer 实例
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
using Dolphin.ExamPictureCut.Domains.Quest;
|
||||
using Dolphin.ExamPictureCut.Exams.Dto;
|
||||
|
||||
namespace Dolphin.ExamPictureCut.Extensions;
|
||||
|
||||
public static class RectExt
|
||||
{
|
||||
public static float MMToPX(this float MM)
|
||||
{
|
||||
return MM * 96 / 25.4F;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AU转像素
|
||||
/// </summary>
|
||||
/// <param name="AU"></param>
|
||||
/// <returns></returns>
|
||||
public static float AUToPX(this int AU)
|
||||
{
|
||||
|
||||
return (AU * 0.3F / 8).MMToPX();
|
||||
}
|
||||
|
||||
public static bool IsRectContainsLattice(TemplateJsonModel_Rect rect, PenOfflineData latt)
|
||||
{
|
||||
var x = latt.CX.AUToPX();
|
||||
var y = latt.CY.AUToPX();
|
||||
return rect.pxLeft <= x && x <= rect.pxLeft + rect.pxWidth
|
||||
&& rect.pxTop <= y && y <= rect.pxTop + rect.pxHeight;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
|
|
@ -10,13 +10,11 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="5.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
|
||||
<PackageReference Include="Volo.Abp.AspNetCore.Authentication.JwtBearer" Version="8.0.2" />
|
||||
<PackageReference Include="Volo.Abp.AspNetCore.Serilog" Version="8.0.1" />
|
||||
<PackageReference Include="Volo.Abp.Autofac" Version="8.0.1" />
|
||||
<PackageReference Include="Volo.Abp.Caching.StackExchangeRedis" Version="8.0.1" />
|
||||
<PackageReference Include="Volo.Abp.Swashbuckle" Version="8.0.1" />
|
||||
<PackageReference Include="Volo.Abp.AspNetCore.Serilog" Version="8.0.2" />
|
||||
<PackageReference Include="Volo.Abp.Autofac" Version="8.0.2" />
|
||||
<PackageReference Include="Volo.Abp.Swashbuckle" Version="8.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -1,19 +1,20 @@
|
|||
using Dolphin.ExamPictureCut.Constants;
|
||||
using Dolphin.ExamPictureCut.Extensions;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using NoFurion.SqlSugar;
|
||||
using SqlSugar;
|
||||
using StackExchange.Redis;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Reflection;
|
||||
using Volo.Abp;
|
||||
using Volo.Abp.AspNetCore.Authentication.JwtBearer;
|
||||
using Volo.Abp.AspNetCore.Serilog;
|
||||
using Volo.Abp.Autofac;
|
||||
using Volo.Abp.Caching;
|
||||
using Volo.Abp.Caching.StackExchangeRedis;
|
||||
using Volo.Abp.Modularity;
|
||||
using Volo.Abp.Swashbuckle;
|
||||
using Volo.Abp.VirtualFileSystem;
|
||||
using Yitter.IdGenerator;
|
||||
|
||||
namespace Dolphin.ExamPictureCut;
|
||||
|
||||
|
|
@ -22,7 +23,6 @@ namespace Dolphin.ExamPictureCut;
|
|||
typeof(AbpAutofacModule),
|
||||
typeof(AbpAspNetCoreSerilogModule),
|
||||
typeof(AbpAspNetCoreAuthenticationJwtBearerModule),
|
||||
typeof(AbpCachingStackExchangeRedisModule),
|
||||
typeof(AbpSwashbuckleModule)
|
||||
)]
|
||||
public class DolphinExamPictureCutHttpApiHostModule : AbpModule
|
||||
|
|
@ -42,17 +42,63 @@ public class DolphinExamPictureCutHttpApiHostModule : AbpModule
|
|||
private void ConfigureSqlSugar(ServiceConfigurationContext context, IConfiguration configuration)
|
||||
{
|
||||
var config = new SqlSugarConfig();
|
||||
var ExtService = new ConfigureExternalServices
|
||||
{
|
||||
EntityService = delegate (PropertyInfo prop, EntityColumnInfo col)
|
||||
{
|
||||
if (prop.GetCustomAttribute<NotMappedAttribute>() != null)
|
||||
{
|
||||
col.IsIgnore = true;
|
||||
}
|
||||
|
||||
if (prop.PropertyType == typeof(string))
|
||||
{
|
||||
col.DataType = "text";
|
||||
}
|
||||
|
||||
if (!col.IsPrimarykey)
|
||||
{
|
||||
if (prop.GetCustomAttribute<RequiredAttribute>() != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (new NullabilityInfoContext().Create(prop).WriteState == NullabilityState.Nullable || prop.PropertyType == typeof(string))
|
||||
{
|
||||
col.IsNullable = true;
|
||||
}
|
||||
}
|
||||
|
||||
col.DbColumnName = UtilMethods.ToUnderLine(col.DbColumnName);
|
||||
},
|
||||
EntityNameService = delegate (Type t, EntityInfo entity)
|
||||
{
|
||||
TableAttribute customAttribute = t.GetCustomAttribute<TableAttribute>();
|
||||
if (customAttribute != null)
|
||||
{
|
||||
entity.DbTableName = UtilMethods.ToUnderLine(customAttribute.Name);
|
||||
}
|
||||
}
|
||||
};
|
||||
context.Services.AddSingleton<ISqlSugarClient>(s =>
|
||||
{
|
||||
var scope = new SqlSugarScope(
|
||||
new List<ConnectionConfig>() {
|
||||
new ConnectionConfig()
|
||||
{
|
||||
ConfigId = DbConsts.basic,
|
||||
DbType = DbType.PostgreSQL,
|
||||
ConnectionString = configuration.GetConnectionString(DbConsts.basic),
|
||||
ConfigId = DbConsts.marking_basic,
|
||||
DbType = DbType.MySql,
|
||||
ConnectionString = configuration.GetConnectionString(DbConsts.marking_basic),
|
||||
IsAutoCloseConnection = true,
|
||||
ConfigureExternalServices = config.ExtService,
|
||||
ConfigureExternalServices = ExtService,
|
||||
},
|
||||
new ConnectionConfig()
|
||||
{
|
||||
ConfigId = DbConsts.penoffline,
|
||||
DbType = DbType.QuestDB,
|
||||
ConnectionString = configuration.GetConnectionString(DbConsts.penoffline),
|
||||
IsAutoCloseConnection = true,
|
||||
ConfigureExternalServices = ExtService,
|
||||
},
|
||||
},
|
||||
db =>
|
||||
|
|
@ -74,7 +120,9 @@ public class DolphinExamPictureCutHttpApiHostModule : AbpModule
|
|||
|
||||
private void ConfigureCache(IConfiguration configuration)
|
||||
{
|
||||
//Configure<AbpDistributedCacheOptions>(options => { options.KeyPrefix = "Dolphin:"; });
|
||||
RedisHelper.Initialization(new CSRedis.CSRedisClient(configuration.GetValue<string>("Redis:Configuration")));
|
||||
|
||||
YitIdHelper.SetIdGenerator(IdExt.GetIdGeneratorOptions("mk"));
|
||||
}
|
||||
|
||||
private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
|
||||
|
|
@ -90,12 +138,7 @@ public class DolphinExamPictureCutHttpApiHostModule : AbpModule
|
|||
|
||||
private static void ConfigureSwaggerServices(ServiceConfigurationContext context, IConfiguration configuration)
|
||||
{
|
||||
context.Services.AddAbpSwaggerGenWithOAuth(
|
||||
configuration["AuthServer:Authority"],
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
{"Dolphin", "Dolphin API"}
|
||||
},
|
||||
context.Services.AddAbpSwaggerGen(
|
||||
options =>
|
||||
{
|
||||
options.HideAbpEndpoints(); // 隐藏 Abp 相关的 endpoints
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
{
|
||||
"profiles": {
|
||||
"Dolphin.ExamPictureCut.HttpApi.Host": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "https://localhost:53350;http://localhost:53351"
|
||||
"commandName": "Project",
|
||||
"launchBrowser": false,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "http://192.168.2.138:53350"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"App": {
|
||||
"CorsOrigins": "https://*.23544.com"
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"marking_basic": "Server=192.168.2.9;Port=3306;Database=marking_basic;Uid=root;Pwd=qwe123!@#;AllowLoadLocalInfile=true;",
|
||||
"penoffline": "host=192.168.2.7;port=8812;username=zhjs;password=zhjsniubi;database=qdb;ServerCompatibilityMode=NoTypeLoading;"
|
||||
},
|
||||
"Redis": {
|
||||
"Configuration": "192.168.2.7:6379,password=qwe123!@#,defaultDatabase=14,idleTimeout=3000,poolsize=5,prefix=marking"
|
||||
},
|
||||
"StringEncryption": {
|
||||
"DefaultPassPhrase": "LB6Ts3T0sdE5VSNq"
|
||||
},
|
||||
"RabbitMQ": {
|
||||
"Connections": {
|
||||
"Default": {
|
||||
"HostName": "192.168.2.7",
|
||||
"Port": "5672",
|
||||
"UserName": "rabbit",
|
||||
"Password": "qwe123!@#"
|
||||
}
|
||||
},
|
||||
"EventBus": {
|
||||
"ClientName": "collect_queue",
|
||||
"ExchangeName": "tenant_ex"
|
||||
}
|
||||
},
|
||||
"Aliyun": {
|
||||
"AccessKeyId": "LTAI5tJ6stiMWGhVU3TtRyAf",
|
||||
"AccessKeySecret": "A3pwnGx2SW1orvraCkXta6Lx4sV06e",
|
||||
"Endpoint": "https://oss-cn-chengdu.aliyuncs.com",
|
||||
"RegionId": "oss-cn-chengdu",
|
||||
"ContainerName": "mk-xk-test",
|
||||
"CustomHostUrl": "//mk-xk-test.23544.com"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +1,37 @@
|
|||
{
|
||||
"App": {
|
||||
"CorsOrigins": "https://*.nofurion.com"
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"Default": "Host=localhost;Port=5432;Database=nofurion;User ID=root;Password=myPassword;"
|
||||
},
|
||||
"Redis": {
|
||||
"Configuration": "127.0.0.1"
|
||||
},
|
||||
"AuthServer": {
|
||||
"Authority": "https://localhost:44377",
|
||||
"RequireHttpsMetadata": "true",
|
||||
"SwaggerClientId": "Dolphin_Swagger"
|
||||
},
|
||||
"StringEncryption": {
|
||||
"DefaultPassPhrase": "LB6Ts3T0sdE5VSNq"
|
||||
}
|
||||
{
|
||||
"App": {
|
||||
"CorsOrigins": "https://*.23544.com"
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"marking_basic": "Server=192.168.2.9;Port=3306;Database=marking_basic;Uid=root;Pwd=qwe123!@#;AllowLoadLocalInfile=true;",
|
||||
"penoffline": "host=192.168.2.7;port=8812;username=zhjs;password=zhjsniubi;database=qdb;ServerCompatibilityMode=NoTypeLoading;"
|
||||
},
|
||||
"Redis": {
|
||||
"Configuration": "192.168.2.7:6379,password=qwe123!@#,defaultDatabase=14,idleTimeout=3000,poolsize=5,prefix=marking"
|
||||
},
|
||||
"StringEncryption": {
|
||||
"DefaultPassPhrase": "LB6Ts3T0sdE5VSNq"
|
||||
},
|
||||
"RabbitMQ": {
|
||||
"Connections": {
|
||||
"Default": {
|
||||
"HostName": "192.168.2.7",
|
||||
"Port": "5672",
|
||||
"UserName": "rabbit",
|
||||
"Password": "qwe123!@#"
|
||||
}
|
||||
},
|
||||
"EventBus": {
|
||||
"ClientName": "collect_queue",
|
||||
"ExchangeName": "tenant_ex"
|
||||
}
|
||||
},
|
||||
"Aliyun": {
|
||||
"AccessKeyId": "LTAI5tJ6stiMWGhVU3TtRyAf",
|
||||
"AccessKeySecret": "A3pwnGx2SW1orvraCkXta6Lx4sV06e",
|
||||
"Endpoint": "https://oss-cn-chengdu.aliyuncs.com",
|
||||
"RegionId": "oss-cn-chengdu",
|
||||
"ContainerName": "mk-xk",
|
||||
"CustomHostUrl": "//mk-xk.23544.com"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue