Files
2026-09-18 16:08:14 +08:00

234 lines
9.1 KiB
C#

namespace SimpleAdmin.Application;
/// <summary>
/// 维护员工端动态补充字段和材料类型;与公司、岗位的组合关系由入职模板统一管理。
/// </summary>
public class OnboardingEntryConfigService : IOnboardingEntryConfigService
{
private readonly SqlSugarScope _db;
private readonly IOnboardingDataPermissionService _dataPermission;
public OnboardingEntryConfigService(IOnboardingDataPermissionService dataPermission)
{
_db = DbContext.DB;
_dataPermission = dataPermission;
}
/// <summary>
/// 查询全部动态入职字段。
/// </summary>
public Task<List<ObFormField>> ListFormFields()
{
return _db.Queryable<ObFormField>()
.OrderBy(x => x.FieldCode)
.ToListAsync();
}
/// <summary>
/// 新增或编辑动态入职字段。
/// </summary>
public async Task SaveFormField(OnboardingFormFieldSaveInput input)
{
input.FieldCode = OnboardingCodeValidator.Normalize(input.FieldCode, "字段编码", 100);
input.FieldName = input.FieldName?.Trim();
if (string.IsNullOrWhiteSpace(input.FieldName))
throw Oops.Bah("字段名称不能为空");
var fieldCodeQuery = _db.Queryable<ObFormField>()
.Where(x => x.FieldCode == input.FieldCode);
if (input.Id.HasValue)
fieldCodeQuery = fieldCodeQuery.Where(x => x.Id != input.Id.Value);
var fieldCodeExists = await fieldCodeQuery.AnyAsync();
if (fieldCodeExists)
throw Oops.Bah("字段编码已存在");
var entity = new ObFormField
{
Id = input.Id ?? global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId(),
FieldCode = input.FieldCode,
FieldName = input.FieldName,
DataType = input.DataType,
OptionsJson = NormalizeOptionsJson(input.OptionsJson),
ValidationRule = input.ValidationRule,
Placeholder = input.Placeholder,
Status = input.Status
};
if (input.Id.HasValue)
{
var exists = await _db.Queryable<ObFormField>().InSingleAsync(input.Id.Value)
?? throw Oops.Bah("字段不存在");
await _dataPermission.EnsureCanManage(
exists.CreateUserId,
OnboardingPermissionCodes.EntryConfigManageAll,
"入职字段");
entity.CreateUserId = exists.CreateUserId;
entity.CreateUser = exists.CreateUser;
entity.CreateTime = exists.CreateTime;
await _db.Updateable(entity).ExecuteCommandAsync();
return;
}
await _db.Insertable(entity).ExecuteCommandAsync();
}
/// <summary>
/// 将空白的可选项转换为 null,避免 MySQL JSON 字段写入空字符串时报错。
/// </summary>
private static string NormalizeOptionsJson(string optionsJson)
{
if (string.IsNullOrWhiteSpace(optionsJson))
return null;
var normalizedJson = optionsJson.Trim();
try
{
using var jsonDocument = global::System.Text.Json.JsonDocument.Parse(normalizedJson);
return normalizedJson;
}
catch (global::System.Text.Json.JsonException)
{
throw Oops.Bah("可选项不是有效的 JSON 格式");
}
}
/// <summary>
/// 删除未被填写要求引用的动态字段。
/// </summary>
public async Task DeleteFormFields(BaseIdListInput input)
{
if (input.Ids.Count == 0)
return;
var fields = await _db.Queryable<ObFormField>()
.Where(item => input.Ids.Contains(item.Id))
.ToListAsync();
if (fields.Count != input.Ids.Distinct().Count())
throw Oops.Bah("字段不存在");
await _dataPermission.EnsureCanManage(
fields.Select(item => item.CreateUserId),
OnboardingPermissionCodes.EntryConfigManageAll,
"入职字段");
var isReferencedByTask = await _db.Queryable<ObTaskFormRequirement>()
.AnyAsync(x => input.Ids.Contains(x.FieldId));
if (isReferencedByTask)
throw Oops.Bah("字段已被入职任务引用,不能删除,可改为停用");
await _db.Deleteable<ObFormField>().In(input.Ids).ExecuteCommandAsync();
}
/// <summary>
/// 查询全部入职材料类型。
/// </summary>
public Task<List<ObMaterialType>> ListMaterialTypes()
{
return _db.Queryable<ObMaterialType>()
.OrderBy(x => x.MaterialCode)
.ToListAsync();
}
/// <summary>
/// 新增或编辑入职材料类型。
/// </summary>
public async Task SaveMaterialType(OnboardingMaterialTypeSaveInput input)
{
input.MaterialCode = OnboardingCodeValidator.Normalize(input.MaterialCode, "资料编码", 100);
input.MaterialName = input.MaterialName?.Trim();
if (string.IsNullOrWhiteSpace(input.MaterialName))
throw Oops.Bah("资料名称不能为空");
if (input.MaxFileCount <= 0)
throw Oops.Bah("最大文件数量必须大于0");
if (input.MaxFileSizeMb.HasValue && (input.MaxFileSizeMb.Value <= 0 || input.MaxFileSizeMb.Value > 20))
throw Oops.Bah("单个文件大小必须设置在1至20MB之间");
var supportedExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"png", "jpg", "jpeg", "gif", "bmp", "webp", "pdf"
};
var allowedExtensions = (input.AllowedExtensions ?? string.Empty)
.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
.Select(item => item.TrimStart('.').ToLowerInvariant())
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
if (allowedExtensions.Count == 0)
allowedExtensions.AddRange(new[] { "png", "jpg", "jpeg", "pdf" });
var unsupportedExtensions = allowedExtensions.Where(item => !supportedExtensions.Contains(item)).ToList();
if (unsupportedExtensions.Count > 0)
throw Oops.Bah($"不支持的材料扩展名:{string.Join("、", unsupportedExtensions)},仅允许图片或PDF文件");
input.AllowedExtensions = string.Join(",", allowedExtensions);
var materialCodeExists = await _db.Queryable<ObMaterialType>()
.AnyAsync(x => x.MaterialCode == input.MaterialCode && x.Id != input.Id);
if (materialCodeExists)
throw Oops.Bah("资料编码已存在");
var entity = new ObMaterialType
{
Id = input.Id ?? global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId(),
MaterialCode = input.MaterialCode,
MaterialName = input.MaterialName,
AllowedExtensions = input.AllowedExtensions,
MaxFileCount = input.MaxFileCount,
MaxFileSizeMb = input.MaxFileSizeMb,
Status = input.Status
};
if (input.Id.HasValue)
{
var exists = await _db.Queryable<ObMaterialType>().InSingleAsync(input.Id.Value)
?? throw Oops.Bah("资料类型不存在");
await _dataPermission.EnsureCanManage(
exists.CreateUserId,
OnboardingPermissionCodes.EntryConfigManageAll,
"资料类型");
entity.CreateUserId = exists.CreateUserId;
entity.CreateUser = exists.CreateUser;
entity.CreateTime = exists.CreateTime;
await _db.Updateable(entity).ExecuteCommandAsync();
return;
}
await _db.Insertable(entity).ExecuteCommandAsync();
}
/// <summary>
/// 删除未被要求或上传记录引用的材料类型。
/// </summary>
public async Task DeleteMaterialTypes(BaseIdListInput input)
{
if (input.Ids.Count == 0)
return;
var materialTypes = await _db.Queryable<ObMaterialType>()
.Where(item => input.Ids.Contains(item.Id))
.ToListAsync();
if (materialTypes.Count != input.Ids.Distinct().Count())
throw Oops.Bah("资料类型不存在");
await _dataPermission.EnsureCanManage(
materialTypes.Select(item => item.CreateUserId),
OnboardingPermissionCodes.EntryConfigManageAll,
"资料类型");
var isReferencedByTemplate = await _db.Queryable<ObOnboardingTemplateMaterial>()
.AnyAsync(x => input.Ids.Contains(x.MaterialTypeId));
if (isReferencedByTemplate)
throw Oops.Bah("资料类型已被入职模板引用,不能删除");
var isReferencedByTask = await _db.Queryable<ObTaskMaterialRequirement>()
.AnyAsync(x => input.Ids.Contains(x.MaterialTypeId));
if (isReferencedByTask)
throw Oops.Bah("资料类型已被入职任务引用,不能删除,可改为停用");
var hasUploads = await _db.Queryable<ObTaskMaterial>()
.AnyAsync(x => input.Ids.Contains(x.MaterialTypeId));
if (hasUploads)
throw Oops.Bah("资料类型已有上传记录,不能删除");
await _db.Deleteable<ObMaterialType>().In(input.Ids).ExecuteCommandAsync();
}
}