fix: 员工排班移除mock与兜底逻辑
All checks were successful
Build and Deploy TenantApi / build-and-deploy (push) Successful in 43s
All checks were successful
Build and Deploy TenantApi / build-and-deploy (push) Successful in 43s
This commit is contained in:
@@ -154,7 +154,15 @@ public sealed class StoreStaffScheduleDto
|
||||
/// <summary>
|
||||
/// Templates。
|
||||
/// </summary>
|
||||
public StoreShiftTemplatesDto Templates { get; set; } = new();
|
||||
public StoreShiftTemplatesDto? Templates { get; set; }
|
||||
/// <summary>
|
||||
/// IsTemplateConfigured。
|
||||
/// </summary>
|
||||
public bool IsTemplateConfigured { get; set; }
|
||||
/// <summary>
|
||||
/// IsScheduleConfigured。
|
||||
/// </summary>
|
||||
public bool IsScheduleConfigured { get; set; }
|
||||
/// <summary>
|
||||
/// Schedules。
|
||||
/// </summary>
|
||||
|
||||
@@ -135,9 +135,6 @@ public sealed class StoreStaffController(
|
||||
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
|
||||
var template = await GetOrCreateTemplateDtoAsync(tenantId, parsedStoreId, cancellationToken);
|
||||
await EnsureStaffScheduleAsync(tenantId, parsedStoreId, entity, template, cancellationToken);
|
||||
|
||||
return ApiResponse<StoreStaffItemDto>.Ok(MapStaff(entity));
|
||||
}
|
||||
|
||||
@@ -186,7 +183,7 @@ public sealed class StoreStaffController(
|
||||
var (tenantId, merchantId) = StoreApiHelpers.GetTenantMerchantContext(storeContextService);
|
||||
await StoreApiHelpers.EnsureStoreAccessibleAsync(dbContext, tenantId, merchantId, parsedStoreId, cancellationToken);
|
||||
|
||||
var template = await GetOrCreateTemplateDtoAsync(tenantId, parsedStoreId, cancellationToken);
|
||||
var template = await GetTemplateDtoAsync(tenantId, parsedStoreId, cancellationToken);
|
||||
var staffs = await dbContext.MerchantStaff
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.StoreId == parsedStoreId)
|
||||
@@ -205,8 +202,10 @@ public sealed class StoreStaffController(
|
||||
var schedules = staffs.Select(staff =>
|
||||
{
|
||||
var shifts = scheduleMap.TryGetValue(staff.Id, out var rows)
|
||||
? NormalizeRowsToShifts(rows, staff, template)
|
||||
: CreateDefaultWeekByRole(staff.RoleType, template);
|
||||
? template is null
|
||||
? []
|
||||
: NormalizeRowsToShifts(rows, template)
|
||||
: [];
|
||||
|
||||
if (staff.Status == StaffStatus.Resigned)
|
||||
{
|
||||
@@ -225,6 +224,8 @@ public sealed class StoreStaffController(
|
||||
StoreId = parsedStoreId.ToString(),
|
||||
WeekStartDate = StoreApiHelpers.ResolveWeekStartDate(weekStartDate),
|
||||
Templates = template,
|
||||
IsTemplateConfigured = template is not null,
|
||||
IsScheduleConfigured = scheduleRows.Count > 0,
|
||||
Schedules = schedules
|
||||
});
|
||||
}
|
||||
@@ -298,19 +299,23 @@ public sealed class StoreStaffController(
|
||||
throw new BusinessException(ErrorCodes.BadRequest, "员工不存在");
|
||||
}
|
||||
|
||||
var template = await GetOrCreateTemplateDtoAsync(tenantId, parsedStoreId, cancellationToken);
|
||||
var template = await GetTemplateDtoAsync(tenantId, parsedStoreId, cancellationToken);
|
||||
if (template is null)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.BadRequest, "请先配置班次模板");
|
||||
}
|
||||
var existingRows = await dbContext.StoreStaffWeeklySchedules
|
||||
.Where(x => x.TenantId == tenantId && x.StoreId == parsedStoreId && x.StaffId == parsedStaffId)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var fallback = NormalizeRowsToShifts(existingRows, staff, template);
|
||||
var fallback = NormalizeRowsToShifts(existingRows, template);
|
||||
var shifts = staff.Status == StaffStatus.Resigned
|
||||
? CreateOffWeekShifts()
|
||||
: NormalizeShifts(request.Shifts, fallback, template);
|
||||
|
||||
dbContext.StoreStaffWeeklySchedules.RemoveRange(existingRows);
|
||||
await dbContext.StoreStaffWeeklySchedules.AddRangeAsync(
|
||||
ToWeeklyEntities(parsedStoreId, parsedStaffId, shifts),
|
||||
ToWeeklyEntities(parsedStoreId, parsedStaffId, shifts, template),
|
||||
cancellationToken);
|
||||
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
@@ -343,7 +348,11 @@ public sealed class StoreStaffController(
|
||||
.ToListAsync(cancellationToken);
|
||||
var staffMap = staffs.ToDictionary(x => x.Id);
|
||||
|
||||
var template = await GetOrCreateTemplateDtoAsync(tenantId, parsedStoreId, cancellationToken);
|
||||
var template = await GetTemplateDtoAsync(tenantId, parsedStoreId, cancellationToken);
|
||||
if (template is null)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.BadRequest, "请先配置班次模板");
|
||||
}
|
||||
var existingRows = await dbContext.StoreStaffWeeklySchedules
|
||||
.Where(x => x.TenantId == tenantId && x.StoreId == parsedStoreId)
|
||||
.ToListAsync(cancellationToken);
|
||||
@@ -351,7 +360,7 @@ public sealed class StoreStaffController(
|
||||
.GroupBy(x => x.StaffId)
|
||||
.ToDictionary(
|
||||
x => x.Key,
|
||||
x => NormalizeRowsToShifts(x.ToList(), staffMap.GetValueOrDefault(x.Key), template));
|
||||
x => NormalizeRowsToShifts(x.ToList(), template));
|
||||
|
||||
var incomingMap = new Dictionary<long, List<StaffDayShiftDto>>();
|
||||
foreach (var schedule in request.Schedules ?? [])
|
||||
@@ -367,7 +376,7 @@ public sealed class StoreStaffController(
|
||||
continue;
|
||||
}
|
||||
|
||||
var fallback = existingMap.GetValueOrDefault(staffId.Value) ?? CreateDefaultWeekByRole(staff.RoleType, template);
|
||||
var fallback = existingMap.GetValueOrDefault(staffId.Value) ?? [];
|
||||
incomingMap[staffId.Value] = NormalizeShifts(schedule.Shifts, fallback, template);
|
||||
}
|
||||
|
||||
@@ -385,7 +394,7 @@ public sealed class StoreStaffController(
|
||||
}
|
||||
else
|
||||
{
|
||||
shifts = existingMap.GetValueOrDefault(staff.Id) ?? CreateDefaultWeekByRole(staff.RoleType, template);
|
||||
shifts = existingMap.GetValueOrDefault(staff.Id) ?? [];
|
||||
}
|
||||
|
||||
finalSchedules.Add(new StaffScheduleDto
|
||||
@@ -397,7 +406,7 @@ public sealed class StoreStaffController(
|
||||
|
||||
dbContext.StoreStaffWeeklySchedules.RemoveRange(existingRows);
|
||||
var entities = finalSchedules
|
||||
.SelectMany(x => ToWeeklyEntities(parsedStoreId, long.Parse(x.StaffId), x.Shifts))
|
||||
.SelectMany(x => ToWeeklyEntities(parsedStoreId, long.Parse(x.StaffId), x.Shifts, template))
|
||||
.ToList();
|
||||
await dbContext.StoreStaffWeeklySchedules.AddRangeAsync(entities, cancellationToken);
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
@@ -407,6 +416,8 @@ public sealed class StoreStaffController(
|
||||
StoreId = parsedStoreId.ToString(),
|
||||
WeekStartDate = StoreApiHelpers.ResolveWeekStartDate(null),
|
||||
Templates = template,
|
||||
IsTemplateConfigured = true,
|
||||
IsScheduleConfigured = entities.Count > 0,
|
||||
Schedules = finalSchedules
|
||||
});
|
||||
}
|
||||
@@ -451,7 +462,11 @@ public sealed class StoreStaffController(
|
||||
});
|
||||
}
|
||||
|
||||
var sourceTemplate = await GetOrCreateTemplateDtoAsync(tenantId, sourceStoreId, cancellationToken);
|
||||
var sourceTemplate = await GetTemplateDtoAsync(tenantId, sourceStoreId, cancellationToken);
|
||||
if (sourceTemplate is null)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.BadRequest, "源门店未配置班次模板,无法复制");
|
||||
}
|
||||
var sourceRows = await dbContext.StoreStaffWeeklySchedules
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.StoreId == sourceStoreId)
|
||||
@@ -465,14 +480,14 @@ public sealed class StoreStaffController(
|
||||
.GroupBy(x => x.StaffId)
|
||||
.ToDictionary(
|
||||
x => x.Key,
|
||||
x => NormalizeRowsToShifts(x.ToList(), sourceStaffMap.GetValueOrDefault(x.Key), sourceTemplate));
|
||||
x => NormalizeRowsToShifts(x.ToList(), sourceTemplate));
|
||||
var sourceScheduleSequence = sourceStaffs
|
||||
.OrderBy(x => x.CreatedAt)
|
||||
.ThenBy(x => x.Name)
|
||||
.ThenBy(x => x.Id)
|
||||
.Select(staff => staff.Status == StaffStatus.Resigned
|
||||
? CreateOffWeekShifts()
|
||||
: sourceScheduleMap.GetValueOrDefault(staff.Id) ?? CreateDefaultWeekByRole(staff.RoleType, sourceTemplate))
|
||||
: sourceScheduleMap.GetValueOrDefault(staff.Id) ?? [])
|
||||
.ToList();
|
||||
|
||||
var targetTemplates = await dbContext.StoreStaffTemplates
|
||||
@@ -526,9 +541,9 @@ public sealed class StoreStaffController(
|
||||
? CreateOffWeekShifts()
|
||||
: sourceScheduleSequence.Count > 0
|
||||
? sourceScheduleSequence[index % sourceScheduleSequence.Count]
|
||||
: CreateDefaultWeekByRole(staff.RoleType, sourceTemplate);
|
||||
: [];
|
||||
|
||||
entities.AddRange(ToWeeklyEntities(targetStoreId, staff.Id, shifts));
|
||||
entities.AddRange(ToWeeklyEntities(targetStoreId, staff.Id, shifts, sourceTemplate));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -602,7 +617,7 @@ public sealed class StoreStaffController(
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private async Task<StoreShiftTemplatesDto> GetOrCreateTemplateDtoAsync(long tenantId, long storeId, CancellationToken cancellationToken)
|
||||
private async Task<StoreShiftTemplatesDto?> GetTemplateDtoAsync(long tenantId, long storeId, CancellationToken cancellationToken)
|
||||
{
|
||||
var entity = await dbContext.StoreStaffTemplates
|
||||
.AsNoTracking()
|
||||
@@ -610,7 +625,7 @@ public sealed class StoreStaffController(
|
||||
|
||||
if (entity is null)
|
||||
{
|
||||
return CreateDefaultTemplate();
|
||||
return null;
|
||||
}
|
||||
|
||||
return new StoreShiftTemplatesDto
|
||||
@@ -633,83 +648,44 @@ public sealed class StoreStaffController(
|
||||
};
|
||||
}
|
||||
|
||||
private async Task EnsureStaffScheduleAsync(
|
||||
long tenantId,
|
||||
long storeId,
|
||||
MerchantStaff staff,
|
||||
StoreShiftTemplatesDto template,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var existingRows = await dbContext.StoreStaffWeeklySchedules
|
||||
.Where(x => x.TenantId == tenantId && x.StoreId == storeId && x.StaffId == staff.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (existingRows.Count == 0)
|
||||
{
|
||||
var initialShifts = staff.Status == StaffStatus.Resigned
|
||||
? CreateOffWeekShifts()
|
||||
: CreateDefaultWeekByRole(staff.RoleType, template);
|
||||
await dbContext.StoreStaffWeeklySchedules.AddRangeAsync(
|
||||
ToWeeklyEntities(storeId, staff.Id, initialShifts),
|
||||
cancellationToken);
|
||||
}
|
||||
else if (staff.Status == StaffStatus.Resigned)
|
||||
{
|
||||
dbContext.StoreStaffWeeklySchedules.RemoveRange(existingRows);
|
||||
await dbContext.StoreStaffWeeklySchedules.AddRangeAsync(
|
||||
ToWeeklyEntities(storeId, staff.Id, CreateOffWeekShifts()),
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private static StoreShiftTemplatesDto NormalizeTemplate(StoreShiftTemplatesDto source)
|
||||
{
|
||||
var fallback = CreateDefaultTemplate();
|
||||
source ??= new StoreShiftTemplatesDto();
|
||||
return new StoreShiftTemplatesDto
|
||||
{
|
||||
Morning = new ShiftTemplateItemDto
|
||||
{
|
||||
StartTime = NormalizeTime(source.Morning.StartTime, fallback.Morning.StartTime),
|
||||
EndTime = NormalizeTime(source.Morning.EndTime, fallback.Morning.EndTime)
|
||||
StartTime = NormalizeTime(source.Morning.StartTime, string.Empty),
|
||||
EndTime = NormalizeTime(source.Morning.EndTime, string.Empty)
|
||||
},
|
||||
Evening = new ShiftTemplateItemDto
|
||||
{
|
||||
StartTime = NormalizeTime(source.Evening.StartTime, fallback.Evening.StartTime),
|
||||
EndTime = NormalizeTime(source.Evening.EndTime, fallback.Evening.EndTime)
|
||||
StartTime = NormalizeTime(source.Evening.StartTime, string.Empty),
|
||||
EndTime = NormalizeTime(source.Evening.EndTime, string.Empty)
|
||||
},
|
||||
Full = new ShiftTemplateItemDto
|
||||
{
|
||||
StartTime = NormalizeTime(source.Full.StartTime, fallback.Full.StartTime),
|
||||
EndTime = NormalizeTime(source.Full.EndTime, fallback.Full.EndTime)
|
||||
StartTime = NormalizeTime(source.Full.StartTime, string.Empty),
|
||||
EndTime = NormalizeTime(source.Full.EndTime, string.Empty)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static List<StaffDayShiftDto> NormalizeRowsToShifts(
|
||||
List<StoreStaffWeeklySchedule> rows,
|
||||
MerchantStaff? staff,
|
||||
StoreShiftTemplatesDto template)
|
||||
{
|
||||
var fallback = staff is null ? CreateOffWeekShifts() : CreateDefaultWeekByRole(staff.RoleType, template);
|
||||
var rowMap = rows.ToDictionary(x => x.DayOfWeek, x => x);
|
||||
var results = new List<StaffDayShiftDto>();
|
||||
|
||||
for (var day = 0; day < 7; day++)
|
||||
foreach (var row in rows
|
||||
.Where(x => x.DayOfWeek is >= 0 and <= 6)
|
||||
.OrderBy(x => x.DayOfWeek))
|
||||
{
|
||||
if (!rowMap.TryGetValue(day, out var row))
|
||||
{
|
||||
results.Add(fallback[day]);
|
||||
continue;
|
||||
}
|
||||
|
||||
var shiftType = StoreApiHelpers.ToShiftTypeText(row.ShiftType);
|
||||
if (row.ShiftType == StoreStaffShiftType.Off)
|
||||
{
|
||||
results.Add(new StaffDayShiftDto
|
||||
{
|
||||
DayOfWeek = day,
|
||||
DayOfWeek = row.DayOfWeek,
|
||||
ShiftType = shiftType,
|
||||
StartTime = string.Empty,
|
||||
EndTime = string.Empty
|
||||
@@ -720,7 +696,7 @@ public sealed class StoreStaffController(
|
||||
var (defaultStart, defaultEnd) = ResolveShiftTimeRange(row.ShiftType, template);
|
||||
results.Add(new StaffDayShiftDto
|
||||
{
|
||||
DayOfWeek = day,
|
||||
DayOfWeek = row.DayOfWeek,
|
||||
ShiftType = shiftType,
|
||||
StartTime = StoreApiHelpers.ToHHmm(row.StartTime ?? defaultStart ?? TimeSpan.Zero),
|
||||
EndTime = StoreApiHelpers.ToHHmm(row.EndTime ?? defaultEnd ?? TimeSpan.Zero)
|
||||
@@ -743,16 +719,11 @@ public sealed class StoreStaffController(
|
||||
var normalized = new List<StaffDayShiftDto>();
|
||||
for (var day = 0; day < 7; day++)
|
||||
{
|
||||
var fallbackShift = fallback.FirstOrDefault(x => x.DayOfWeek == day) ?? new StaffDayShiftDto
|
||||
{
|
||||
DayOfWeek = day,
|
||||
ShiftType = "off",
|
||||
StartTime = string.Empty,
|
||||
EndTime = string.Empty
|
||||
};
|
||||
var fallbackShift = fallback.FirstOrDefault(x => x.DayOfWeek == day);
|
||||
|
||||
if (!inputMap.TryGetValue(day, out var input))
|
||||
{
|
||||
if (fallbackShift is null) continue;
|
||||
normalized.Add(new StaffDayShiftDto
|
||||
{
|
||||
DayOfWeek = day,
|
||||
@@ -781,8 +752,16 @@ public sealed class StoreStaffController(
|
||||
{
|
||||
DayOfWeek = day,
|
||||
ShiftType = StoreApiHelpers.ToShiftTypeText(shiftType),
|
||||
StartTime = NormalizeTime(input.StartTime, defaultStart.HasValue ? StoreApiHelpers.ToHHmm(defaultStart.Value) : fallbackShift.StartTime),
|
||||
EndTime = NormalizeTime(input.EndTime, defaultEnd.HasValue ? StoreApiHelpers.ToHHmm(defaultEnd.Value) : fallbackShift.EndTime)
|
||||
StartTime = NormalizeTime(
|
||||
input.StartTime,
|
||||
defaultStart.HasValue
|
||||
? StoreApiHelpers.ToHHmm(defaultStart.Value)
|
||||
: fallbackShift?.StartTime ?? string.Empty),
|
||||
EndTime = NormalizeTime(
|
||||
input.EndTime,
|
||||
defaultEnd.HasValue
|
||||
? StoreApiHelpers.ToHHmm(defaultEnd.Value)
|
||||
: fallbackShift?.EndTime ?? string.Empty)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -792,21 +771,22 @@ public sealed class StoreStaffController(
|
||||
private static IEnumerable<StoreStaffWeeklySchedule> ToWeeklyEntities(
|
||||
long storeId,
|
||||
long staffId,
|
||||
IEnumerable<StaffDayShiftDto> shifts)
|
||||
IEnumerable<StaffDayShiftDto> shifts,
|
||||
StoreShiftTemplatesDto template)
|
||||
{
|
||||
foreach (var shift in shifts)
|
||||
{
|
||||
var shiftType = StoreApiHelpers.ToShiftType(shift.ShiftType);
|
||||
var (defaultStart, defaultEnd) = ResolveShiftTimeRange(shiftType, CreateDefaultTemplate());
|
||||
var (defaultStart, defaultEnd) = ResolveShiftTimeRange(shiftType, template);
|
||||
var startTime = shiftType == StoreStaffShiftType.Off
|
||||
? null
|
||||
: (TimeSpan?)StoreApiHelpers.ParseRequiredTime(
|
||||
NormalizeTime(shift.StartTime, defaultStart.HasValue ? StoreApiHelpers.ToHHmm(defaultStart.Value) : "09:00"),
|
||||
NormalizeTime(shift.StartTime, defaultStart.HasValue ? StoreApiHelpers.ToHHmm(defaultStart.Value) : string.Empty),
|
||||
"shift.startTime");
|
||||
var endTime = shiftType == StoreStaffShiftType.Off
|
||||
? null
|
||||
: (TimeSpan?)StoreApiHelpers.ParseRequiredTime(
|
||||
NormalizeTime(shift.EndTime, defaultEnd.HasValue ? StoreApiHelpers.ToHHmm(defaultEnd.Value) : "21:00"),
|
||||
NormalizeTime(shift.EndTime, defaultEnd.HasValue ? StoreApiHelpers.ToHHmm(defaultEnd.Value) : string.Empty),
|
||||
"shift.endTime");
|
||||
|
||||
yield return new StoreStaffWeeklySchedule
|
||||
@@ -821,34 +801,6 @@ public sealed class StoreStaffController(
|
||||
}
|
||||
}
|
||||
|
||||
private static List<StaffDayShiftDto> CreateDefaultWeekByRole(StaffRoleType roleType, StoreShiftTemplatesDto template)
|
||||
{
|
||||
var pattern = roleType switch
|
||||
{
|
||||
StaffRoleType.Admin or StaffRoleType.Operator => new[] { "full", "full", "full", "full", "full", "morning", "off" },
|
||||
StaffRoleType.FrontDesk => new[] { "morning", "morning", "off", "morning", "evening", "full", "full" },
|
||||
StaffRoleType.Courier => new[] { "morning", "evening", "morning", "evening", "morning", "evening", "off" },
|
||||
StaffRoleType.Kitchen => new[] { "full", "full", "evening", "off", "full", "full", "morning" },
|
||||
_ => new[] { "morning", "morning", "off", "morning", "evening", "full", "full" }
|
||||
};
|
||||
|
||||
var results = new List<StaffDayShiftDto>();
|
||||
for (var day = 0; day < 7; day++)
|
||||
{
|
||||
var shiftType = StoreApiHelpers.ToShiftType(pattern[day]);
|
||||
var (start, end) = ResolveShiftTimeRange(shiftType, template);
|
||||
results.Add(new StaffDayShiftDto
|
||||
{
|
||||
DayOfWeek = day,
|
||||
ShiftType = StoreApiHelpers.ToShiftTypeText(shiftType),
|
||||
StartTime = start.HasValue ? StoreApiHelpers.ToHHmm(start.Value) : string.Empty,
|
||||
EndTime = end.HasValue ? StoreApiHelpers.ToHHmm(end.Value) : string.Empty
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private static List<StaffDayShiftDto> CreateOffWeekShifts()
|
||||
{
|
||||
return Enumerable.Range(0, 7).Select(day => new StaffDayShiftDto
|
||||
@@ -883,26 +835,4 @@ public sealed class StoreStaffController(
|
||||
? StoreApiHelpers.ToHHmm(parsed)
|
||||
: fallback;
|
||||
}
|
||||
|
||||
private static StoreShiftTemplatesDto CreateDefaultTemplate()
|
||||
{
|
||||
return new StoreShiftTemplatesDto
|
||||
{
|
||||
Morning = new ShiftTemplateItemDto
|
||||
{
|
||||
StartTime = "09:00",
|
||||
EndTime = "14:00"
|
||||
},
|
||||
Evening = new ShiftTemplateItemDto
|
||||
{
|
||||
StartTime = "14:00",
|
||||
EndTime = "21:00"
|
||||
},
|
||||
Full = new ShiftTemplateItemDto
|
||||
{
|
||||
StartTime = "09:00",
|
||||
EndTime = "21:00"
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user