using System.Collections.Generic;
using System.Text;
using Dlhis.Basic.Api;
using Dlhis.Canteen.Tickets.Services;
using Dlhis.Canteen.Tickets.Services.Implements;
using Dlhis.Canteen.Voucher.Requests;
using Dlhis.Canteen.Voucher.Response;
using Dlhis.Constants;
using Dlhis.Entity.Basic.Entity;
using Dlhis.Entity.Basic.Enums;
using Dlhis.Entity.Canteen.Entities;
using Dlhis.Entity.Canteen.Entities.Voucher;
using Dlhis.Entity.Canteen.Enums;
using Kurisu.AspNetCore.CustomClass;
using Kurisu.AspNetCore.DataAccess.Dto;
using Kurisu.AspNetCore.DataAccess.Entity;
using Kurisu.AspNetCore.DataAccess.SqlSugar.Extensions;
using Kurisu.AspNetCore.DataAccess.SqlSugar.Services;
using Kurisu.AspNetCore.Utils.Extensions;
using SqlSugar;
namespace Dlhis.Canteen.Voucher.Services.Implements;
[Service("BirthdayVoucherService")]
public class BirthdayVoucherService : BaseVoucherService, IVoucherService
{
private readonly IFactorySettingApi _factorySettingApi;
///
public BirthdayVoucherService(IDbContext dbContext, IFactorySettingApi factorySettingApi) : base(dbContext)
{
_factorySettingApi = factorySettingApi;
}
///
public override async Task CreateAsync(Dictionary pairs)
{
var p = GetDateRange(pairs);
var remark = pairs != null && pairs.TryGetValue("remark", out var r) ? r?.ToString() : string.Empty;
var userCodes = pairs != null && pairs.TryGetValue("userCodes", out var c) ? c as List : new List();
//忽略tenant操作
await DbContext.IgnoreAsync(async () =>
{
var gainfos = await GetGainfosAsync(userCodes, p.begin, p.end);
var departmentNos = gainfos.Select(g => g.DepartmentNo).Distinct().ToList();
var departmentInfos = await DbContext.Queryable()
.Where(x => departmentNos.Contains(x.No))
.ToListAsync();
foreach (var gainfo in gainfos.Where(x => x.Birthday.HasValue))
{
await DbContext.Client.Ado.BeginTranAsync();
try
{
var years = GetBirthdayYears(gainfo.Birthday.Value, p.begin, p.end);
foreach (var year in years)
{
await GenerateVoucherAsync(gainfo, departmentInfos, year, remark);
}
await DbContext.Client.Ado.CommitTranAsync();
}
catch (Exception)
{
await DbContext.Client.Ado.RollbackTranAsync();
}
}
});
}
private async Task GenerateVoucherAsync(GainfoEntity gainfo, List departments, int year, string remark)
{
//生日券金额固定100元
const decimal amount = 100;
var dayStr = year + "-" + gainfo.Birthday.Value.ToString("MM-dd");
//由数据类型+工号+生日组成唯一标识
var no = GenerateUniqueCode(VoucherEnumType.Birthday.ToString() + gainfo.Code + dayStr);
if (!await DbContext.Queryable()
.Where(x => x.No == no)
.Where(x => x.Type == VoucherEnumType.Birthday)
.AnyAsync())
{
await DbContext.InsertAsync(new VoucherEntity
{
No = no,
Type = VoucherEnumType.Birthday,
ClaimDateTime = new DateTime(year, gainfo.Birthday.Value.Month, gainfo.Birthday.Value.Day),
OriginalBalance = amount,
Balance = amount,
Description = dayStr + "生日券",
Remark = remark,
CreatedBy = 1,
CreateTime = DateTime.Now,
ModifiedBy = 1,
ModifiedTime = DateTime.Now,
Version = 0
});
var department = departments.FirstOrDefault(x => x.No == gainfo.DepartmentNo);
await DbContext.InsertAsync(new VoucherUserInfoEntity
{
VoucherNo = no,
UserCode = gainfo.Code,
CostId = gainfo.CostId,
Werks = gainfo.Werks,
Factory = gainfo.Factory,
DepartmentNo = gainfo.DepartmentNo,
DepartmentNoFirst = department?.DepartmentNoFirst,
DepartmentNoSecond = department?.DepartmentNoSecond,
CreatedBy = 1,
CreateTime = DateTime.Now,
ModifiedBy = 1,
ModifiedTime = DateTime.Now
});
}
}
private (DateTime begin, DateTime end) GetDateRange(Dictionary pairs)
{
var begin = DateTime.Today;
var end = begin;
if (pairs != null)
{
if (pairs.TryGetValue("begin", out var b))
{
if (b is DateTime bb)
{
begin = bb;
}
}
if (pairs.TryGetValue("end", out var e))
{
if (e is DateTime ee)
{
end = ee;
}
}
}
return (begin, end);
}
private async Task> GetGainfosAsync(List userCodes, DateTime begin, DateTime end)
{
var factries = await _factorySettingApi.GetEnableBirthdayVoucherFactoriesAsync();
var isToday = begin.Date == end.Date;
var beginMonth = begin.Month;
var endMonth = end.Month;
var overYear = endMonth < beginMonth;
//var limit = end.Date.AddHours(9);//9点入职
return await DbContext.Queryable()
.WhereIF(userCodes.IsPresent(), x => userCodes.Contains(x.Code))
.Where(x => x.Type == GainfoUserTypeEnumType.Normal || x.Type == GainfoUserTypeEnumType.SecurityWhite)
.Where(x => x.Birthday.HasValue && !string.IsNullOrEmpty(x.CostId))
.WhereIF(isToday, x => SqlFunc.DateValue(x.Birthday.Value, DateType.Month) == begin.Month && SqlFunc.DateValue(x.Birthday.Value, DateType.Day) == begin.Day)
.WhereIF(!isToday && !overYear, x => SqlFunc.DateValue(x.Birthday.Value, DateType.Month) >= beginMonth && SqlFunc.DateValue(x.Birthday.Value, DateType.Month) <= endMonth)
.WhereIF(!isToday && overYear, x => SqlFunc.DateValue(x.Birthday.Value, DateType.Month) >= beginMonth || SqlFunc.DateValue(x.Birthday.Value, DateType.Month) <= endMonth)
.Where(x => factries.Contains(x.Factory))
.ToListAsync();
}
private static string GenerateUniqueCode(string data)
{
return QrCodeConstants.Birthday + data.ToArray().Select(x =>
{
var b = Encoding.ASCII.GetBytes(x.ToString());
int i = b[0];
return Convert.ToString(i);
}).Join(string.Empty);
}
///
/// 计算给定生日在区间 [begin, end] 内出现的所有年份(可能为0、1或多个)。
/// - 对于每个候选年份构造安全的日期(处理 2/29 在非闰年的情况,退到当年最大天数)。
/// - 若区间内找不到匹配年份,则返回包含 begin.Year 的单元素列表作为兜底行为,保持向后兼容。
///
private static List GetBirthdayYears(DateTime birthday, DateTime begin, DateTime end)
{
var years = new List();
// 扩展范围到 begin.Year-1 .. end.Year+1 以覆盖跨年边界的极端情况
var startYear = begin.Year - 1;
var endYear = end.Year + 1;
for (var y = startYear; y <= endYear; y++)
{
var candidate = SafeDate(y, birthday);
if (candidate >= begin && candidate <= end)
{
years.Add(candidate.Year);
}
}
// 去重并排序
years = years.Distinct().OrderBy(x => x).ToList();
return years;
static DateTime SafeDate(int year, DateTime bday)
{
var day = Math.Min(bday.Day, DateTime.DaysInMonth(year, bday.Month));
return new DateTime(year, bday.Month, day);
}
}
public override async Task> GetVoucherBalancePageAsync(VoucherBalancePageRequest request)
{
return await DbContext.Queryable()
.LeftJoin((x, bPlace) => x.BusinessPlaceCode == bPlace.Code)
.LeftJoin((x, bPlace, department) => x.DepartmentNo == department.No)
.Where(x => x.UserCode == request.UserCode)
.WhereIF(request.SearchDateTimePeriod.HasValue(), x => x.CostDate >= request.SearchDateTimePeriod.Start && x.CostDate <= request.SearchDateTimePeriod.End) //成本日期
.Where(x => x.Status == BalanceRecordStatusEnumType.Valid && x.Type == BalanceRecordTypeEnumType.Out) //有效,消费数据
.Where(x => x.Source == BalanceRecordSourceEnumType.Birthday)
.Select((x, bPlace, department) => new VoucherBalancePageResponse
{
No = x.No,
UserNo = x.UserNo,
Name = x.Name,
UserCode = x.UserCode,
DepartmentNo = x.DepartmentNo,
DepartmentName = department.Name,
CostDate = x.CostDate,
Remark = x.Remark,
Source = x.Source,
Type = x.Type,
Amount = SqlFunc.Abs(x.Amount),
BusinessPlaceName = bPlace.Name,
ShiftType = x.ShiftType,
DeviceInfo = x.DeviceUniqueNo,
CostId = x.CostId,
OriginTenantId = x.OriginTenantId,
Status = x.Status,
CostDateTime = x.CostDateTime,
Factory = x.TenantId,
Werks = x.Werks
})
.ToPageListAsync(request);
}
}