From fae8a3a6f4da8116b6194cdcd7a089bdcff00ad4 Mon Sep 17 00:00:00 2001 From: liguoyang Date: Tue, 18 Feb 2025 16:10:38 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat=20=E5=B7=A5=E4=BD=9C=E7=BD=91=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Src/Dlhis.Basic/Organization/Dto/GainfoDto.cs | 2 - .../Controllers/WorkGridsController.cs | 154 ++++++++++++++++++ .../Request/WorkGridChangeRequest.cs | 12 ++ .../WorkGrids/Request/WorkGridPageRequest.cs | 8 + .../WorkGrids/Request/WorkGridSaveRequest.cs | 22 +++ .../Request/WorkGridScheduleSaveRequest.cs | 14 ++ .../Request/WorkGridTimePeriodRequest.cs | 7 + .../Request/WorkGridTimePeriodSaveRequest.cs | 36 ++++ .../Request/WorkGridUserAssignRequest.cs | 14 ++ .../Request/WorkGridUserRemoveRequest.cs | 14 ++ .../Response/WorkGridPageResponse.cs | 31 ++++ .../Response/WorkGridScheduleResponse.cs | 12 ++ .../WorkGridScheduleSummaryResponse.cs | 16 ++ .../WorkGridTimePeriodPageResponse.cs | 72 ++++++++ .../Response/WorkGridUserPageResponse.cs | 57 +++++++ .../Services/IWorkGridScheduleService.cs | 16 ++ .../WorkGrids/Services/IWorkGridService.cs | 52 ++++++ .../Services/IWorkGridTimePeriodService.cs | 25 +++ .../Implements/WorkGridScheduleService.cs | 81 +++++++++ .../Services/Implements/WorkGridService.cs | 154 ++++++++++++++++++ .../Implements/WorkGridTimePeriodService.cs | 93 +++++++++++ .../Basic/Entity/WorkGridEntity.cs | 37 +++++ .../Basic/Entity/WorkGridScheduleEntity.cs | 42 +++++ .../Basic/Entity/WorkGridTimePeriodEntity.cs | 44 +++++ .../Basic/Entity/WorkGridUserEntity.cs | 35 ++++ .../Basic/Enums/WorkGridTimePeriodEnumType.cs | 22 +++ 26 files changed, 1070 insertions(+), 2 deletions(-) create mode 100644 Src/Dlhis.Basic/WorkGrids/Controllers/WorkGridsController.cs create mode 100644 Src/Dlhis.Basic/WorkGrids/Request/WorkGridChangeRequest.cs create mode 100644 Src/Dlhis.Basic/WorkGrids/Request/WorkGridPageRequest.cs create mode 100644 Src/Dlhis.Basic/WorkGrids/Request/WorkGridSaveRequest.cs create mode 100644 Src/Dlhis.Basic/WorkGrids/Request/WorkGridScheduleSaveRequest.cs create mode 100644 Src/Dlhis.Basic/WorkGrids/Request/WorkGridTimePeriodRequest.cs create mode 100644 Src/Dlhis.Basic/WorkGrids/Request/WorkGridTimePeriodSaveRequest.cs create mode 100644 Src/Dlhis.Basic/WorkGrids/Request/WorkGridUserAssignRequest.cs create mode 100644 Src/Dlhis.Basic/WorkGrids/Request/WorkGridUserRemoveRequest.cs create mode 100644 Src/Dlhis.Basic/WorkGrids/Response/WorkGridPageResponse.cs create mode 100644 Src/Dlhis.Basic/WorkGrids/Response/WorkGridScheduleResponse.cs create mode 100644 Src/Dlhis.Basic/WorkGrids/Response/WorkGridScheduleSummaryResponse.cs create mode 100644 Src/Dlhis.Basic/WorkGrids/Response/WorkGridTimePeriodPageResponse.cs create mode 100644 Src/Dlhis.Basic/WorkGrids/Response/WorkGridUserPageResponse.cs create mode 100644 Src/Dlhis.Basic/WorkGrids/Services/IWorkGridScheduleService.cs create mode 100644 Src/Dlhis.Basic/WorkGrids/Services/IWorkGridService.cs create mode 100644 Src/Dlhis.Basic/WorkGrids/Services/IWorkGridTimePeriodService.cs create mode 100644 Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridScheduleService.cs create mode 100644 Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridService.cs create mode 100644 Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridTimePeriodService.cs create mode 100644 Src/Dlhis.Entity/Basic/Entity/WorkGridEntity.cs create mode 100644 Src/Dlhis.Entity/Basic/Entity/WorkGridScheduleEntity.cs create mode 100644 Src/Dlhis.Entity/Basic/Entity/WorkGridTimePeriodEntity.cs create mode 100644 Src/Dlhis.Entity/Basic/Entity/WorkGridUserEntity.cs create mode 100644 Src/Dlhis.Entity/Basic/Enums/WorkGridTimePeriodEnumType.cs diff --git a/Src/Dlhis.Basic/Organization/Dto/GainfoDto.cs b/Src/Dlhis.Basic/Organization/Dto/GainfoDto.cs index 0b378beb..0cf86880 100644 --- a/Src/Dlhis.Basic/Organization/Dto/GainfoDto.cs +++ b/Src/Dlhis.Basic/Organization/Dto/GainfoDto.cs @@ -21,8 +21,6 @@ public class GainfoDto public class AnalyzePackDto { - - public string UserNo { get; set; } public string UserDepartment { get; set; } diff --git a/Src/Dlhis.Basic/WorkGrids/Controllers/WorkGridsController.cs b/Src/Dlhis.Basic/WorkGrids/Controllers/WorkGridsController.cs new file mode 100644 index 00000000..4549aca8 --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Controllers/WorkGridsController.cs @@ -0,0 +1,154 @@ +using Dlhis.Basic.WorkGrids.Request; +using Dlhis.Basic.WorkGrids.Response; +using Dlhis.Basic.WorkGrids.Services; +using Kurisu.AspNetCore.DataAccess.Dto; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Aop; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Dlhis.Basic.WorkGrids.Controllers; + +/// +/// 工作网格 +/// +[Authorize] +[Route("api/[controller]")] +[ApiController] +public class WorkGridsController : ControllerBase +{ + private readonly IWorkGridService _workGridService; + private readonly IWorkGridTimePeriodService _workGridTimePeriodService; + private readonly IWorkGridScheduleService _workGridScheduleService; + + public WorkGridsController(IWorkGridService workGridService, + IWorkGridTimePeriodService workGridTimePeriodService, + IWorkGridScheduleService workGridScheduleService) + { + _workGridService = workGridService; + _workGridTimePeriodService = workGridTimePeriodService; + _workGridScheduleService = workGridScheduleService; + } + + /// + /// 保存 + /// + /// + /// + [HttpPost("save")] + [Transactional] + public async Task SaveWorkGridsAsync(List requests) + { + await _workGridService.SaveAsync(requests); + } + + /// + /// /网格分页 + /// + /// + /// + [HttpGet("page")] + public async Task> GetWorkGridPageAsync([FromQuery] WorkGridPageRequest request) + { + return await _workGridService.GetPageAsync(request); + } + + /// + /// 修改网格管理者 + /// + /// + [HttpPost("leader/change")] + public async Task ChangeWorkGridLeaderAsync(WorkGridChangeRequest request) + { + await _workGridService.ChangeAsync(request.From, request.To); + } + + /// + /// 获取我的网格信息 + /// + /// + /// + [HttpGet("info")] + public async Task GetMyWorkGridAsync([FromQuery] WorkGridPageRequest request) + { + return await _workGridService.GetMyWorkGridAsync(request); + } + + /// + /// 保存网格工作时间 + /// + /// + /// + [HttpPost("timePeriod/save")] + public async Task SaveWorkGridTimePeriodAsync(WorkGridTimePeriodSaveRequest request) + { + await _workGridTimePeriodService.SaveAsync(request); + } + + /// + /// /获取网格工作时间分页 + /// + /// + /// + [HttpGet("timePeriod/page")] + public async Task> GetWorkGridTimePeriodPageAsync([FromQuery] WorkGridTimePeriodPageRequest request) + { + return await _workGridTimePeriodService.GetPageAsync(request); + } + + /// + /// 分配人员到网格中 + /// + /// + /// + [HttpPost("user/assign")] + public async Task AssignUsersToWorkGridAsync(WorkGridUserAssignRequest request) + { + await _workGridService.AssignAsync(request); + } + + /// + /// 移除网格的人员 + /// + /// + /// + [HttpPost("user/remove")] + public async Task RemoveUsersFromWorkGridAsync(WorkGridUserRemoveRequest request) + { + await _workGridService.RemoveAsync(request); + } + + /// + /// 保存网格人员排班 + /// + /// + [HttpPost("user/schedule/save")] + public async Task SaveScheduleAsync(WorkGridScheduleSaveRequest request) + { + await _workGridScheduleService.SaveAsync(request); + } + + /// + /// 获取人网格员排班列表 + /// + /// 网格code + /// + /// + [HttpGet("user/schedule/list")] + public async Task> GetListAsync(Guid workGridCode, DateOnly date) + { + return await _workGridScheduleService.GetListAsync(workGridCode, date); + } + + /// + /// 获取网格人员排班详情 + /// + /// 网格 + /// 开始日期 + /// 结束日期 + /// + [HttpGet("user/schedule/summary")] + public async Task> GetListSummaryAsync([FromQuery] Guid workGridCode, [FromQuery] DateOnly start, [FromQuery] DateOnly end) + { + return await _workGridScheduleService.GetListSummaryAsync(workGridCode, start, end); + } +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Request/WorkGridChangeRequest.cs b/Src/Dlhis.Basic/WorkGrids/Request/WorkGridChangeRequest.cs new file mode 100644 index 00000000..568cd2f1 --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Request/WorkGridChangeRequest.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations; + +namespace Dlhis.Basic.WorkGrids.Request; + +public class WorkGridChangeRequest +{ + [Required] + public string From { get; set; } + + [Required] + public string To { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Request/WorkGridPageRequest.cs b/Src/Dlhis.Basic/WorkGrids/Request/WorkGridPageRequest.cs new file mode 100644 index 00000000..936ee923 --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Request/WorkGridPageRequest.cs @@ -0,0 +1,8 @@ +using Kurisu.AspNetCore.DataAccess.Dto; + +namespace Dlhis.Basic.WorkGrids.Request; + +public class WorkGridPageRequest : PageDto +{ + +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Request/WorkGridSaveRequest.cs b/Src/Dlhis.Basic/WorkGrids/Request/WorkGridSaveRequest.cs new file mode 100644 index 00000000..8b5f8029 --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Request/WorkGridSaveRequest.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations; + +namespace Dlhis.Basic.WorkGrids.Request; + +public class WorkGridSaveRequest +{ + public long? Id { get; set; } + + [Required(ErrorMessage = "名称不可为空")] + public string Name { get; set; } + + /// + /// 负责人 + /// + [Required(ErrorMessage = "负责人不可为空")] + public string LeaderUserCode { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Request/WorkGridScheduleSaveRequest.cs b/Src/Dlhis.Basic/WorkGrids/Request/WorkGridScheduleSaveRequest.cs new file mode 100644 index 00000000..dcede077 --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Request/WorkGridScheduleSaveRequest.cs @@ -0,0 +1,14 @@ +using Dlhis.Entity.Basic.Enums; + +namespace Dlhis.Basic.WorkGrids.Request; + +public class WorkGridScheduleSaveRequest +{ + public Guid WorkGridCode { get; set; } + + public DateOnly Date { get; set; } + + public WorkGridTimePeriodEnumType PeriodType { get; set; } + + public List UserCodes { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Request/WorkGridTimePeriodRequest.cs b/Src/Dlhis.Basic/WorkGrids/Request/WorkGridTimePeriodRequest.cs new file mode 100644 index 00000000..0103492f --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Request/WorkGridTimePeriodRequest.cs @@ -0,0 +1,7 @@ +using Kurisu.AspNetCore.DataAccess.Dto; + +namespace Dlhis.Basic.WorkGrids.Request; + +public class WorkGridTimePeriodPageRequest : PageDto +{ +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Request/WorkGridTimePeriodSaveRequest.cs b/Src/Dlhis.Basic/WorkGrids/Request/WorkGridTimePeriodSaveRequest.cs new file mode 100644 index 00000000..85bcc07a --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Request/WorkGridTimePeriodSaveRequest.cs @@ -0,0 +1,36 @@ +using System.ComponentModel.DataAnnotations; +using Dlhis.Entity.Basic.Enums; + +namespace Dlhis.Basic.WorkGrids.Request; + +public class WorkGridTimePeriodSaveRequest +{ + public long? Id { get; set; } + + /// + /// 班次, eg: 禁止编辑 + /// + public WorkGridTimePeriodEnumType Type { get; set; } + + /// + /// 前(min) + /// + [Range(1, int.MaxValue)] + public int Before { get; set; } + + /// + /// 后(min) + /// + [Range(1, int.MaxValue)] + public int After { get; set; } + + /// + /// 开始时间 + /// + public TimeOnly Start { get; set; } + + /// + /// 结束时间 + /// + public TimeOnly End { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Request/WorkGridUserAssignRequest.cs b/Src/Dlhis.Basic/WorkGrids/Request/WorkGridUserAssignRequest.cs new file mode 100644 index 00000000..c13e6e7c --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Request/WorkGridUserAssignRequest.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; + +namespace Dlhis.Basic.WorkGrids.Request; + +public class WorkGridUserAssignRequest +{ + public Guid? WorkGridCode { get; set; } + + /// + /// 人员 + /// + [MinLength(1, ErrorMessage = "请选择人员")] + public List UserCodes { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Request/WorkGridUserRemoveRequest.cs b/Src/Dlhis.Basic/WorkGrids/Request/WorkGridUserRemoveRequest.cs new file mode 100644 index 00000000..e5358bc5 --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Request/WorkGridUserRemoveRequest.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; + +namespace Dlhis.Basic.WorkGrids.Request; + +public class WorkGridUserRemoveRequest +{ + public Guid? WorkGridCode { get; set; } + + /// + /// 人员 + /// + [MinLength(1, ErrorMessage = "请选择人员")] + public List UserCodes { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Response/WorkGridPageResponse.cs b/Src/Dlhis.Basic/WorkGrids/Response/WorkGridPageResponse.cs new file mode 100644 index 00000000..3494011a --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Response/WorkGridPageResponse.cs @@ -0,0 +1,31 @@ +namespace Dlhis.Basic.WorkGrids.Response; + +public class WorkGridPageResponse +{ + public long Id { get; set; } + + /// + /// 网格code + /// + public Guid Code { get; set; } + + /// + /// 网格名称 + /// + public string Name { get; set; } + + /// + /// 负责人工号 + /// + public string LeaderUserCode { get; set; } + + /// + /// 负责人姓名 + /// + public string UserName { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Response/WorkGridScheduleResponse.cs b/Src/Dlhis.Basic/WorkGrids/Response/WorkGridScheduleResponse.cs new file mode 100644 index 00000000..eddfad3e --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Response/WorkGridScheduleResponse.cs @@ -0,0 +1,12 @@ +using Dlhis.Entity.Basic.Enums; + +namespace Dlhis.Basic.WorkGrids.Response; + +public class WorkGridScheduleResponse +{ + public DateOnly Date { get; set; } + + public WorkGridTimePeriodEnumType PeriodType { get; set; } + + public string UserCode { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Response/WorkGridScheduleSummaryResponse.cs b/Src/Dlhis.Basic/WorkGrids/Response/WorkGridScheduleSummaryResponse.cs new file mode 100644 index 00000000..b55d9606 --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Response/WorkGridScheduleSummaryResponse.cs @@ -0,0 +1,16 @@ +using Dlhis.Entity.Basic.Enums; + +namespace Dlhis.Basic.WorkGrids.Response; + +public class WorkGridScheduleSummaryResponse +{ + public DateOnly Date { get; set; } + public IEnumerable Details { get; set; } +} + +public class WorkGridScheduleSummaryDetailResponse +{ + public WorkGridTimePeriodEnumType PeriodType { get; set; } + + public int Count { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Response/WorkGridTimePeriodPageResponse.cs b/Src/Dlhis.Basic/WorkGrids/Response/WorkGridTimePeriodPageResponse.cs new file mode 100644 index 00000000..03c23873 --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Response/WorkGridTimePeriodPageResponse.cs @@ -0,0 +1,72 @@ +using Dlhis.Entity.Basic.Enums; +using Kurisu.AspNetCore.Utils.Extensions; + +namespace Dlhis.Basic.WorkGrids.Response; + +public class WorkGridTimePeriodPageResponse +{ + public long Id { get; set; } + + /// + /// 班次 + /// + public WorkGridTimePeriodEnumType Type { get; set; } + + /// + /// 班次描述 + /// + public string TypeDesc => Type.GetDescription(); + + /// + /// 前 + /// + public int Before { get; set; } + + /// + /// 后 + /// + public int After { get; set; } + + /// + /// 开始时间 + /// + public TimeOnly Start { get; set; } + + /// + /// 结束时间 + /// + public TimeOnly End { get; set; } + + /// + /// 配置描述 + /// + public string Description + { + get { return Type.GetDescription() + "打卡时间从" + Start + "到" + End + "\r\n" + "上班允许提前" + GetTimeDescription(Before) + "\r\n" + "下班允许延迟" + GetTimeDescription(After); } + } + + + private string GetTimeDescription(int minutes) + { + string d; + + if (minutes >= 60) + { + var remaining = minutes % 60; + + d = $"{minutes / 60}小时"; + + if (remaining > 0) + { + d += $"{remaining}分钟"; + } + } + else + { + d = $"{minutes}分钟"; + } + + + return d; + } +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Response/WorkGridUserPageResponse.cs b/Src/Dlhis.Basic/WorkGrids/Response/WorkGridUserPageResponse.cs new file mode 100644 index 00000000..bd601ea0 --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Response/WorkGridUserPageResponse.cs @@ -0,0 +1,57 @@ +namespace Dlhis.Basic.WorkGrids.Response; + +public class WorkGridUserPageResponse +{ + public long Id { get; set; } + + /// + /// 网格code + /// + public Guid Code { get; set; } + + /// + /// 网格名称 + /// + public string Name { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + + /// + /// 员工明细 + /// + public List UserInfos { get; set; } +} + +public class WorkGridUserPageDetailResponse +{ + /// + /// 人员工号 + /// + public string Code { get; set; } + + /// + /// 人员姓名 + /// + public string Name { get; set; } + + // /// + // /// 排班值 + // /// + // public WorkGridTimePeriodEnumType? ScheduleType { get; set; } + // + // /// + // /// 排班描述 + // /// + // public string ScheduleTypeDesc + // { + // get + // { + // return !ScheduleType.HasValue + // ? "未排班" + // : ScheduleType.Value.GetDescription(); + // } + // } +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Services/IWorkGridScheduleService.cs b/Src/Dlhis.Basic/WorkGrids/Services/IWorkGridScheduleService.cs new file mode 100644 index 00000000..2b6783d9 --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Services/IWorkGridScheduleService.cs @@ -0,0 +1,16 @@ +using Dlhis.Basic.WorkGrids.Request; +using Dlhis.Basic.WorkGrids.Response; + +namespace Dlhis.Basic.WorkGrids.Services; + +/// +/// 网格排班 +/// +public interface IWorkGridScheduleService : IScopeDependency +{ + Task SaveAsync(WorkGridScheduleSaveRequest request); + + Task> GetListAsync(Guid workGridCode, DateOnly date); + + Task> GetListSummaryAsync(Guid workGridCode, DateOnly start, DateOnly end); +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Services/IWorkGridService.cs b/Src/Dlhis.Basic/WorkGrids/Services/IWorkGridService.cs new file mode 100644 index 00000000..0270987f --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Services/IWorkGridService.cs @@ -0,0 +1,52 @@ +using Dlhis.Basic.WorkGrids.Request; +using Dlhis.Basic.WorkGrids.Response; +using Kurisu.AspNetCore.DataAccess.Dto; + +namespace Dlhis.Basic.WorkGrids.Services; + +/// +/// 工作网格 +/// +public interface IWorkGridService : IScopeDependency +{ + /// + /// 保存 + /// + /// + /// + Task SaveAsync(List requests); + + /// + /// /网格分页 + /// + /// + /// + Task> GetPageAsync(WorkGridPageRequest request); + + /// + /// 修改管理者 + /// + /// + Task ChangeAsync(string from, string to); + + /// + /// 获取 + /// + /// + /// + Task GetMyWorkGridAsync(WorkGridPageRequest request); + + /// + /// 分配人员 + /// + /// + /// + Task AssignAsync(WorkGridUserAssignRequest request); + + /// + /// 移除人员 + /// + /// + /// + Task RemoveAsync(WorkGridUserRemoveRequest request); +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Services/IWorkGridTimePeriodService.cs b/Src/Dlhis.Basic/WorkGrids/Services/IWorkGridTimePeriodService.cs new file mode 100644 index 00000000..f2f848da --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Services/IWorkGridTimePeriodService.cs @@ -0,0 +1,25 @@ +using Dlhis.Basic.WorkGrids.Request; +using Dlhis.Basic.WorkGrids.Response; +using Kurisu.AspNetCore.DataAccess.Dto; + +namespace Dlhis.Basic.WorkGrids.Services; + +/// +/// 工作网格班次时段配置 +/// +public interface IWorkGridTimePeriodService : IScopeDependency +{ + /// + /// 保存 + /// + /// + /// + Task SaveAsync(WorkGridTimePeriodSaveRequest request); + + /// + /// 获取分页 + /// + /// + /// + Task> GetPageAsync(WorkGridTimePeriodPageRequest request); +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridScheduleService.cs b/Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridScheduleService.cs new file mode 100644 index 00000000..1e2f25c7 --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridScheduleService.cs @@ -0,0 +1,81 @@ +using Dlhis.Basic.WorkGrids.Request; +using Dlhis.Basic.WorkGrids.Response; +using Dlhis.Entity.Basic.Entity; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; +using Kurisu.AspNetCore.Utils.Extensions; + +namespace Dlhis.Basic.WorkGrids.Services.Implements; + +public class WorkGridScheduleService : IWorkGridScheduleService +{ + private readonly IDbContext _dbContext; + + public WorkGridScheduleService(IDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task SaveAsync(WorkGridScheduleSaveRequest request) + { + var exists = await _dbContext.Queryable() + .Where(x => x.Date == request.Date) + .Where(x => x.PeriodType != request.PeriodType) + .Where(x => request.UserCodes.Contains(x.UserCode)) + .AnyAsync(); + + exists.ThrowIfTrue($"存在已经分配的人员"); + + var data = await _dbContext.Queryable() + .Where(x => x.WorkGridCode == request.WorkGridCode) + .Where(x => x.Date == request.Date) + .Where(x => x.PeriodType == request.PeriodType) + .ToListAsync(); + + await _dbContext.DeleteReallyAsync(data); + + var toInserts = request.UserCodes.Select(x => new WorkGridScheduleEntity + { + WorkGridCode = request.WorkGridCode, + Date = request.Date, + PeriodType = request.PeriodType, + UserCode = x + }).ToList(); + + await _dbContext.InsertAsync(toInserts); + } + + /// + public async Task> GetListAsync(Guid workGridCode, DateOnly date) + { + return await _dbContext.Queryable() + .Where(x => x.WorkGridCode == workGridCode && x.Date == date) + .Select(x => new WorkGridScheduleResponse + { + Date = x.Date, + PeriodType = x.PeriodType, + UserCode = x.UserCode + }) + .ToListAsync(); + } + + /// + public async Task> GetListSummaryAsync(Guid workGridCode, DateOnly start, DateOnly end) + { + var workGridSchedules = await _dbContext.Queryable() + .Where(x => x.WorkGridCode == workGridCode && x.Date >= start && x.Date <= end) + .ToListAsync(); + + return workGridSchedules.GroupBy(x => x.Date).Select(x => + { + return new WorkGridScheduleSummaryResponse + { + Date = x.Key, + Details = x.GroupBy(p => p.PeriodType).Select(p => new WorkGridScheduleSummaryDetailResponse + { + PeriodType = p.Key, + Count = p.Count() + }) + }; + }); + } +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridService.cs b/Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridService.cs new file mode 100644 index 00000000..f087dd94 --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridService.cs @@ -0,0 +1,154 @@ +using Dlhis.Basic.WorkGrids.Request; +using Dlhis.Basic.WorkGrids.Response; +using Dlhis.Entity.Basic.Entity; +using Kurisu.AspNetCore.Authentication.Abstractions; +using Kurisu.AspNetCore.DataAccess.Dto; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Extensions; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; +using Kurisu.AspNetCore.Utils.Extensions; +using Mapster; +using SqlSugar; + +namespace Dlhis.Basic.WorkGrids.Services.Implements; + +public class WorkGridService : IWorkGridService +{ + private readonly IDbContext _dbContext; + private readonly ICurrentUser _currentUser; + + public WorkGridService(IDbContext dbContext, ICurrentUser currentUser) + { + _dbContext = dbContext; + _currentUser = currentUser; + } + + /// + public async Task SaveAsync(List requests) + { + foreach (var request in requests) + { + var exists = await _dbContext.Queryable() + .Where(x => x.LeaderUserCode == request.LeaderUserCode) + .WhereIF(request.Id.HasValue, x => x.Id != request.Id) + .AnyAsync(); + + exists.ThrowIfTrue($"{request.LeaderUserCode}已存在管理的网格"); + + if (!request.Id.HasValue) + { + var entity = request.Adapt(); + entity.Code = Guid.NewGuid(); + + await _dbContext.InsertAsync(entity); + } + else + { + var entity = await _dbContext.Queryable().Where(x => x.Id == request.Id).SingleAsync(); + request.Adapt(entity); + + await _dbContext.UpdateAsync(entity); + } + } + } + + /// + public async Task> GetPageAsync(WorkGridPageRequest request) + { + return await _dbContext.Queryable() + .LeftJoin((x, ga) => x.LeaderUserCode == ga.Code) + .Select((x, ga) => new WorkGridPageResponse + { + Id = x.Id, + Code = x.Code, + Name = x.Name, + LeaderUserCode = x.LeaderUserCode, + UserName = ga.Name, + Remark = x.Remark + }) + .ToPageListAsync(request); + } + + /// + public async Task ChangeAsync(string from, string to) + { + var workGrid = await _dbContext.Queryable().Where(x => x.LeaderUserCode == from).SingleAsync(); + workGrid.ThrowIfNull("网格不存在"); + + var gainfo = await _dbContext.Queryable().Where(x => x.Code == to).SingleAsync(); + gainfo.ThrowIfNull($"{to}人员不存在"); + + workGrid.LeaderUserCode = to; + await _dbContext.UpdateAsync(workGrid); + } + + /// + public async Task GetMyWorkGridAsync(WorkGridPageRequest request) + { + var currentUserCode = _currentUser.GetUserClaim("jinko-code"); + var myWorkGrid = await _dbContext.Queryable() + .Where(x => x.LeaderUserCode == currentUserCode) + .Select(x => new WorkGridUserPageResponse + { + Id = x.Id, + Code = x.Code, + Name = x.Name, + Remark = x.Remark, + UserInfos = SqlFunc.Subqueryable() + .LeftJoin((p, ga) => p.UserCode == ga.Code) + .Where((p, ga) => p.WorkGridCode == x.Code) + .ToList((p, ga) => new WorkGridUserPageDetailResponse + { + Code = p.UserCode, + Name = ga.Name + }) + }) + .SingleAsync(); + + return myWorkGrid; + } + + /// + public async Task AssignAsync(WorkGridUserAssignRequest request) + { + var currentFactory = _currentUser.GetStringTenantId(); + var currentUserCode = _currentUser.GetUserClaim("jinko-code"); + + var myWorkGrid = await _dbContext.Queryable().Where(x => x.LeaderUserCode == currentUserCode).SingleAsync(); + myWorkGrid.ThrowIfNull("工作网格尚未配置,请联系管理员"); + + //检查配置的人员 + var count = await _dbContext.Queryable() + .Where(x => x.Factory == currentFactory) + .Where(x => request.UserCodes.Contains(x.Code)) + .CountAsync(); + + (count != request.UserCodes.Count).ThrowIfTrue("存在非当前厂区人员,请检查"); + + var existsAssigned = await _dbContext.Queryable().Where(x => x.WorkGridCode == myWorkGrid.Code) + .Where(x => request.UserCodes.Contains(x.UserCode)) + .AnyAsync(); + existsAssigned.ThrowIfTrue("存在已经分配人员,请勿重复分配"); + + var dataToInserts = request.UserCodes.Select(x => new WorkGridUserEntity + { + WorkGridCode = myWorkGrid.Code, + UserCode = x + }).ToList(); + + await _dbContext.InsertAsync(dataToInserts); + } + + /// + public async Task RemoveAsync(WorkGridUserRemoveRequest request) + { + var currentUserCode = _currentUser.GetUserClaim("jinko-code"); + var myWorkGrid = await _dbContext.Queryable().Where(x => x.LeaderUserCode == currentUserCode).SingleAsync(); + myWorkGrid.ThrowIfNull("工作网格尚未配置,请联系管理员"); + + var workGridUsers = await _dbContext.Queryable().Where(x => x.WorkGridCode == myWorkGrid.Code) + .Where(x => request.UserCodes.Contains(x.UserCode)) + .ToListAsync(); + + await _dbContext.DeleteReallyAsync(workGridUsers); + } +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridTimePeriodService.cs b/Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridTimePeriodService.cs new file mode 100644 index 00000000..b07b4396 --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridTimePeriodService.cs @@ -0,0 +1,93 @@ +using Dlhis.Basic.WorkGrids.Request; +using Dlhis.Basic.WorkGrids.Response; +using Dlhis.Entity.Basic.Entity; +using Dlhis.Entity.Basic.Enums; +using Kurisu.AspNetCore.CustomClass; +using Kurisu.AspNetCore.DataAccess.Dto; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Extensions; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; +using Kurisu.AspNetCore.Utils.Extensions; +using Mapster; + +namespace Dlhis.Basic.WorkGrids.Services.Implements; + +public class WorkGridTimePeriodService : IWorkGridTimePeriodService +{ + private readonly IDbContext _dbContext; + + public WorkGridTimePeriodService(IDbContext dbContext) + { + _dbContext = dbContext; + } + + /// + public async Task SaveAsync(WorkGridTimePeriodSaveRequest request) + { + //await CheckDuplicateTimePeriodAsync(request); + + if (!request.Id.HasValue) + { + //判断重复配置 + var existsSameType = await _dbContext.Queryable() + .Where(x => x.Type == request.Type) + .AnyAsync(); + + existsSameType.ThrowIfTrue($"已存在{request.Type}配置"); + + var data = request.Adapt(); + data.Type = request.Type; + await _dbContext.InsertAsync(data); + } + else + { + var entity = await _dbContext.Queryable().Where(x => x.Id == request.Id).SingleAsync(); + request.Adapt(entity); + + await _dbContext.UpdateAsync(entity); + } + } + + /// + public async Task> GetPageAsync(WorkGridTimePeriodPageRequest request) + { + return await _dbContext.Queryable() + .Select() + .ToPageListAsync(request); + } + + //************************************************************************************************************************************************* + + /// + /// 根据类型获取配置 + /// + /// + /// + private async Task GetSettingAsync(WorkGridTimePeriodEnumType timePeriodType) + { + return await _dbContext.Queryable() + .Where(x => x.Type == timePeriodType) + .SingleAsync(); + } + + /// + /// 检查时间段重合检查 + /// + /// + /// + private async Task CheckDuplicateTimePeriodAsync(WorkGridTimePeriodSaveRequest request) + { + var periods = await _dbContext.Queryable() + .Where(x => x.Type != request.Type) + .Select(x => new TimePeriod + { + Start = x.Start, + End = x.End + }) + .ToListAsync(); + + foreach (var period in periods) + { + period.IsDuplicate(request.Adapt()).ThrowIfTrue($"存在时间段重合[{period.Start}-{period.End}]"); + } + } +} \ No newline at end of file diff --git a/Src/Dlhis.Entity/Basic/Entity/WorkGridEntity.cs b/Src/Dlhis.Entity/Basic/Entity/WorkGridEntity.cs new file mode 100644 index 00000000..f616edcf --- /dev/null +++ b/Src/Dlhis.Entity/Basic/Entity/WorkGridEntity.cs @@ -0,0 +1,37 @@ +using Kurisu.AspNetCore.DataAccess.SqlSugar; +using Mapster; +using SqlSugar; + +namespace Dlhis.Entity.Basic.Entity; + +/// +/// 工作网格 +/// +[SugarTable(TableName = "WorkGrid")] +public class WorkGridEntity : SugarBaseEntity +{ + /// + /// 网格code + /// + [AdaptIgnore(MemberSide.Destination)] + [SugarColumn(IsNullable = false, ColumnDescription = "网格code")] + public Guid Code { get; set; } + + /// + /// 网格名称 + /// + [SugarColumn(IsNullable = false, ColumnDescription = "网格名称")] + public string Name { get; set; } + + /// + /// 负责人工号 + /// + [SugarColumn(IsNullable = false, ColumnDescription = "负责人工号", UniqueGroupNameList = new[] { "idx_WorkGrid_LeaderUserCode" })] + public string LeaderUserCode { get; set; } + + /// + /// 备注 + /// + [SugarColumn(IsNullable = true, ColumnDescription = "备注")] + public string Remark { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Entity/Basic/Entity/WorkGridScheduleEntity.cs b/Src/Dlhis.Entity/Basic/Entity/WorkGridScheduleEntity.cs new file mode 100644 index 00000000..22e58f7d --- /dev/null +++ b/Src/Dlhis.Entity/Basic/Entity/WorkGridScheduleEntity.cs @@ -0,0 +1,42 @@ +using Dlhis.Entity.Basic.Enums; +using Kurisu.AspNetCore.DataAccess.Entity; +using Kurisu.AspNetCore.DataAccess.SqlSugar; +using SqlSugar; +using SqlSugar.DbConvert; + +namespace Dlhis.Entity.Basic.Entity; + +/// +/// +/// +[SugarTable(TableName = "WorkGridSchedule")] +public class WorkGridScheduleEntity : SugarBaseEntity, ITenantId +{ + /// + /// 网格code + /// + [SugarColumn(IsNullable = false, ColumnDescription = "网格code")] + public Guid WorkGridCode { get; set; } + + /// + /// 排班日期 + /// + [SugarColumn(IsNullable = false, ColumnDescription = "排班日期")] + public DateOnly Date { get; set; } + + /// + /// 排班类型 + /// + [SugarColumn(IsNullable = false, ColumnDescription = "排班类型", SqlParameterDbType = typeof(EnumToStringConvert))] + public WorkGridTimePeriodEnumType PeriodType { get; set; } + + /// + /// 工号 + /// + [SugarColumn(IsNullable = false, ColumnDescription = "工号")] + public string UserCode { get; set; } + + + /// + public string TenantId { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Entity/Basic/Entity/WorkGridTimePeriodEntity.cs b/Src/Dlhis.Entity/Basic/Entity/WorkGridTimePeriodEntity.cs new file mode 100644 index 00000000..1d842130 --- /dev/null +++ b/Src/Dlhis.Entity/Basic/Entity/WorkGridTimePeriodEntity.cs @@ -0,0 +1,44 @@ +using Dlhis.Entity.Basic.Enums; +using Kurisu.AspNetCore.DataAccess.Entity; +using Kurisu.AspNetCore.DataAccess.SqlSugar; +using Mapster; +using SqlSugar; + +namespace Dlhis.Entity.Basic.Entity; + +/// +/// 工作网格 +/// +[SugarTable(TableName = "WorkGridTimePeriod")] +public class WorkGridTimePeriodEntity : SugarBaseEntity, ITenantId +{ + /// + /// 班次 + /// + [AdaptIgnore(MemberSide.Destination)] + public WorkGridTimePeriodEnumType Type { get; set; } + + /// + /// 前 + /// + public int Before { get; set; } + + /// + /// 后 + /// + public int After { get; set; } + + /// + /// 开始时间 + /// + public TimeOnly Start { get; set; } + + /// + /// 结束时间 + /// + public TimeOnly End { get; set; } + + + [SugarColumn(UniqueGroupNameList = new[] { "idx_workGridTimePeriod_type_tenantId" })] + public string TenantId { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Entity/Basic/Entity/WorkGridUserEntity.cs b/Src/Dlhis.Entity/Basic/Entity/WorkGridUserEntity.cs new file mode 100644 index 00000000..7685a578 --- /dev/null +++ b/Src/Dlhis.Entity/Basic/Entity/WorkGridUserEntity.cs @@ -0,0 +1,35 @@ +using Dlhis.Entity.Basic.Enums; +using Kurisu.AspNetCore.DataAccess.Entity; +using Kurisu.AspNetCore.DataAccess.SqlSugar; +using SqlSugar; +using SqlSugar.DbConvert; + +namespace Dlhis.Entity.Basic.Entity; + +/// +/// 网格人员 +/// +[SugarTable(TableName = "WorkGridUsers")] +public class WorkGridUserEntity : SugarBaseEntity, ITenantId +{ + /// + /// 网格code + /// + [SugarColumn(IsNullable = false, ColumnDescription = "网格code")] + public Guid WorkGridCode { get; set; } + + /// + /// 网格人员 + /// + [SugarColumn(IsNullable = false, ColumnDescription = "网格人员")] + public string UserCode { get; set; } + + /// + /// 排班类型 + /// + [SugarColumn(IsNullable = true, ColumnDescription = "排班类型", SqlParameterDbType = typeof(EnumToStringConvert))] + public WorkGridTimePeriodEnumType? ScheduleType { get; set; } + + /// + public string TenantId { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Entity/Basic/Enums/WorkGridTimePeriodEnumType.cs b/Src/Dlhis.Entity/Basic/Enums/WorkGridTimePeriodEnumType.cs new file mode 100644 index 00000000..e666372a --- /dev/null +++ b/Src/Dlhis.Entity/Basic/Enums/WorkGridTimePeriodEnumType.cs @@ -0,0 +1,22 @@ +using System.ComponentModel; + +namespace Dlhis.Entity.Basic.Enums; + +/// +/// 网格班次 +/// +[Description("网格班次")] +public enum WorkGridTimePeriodEnumType +{ + /// + /// 白班 + /// + [Description("白班")] + Day = 1, + + /// + /// 晚班 + /// + [Description("晚班")] + Night = 2, +} \ No newline at end of file From 03584778e5d21a12b9725251c179d5bd064d9125 Mon Sep 17 00:00:00 2001 From: liguoyang Date: Tue, 11 Feb 2025 16:33:21 +0800 Subject: [PATCH 2/4] feat tickets --- Dlhis.Jinko.Face.sln | 13 ++++ .../Services/Implements/DishesService.cs | 5 +- Src/Dlhis.Basic/Dlhis.Basic.csproj | 1 + .../Implements/GainfoQrCodeService.cs | 6 +- .../Services/Implements/GainfoService.cs | 3 +- .../Implements/IdentityUserSyncService.cs | 3 +- .../Dlhis.Canteen.Api.csproj | 13 ++++ Src/Dlhis.Canteen.Api/ITicketApi.cs | 16 +++++ Src/Dlhis.Canteen/Dlhis.Canteen.csproj | 1 + .../Implements/EMealVoucherService.cs | 5 +- .../Implements/MealSupplementRecordService.cs | 3 +- .../Services/Implements/BalanceService.cs | 3 +- .../Order/Services/Implements/OrderService.cs | 21 ++++-- .../Services/Implements/WalletService.cs | 3 +- .../Tickets/Controllers/TicketsController.cs | 50 ++++++++++++++ .../Tickets/Request/TicketPageRequest.cs | 19 ++++++ .../Tickets/Response/TicketPageResponse.cs | 41 ++++++++++++ .../Services/IBirthdayTicketService.cs | 6 ++ .../Tickets/Services/ITicketService.cs | 24 +++++++ .../Implements/BirthdayTicketService.cs | 65 +++++++++++++++++++ .../Services/Implements/TicketService.cs | 32 +++++++++ Src/Dlhis.Constants/Dlhis.Constants.csproj | 9 +++ Src/Dlhis.Constants/QrCodeConstants.cs | 19 ++++++ Src/Dlhis.Constants/TickConstants.cs | 6 ++ Src/Dlhis.Constants/UserClaimConstants.cs | 9 +++ .../Canteen/Entities/TicketEntity.cs | 52 +++++++++++++++ .../Canteen/Enums/TicketEnumType.cs | 16 +++++ .../Implements/DeviceUploadLogService.cs | 5 +- .../Dlhis.ScheduledTask.csproj | 3 + Src/Dlhis.ScheduledTask/Dockerfile | 13 +++- .../Jobs/CreateBirthdayTicketJob.cs | 22 +++++++ .../appsettings.Development.json | 19 +++--- Src/Dlhis.ScheduledTask/appsettings.json | 3 +- 33 files changed, 479 insertions(+), 30 deletions(-) create mode 100644 Src/Dlhis.Canteen.Api/Dlhis.Canteen.Api.csproj create mode 100644 Src/Dlhis.Canteen.Api/ITicketApi.cs create mode 100644 Src/Dlhis.Canteen/Tickets/Controllers/TicketsController.cs create mode 100644 Src/Dlhis.Canteen/Tickets/Request/TicketPageRequest.cs create mode 100644 Src/Dlhis.Canteen/Tickets/Response/TicketPageResponse.cs create mode 100644 Src/Dlhis.Canteen/Tickets/Services/IBirthdayTicketService.cs create mode 100644 Src/Dlhis.Canteen/Tickets/Services/ITicketService.cs create mode 100644 Src/Dlhis.Canteen/Tickets/Services/Implements/BirthdayTicketService.cs create mode 100644 Src/Dlhis.Canteen/Tickets/Services/Implements/TicketService.cs create mode 100644 Src/Dlhis.Constants/Dlhis.Constants.csproj create mode 100644 Src/Dlhis.Constants/QrCodeConstants.cs create mode 100644 Src/Dlhis.Constants/TickConstants.cs create mode 100644 Src/Dlhis.Constants/UserClaimConstants.cs create mode 100644 Src/Dlhis.Entity/Canteen/Entities/TicketEntity.cs create mode 100644 Src/Dlhis.Entity/Canteen/Enums/TicketEnumType.cs create mode 100644 Src/Dlhis.ScheduledTask/Jobs/CreateBirthdayTicketJob.cs diff --git a/Dlhis.Jinko.Face.sln b/Dlhis.Jinko.Face.sln index 8ab23f97..0cf2b39a 100644 --- a/Dlhis.Jinko.Face.sln +++ b/Dlhis.Jinko.Face.sln @@ -47,6 +47,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dlhis.Jip.Api", "Src\Dlhis. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dlhis.Dh.Api", "Src\Dlhis.Dh.Api\Dlhis.Dh.Api.csproj", "{E673CA04-3567-4E03-9A86-994F50291ACC}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dlhis.Constants", "Src\Dlhis.Constants\Dlhis.Constants.csproj", "{F98329D8-F8CA-4794-B1BF-8659112119F7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dlhis.Canteen.Api", "Src\Dlhis.Canteen.Api\Dlhis.Canteen.Api.csproj", "{7934BE46-9409-4D28-8B35-82A0F900DD01}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -117,6 +121,14 @@ Global {E673CA04-3567-4E03-9A86-994F50291ACC}.Debug|Any CPU.Build.0 = Debug|Any CPU {E673CA04-3567-4E03-9A86-994F50291ACC}.Release|Any CPU.ActiveCfg = Release|Any CPU {E673CA04-3567-4E03-9A86-994F50291ACC}.Release|Any CPU.Build.0 = Release|Any CPU + {F98329D8-F8CA-4794-B1BF-8659112119F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F98329D8-F8CA-4794-B1BF-8659112119F7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F98329D8-F8CA-4794-B1BF-8659112119F7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F98329D8-F8CA-4794-B1BF-8659112119F7}.Release|Any CPU.Build.0 = Release|Any CPU + {7934BE46-9409-4D28-8B35-82A0F900DD01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7934BE46-9409-4D28-8B35-82A0F900DD01}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7934BE46-9409-4D28-8B35-82A0F900DD01}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7934BE46-9409-4D28-8B35-82A0F900DD01}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -134,6 +146,7 @@ Global {A501D16E-638F-4156-989C-2BACCC2FAC2B} = {4EBCE46B-CE3A-4A47-9432-82A1854CA58A} {841B7B43-5394-413F-9D68-A07EBDD6D4CD} = {4EBCE46B-CE3A-4A47-9432-82A1854CA58A} {E673CA04-3567-4E03-9A86-994F50291ACC} = {4EBCE46B-CE3A-4A47-9432-82A1854CA58A} + {7934BE46-9409-4D28-8B35-82A0F900DD01} = {3D8A98C1-83C9-4375-AEAB-4F72AFAF365B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7439494A-E2FD-4FEA-8ABF-74AE57A4A5A0} diff --git a/Src/Dlhis.Basic/Dishes/Services/Implements/DishesService.cs b/Src/Dlhis.Basic/Dishes/Services/Implements/DishesService.cs index 17d54967..4e738197 100644 --- a/Src/Dlhis.Basic/Dishes/Services/Implements/DishesService.cs +++ b/Src/Dlhis.Basic/Dishes/Services/Implements/DishesService.cs @@ -1,6 +1,7 @@ using Dlhis.Basic.Dishes.Request; using Dlhis.Basic.Dishes.Response; using Dlhis.Basic.Places.Services; +using Dlhis.Constants; using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Canteen.Entities; using Dlhis.Entity.Canteen.Enums; @@ -45,7 +46,7 @@ public class DishesService : IDishesService .ToPageListAsync(request); } - + public async Task SaveDishesAsync(DishesSaveRequest request) { if (!request.Id.HasValue) @@ -68,7 +69,7 @@ public class DishesService : IDishesService { var subjectId = _currentUser.GetIntSubjectId(); var gainfo = await _dbContext.Queryable().Where(x => x.IdentityUserId == subjectId).FirstAsync(); - var jinkoCode = _currentUser.GetUserClaim("jinko-code") ?? string.Empty; + var jinkoCode = _currentUser.GetUserClaim(UserClaimConstants.JinkoCode) ?? string.Empty; if (jinkoCode.IsPresent()) { gainfo = await _dbContext.Queryable().Where(x => x.Code == jinkoCode).FirstAsync(); diff --git a/Src/Dlhis.Basic/Dlhis.Basic.csproj b/Src/Dlhis.Basic/Dlhis.Basic.csproj index eb9f5b54..7b161cb8 100644 --- a/Src/Dlhis.Basic/Dlhis.Basic.csproj +++ b/Src/Dlhis.Basic/Dlhis.Basic.csproj @@ -26,6 +26,7 @@ + diff --git a/Src/Dlhis.Basic/Organization/Services/Implements/GainfoQrCodeService.cs b/Src/Dlhis.Basic/Organization/Services/Implements/GainfoQrCodeService.cs index ab6564d7..fac551b2 100644 --- a/Src/Dlhis.Basic/Organization/Services/Implements/GainfoQrCodeService.cs +++ b/Src/Dlhis.Basic/Organization/Services/Implements/GainfoQrCodeService.cs @@ -1,4 +1,5 @@ using System.Text; +using Dlhis.Constants; using Dlhis.Entity.Basic.Entity; using Kurisu.AspNetCore.Authentication.Abstractions; using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; @@ -21,7 +22,7 @@ public class GainfoQrCodeService : IGainfoQrCodeService public async Task GetMyQrCodeAsync(bool isNeedRefresh = false) { - var jinkoCode = _currentUser.GetUserClaim("jinko-code"); + var jinkoCode = _currentUser.GetUserClaim(UserClaimConstants.JinkoCode); if (jinkoCode.IsEmpty()) { return null; @@ -63,11 +64,12 @@ public class GainfoQrCodeService : IGainfoQrCodeService var ts = DateTimeOffset.Now.ToUnixTimeMilliseconds(); var qrCode = string.Concat("consumer", ":", code, ":"); - return "01" + qrCode.ToArray().Select(x => + return QrCodeConstants.User + qrCode.ToArray().Select(x => { var b = Encoding.ASCII.GetBytes(x.ToString()); int i = b[0]; return Convert.ToString(i); + }).Join(string.Empty) + ts; } } \ No newline at end of file diff --git a/Src/Dlhis.Basic/Organization/Services/Implements/GainfoService.cs b/Src/Dlhis.Basic/Organization/Services/Implements/GainfoService.cs index f6b88f2f..198382e6 100644 --- a/Src/Dlhis.Basic/Organization/Services/Implements/GainfoService.cs +++ b/Src/Dlhis.Basic/Organization/Services/Implements/GainfoService.cs @@ -2,6 +2,7 @@ using Dlhis.Basic.Device.Services; using Dlhis.Basic.Organization.Request; using Dlhis.Basic.Organization.Response; +using Dlhis.Constants; using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Basic.Enums; using Dlhis.Entity.Device.Entities; @@ -195,7 +196,7 @@ public class GainfoService : IGainfoService public async Task CurrentUserInfoAsync() { - var code = _currentUser.GetUserClaim("jinko-code"); + var code = _currentUser.GetUserClaim(UserClaimConstants.JinkoCode); if (code.IsEmpty()) { _logger.LogWarning("工号为空尝试获取人员信息"); diff --git a/Src/Dlhis.Basic/Organization/Services/Implements/IdentityUserSyncService.cs b/Src/Dlhis.Basic/Organization/Services/Implements/IdentityUserSyncService.cs index 5bde9773..a68e296d 100644 --- a/Src/Dlhis.Basic/Organization/Services/Implements/IdentityUserSyncService.cs +++ b/Src/Dlhis.Basic/Organization/Services/Implements/IdentityUserSyncService.cs @@ -1,3 +1,4 @@ +using Dlhis.Constants; using Dlhis.Entity.Sys.Entities; using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; using Kurisu.AspNetCore.Utils.Extensions; @@ -41,7 +42,7 @@ public class IdentityUserSyncService : IIdentityUserSyncService var id = (int)await _identityDbContext.InsertReturnIdentityAsync(user); user.Id = id; - await AddUserClaim(_identityDbContext, id, "jinko-code", userName); + await AddUserClaim(_identityDbContext, id, UserClaimConstants.JinkoCode, userName); await AddUserClaim(_identityDbContext, id, "name", name); } diff --git a/Src/Dlhis.Canteen.Api/Dlhis.Canteen.Api.csproj b/Src/Dlhis.Canteen.Api/Dlhis.Canteen.Api.csproj new file mode 100644 index 00000000..dba64aca --- /dev/null +++ b/Src/Dlhis.Canteen.Api/Dlhis.Canteen.Api.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + disable + + + + + + + diff --git a/Src/Dlhis.Canteen.Api/ITicketApi.cs b/Src/Dlhis.Canteen.Api/ITicketApi.cs new file mode 100644 index 00000000..149b9826 --- /dev/null +++ b/Src/Dlhis.Canteen.Api/ITicketApi.cs @@ -0,0 +1,16 @@ +using Kurisu.RemoteCall.Attributes; + +namespace Dlhis.Canteen.Api; + +[RequestResultHandler] +[EnableRemoteClient(Name = "canteen-service", BaseUrl = "${InternalRequest:CanteenRequestUrl}")] +public interface ITicketApi +{ + /// + /// 创建券 + /// + /// + /// + [Post("/api/tickets/{type}/create")] + Task CreateAsync(string type); +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Dlhis.Canteen.csproj b/Src/Dlhis.Canteen/Dlhis.Canteen.csproj index f893a588..b3030c28 100644 --- a/Src/Dlhis.Canteen/Dlhis.Canteen.csproj +++ b/Src/Dlhis.Canteen/Dlhis.Canteen.csproj @@ -22,6 +22,7 @@ + diff --git a/Src/Dlhis.Canteen/EMealVoucher/Services/Implements/EMealVoucherService.cs b/Src/Dlhis.Canteen/EMealVoucher/Services/Implements/EMealVoucherService.cs index 466e2e5d..9bb4b838 100644 --- a/Src/Dlhis.Canteen/EMealVoucher/Services/Implements/EMealVoucherService.cs +++ b/Src/Dlhis.Canteen/EMealVoucher/Services/Implements/EMealVoucherService.cs @@ -3,6 +3,7 @@ using Dlhis.Canteen.EMealVoucher.Request; using Dlhis.Canteen.EMealVoucher.Response; using Dlhis.Canteen.Order.Request; using Dlhis.Canteen.Order.Response; +using Dlhis.Constants; using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Canteen.Entities; using Dlhis.Entity.Canteen.Enums; @@ -65,7 +66,7 @@ public class EMealVoucherService : IEMealVoucherService public async Task> GetMyEMealVoucherPageAsync(EMealVoucherPageRequest request) { - var code = _currentUser.GetUserClaim("jinko-code"); + var code = _currentUser.GetUserClaim(UserClaimConstants.JinkoCode); return await _dbContext.Queryable() .Where(x => x.Code == code) @@ -126,7 +127,7 @@ public class EMealVoucherService : IEMealVoucherService var ts = DateTimeOffset.Now.ToUnixTimeMilliseconds().ToString().Reverse().Join(string.Empty); var originData = string.Concat(eMealVoucher.No, ":", ts, ":", eMealVoucher.Code, ":", eMealVoucherDetail.IdNo); - var qrCode = "02" + originData.ToArray().Select(x => + var qrCode = QrCodeConstants.EMealVoucher + originData.ToArray().Select(x => { var b = Encoding.ASCII.GetBytes(x.ToString()); int i = b[0]; diff --git a/Src/Dlhis.Canteen/MealSupplementRecord/Services/Implements/MealSupplementRecordService.cs b/Src/Dlhis.Canteen/MealSupplementRecord/Services/Implements/MealSupplementRecordService.cs index 0e2094cf..79f03e25 100644 --- a/Src/Dlhis.Canteen/MealSupplementRecord/Services/Implements/MealSupplementRecordService.cs +++ b/Src/Dlhis.Canteen/MealSupplementRecord/Services/Implements/MealSupplementRecordService.cs @@ -1,5 +1,6 @@ using Dlhis.Canteen.MealSupplementRecord.Request; using Dlhis.Canteen.MealSupplementRecord.Response; +using Dlhis.Constants; using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Canteen.Entities; using Dlhis.Entity.Canteen.Enums; @@ -93,7 +94,7 @@ public class MealSupplementRecordService : IMealSupplementRecordService public async Task GetMyMealSupplementRecords(MealSupplementRecordMyRequest request) { - var code = _currentUser.GetUserClaim("jinko-code") ?? string.Empty; + var code = _currentUser.GetUserClaim(UserClaimConstants.JinkoCode) ?? string.Empty; var data = await _dbContext.Queryable() .Where(x => x.CostDateTime >= request.SearchDateTimePeriod.Start && x.CostDateTime <= request.SearchDateTimePeriod.End) diff --git a/Src/Dlhis.Canteen/Order/Services/Implements/BalanceService.cs b/Src/Dlhis.Canteen/Order/Services/Implements/BalanceService.cs index 8929aed6..1e9457e5 100644 --- a/Src/Dlhis.Canteen/Order/Services/Implements/BalanceService.cs +++ b/Src/Dlhis.Canteen/Order/Services/Implements/BalanceService.cs @@ -3,6 +3,7 @@ using Dlhis.Canteen.Order.Repository; using Dlhis.Canteen.Order.Request; using Dlhis.Canteen.Order.Response; using Dlhis.Canteen.Setting.Services; +using Dlhis.Constants; using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Canteen.Entities; using Dlhis.Entity.Canteen.Enums; @@ -332,7 +333,7 @@ public class BalanceService : IBalanceService public async Task GetPageByUserAsync(BalanceRecordUserPageRequest request) { - var userCode = _currentUser.GetUserClaim("jinko-code") ?? string.Empty; + var userCode = _currentUser.GetUserClaim(UserClaimConstants.JinkoCode) ?? string.Empty; var factory = await _dbContext.Queryable().Where(x => x.Code == request.WalletCode).Select(x => x.Factory).FirstAsync(); var factories = await _balanceRepository.GetWalletFactoriesAsync(factory); diff --git a/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs b/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs index 6a849f72..d6284a13 100644 --- a/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs +++ b/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs @@ -21,6 +21,7 @@ using Kurisu.AspNetCore.DataAccess.Dto; using Kurisu.AspNetCore.DataAccess.SqlSugar.Extensions; using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; using Kurisu.AspNetCore.UnifyResultAndValidation; +using Dlhis.Constants; namespace Dlhis.Canteen.Order.Services.Implements; @@ -88,7 +89,7 @@ public class OrderService : IOrderService { var subjectId = _currentUser.GetIntSubjectId(); var gainfo = await _dbContext.Queryable().Where(x => x.IdentityUserId == subjectId).FirstAsync(); - var jinkoCode = _currentUser.GetUserClaim("jinko-code") ?? string.Empty; + var jinkoCode = _currentUser.GetUserClaim(UserClaimConstants.JinkoCode) ?? string.Empty; if (jinkoCode.IsPresent()) { gainfo = await _dbContext.Queryable().Where(x => x.Code == jinkoCode).SingleAsync(); @@ -153,12 +154,12 @@ public class OrderService : IOrderService if (request.Source == BalanceRecordSourceEnumType.QrCode) { - if (request.UserUniqueNo.StartsWith("01")) + if (request.UserUniqueNo.StartsWith(QrCodeConstants.User)) { gainfo = await GetUserByQrCodeAsync(request.UserUniqueNo); await CheckPermissionAsync(gainfo.Code, request.DeviceUniqueNo); } - else if (request.UserUniqueNo.StartsWith("02")) + else if (request.UserUniqueNo.StartsWith(QrCodeConstants.EMealVoucher)) { request.Source = BalanceRecordSourceEnumType.EMealVoucher; var eMealVoucherQrCode = await _dbContext.Queryable().Where(x => x.QrCode == request.UserUniqueNo).SingleAsync(); @@ -166,6 +167,12 @@ public class OrderService : IOrderService var code = eMealVoucherQrCode.OriginData.Split(':')[2]; gainfo = await _dbContext.Queryable().Where(x => x.Code == code).SingleAsync(); } + else if (request.UserUniqueNo.StartsWith(QrCodeConstants.Birthday)) + { + var ticket = await _dbContext.Queryable().Where(x => x.Type == TicketEnumType.Birthday).Where(x => x.QrCode == request.UserUniqueNo).SingleAsync(); + ticket.ThrowIfNull("无法识别"); + gainfo = await _dbContext.Queryable().Where(x => x.Code == ticket.UserCode).SingleAsync(); + } else { throw new UserFriendlyException($"无法识别{request.UserUniqueNo}"); @@ -276,7 +283,9 @@ public class OrderService : IOrderService .Where(x => x.DeviceUniqueNo == request.DeviceUniqueNo && x.BusinessPlaceCode.HasValue) .Select((x, b, bs) => new { - b.Code, bs.ShiftCode, Factory = x.TenantId + b.Code, + bs.ShiftCode, + Factory = x.TenantId }) .SingleAsync(); @@ -615,7 +624,9 @@ public class OrderService : IOrderService .Where(x => x.DeviceUniqueNo == deviceUniqueNo && x.BusinessPlaceCode.HasValue) .Select((x, b, bs) => new { - b.Code, bs.ShiftCode, Factory = x.TenantId + b.Code, + bs.ShiftCode, + Factory = x.TenantId }) .SingleAsync(); diff --git a/Src/Dlhis.Canteen/Setting/Services/Implements/WalletService.cs b/Src/Dlhis.Canteen/Setting/Services/Implements/WalletService.cs index 42f3a8fc..ffe3dd45 100644 --- a/Src/Dlhis.Canteen/Setting/Services/Implements/WalletService.cs +++ b/Src/Dlhis.Canteen/Setting/Services/Implements/WalletService.cs @@ -1,5 +1,6 @@ using Dlhis.Canteen.Order.Repository; using Dlhis.Canteen.Setting.Response; +using Dlhis.Constants; using Dlhis.Entity.Canteen.Entities; using Kurisu.AspNetCore.Authentication.Abstractions; using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; @@ -37,7 +38,7 @@ public class WalletService : IWalletService /// public async Task GetBalanceAsync(string walletCode) { - var userCode = _currentUser.GetUserClaim("jinko-code"); + var userCode = _currentUser.GetUserClaim(UserClaimConstants.JinkoCode); if (!await _dbContext.Queryable().Where(x => x.UserCode == userCode).AnyAsync()) { diff --git a/Src/Dlhis.Canteen/Tickets/Controllers/TicketsController.cs b/Src/Dlhis.Canteen/Tickets/Controllers/TicketsController.cs new file mode 100644 index 00000000..9dfb8ae1 --- /dev/null +++ b/Src/Dlhis.Canteen/Tickets/Controllers/TicketsController.cs @@ -0,0 +1,50 @@ +using Dlhis.Canteen.Tickets.Request; +using Dlhis.Canteen.Tickets.Response; +using Dlhis.Canteen.Tickets.Services; +using Kurisu.AspNetCore.DataAccess.Dto; +using Kurisu.AspNetCore.Utils.Extensions; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Dlhis.Canteen.Tickets.Controllers; + +/// +/// 券 +/// +[Authorize] +[Route("api/[controller]")] +[ApiController] +public class TicketsController : ControllerBase +{ + private readonly INamedResolver _namedResolver; + + public TicketsController(INamedResolver namedResolver) + { + _namedResolver = namedResolver; + } + + /// + /// 获取分页数据(按券类型) + /// + /// + /// + [HttpGet("page")] + public async Task> GetPageAsync(TicketPageRequest request) + { + return await _namedResolver.GetService(request.Type.ToString()).GetPageAsync(request); + } + + /// + /// 创建券 + /// + /// 券类型,需要后端支持 + [AllowAnonymous] + [HttpPost("{type:required}/create")] + public async Task CreateAsync(string type) + { + var srv = _namedResolver.GetService(type); + srv.ThrowIfNull("不支持的券类型"); + + await srv.CreateAsync(); + } +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Tickets/Request/TicketPageRequest.cs b/Src/Dlhis.Canteen/Tickets/Request/TicketPageRequest.cs new file mode 100644 index 00000000..02620a43 --- /dev/null +++ b/Src/Dlhis.Canteen/Tickets/Request/TicketPageRequest.cs @@ -0,0 +1,19 @@ +using System.ComponentModel.DataAnnotations; +using Dlhis.Entity.Canteen.Enums; +using Kurisu.AspNetCore.DataAccess.Dto; + +namespace Dlhis.Canteen.Tickets.Request; + +public class TicketPageRequest : PageDto +{ + /// + /// 类型 + /// + [EnumDataType(typeof(TicketEnumType))] + public TicketEnumType Type { get; set; } + + /// + /// 工号 + /// + public string UserCode { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Tickets/Response/TicketPageResponse.cs b/Src/Dlhis.Canteen/Tickets/Response/TicketPageResponse.cs new file mode 100644 index 00000000..b7be7bc6 --- /dev/null +++ b/Src/Dlhis.Canteen/Tickets/Response/TicketPageResponse.cs @@ -0,0 +1,41 @@ +using Dlhis.Entity.Canteen.Enums; + +namespace Dlhis.Canteen.Tickets.Response; + +public class TicketPageResponse +{ + /// + /// 券类型 + /// + public TicketEnumType Type { get; set; } + + /// + /// 工号 + /// + public string UserCode { get; set; } + + /// + /// 用户名称 + /// + public string UserName { get; set; } + + /// + /// 初始余额 + /// + public decimal OriginalAmount { get; set; } + + /// + /// 当前余额 + /// + public decimal Amount { get; set; } + + /// + /// 消费码 + /// + public string QrCode { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Tickets/Services/IBirthdayTicketService.cs b/Src/Dlhis.Canteen/Tickets/Services/IBirthdayTicketService.cs new file mode 100644 index 00000000..1b67cc3a --- /dev/null +++ b/Src/Dlhis.Canteen/Tickets/Services/IBirthdayTicketService.cs @@ -0,0 +1,6 @@ +namespace Dlhis.Canteen.Tickets.Services; + +public interface IBirthdayTicketService : ITicketService +{ + +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Tickets/Services/ITicketService.cs b/Src/Dlhis.Canteen/Tickets/Services/ITicketService.cs new file mode 100644 index 00000000..bc8731f2 --- /dev/null +++ b/Src/Dlhis.Canteen/Tickets/Services/ITicketService.cs @@ -0,0 +1,24 @@ +using Dlhis.Canteen.Tickets.Request; +using Dlhis.Canteen.Tickets.Response; +using Kurisu.AspNetCore.DataAccess.Dto; + +namespace Dlhis.Canteen.Tickets.Services; + +/// +/// 券 +/// +public interface ITicketService : IScopeDependency +{ + /// + /// 分页数据 + /// + /// + /// + Task> GetPageAsync(TicketPageRequest request); + + /// + /// 创建 + /// + /// + Task CreateAsync(); +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Tickets/Services/Implements/BirthdayTicketService.cs b/Src/Dlhis.Canteen/Tickets/Services/Implements/BirthdayTicketService.cs new file mode 100644 index 00000000..ba75b0e9 --- /dev/null +++ b/Src/Dlhis.Canteen/Tickets/Services/Implements/BirthdayTicketService.cs @@ -0,0 +1,65 @@ +using System.Text; +using Dlhis.Constants; +using Dlhis.Entity.Basic.Entity; +using Dlhis.Entity.Canteen.Entities; +using Dlhis.Entity.Canteen.Enums; +using Kurisu.AspNetCore.DataAccess.Entity; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; +using Kurisu.AspNetCore.Utils.Extensions; +using SqlSugar; + +namespace Dlhis.Canteen.Tickets.Services.Implements; + +[Service(nameof(TicketEnumType.Birthday))] +public class BirthdayTicketService : BaseTicketService, IBirthdayTicketService +{ + /// + public BirthdayTicketService(IDbContext dbContext) : base(dbContext) + { + } + + /// + public override async Task CreateAsync() + { + await DbContext.IgnoreAsync(async () => + { + var today = DateTime.Today; + var userCodes = await DbContext.Queryable() + .Where(x => SqlFunc.DateIsSame(SqlFunc.ToDate(x.Birthday), today, DateType.Day)) + .Select(x => new { x.Code, x.Factory }) + .ToListAsync(); + + const decimal amount = 30; + var todayStr = today.ToString("yyyy-MM-dd"); + foreach (var item in userCodes) + { + var qrCode = GetQrCode(item.Code + todayStr); + if (!await DbContext.Queryable().Where(x => x.Type == TicketEnumType.Birthday && x.UserCode == item.Code && x.QrCode == qrCode).AnyAsync()) + { + await DbContext.InsertAsync(new TicketEntity + { + Type = TicketEnumType.Birthday, + UserCode = item.Code, + OriginalAmount = amount, + Amount = amount, + QrCode = qrCode, + Remark = todayStr + "生日券", + TenantId = item.Factory + }); + } + } + }); + } + + private static string GetQrCode(string data) + { + var qrCode = QrCodeConstants.Birthday + data.ToArray().Select(x => + { + var b = Encoding.ASCII.GetBytes(x.ToString()); + int i = b[0]; + return Convert.ToString(i); + }).Join(string.Empty); + + return qrCode; + } +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Tickets/Services/Implements/TicketService.cs b/Src/Dlhis.Canteen/Tickets/Services/Implements/TicketService.cs new file mode 100644 index 00000000..8af5da71 --- /dev/null +++ b/Src/Dlhis.Canteen/Tickets/Services/Implements/TicketService.cs @@ -0,0 +1,32 @@ +using Dlhis.Canteen.Tickets.Request; +using Dlhis.Canteen.Tickets.Response; +using Dlhis.Entity.Basic.Entity; +using Dlhis.Entity.Canteen.Entities; +using Kurisu.AspNetCore.DataAccess.Dto; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Extensions; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; +using Kurisu.AspNetCore.Utils.Extensions; + +namespace Dlhis.Canteen.Tickets.Services.Implements; + +public abstract class BaseTicketService : ITicketService +{ + protected IDbContext DbContext { get; set; } + + protected BaseTicketService(IDbContext dbContext) + { + DbContext = dbContext; + } + + public async Task> GetPageAsync(TicketPageRequest request) + { + return await DbContext.Queryable() + .LeftJoin((x, ga) => x.UserCode == ga.Code) + .Where(x => x.Type == request.Type) + .WhereIF(request.UserCode.IsPresent(), x => x.UserCode == request.UserCode) + .Select((x, ga) => new TicketPageResponse { UserName = ga.Name }, true) + .ToPageListAsync(request); + } + + public abstract Task CreateAsync(); +} \ No newline at end of file diff --git a/Src/Dlhis.Constants/Dlhis.Constants.csproj b/Src/Dlhis.Constants/Dlhis.Constants.csproj new file mode 100644 index 00000000..132c02c5 --- /dev/null +++ b/Src/Dlhis.Constants/Dlhis.Constants.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/Src/Dlhis.Constants/QrCodeConstants.cs b/Src/Dlhis.Constants/QrCodeConstants.cs new file mode 100644 index 00000000..ed15fccc --- /dev/null +++ b/Src/Dlhis.Constants/QrCodeConstants.cs @@ -0,0 +1,19 @@ +namespace Dlhis.Constants; + +public class QrCodeConstants +{ + /// + /// 个人码 + /// + public const string User = "01"; + + /// + /// 电子餐券 + /// + public const string EMealVoucher = "02"; + + /// + /// 生日券 + /// + public const string Birthday = "03"; +} diff --git a/Src/Dlhis.Constants/TickConstants.cs b/Src/Dlhis.Constants/TickConstants.cs new file mode 100644 index 00000000..b24c0775 --- /dev/null +++ b/Src/Dlhis.Constants/TickConstants.cs @@ -0,0 +1,6 @@ +// namespace Dlhis.Constants; +// +// public class TickConstants +// { +// public const string { get; set; } +// } diff --git a/Src/Dlhis.Constants/UserClaimConstants.cs b/Src/Dlhis.Constants/UserClaimConstants.cs new file mode 100644 index 00000000..635fdf0f --- /dev/null +++ b/Src/Dlhis.Constants/UserClaimConstants.cs @@ -0,0 +1,9 @@ +namespace Dlhis.Constants; + +public class UserClaimConstants +{ + /// + /// 晶科工号 + /// + public const string JinkoCode = "jinko-code"; +} diff --git a/Src/Dlhis.Entity/Canteen/Entities/TicketEntity.cs b/Src/Dlhis.Entity/Canteen/Entities/TicketEntity.cs new file mode 100644 index 00000000..d3ca3c05 --- /dev/null +++ b/Src/Dlhis.Entity/Canteen/Entities/TicketEntity.cs @@ -0,0 +1,52 @@ +using Dlhis.Entity.Canteen.Enums; +using Kurisu.AspNetCore.DataAccess.Entity; +using Kurisu.AspNetCore.DataAccess.SqlSugar; +using SqlSugar; +using SqlSugar.DbConvert; + +namespace Dlhis.Entity.Canteen.Entities; + +/// +/// 券 +/// +[SugarTable("Tickets", TableDescription = "券")] +public class TicketEntity : SugarBaseEntity, ITenantId +{ + /// + /// 类型 + /// + [SugarColumn(IsNullable = false, ColumnDataType = "varchar(50)", ColumnDescription = "券类型", SqlParameterDbType = typeof(EnumToStringConvert))] + public TicketEnumType Type { get; set; } + + /// + /// 工号 + /// + [SugarColumn(IsNullable = false, ColumnDescription = "工号")] + public string UserCode { get; set; } + + /// + /// 初始余额 + /// + [SugarColumn(IsNullable = false, ColumnDescription = "初始余额")] + public decimal OriginalAmount { get; set; } + + /// + /// 当前余额 + /// + [SugarColumn(IsNullable = false, ColumnDescription = "当前余额")] + public decimal Amount { get; set; } + + /// + /// 消费码 + /// + [SugarColumn(IsNullable = false, ColumnDescription = "消费码")] + public string QrCode { get; set; } + + /// + /// 备注 + /// + [SugarColumn(IsNullable = true, ColumnDescription = "备注")] + public string Remark { get; set; } + + public string TenantId { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Entity/Canteen/Enums/TicketEnumType.cs b/Src/Dlhis.Entity/Canteen/Enums/TicketEnumType.cs new file mode 100644 index 00000000..3df284e2 --- /dev/null +++ b/Src/Dlhis.Entity/Canteen/Enums/TicketEnumType.cs @@ -0,0 +1,16 @@ +using System.ComponentModel; + +namespace Dlhis.Entity.Canteen.Enums; + +/// +/// 券类型 +/// +[Description("券类型")] +public enum TicketEnumType +{ + /// + /// 生日券 + /// + [Description("生日券")] + Birthday = 1 +} \ No newline at end of file diff --git a/Src/Dlhis.ScheduledTask/DeviceUploadLog/Services/Implements/DeviceUploadLogService.cs b/Src/Dlhis.ScheduledTask/DeviceUploadLog/Services/Implements/DeviceUploadLogService.cs index da1be9e3..6336d998 100644 --- a/Src/Dlhis.ScheduledTask/DeviceUploadLog/Services/Implements/DeviceUploadLogService.cs +++ b/Src/Dlhis.ScheduledTask/DeviceUploadLog/Services/Implements/DeviceUploadLogService.cs @@ -1,4 +1,5 @@ -using Dlhis.Entity.Basic.Entity; +using Dlhis.Constants; +using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Basic.Enums; using Dlhis.Entity.Device.Entities; using Dlhis.Entity.Device.Enums; @@ -95,7 +96,7 @@ public class DeviceUploadLogService : IDeviceUploadLogService public async Task> GetMyRecordsAsync(DeviceUploadLogMyRecordRequest request) { - var jinkoCode = _currentUser.GetUserClaim("jinko-code") ?? string.Empty; + var jinkoCode = _currentUser.GetUserClaim(UserClaimConstants.JinkoCode) ?? string.Empty; return await _dbContext.Queryable() .LeftJoin((log, d) => log.DeviceUniqueNo == d.UniqueNo) diff --git a/Src/Dlhis.ScheduledTask/Dlhis.ScheduledTask.csproj b/Src/Dlhis.ScheduledTask/Dlhis.ScheduledTask.csproj index 0376df7c..0429e8ce 100644 --- a/Src/Dlhis.ScheduledTask/Dlhis.ScheduledTask.csproj +++ b/Src/Dlhis.ScheduledTask/Dlhis.ScheduledTask.csproj @@ -20,10 +20,13 @@ + + + diff --git a/Src/Dlhis.ScheduledTask/Dockerfile b/Src/Dlhis.ScheduledTask/Dockerfile index babe2308..dadbd4fe 100644 --- a/Src/Dlhis.ScheduledTask/Dockerfile +++ b/Src/Dlhis.ScheduledTask/Dockerfile @@ -1,25 +1,34 @@ -#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. +# 请参阅 https://aka.ms/customizecontainer 以了解如何自定义调试容器,以及 Visual Studio 如何使用此 Dockerfile 生成映像以更快地进行调试。 +# 此阶段用于在快速模式(默认为调试配置)下从 VS 运行时 FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base WORKDIR /app EXPOSE 80 + +# 此阶段用于生成服务项目 FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build ARG BUILD_CONFIGURATION=Release WORKDIR /src COPY ["Src/Dlhis.ScheduledTask/Dlhis.ScheduledTask.csproj", "Src/Dlhis.ScheduledTask/"] COPY ["Src/Dlhis.Basic.Api/Dlhis.Basic.Api.csproj", "Src/Dlhis.Basic.Api/"] +COPY ["Src/Dlhis.Canteen.Api/Dlhis.Canteen.Api.csproj", "Src/Dlhis.Canteen.Api/"] +COPY ["Src/Dlhis.Constants/Dlhis.Constants.csproj", "Src/Dlhis.Constants/"] COPY ["Src/Dlhis.Entity/Dlhis.Entity.csproj", "Src/Dlhis.Entity/"] +COPY ["Src/Dlhis.DataPermission.Model/Dlhis.DataPermission.Model.csproj", "Src/Dlhis.DataPermission.Model/"] +COPY ["Src/Dlhis.Jip.Api/Dlhis.Jip.Api.csproj", "Src/Dlhis.Jip.Api/"] COPY ["Src/Dlhis.Sys.Api/Dlhis.Sys.Api.csproj", "Src/Dlhis.Sys.Api/"] -RUN dotnet restore "./Src/Dlhis.ScheduledTask/./Dlhis.ScheduledTask.csproj" +RUN dotnet restore "./Src/Dlhis.ScheduledTask/Dlhis.ScheduledTask.csproj" COPY . . WORKDIR "/src/Src/Dlhis.ScheduledTask" RUN dotnet build "./Dlhis.ScheduledTask.csproj" -c $BUILD_CONFIGURATION -o /app/build +# 此阶段用于发布要复制到最终阶段的服务项目 FROM build AS publish ARG BUILD_CONFIGURATION=Release RUN dotnet publish "./Dlhis.ScheduledTask.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false +# 此阶段在生产中使用,或在常规模式下从 VS 运行时使用(在不使用调试配置时为默认值) FROM base AS final WORKDIR /app COPY --from=publish /app/publish . diff --git a/Src/Dlhis.ScheduledTask/Jobs/CreateBirthdayTicketJob.cs b/Src/Dlhis.ScheduledTask/Jobs/CreateBirthdayTicketJob.cs new file mode 100644 index 00000000..75058d75 --- /dev/null +++ b/Src/Dlhis.ScheduledTask/Jobs/CreateBirthdayTicketJob.cs @@ -0,0 +1,22 @@ +using Dlhis.Canteen.Api; +using Dlhis.Entity.Canteen.Enums; +using Dlhis.ScheduledTask.Attributes; + +namespace Dlhis.ScheduledTask.Jobs; + +/// +/// +/// +[RecurringJob(nameof(CreateBirthdayTicketJob), "0 0 1 * * ?", Name = "生日券发放")] +public class CreateBirthdayTicketJob : BaseJob +{ + public CreateBirthdayTicketJob(IServiceProvider serviceProvider) : base(serviceProvider) + { + } + + /// + protected override async Task RunAsync(IServiceProvider serviceProvider) + { + await serviceProvider.GetService().CreateAsync(TicketEnumType.Birthday.ToString()); + } +} \ No newline at end of file diff --git a/Src/Dlhis.ScheduledTask/appsettings.Development.json b/Src/Dlhis.ScheduledTask/appsettings.Development.json index da038ced..143db3b6 100644 --- a/Src/Dlhis.ScheduledTask/appsettings.Development.json +++ b/Src/Dlhis.ScheduledTask/appsettings.Development.json @@ -7,7 +7,8 @@ }, "InternalRequest": { "SysRequestUrl": "http://localhost:5003", - "BasicRequestUrl": "http://localhost:5002" + "BasicRequestUrl": "http://localhost:5002", + "CanteenRequestUrl": "http://localhost:5008" }, "FaceUrlOptions": { "Url": "http://192.168.199.115:5002/api/gainfos/face?token=", @@ -36,18 +37,18 @@ "ClientSecret": "123456" } }, -// "JinkoOptions": { -// "Jip": { -// "BaseUrl": "http://172.16.202.34:8000", -// "Key": "FACE-7409WIBX", -// "Secret": "be0a4b88-cd80-4509-b9b9-865205d0d3d8" -// } -// }, + // "JinkoOptions": { + // "Jip": { + // "BaseUrl": "http://172.16.202.34:8000", + // "Key": "FACE-7409WIBX", + // "Secret": "be0a4b88-cd80-4509-b9b9-865205d0d3d8" + // } + // }, "JinkoOptions": { "Jip": { "BaseUrl": "http://jip.jinkosolar.com", "Key": "FACE-CUOJG9N7", "Secret": "130c2f2d-03a0-4720-8e25-538ffac7a7cb" } - }, + } } diff --git a/Src/Dlhis.ScheduledTask/appsettings.json b/Src/Dlhis.ScheduledTask/appsettings.json index b2bd19b9..d3bba67c 100644 --- a/Src/Dlhis.ScheduledTask/appsettings.json +++ b/Src/Dlhis.ScheduledTask/appsettings.json @@ -7,7 +7,8 @@ }, "InternalRequest": { "SysRequestUrl": "http://sys", - "BasicRequestUrl": "http://basic" + "BasicRequestUrl": "http://basic", + "CanteenRequestUrl": "http://canteen" }, "DataProtectionOptions": { "Enable": true, From 636874efe47dc2ce3c7ded94db4352545567497d Mon Sep 17 00:00:00 2001 From: liguoyang Date: Thu, 13 Feb 2025 17:37:47 +0800 Subject: [PATCH 3/4] =?UTF-8?q?feat=20=E4=BC=98=E5=8C=96=E6=B6=88=E8=B4=B9?= =?UTF-8?q?=E8=BF=87=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Order/Dto/OrderCheckResultDto.cs | 31 ++ .../Order/Dto/OrderPreCheckDto.cs | 16 - .../Order/Repository/IOrderRepository.cs | 2 +- .../Repository/Implements/OrderRepository.cs | 2 +- .../ConsumeHandlers/BirthdayConsumeHandler.cs | 110 ++++++ .../ConsumeHandlers/EMealVoucherHandler.cs | 166 +++++++++ .../ConsumeHandlers/OrderCommonHandler.cs | 144 ++++++++ .../Order/Services/IConsumeHandler.cs | 20 ++ .../Order/Services/IOrderCommonHandler.cs | 29 ++ .../Order/Services/Implements/OrderService.cs | 340 ++++-------------- .../Implements/BirthdayTicketService.cs | 4 +- .../Canteen/Entities/TicketEntity.cs | 9 +- .../Enums/BalanceRecordSourceEnumType.cs | 10 +- 13 files changed, 579 insertions(+), 304 deletions(-) create mode 100644 Src/Dlhis.Canteen/Order/Dto/OrderCheckResultDto.cs delete mode 100644 Src/Dlhis.Canteen/Order/Dto/OrderPreCheckDto.cs create mode 100644 Src/Dlhis.Canteen/Order/Services/ConsumeHandlers/BirthdayConsumeHandler.cs create mode 100644 Src/Dlhis.Canteen/Order/Services/ConsumeHandlers/EMealVoucherHandler.cs create mode 100644 Src/Dlhis.Canteen/Order/Services/ConsumeHandlers/OrderCommonHandler.cs create mode 100644 Src/Dlhis.Canteen/Order/Services/IConsumeHandler.cs create mode 100644 Src/Dlhis.Canteen/Order/Services/IOrderCommonHandler.cs diff --git a/Src/Dlhis.Canteen/Order/Dto/OrderCheckResultDto.cs b/Src/Dlhis.Canteen/Order/Dto/OrderCheckResultDto.cs new file mode 100644 index 00000000..8d0af378 --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Dto/OrderCheckResultDto.cs @@ -0,0 +1,31 @@ +using Dlhis.Entity.Canteen.Enums; + +namespace Dlhis.Canteen.Order.Dto; + +public class OrderCheckResultDto +{ + /// + /// 消费厂区 + /// + public string Factory { get; set; } + + /// + /// 消费日期 + /// + public DateTime CostDate { get; set; } + + /// + /// 消费餐次 + /// + public ShiftEnumType ShiftType { get; set; } + + /// + /// 消费营业点 + /// + public Guid BusinessPlaceCode { get; set; } + + /// + /// 消费餐别 + /// + public Guid BusinessShiftCode { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Dto/OrderPreCheckDto.cs b/Src/Dlhis.Canteen/Order/Dto/OrderPreCheckDto.cs deleted file mode 100644 index c4f9c9dc..00000000 --- a/Src/Dlhis.Canteen/Order/Dto/OrderPreCheckDto.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Dlhis.Entity.Canteen.Enums; - -namespace Dlhis.Canteen.Order.Dto; - -public class OrderPreCheckDto -{ - public string Factory { get; set; } - - public DateTime CostDate { get; set; } - - public ShiftEnumType ShiftType { get; set; } - - public Guid BusinessPlaceCode { get; set; } - - public Guid BusinessShiftCode { get; set; } -} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Repository/IOrderRepository.cs b/Src/Dlhis.Canteen/Order/Repository/IOrderRepository.cs index d9e904c4..f108f713 100644 --- a/Src/Dlhis.Canteen/Order/Repository/IOrderRepository.cs +++ b/Src/Dlhis.Canteen/Order/Repository/IOrderRepository.cs @@ -7,5 +7,5 @@ namespace Dlhis.Canteen.Order.Repository; public interface IOrderRepository : IScopeDependency { - Task CreateDeviceConsumeOrderAsync(OrderPreCheckDto checkData, ConsumeRequest request, GainfoEntity gainfo); + Task CreateDeviceConsumeOrderAsync(OrderCheckResultDto checkData, ConsumeRequest request, GainfoEntity gainfo); } \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Repository/Implements/OrderRepository.cs b/Src/Dlhis.Canteen/Order/Repository/Implements/OrderRepository.cs index a9e31997..23402c47 100644 --- a/Src/Dlhis.Canteen/Order/Repository/Implements/OrderRepository.cs +++ b/Src/Dlhis.Canteen/Order/Repository/Implements/OrderRepository.cs @@ -19,7 +19,7 @@ public class OrderRepository : IOrderRepository } /// - public async Task CreateDeviceConsumeOrderAsync(OrderPreCheckDto checkData, ConsumeRequest request, GainfoEntity gainfo) + public async Task CreateDeviceConsumeOrderAsync(OrderCheckResultDto checkData, ConsumeRequest request, GainfoEntity gainfo) { var order = new CanteenOrderEntity { diff --git a/Src/Dlhis.Canteen/Order/Services/ConsumeHandlers/BirthdayConsumeHandler.cs b/Src/Dlhis.Canteen/Order/Services/ConsumeHandlers/BirthdayConsumeHandler.cs new file mode 100644 index 00000000..dafafc7d --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Services/ConsumeHandlers/BirthdayConsumeHandler.cs @@ -0,0 +1,110 @@ +using Dlhis.Canteen.Order.Repository; +using Dlhis.Canteen.Order.Request; +using Dlhis.Canteen.Order.Response; +using Dlhis.Entity.Basic.Entity; +using Dlhis.Entity.Canteen.Entities; +using Dlhis.Entity.Canteen.Enums; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Extensions; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; +using Kurisu.AspNetCore.UnifyResultAndValidation; +using Kurisu.AspNetCore.Utils; +using Kurisu.AspNetCore.Utils.Extensions; +using Mapster; + +namespace Dlhis.Canteen.Order.Services.ConsumeHandlers; + +/// +/// 生日券消费处理 +/// +[Service(nameof(BalanceRecordSourceEnumType.Birthday))] +public class BirthdayConsumeHandler : IConsumeHandler +{ + private readonly IDbContext _dbContext; + private readonly IOrderCommonHandler _orderCommonHandler; + private readonly IOrderRepository _orderRepository; + + public BirthdayConsumeHandler(IDbContext dbContext, + IOrderCommonHandler orderCommonHandler, + IOrderRepository orderRepository) + { + _dbContext = dbContext; + _orderCommonHandler = orderCommonHandler; + _orderRepository = orderRepository; + } + + public async Task> ConsumeAsync(OrderAddRequest request, GainfoEntity gainfo) + { + var msg = string.Empty; + var ticketNo = request.UserUniqueNo; + + var exists = await _dbContext.Queryable() + .Where(x => x.Type == TicketEnumType.Birthday) + .Where(x => x.QrCode == ticketNo) + .AnyAsync(); + + exists.ThrowIfFalse("生日券无效"); + + try + { + var checkData = await _orderCommonHandler.PreCheckAsync(request.DeviceUniqueNo, request.CostDateTime!.Value); + var isSupermarket = await _dbContext.Queryable() + .Where(x => x.Code == checkData.BusinessPlaceCode) + .Select(x => x.Catalog == CanteenBusinessPlaceCatalogEnumType.Supermarket) + .SingleAsync(); + + isSupermarket.ThrowIfFalse("请在超市消费"); + + var order = await _orderRepository.CreateDeviceConsumeOrderAsync(checkData, request.Adapt(), gainfo); + + await _orderCommonHandler.TryConsumeAsync(order.Id, async () => + { + await CommonHelper.RetryAsync(async () => + { + var ticket = await _dbContext.Queryable() + .Where(x => x.Type == TicketEnumType.Birthday) + .Where(x => x.QrCode == ticketNo) + .SingleAsync(); + + //金额+- + ticket.Balance += order.Amount; + (ticket.Balance < 0).ThrowIfTrue("余额不足,无法消费"); + + await _dbContext.Updateable() + .SetColumns(x => x.Balance == ticket.Balance) + .SetColumns(x => x.Version == x.Version + 1) + .SetColumns(x => x.ModifiedTime == DateTime.Now) + .Where(x => x.QrCode == ticketNo && x.Version == ticket.Version) //必须与查询时的version一致 + .ExecuteCommandThrowVersionAsync(); + + //写入记录 + var balanceRecord = order.Adapt(); + balanceRecord.Remark = $"{ticket.QrCode}(生日券消费)"; + await _dbContext.InsertAsync(balanceRecord); + }, 5); + }); + } + catch (Exception ex) + { + msg = ex.Message; + } + + //获取餐券余额 + var data = await _dbContext.Queryable() + .LeftJoin((x, ga) => x.UserCode == ga.Code) + .Where(x => x.Type == TicketEnumType.Birthday && x.QrCode == ticketNo) + .Select((x, ga) => new OrderResultResponse + { + UserCode = x.UserCode, + UserName = ga.Name, + CurrentBalance = x.Balance + }) + .SingleAsync(); + + return new DefaultApiResult + { + Code = msg.IsPresent() ? ApiStateCode.Error : ApiStateCode.Success, + Data = data, + Msg = msg + }; + } +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Services/ConsumeHandlers/EMealVoucherHandler.cs b/Src/Dlhis.Canteen/Order/Services/ConsumeHandlers/EMealVoucherHandler.cs new file mode 100644 index 00000000..86140edf --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Services/ConsumeHandlers/EMealVoucherHandler.cs @@ -0,0 +1,166 @@ +using Dlhis.Canteen.Order.Repository; +using Dlhis.Canteen.Order.Request; +using Dlhis.Canteen.Order.Response; +using Dlhis.Entity.Basic.Entity; +using Dlhis.Entity.Canteen.Entities; +using Dlhis.Entity.Canteen.Enums; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Extensions; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; +using Kurisu.AspNetCore.UnifyResultAndValidation; +using Kurisu.AspNetCore.Utils; +using Kurisu.AspNetCore.Utils.Extensions; +using Mapster; + +namespace Dlhis.Canteen.Order.Services.ConsumeHandlers; + +/// +/// 电子餐券处理 +/// +[Service(nameof(BalanceRecordSourceEnumType.EMealVoucher))] +public class EMealVoucherHandler : IConsumeHandler +{ + private readonly IDbContext _dbContext; + private readonly IOrderRepository _orderRepository; + private readonly IOrderCommonHandler _orderCommonHandler; + + public EMealVoucherHandler(IDbContext dbContext, + IOrderRepository orderRepository, + IOrderCommonHandler orderCommonHandler) + { + _dbContext = dbContext; + _orderRepository = orderRepository; + _orderCommonHandler = orderCommonHandler; + } + + public async Task> ConsumeAsync(OrderAddRequest request, GainfoEntity gainfo) + { + var msg = string.Empty; + var originData = await _dbContext.Queryable() + .Where(x => x.QrCode == request.UserUniqueNo) + .Select(x => x.OriginData) + .SingleAsync(); + + if (originData.IsEmpty()) + { + msg = "无法识别"; + return new DefaultApiResult + { + Code = ApiStateCode.Error, + Data = null, + Msg = msg + }; + } + + var originDataItems = originData.Split(":"); + var no = originDataItems[0]; + var idNo = originDataItems[3]; + + try + { + await EMealVoucherConsumeAsync(request.Adapt(), gainfo, originData, no, idNo); + } + catch (Exception ex) + { + msg = ex.Message; + } + + //获取餐券余额 + var data = await _dbContext.Queryable() + .LeftJoin((x, d) => x.No == d.No) + .Where((x, d) => x.No == no && d.IdNo == idNo) + .Select((x, d) => new OrderResultResponse + { + UserCode = d.IdNo, + UserName = d.Name, + CurrentBalance = d.Balance + }) + .SingleAsync(); + + try + { + var v = data.UserCode ?? string.Empty; + var vc = v.ToCharArray().Reverse().ToList(); + + for (int i = 0; i < vc.Count; i++) + { + if (i + 1 > 4) + { + vc[i] = '*'; + } + } + + vc.Reverse(); + data.UserCode = string.Join("", vc); + } + catch (Exception) + { + //ignore + } + + return new DefaultApiResult + { + Code = msg.IsPresent() ? ApiStateCode.Error : ApiStateCode.Success, + Data = data, + Msg = msg + }; + } + + + /// + /// 电子餐券消费 + /// + /// + /// + /// + /// + /// + private async Task EMealVoucherConsumeAsync(ConsumeRequest request, GainfoEntity gainfo, string originData, string no, string idNo) + { + var now = DateTime.Now; + + var current = await _dbContext.Queryable() + .Where(x => x.No == no && x.IdNo == idNo) + .Select(x => new { x.Name, x.Start, x.End }) + .SingleAsync(); + + (now >= current.Start && now <= current.End).ThrowIfFalse("餐券已过期"); + + var eMealVoucher = await _dbContext.Queryable().Where(x => x.No == no).SingleAsync(); + + var checkData = await _orderCommonHandler.PreCheckAsync(request.DeviceUniqueNo, request.CostDateTime); + var isSupermarket = await _dbContext.Queryable() + .Where(x => x.Code == checkData.BusinessPlaceCode) + .Select(x => x.Catalog == CanteenBusinessPlaceCatalogEnumType.Supermarket) + .SingleAsync(); + + isSupermarket.ThrowIfTrue("禁止在超市消费"); + + eMealVoucher.Factories.Contains(checkData.Factory).ThrowIfFalse("禁止在当前厂区消费"); + + gainfo.No = originData; + var order = await _orderRepository.CreateDeviceConsumeOrderAsync(checkData, request, gainfo); + + await _orderCommonHandler.TryConsumeAsync(order.Id, async () => + { + await CommonHelper.RetryAsync(async () => + { + var detail = await _dbContext.Queryable().Where(x => x.No == no && x.IdNo == idNo).SingleAsync(); + //金额+- + detail.Balance += order.Amount; + (detail.Balance < 0).ThrowIfTrue("余额不足,无法消费"); + + await _dbContext.Updateable() + .SetColumns(x => x.Balance == detail.Balance) + .SetColumns(x => x.Version == x.Version + 1) + .SetColumns(x => x.ModifiedTime == DateTime.Now) + .Where(x => x.No == no && x.IdNo == idNo && x.Version == detail.Version) //必须与查询时的version一致 + .ExecuteCommandThrowVersionAsync(); + + //写入记录 + var balanceRecord = order.Adapt(); + balanceRecord.Remark = $"({no})" + current.Name; + await _dbContext.InsertAsync(balanceRecord); + }, 5); + }); + } +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Services/ConsumeHandlers/OrderCommonHandler.cs b/Src/Dlhis.Canteen/Order/Services/ConsumeHandlers/OrderCommonHandler.cs new file mode 100644 index 00000000..21ac6801 --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Services/ConsumeHandlers/OrderCommonHandler.cs @@ -0,0 +1,144 @@ +using Dlhis.Canteen.Order.Dto; +using Dlhis.Canteen.Setting.Services; +using Dlhis.Entity.Basic.Entity; +using Dlhis.Entity.Canteen.Entities; +using Dlhis.Entity.Canteen.Enums; +using Dlhis.Entity.Device.Entities; +using Dlhis.Entity.Device.Enums; +using Kurisu.AspNetCore.CustomClass; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; +using Kurisu.AspNetCore.Utils.Extensions; + +namespace Dlhis.Canteen.Order.Services.ConsumeHandlers; + +/// +/// 订单通用处理 +/// +public class OrderCommonHandler : IOrderCommonHandler +{ + private readonly IDbContext _dbContext; + private readonly ICanteenSettingService _canteenSettingService; + private readonly ILogger _logger; + private readonly IHttpContextAccessor _httpContextAccessor; + + public OrderCommonHandler(IDbContext dbContext, + IHttpContextAccessor httpContextAccessor, + ICanteenSettingService canteenSettingService, + ILogger logger) + { + _dbContext = dbContext; + _canteenSettingService = canteenSettingService; + _httpContextAccessor = httpContextAccessor; + _logger = logger; + } + + /// + /// 设置设备ip + /// + /// + public async Task SetDeviceIpInfoAsync(string deviceNo) + { + try + { + if (_httpContextAccessor.HttpContext!.Request.Headers.TryGetValue("X-Requested-DeviceIp", out var ip)) + { + var device = await _dbContext.Queryable().Where(x => x.UniqueNo == deviceNo).SingleAsync(); + device.ConnectionInfo = new + { + ip = ip.ToString() + }.ToJson(); + + await _dbContext.UpdateAsync(device); + } + + _logger.LogInformation("设备ip获取:{deviceNo} {ip}", deviceNo, ip); + } + catch (Exception) + { + //ignore + } + } + + /// + /// 消费前预检 + /// + /// 设备号 + /// 消费时间 + /// + public async Task PreCheckAsync(string deviceUniqueNo, DateTime costDateTime) + { + var device = await _dbContext.Queryable().Where(x => x.UniqueNo == deviceUniqueNo).SingleAsync(); + device.ThrowIfNull("设备尚未登记"); + device.Enable.ThrowIfFalse("设备已被停用"); + (device.Type != DeviceTypeEnumType.Meal).ThrowIfTrue("设备类型有误"); + + var bindingInfo = await _dbContext.Queryable() + .LeftJoin((x, b) => x.BusinessPlaceCode == b.Code) + .LeftJoin((x, b, bs) => bs.PlaceCode == b.Code) + .Where(x => x.DeviceUniqueNo == deviceUniqueNo && x.BusinessPlaceCode.HasValue) + .Select((x, b, bs) => new + { + b.Code, + bs.ShiftCode, + Factory = x.TenantId + }) + .SingleAsync(); + + bindingInfo.ThrowIfNull("设备尚未登记营业点"); + + //获取当前消费餐次 + var shifts = await _dbContext.Queryable() + .Where(x => x.ShiftCode == bindingInfo.ShiftCode) + .Select(x => new { x.ShiftType, x.Start, x.End }) + .ToListAsync(); + shifts.ThrowIfEmpty("营业点餐次尚未设置"); + + var shift = shifts.FirstOrDefault(x => new TimePeriod + { + Start = x.Start, + End = x.End + }.IsPresent(costDateTime)); + shift.ThrowIfNull("非就餐时间"); + + var costDate = await _canteenSettingService.GetCostDateAsync(bindingInfo.Factory, costDateTime); + + return new OrderCheckResultDto + { + Factory = bindingInfo.Factory, + CostDate = costDate, + ShiftType = shift!.ShiftType, + BusinessPlaceCode = bindingInfo.Code, + BusinessShiftCode = bindingInfo.ShiftCode + }; + } + + /// + /// 消费更新处理 + /// + /// + /// + public async Task TryConsumeAsync(long orderId, Func action) + { + try + { + await action(); + + await _dbContext.Updateable() + .SetColumns(x => x.OrderStatus == CanteenOrderStatusEnumType.Success) + .SetColumns(x => x.ModifiedTime == DateTime.Now) + .Where(x => x.Id == orderId) + .ExecuteCommandAsync(); + } + catch (Exception ex) + { + await _dbContext.Updateable() + .SetColumns(x => x.OrderStatus == CanteenOrderStatusEnumType.Failure) + .SetColumns(x => x.ModifiedTime == DateTime.Now) + .SetColumns(x => x.Result == ex.Message) + .Where(x => x.Id == orderId) + .ExecuteCommandAsync(); + + throw; + } + } +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Services/IConsumeHandler.cs b/Src/Dlhis.Canteen/Order/Services/IConsumeHandler.cs new file mode 100644 index 00000000..4de14676 --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Services/IConsumeHandler.cs @@ -0,0 +1,20 @@ +using Dlhis.Canteen.Order.Request; +using Dlhis.Canteen.Order.Response; +using Dlhis.Entity.Basic.Entity; +using Kurisu.AspNetCore.UnifyResultAndValidation; + +namespace Dlhis.Canteen.Order.Services; + +/// +/// 消费处理器 +/// +public interface IConsumeHandler : IScopeDependency +{ + /// + /// 消费 + /// + /// + /// + /// + Task> ConsumeAsync(OrderAddRequest request, GainfoEntity gainfo); +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Services/IOrderCommonHandler.cs b/Src/Dlhis.Canteen/Order/Services/IOrderCommonHandler.cs new file mode 100644 index 00000000..c1762121 --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Services/IOrderCommonHandler.cs @@ -0,0 +1,29 @@ +using Dlhis.Canteen.Order.Dto; + +namespace Dlhis.Canteen.Order.Services; + +public interface IOrderCommonHandler : IScopeDependency +{ + /// + /// 消费更新处理 + /// + /// + /// + /// + Task TryConsumeAsync(long orderId, Func action); + + /// + /// 设置设备ip + /// + /// + /// + Task SetDeviceIpInfoAsync(string deviceNo); + + /// + /// 消费前预检 + /// + /// 设备号 + /// 消费时间 + /// + Task PreCheckAsync(string deviceUniqueNo, DateTime costDateTime); +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs b/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs index d6284a13..22203026 100644 --- a/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs +++ b/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs @@ -1,19 +1,14 @@ using Dlhis.Basic.Api; -using Dlhis.Canteen.Order.Dto; using Dlhis.Canteen.Order.Repository; +using Dlhis.Canteen.Order.Request; +using Dlhis.Canteen.Order.Response; using Dlhis.Canteen.Setting.Services; +using Dlhis.Constants; using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Canteen.Entities; using Dlhis.Entity.Canteen.Enums; -using Dlhis.Entity.Device.Entities; -using Kurisu.AspNetCore.Utils; -using Kurisu.AspNetCore.Utils.Extensions; -using Mapster; -using SqlSugar; -using Dlhis.Canteen.Order.Response; -using Dlhis.Canteen.Order.Request; using Dlhis.Entity.Device; -using Dlhis.Entity.Device.Enums; +using Dlhis.Entity.Device.Entities; using Kurisu.AspNetCore.Authentication.Abstractions; using Kurisu.AspNetCore.Cache; using Kurisu.AspNetCore.CustomClass; @@ -21,7 +16,10 @@ using Kurisu.AspNetCore.DataAccess.Dto; using Kurisu.AspNetCore.DataAccess.SqlSugar.Extensions; using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; using Kurisu.AspNetCore.UnifyResultAndValidation; -using Dlhis.Constants; +using Kurisu.AspNetCore.Utils; +using Kurisu.AspNetCore.Utils.Extensions; +using Mapster; +using SqlSugar; namespace Dlhis.Canteen.Order.Services.Implements; @@ -30,31 +28,34 @@ public class OrderService : IOrderService private readonly ICanteenSettingService _canteenSettingService; private readonly IDbContext _dbContext; private readonly ICurrentUser _currentUser; - private readonly IHttpContextAccessor _httpContextAccessor; private readonly IBalanceRepository _balanceRepository; private readonly IOrderRepository _orderRepository; private readonly RedisCache _redisCache; private readonly IGainfoPermissionApi _gainfoPermissionApi; + private readonly IOrderCommonHandler _orderCommonHandler; + private readonly INamedResolver _namedResolver; private readonly ILogger _logger; public OrderService(ICanteenSettingService canteenSettingService, IDbContext dbContext, ICurrentUser currentUser, - IHttpContextAccessor httpContextAccessor, IBalanceRepository balanceRepository, IOrderRepository orderRepository, RedisCache redisCache, IGainfoPermissionApi gainfoPermissionApi, + IOrderCommonHandler orderCommonHandler, + INamedResolver namedResolver, ILogger logger) { _canteenSettingService = canteenSettingService; _dbContext = dbContext; _currentUser = currentUser; - _httpContextAccessor = httpContextAccessor; _balanceRepository = balanceRepository; _orderRepository = orderRepository; _redisCache = redisCache; _gainfoPermissionApi = gainfoPermissionApi; + _orderCommonHandler = orderCommonHandler; + _namedResolver = namedResolver; _logger = logger; } @@ -75,7 +76,7 @@ public class OrderService : IOrderService /// public async Task> AddAsync(OrderAddRequest request) { - await SetDeviceIpInfoAsync(request.DeviceUniqueNo); + await _orderCommonHandler.SetDeviceIpInfoAsync(request.DeviceUniqueNo); await using var handler = await _redisCache.LockAsync($"consume:device:{request.UserUniqueNo}"); handler.Acquired.ThrowIfFalse("请勿频繁消费"); @@ -152,39 +153,47 @@ public class OrderService : IOrderService var s = long.TryParse(request.UserUniqueNo, out var userId); GainfoEntity gainfo; + //条码消费 if (request.Source == BalanceRecordSourceEnumType.QrCode) { - if (request.UserUniqueNo.StartsWith(QrCodeConstants.User)) + var flag = request.UserUniqueNo.Substring(0, 2); + switch (flag) { - gainfo = await GetUserByQrCodeAsync(request.UserUniqueNo); - await CheckPermissionAsync(gainfo.Code, request.DeviceUniqueNo); - } - else if (request.UserUniqueNo.StartsWith(QrCodeConstants.EMealVoucher)) - { - request.Source = BalanceRecordSourceEnumType.EMealVoucher; - var eMealVoucherQrCode = await _dbContext.Queryable().Where(x => x.QrCode == request.UserUniqueNo).SingleAsync(); - eMealVoucherQrCode.ThrowIfNull("无法识别"); - var code = eMealVoucherQrCode.OriginData.Split(':')[2]; - gainfo = await _dbContext.Queryable().Where(x => x.Code == code).SingleAsync(); - } - else if (request.UserUniqueNo.StartsWith(QrCodeConstants.Birthday)) - { - var ticket = await _dbContext.Queryable().Where(x => x.Type == TicketEnumType.Birthday).Where(x => x.QrCode == request.UserUniqueNo).SingleAsync(); - ticket.ThrowIfNull("无法识别"); - gainfo = await _dbContext.Queryable().Where(x => x.Code == ticket.UserCode).SingleAsync(); - } - else - { - throw new UserFriendlyException($"无法识别{request.UserUniqueNo}"); + case QrCodeConstants.User: + { + gainfo = await GetUserByQrCodeAsync(request.UserUniqueNo); + await CheckPermissionAsync(gainfo.Code, request.DeviceUniqueNo); + } + break; + case QrCodeConstants.EMealVoucher: + { + request.Source = BalanceRecordSourceEnumType.EMealVoucher; + var eMealVoucherQrCode = await _dbContext.Queryable().Where(x => x.QrCode == request.UserUniqueNo).SingleAsync(); + eMealVoucherQrCode.ThrowIfNull("无法识别"); + var code = eMealVoucherQrCode.OriginData.Split(':')[2]; + gainfo = await _dbContext.Queryable().Where(x => x.Code == code).SingleAsync(); + } + break; + case QrCodeConstants.Birthday: + { + request.Source = BalanceRecordSourceEnumType.Birthday; + var ticket = await _dbContext.Queryable().Where(x => x.Type == TicketEnumType.Birthday).Where(x => x.QrCode == request.UserUniqueNo).SingleAsync(); + ticket.ThrowIfNull("无法识别"); + gainfo = await _dbContext.Queryable().Where(x => x.Code == ticket.UserCode).SingleAsync(); + } + break; + + default: throw new UserFriendlyException($"无法识别{request.UserUniqueNo}"); } } else { //根据档案号 + //区分userId和code: 工号纯数字避免跟userId相同导致查询到的消费人错误 gainfo = await _dbContext.Queryable() - .WhereIF(request.Source == BalanceRecordSourceEnumType.Card, x => x.CardNo == request.UserUniqueNo) - .WhereIF(request.Source != BalanceRecordSourceEnumType.Card && !request.IsUserId, x => x.Code == request.UserUniqueNo) - .WhereIF(request.Source != BalanceRecordSourceEnumType.Card && request.IsUserId && s, x => x.Id == userId) + .WhereIF(request.Source == BalanceRecordSourceEnumType.Card, x => x.CardNo == request.UserUniqueNo) //卡号 + .WhereIF(request.Source != BalanceRecordSourceEnumType.Card && !request.IsUserId, x => x.Code == request.UserUniqueNo) //工号 + .WhereIF(request.Source != BalanceRecordSourceEnumType.Card && request.IsUserId && s, x => x.Id == userId) //user id .SingleAsync(); } @@ -230,7 +239,7 @@ public class OrderService : IOrderService //一天消费金额 var dayCostMax = await _canteenSettingService.GetSettingAsync(CanteenSettingItemEnumType.DayCostMax, order.TenantId, order.UserNo); - if (dayCostMax != default) + if (dayCostMax != 0) { //消费记录一定会有营业点代码!.Value是安全用法 var cost = todayAllBalanceRecords @@ -242,7 +251,7 @@ public class OrderService : IOrderService //一次消费金额 var onceCostMax = await _canteenSettingService.GetSettingAsync(CanteenSettingItemEnumType.OnceCostMax, order.TenantId, order.UserNo); - if (onceCostMax != default) + if (onceCostMax != 0) { (Math.Abs(order.Amount) > onceCostMax).ThrowIfTrue($"单次消费金额不能超出{onceCostMax}元"); } @@ -251,7 +260,7 @@ public class OrderService : IOrderService if (order.BusinessPlaceCode.HasValue && supermarketCodes.Contains(order.BusinessPlaceCode.Value)) { var dayMarketCostMax = await _canteenSettingService.GetSettingAsync(CanteenSettingItemEnumType.DayMarketCostMax, order.TenantId, order.UserNo); - if (dayMarketCostMax != default) + if (dayMarketCostMax != 0) { var cost = todayAllBalanceRecords.Where(x => x.BusinessPlaceCode.HasValue) .Where(x => supermarketCodes.Contains(x.BusinessPlaceCode.Value)) @@ -354,7 +363,7 @@ public class OrderService : IOrderService /// private async Task ConsumeAsync(ConsumeRequest request, GainfoEntity gainfo) { - var checkData = await PreCheckAsync(request.DeviceUniqueNo, request.CostDateTime); + var checkData = await _orderCommonHandler.PreCheckAsync(request.DeviceUniqueNo, request.CostDateTime); var order = await _orderRepository.CreateDeviceConsumeOrderAsync(checkData, request, gainfo); if (request.Image.IsPresent()) @@ -367,7 +376,7 @@ public class OrderService : IOrderService }); } - await TryConsumeAsync(order.Id, async () => + await _orderCommonHandler.TryConsumeAsync(order.Id, async () => { //校验 await Verify(order); @@ -380,78 +389,11 @@ public class OrderService : IOrderService }); } - private async Task> EMealVoucherConsumeAsync(OrderAddRequest request, GainfoEntity gainfo) - { - var msg = string.Empty; - var originData = await _dbContext.Queryable() - .Where(x => x.QrCode == request.UserUniqueNo) - .Select(x => x.OriginData) - .SingleAsync(); - if (originData.IsEmpty()) - { - msg = "无法识别"; - return new DefaultApiResult - { - Code = ApiStateCode.Error, - Data = null, - Msg = msg - }; - } - - var originDataItems = originData.Split(":"); - var no = originDataItems[0]; - var idNo = originDataItems[3]; - - try - { - await EMealVoucherConsumeAsync(request.Adapt(), gainfo, originData, no, idNo); - } - catch (Exception ex) - { - msg = ex.Message; - } - - //获取餐券余额 - var data = await _dbContext.Queryable() - .LeftJoin((x, d) => x.No == d.No) - .Where((x, d) => x.No == no && d.IdNo == idNo) - .Select((x, d) => new OrderResultResponse - { - UserCode = d.IdNo, - UserName = d.Name, - CurrentBalance = d.Balance - }) - .SingleAsync(); - - try - { - var v = data.UserCode ?? string.Empty; - var vc = v.ToCharArray().Reverse().ToList(); - - for (int i = 0; i < vc.Count; i++) - { - if (i + 1 > 4) - { - vc[i] = '*'; - } - } - - vc.Reverse(); - data.UserCode = string.Join("", vc); - } - catch (Exception) - { - //ignore - } - - return new DefaultApiResult - { - Code = msg.IsPresent() ? ApiStateCode.Error : ApiStateCode.Success, - Data = data, - Msg = msg - }; - } + // private async Task> BirthdayConsumeAsync(OrderAddRequest request, GainfoEntity gainfo) + // { + // + // } private async Task OfflineConsumeAsync(OrderAddRequest request, GainfoEntity gainfo) @@ -490,8 +432,9 @@ public class OrderService : IOrderService { var message = string.Empty; - if (request.Source == BalanceRecordSourceEnumType.EMealVoucher) - return await EMealVoucherConsumeAsync(request, gainfo); + var otherTypes = new[] { BalanceRecordSourceEnumType.EMealVoucher, BalanceRecordSourceEnumType.Birthday }; + if (otherTypes.Contains(request.Source)) + return await _namedResolver.GetService(request.Source.ToString()).ConsumeAsync(request, gainfo); if (request.IsOffline) await OfflineConsumeAsync(request, gainfo); @@ -547,144 +490,6 @@ public class OrderService : IOrderService return await _dbContext.Queryable().Where(x => x.Code == gainfoQrCode.UserCode).SingleAsync(); } - /// - /// 电子餐券消费 - /// - /// - /// - /// - /// - /// - private async Task EMealVoucherConsumeAsync(ConsumeRequest request, GainfoEntity gainfo, string originData, string no, string idNo) - { - var now = DateTime.Now; - - var current = await _dbContext.Queryable() - .Where(x => x.No == no && x.IdNo == idNo) - .Select(x => new { x.Name, x.Start, x.End }) - .SingleAsync(); - - (now >= current.Start && now <= current.End).ThrowIfFalse("餐券已过期"); - - - var eMealVoucher = await _dbContext.Queryable().Where(x => x.No == no).SingleAsync(); - - var checkData = await PreCheckAsync(request.DeviceUniqueNo, request.CostDateTime); - var isSupermarket = await _dbContext.Queryable().Where(x => x.Code == checkData.BusinessPlaceCode) - .Select(x => x.Catalog == CanteenBusinessPlaceCatalogEnumType.Supermarket) - .SingleAsync(); - - isSupermarket.ThrowIfTrue("禁止在超市消费"); - - eMealVoucher.Factories.Contains(checkData.Factory).ThrowIfFalse("禁止在当前厂区消费"); - - gainfo.No = originData; - var order = await _orderRepository.CreateDeviceConsumeOrderAsync(checkData, request, gainfo); - - await TryConsumeAsync(order.Id, async () => - { - await CommonHelper.RetryAsync(async () => - { - var detail = await _dbContext.Queryable().Where(x => x.No == no && x.IdNo == idNo).SingleAsync(); - //金额+- - detail.Balance += order.Amount; - (detail.Balance < 0).ThrowIfTrue("余额不足,无法消费"); - - await _dbContext.Updateable() - .SetColumns(x => x.Balance == detail.Balance) - .SetColumns(x => x.Version == x.Version + 1) - .SetColumns(x => x.ModifiedTime == DateTime.Now) - .Where(x => x.No == no && x.IdNo == idNo && x.Version == detail.Version) //必须与查询时的version一致 - .ExecuteCommandThrowVersionAsync(); - - //写入记录 - var balanceRecord = order.Adapt(); - balanceRecord.Remark = $"({no})" + current.Name; - await _dbContext.InsertAsync(balanceRecord); - }, 5); - }); - } - - /// - /// 消费前预检 - /// - /// - /// - /// - private async Task PreCheckAsync(string deviceUniqueNo, DateTime costDateTime) - { - var device = await _dbContext.Queryable().Where(x => x.UniqueNo == deviceUniqueNo).SingleAsync(); - device.ThrowIfNull("设备尚未登记"); - device.Enable.ThrowIfFalse("设备已被停用"); - (device.Type != DeviceTypeEnumType.Meal).ThrowIfTrue("设备类型有误"); - - var bindingInfo = await _dbContext.Queryable() - .LeftJoin((x, b) => x.BusinessPlaceCode == b.Code) - .LeftJoin((x, b, bs) => bs.PlaceCode == b.Code) - .Where(x => x.DeviceUniqueNo == deviceUniqueNo && x.BusinessPlaceCode.HasValue) - .Select((x, b, bs) => new - { - b.Code, - bs.ShiftCode, - Factory = x.TenantId - }) - .SingleAsync(); - - bindingInfo.ThrowIfNull("设备尚未登记营业点"); - - //获取当前消费餐次 - var shifts = await _dbContext.Queryable() - .Where(x => x.ShiftCode == bindingInfo.ShiftCode) - .Select(x => new { x.ShiftType, x.Start, x.End }) - .ToListAsync(); - shifts.ThrowIfEmpty("营业点餐次尚未设置"); - - var shift = shifts.FirstOrDefault(x => new TimePeriod - { - Start = x.Start, - End = x.End - }.IsPresent(costDateTime)); - shift.ThrowIfNull("非就餐时间"); - - var costDate = await _canteenSettingService.GetCostDateAsync(bindingInfo.Factory, costDateTime); - - return new OrderPreCheckDto - { - Factory = bindingInfo.Factory, - CostDate = costDate, - ShiftType = shift!.ShiftType, - BusinessPlaceCode = bindingInfo.Code, - BusinessShiftCode = bindingInfo.ShiftCode - }; - } - - /// - /// 设置设备ip - /// - /// - private async Task SetDeviceIpInfoAsync(string deviceNo) - { - try - { - if (_httpContextAccessor.HttpContext!.Request.Headers.TryGetValue("X-Requested-DeviceIp", out var ip)) - { - var device = await _dbContext.Queryable().Where(x => x.UniqueNo == deviceNo).SingleAsync(); - device.ConnectionInfo = new - { - ip = ip.ToString() - }.ToJson(); - - await _dbContext.UpdateAsync(device); - } - - _logger.LogInformation("设备ip获取:{deviceNo} {ip}", deviceNo, ip); - } - catch (Exception) - { - //ignore - } - } - /// /// 检查是否能够进行离线消费,处理hpt重复提交 /// @@ -700,34 +505,9 @@ public class OrderService : IOrderService if (orders.IsEmpty()) return true; - var order = orders.FirstOrDefault(); + var order = orders.FirstOrDefault()!; //存在在线order数据,但是状态未成功 return !order.IsOffline && order.OrderStatus != CanteenOrderStatusEnumType.Success; } - - private async Task TryConsumeAsync(long orderId, Func action) - { - try - { - await action(); - - await _dbContext.Updateable() - .SetColumns(x => x.OrderStatus == CanteenOrderStatusEnumType.Success) - .SetColumns(x => x.ModifiedTime == DateTime.Now) - .Where(x => x.Id == orderId) - .ExecuteCommandAsync(); - } - catch (Exception ex) - { - await _dbContext.Updateable() - .SetColumns(x => x.OrderStatus == CanteenOrderStatusEnumType.Failure) - .SetColumns(x => x.ModifiedTime == DateTime.Now) - .SetColumns(x => x.Result == ex.Message) - .Where(x => x.Id == orderId) - .ExecuteCommandAsync(); - - throw; - } - } } \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Tickets/Services/Implements/BirthdayTicketService.cs b/Src/Dlhis.Canteen/Tickets/Services/Implements/BirthdayTicketService.cs index ba75b0e9..78bc5e07 100644 --- a/Src/Dlhis.Canteen/Tickets/Services/Implements/BirthdayTicketService.cs +++ b/Src/Dlhis.Canteen/Tickets/Services/Implements/BirthdayTicketService.cs @@ -40,8 +40,8 @@ public class BirthdayTicketService : BaseTicketService, IBirthdayTicketService { Type = TicketEnumType.Birthday, UserCode = item.Code, - OriginalAmount = amount, - Amount = amount, + OriginalBalance = amount, + Balance = amount, QrCode = qrCode, Remark = todayStr + "生日券", TenantId = item.Factory diff --git a/Src/Dlhis.Entity/Canteen/Entities/TicketEntity.cs b/Src/Dlhis.Entity/Canteen/Entities/TicketEntity.cs index d3ca3c05..451b7ba4 100644 --- a/Src/Dlhis.Entity/Canteen/Entities/TicketEntity.cs +++ b/Src/Dlhis.Entity/Canteen/Entities/TicketEntity.cs @@ -10,7 +10,7 @@ namespace Dlhis.Entity.Canteen.Entities; /// 券 /// [SugarTable("Tickets", TableDescription = "券")] -public class TicketEntity : SugarBaseEntity, ITenantId +public class TicketEntity : SugarBaseEntity, ITenantId, IVersion { /// /// 类型 @@ -28,13 +28,13 @@ public class TicketEntity : SugarBaseEntity, ITenantId /// 初始余额 /// [SugarColumn(IsNullable = false, ColumnDescription = "初始余额")] - public decimal OriginalAmount { get; set; } + public decimal OriginalBalance { get; set; } /// /// 当前余额 /// [SugarColumn(IsNullable = false, ColumnDescription = "当前余额")] - public decimal Amount { get; set; } + public decimal Balance { get; set; } /// /// 消费码 @@ -49,4 +49,7 @@ public class TicketEntity : SugarBaseEntity, ITenantId public string Remark { get; set; } public string TenantId { get; set; } + + /// + public int Version { get; set; } } \ No newline at end of file diff --git a/Src/Dlhis.Entity/Canteen/Enums/BalanceRecordSourceEnumType.cs b/Src/Dlhis.Entity/Canteen/Enums/BalanceRecordSourceEnumType.cs index 79d86f43..16f5a7fe 100644 --- a/Src/Dlhis.Entity/Canteen/Enums/BalanceRecordSourceEnumType.cs +++ b/Src/Dlhis.Entity/Canteen/Enums/BalanceRecordSourceEnumType.cs @@ -37,6 +37,14 @@ public enum BalanceRecordSourceEnumType /// [Description("电子餐券")] EMealVoucher, + + + /// + /// 生日券 + /// + [Description("生日券")] + Birthday, + //********************************************************************************************************* /// @@ -70,7 +78,7 @@ public enum BalanceRecordSourceEnumType /// [Description("其他基地餐补")] OtherTenantSubsidies, - + /// /// 余额写入 /// From 20e5350b4947472a4abfb742781c7b5703743225 Mon Sep 17 00:00:00 2001 From: liguoyang Date: Wed, 19 Feb 2025 12:11:13 +0800 Subject: [PATCH 4/4] =?UTF-8?q?add=20=E8=80=83=E5=8B=A4=E5=88=86=E9=A1=B5?= =?UTF-8?q?=E5=92=8C=E8=A1=A5=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Implements/SapBusinessTripService.cs | 1 - .../Controllers/WorkGridsController.cs | 25 ++++++- .../WorkGridScheduleAttendancePageRequest.cs | 13 ++++ .../WorkGridScheduleAttendancePageResponse.cs | 63 +++++++++++++++++ .../Services/IWorkGridScheduleService.cs | 5 ++ .../Implements/WorkGridScheduleService.cs | 68 ++++++++++++++++++- 6 files changed, 171 insertions(+), 4 deletions(-) create mode 100644 Src/Dlhis.Basic/WorkGrids/Request/WorkGridScheduleAttendancePageRequest.cs create mode 100644 Src/Dlhis.Basic/WorkGrids/Response/WorkGridScheduleAttendancePageResponse.cs diff --git a/Src/Dlhis.Basic/Organization/Services/Implements/SapBusinessTripService.cs b/Src/Dlhis.Basic/Organization/Services/Implements/SapBusinessTripService.cs index 7460cf1a..a89f549d 100644 --- a/Src/Dlhis.Basic/Organization/Services/Implements/SapBusinessTripService.cs +++ b/Src/Dlhis.Basic/Organization/Services/Implements/SapBusinessTripService.cs @@ -1,4 +1,3 @@ -using Dlhis.Basic.Device.Services; using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Device.Entities; using Dlhis.Entity.Device.Enums; diff --git a/Src/Dlhis.Basic/WorkGrids/Controllers/WorkGridsController.cs b/Src/Dlhis.Basic/WorkGrids/Controllers/WorkGridsController.cs index 4549aca8..c8a31385 100644 --- a/Src/Dlhis.Basic/WorkGrids/Controllers/WorkGridsController.cs +++ b/Src/Dlhis.Basic/WorkGrids/Controllers/WorkGridsController.cs @@ -51,7 +51,7 @@ public class WorkGridsController : ControllerBase { return await _workGridService.GetPageAsync(request); } - + /// /// 修改网格管理者 /// @@ -61,7 +61,7 @@ public class WorkGridsController : ControllerBase { await _workGridService.ChangeAsync(request.From, request.To); } - + /// /// 获取我的网格信息 /// @@ -151,4 +151,25 @@ public class WorkGridsController : ControllerBase { return await _workGridScheduleService.GetListSummaryAsync(workGridCode, start, end); } + + /// + /// 获取网格人员考勤记录分页 + /// + /// + /// + [HttpGet("user/attendance/page")] + public async Task> GetAttendancePageAsync(WorkGridScheduleAttendancePageRequest request) + { + return await _workGridScheduleService.GetAttendancePageAsync(request); + } + + /// + /// 网格人员考勤记录补传 + /// + /// + [HttpPost("user/attendance/upload")] + public async Task ManuallyUploadAsync(List logIds) + { + await _workGridScheduleService.ManuallyUploadAsync(logIds); + } } \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Request/WorkGridScheduleAttendancePageRequest.cs b/Src/Dlhis.Basic/WorkGrids/Request/WorkGridScheduleAttendancePageRequest.cs new file mode 100644 index 00000000..bc783d40 --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Request/WorkGridScheduleAttendancePageRequest.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations; +using Kurisu.AspNetCore.CustomClass; +using Kurisu.AspNetCore.DataAccess.Dto; + +namespace Dlhis.Basic.WorkGrids.Request; + +public class WorkGridScheduleAttendancePageRequest : PageDto +{ + [Required(ErrorMessage = "工号不能为空")] + public string UserCode { get; set; } + + public DateTimePeriod DateTimePeriod { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Response/WorkGridScheduleAttendancePageResponse.cs b/Src/Dlhis.Basic/WorkGrids/Response/WorkGridScheduleAttendancePageResponse.cs new file mode 100644 index 00000000..e4e3ff16 --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Response/WorkGridScheduleAttendancePageResponse.cs @@ -0,0 +1,63 @@ +using Newtonsoft.Json; + +namespace Dlhis.Basic.WorkGrids.Response; + +public class WorkGridScheduleAttendancePageResponse +{ + public long Id { get; set; } + + /// + /// 设备号 + /// + public string DeviceUniqueNo { get; set; } + + /// + /// 设备部署点 + /// + public string DeployPlace { get; set; } + + /// + /// 用户学工号 + /// + public string UserCode { get; set; } + + + /// + /// 用户名称 + /// + public string UserName { get; set; } + + /// + /// 上传时间 + /// + public DateTime RaiseDateTime { get; set; } + + /// + /// 记录创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 序列号 + /// + public string SerialNo { get; set; } + + + [JsonIgnore] + public string OriginUserUniqueNo { get; set; } + + /// + /// 是否同步sap + /// + public bool Sync { get; set; } + + /// + /// 是否需要上传 + /// + public bool NeedUpload { get; set; } + + /// + /// 是否有效 + /// + public bool Status { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Services/IWorkGridScheduleService.cs b/Src/Dlhis.Basic/WorkGrids/Services/IWorkGridScheduleService.cs index 2b6783d9..df2d46fc 100644 --- a/Src/Dlhis.Basic/WorkGrids/Services/IWorkGridScheduleService.cs +++ b/Src/Dlhis.Basic/WorkGrids/Services/IWorkGridScheduleService.cs @@ -1,5 +1,6 @@ using Dlhis.Basic.WorkGrids.Request; using Dlhis.Basic.WorkGrids.Response; +using Kurisu.AspNetCore.DataAccess.Dto; namespace Dlhis.Basic.WorkGrids.Services; @@ -13,4 +14,8 @@ public interface IWorkGridScheduleService : IScopeDependency Task> GetListAsync(Guid workGridCode, DateOnly date); Task> GetListSummaryAsync(Guid workGridCode, DateOnly start, DateOnly end); + + Task> GetAttendancePageAsync(WorkGridScheduleAttendancePageRequest request); + + Task ManuallyUploadAsync(List logIds); } \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridScheduleService.cs b/Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridScheduleService.cs index 1e2f25c7..40c2d706 100644 --- a/Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridScheduleService.cs +++ b/Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridScheduleService.cs @@ -1,6 +1,12 @@ using Dlhis.Basic.WorkGrids.Request; using Dlhis.Basic.WorkGrids.Response; using Dlhis.Entity.Basic.Entity; +using Dlhis.Entity.Device.Entities; +using Dlhis.Jip.Api; +using Dlhis.Jip.Api.Requests; +using Kurisu.AspNetCore.CustomClass; +using Kurisu.AspNetCore.DataAccess.Dto; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Extensions; using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; using Kurisu.AspNetCore.Utils.Extensions; @@ -9,10 +15,12 @@ namespace Dlhis.Basic.WorkGrids.Services.Implements; public class WorkGridScheduleService : IWorkGridScheduleService { private readonly IDbContext _dbContext; + private readonly IJinkoJipApi _jinkoJipApi; - public WorkGridScheduleService(IDbContext dbContext) + public WorkGridScheduleService(IDbContext dbContext ,IJinkoJipApi jinkoJipApi) { _dbContext = dbContext; + _jinkoJipApi = jinkoJipApi; } public async Task SaveAsync(WorkGridScheduleSaveRequest request) @@ -78,4 +86,62 @@ public class WorkGridScheduleService : IWorkGridScheduleService }; }); } + + /// + public async Task> GetAttendancePageAsync(WorkGridScheduleAttendancePageRequest request) + { + return await _dbContext.Queryable() + .LeftJoin((log, d) => log.DeviceUniqueNo == d.UniqueNo) + .Where(log => log.UserUniqueNo == request.UserCode ) + .WhereIF(request.DateTimePeriod.HasValue(), log => log.RaiseDateTime >= request.DateTimePeriod.Start + && log.RaiseDateTime <= request.DateTimePeriod.End) + .OrderBy(log => log.RaiseDateTime) + .Select((log, d) => new WorkGridScheduleAttendancePageResponse + { + Id = log.Id, + OriginUserUniqueNo = log.UserUniqueNo, + DeviceUniqueNo = log.DeviceUniqueNo, + RaiseDateTime = log.RaiseDateTime, + CreateTime = log.CreateTime, + SerialNo = log.SerialNo, + DeployPlace = d.DeployPlace, + Sync = log.Sync, + Status = log.Status + }) + .ToPageListAsync(request); + } + + /// + public async Task ManuallyUploadAsync(List logIds) + { + var requests = await _dbContext.Queryable() + .LeftJoin((x, device) => x.DeviceUniqueNo == device.UniqueNo) + .Where(x => logIds.Contains(x.Id)) + .Where(x => x.NeedUpload && x.Sync == false) + .Select((x, device) => new UploadAttendanceRequestItem + { + Id = x.Id, + RaiseDateTime = x.RaiseDateTime, + ZNOTE = device.DeployPlace, + ZWORKCODE = x.UserUniqueNo + }) + .ToListAsync(); + + if (requests.Any()) + { + foreach (var items in requests.Chunk(500)) + { + var ids = items.Select(x => x.Id).ToList(); + var r = await _jinkoJipApi.UploadAttendancesAsync(new UploadAttendanceRequest { Data = items.ToList() }); + if (r.OLX == "0") + { + await _dbContext.Updateable() + .SetColumns(x => x.Sync == true) + .SetColumns(x => x.ModifiedTime == DateTime.Now) + .Where(x => ids.Contains(x.Id)) + .ExecuteCommandAsync(); + } + } + } + } } \ No newline at end of file