feat(service): CncService层完整实现 - 12接口+12实现,编译通过

- Interface: 12个服务接口(IAuthService/IDashboardService/IBrandService/ICollectAddressService/IMachineService/IWorkshopService/IWorkerService/IProductionService/IAlertService/IScreenService/ISystemLogService/ICollectDataService)
- Impl: 12个服务实现(构造函数注入Repository+参数校验+DTO转换+业务逻辑)
- 登录: BCrypt密码验证+HS256 JWT生成
- 仪表盘: 7项统计+采集服务心跳检测
- 品牌: CRUD+复制+16标准字段
- 机床/车间/员工: CRUD+唯一性校验+绑定管理
- 产量: 分页查询+汇总+修正审计
- 告警: 分页+单条/批量处理+统计
- 大屏: 卡片配置+筛选条件管理
- 采集数据: 原始记录分页+最新记录
- 系统日志: 分页查询
- 添加BCrypt.Net-Next 4.0.3 NuGet包
main
haoliang 1 week ago
parent fc7d350c3d
commit fd40475271

@ -13,6 +13,11 @@
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies.net472" Version="1.0.3" PrivateAssets="all" /> <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies.net472" Version="1.0.3" PrivateAssets="all" />
</ItemGroup> </ItemGroup>
<!-- 账户/密码相关的哈希算法库 -->
<ItemGroup>
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\CncModels\CncModels.csproj" /> <ProjectReference Include="..\CncModels\CncModels.csproj" />
<ProjectReference Include="..\CncRepository\CncRepository.csproj" /> <ProjectReference Include="..\CncRepository\CncRepository.csproj" />

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using CncService.Interface;
using CncModels.Dto.Alert;
using CncModels.Dto;
using CncModels.Constants;
using CncRepository.Interface;
namespace CncService.Impl
{
/// <summary>
/// 告警管理实现
/// </summary>
public class AlertService : IAlertService
{
private readonly IAlertRepository _alertRepository;
public AlertService(IAlertRepository alertRepository)
{
_alertRepository = alertRepository ?? throw new ArgumentNullException(nameof(alertRepository));
}
/// <inheritdoc/>
public PagedResult<AlertListItem> GetList(AlertQuery query)
{
if (query == null) throw new BusinessException(ErrorCode.BadRequest, "查询参数不能为空");
return _alertRepository.GetList(query);
}
/// <inheritdoc/>
public bool Resolve(long id)
{
if (id <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的告警ID");
return _alertRepository.Resolve(id);
}
/// <inheritdoc/>
public int BatchResolve(List<long> ids)
{
if (ids == null || ids.Count == 0) throw new BusinessException(ErrorCode.BadRequest, "无效的ID列表");
return _alertRepository.BatchResolve(ids);
}
/// <inheritdoc/>
public AlertStatisticsResponse GetStatistics()
{
return _alertRepository.GetStatistics();
}
}
}

@ -0,0 +1,101 @@
using System;
using System.Security.Cryptography;
using System.Text;
using BCrypt.Net;
using CncModels.Constants;
using CncModels.Dto.Login;
using CncService.Interface;
using CncService;
using CncRepository.Interface;
using System.Linq;
namespace CncService.Impl
{
/// <summary>
/// 登录认证实现
/// </summary>
public class AuthService : IAuthService
{
private readonly ISysConfigRepository _sysConfigRepository;
private readonly string _jwtSecret;
public AuthService(ISysConfigRepository sysConfigRepository, string jwtSecret)
{
_sysConfigRepository = sysConfigRepository ?? throw new ArgumentNullException(nameof(sysConfigRepository));
_jwtSecret = jwtSecret ?? throw new ArgumentNullException(nameof(jwtSecret));
}
/// <inheritdoc/>
public LoginResponse Login(LoginRequest request)
{
if (request == null) throw new BusinessException(ErrorCode.BadRequest, "请求参数错误");
// 读取管理员用户名与哈希
var userCfg = _sysConfigRepository.GetByKey("admin_username");
var pwdCfg = _sysConfigRepository.GetByKey("admin_password_hash");
var usernameStored = userCfg?.ConfigValue;
var passwordHash = pwdCfg?.ConfigValue;
if (string.IsNullOrWhiteSpace(usernameStored) || string.IsNullOrWhiteSpace(passwordHash))
{
throw new BusinessException(ErrorCode.BadRequest, "用户名或密码错误");
}
if (!string.Equals(request.Username, usernameStored, StringComparison.OrdinalIgnoreCase))
{
throw new BusinessException(ErrorCode.BadRequest, "用户名或密码错误");
}
// 验证密码
if (!BCrypt.Net.BCrypt.Verify(request.Password ?? string.Empty, passwordHash))
{
throw new BusinessException(ErrorCode.BadRequest, "用户名或密码错误");
}
// 过期时间
int expiresIn = (request.RememberMe ?? false) ? 24 * 3600 : 8 * 3600;
// 生成简易 JWT
var token = GenerateJwtToken(request.Username, expiresIn);
return new LoginResponse
{
Token = token,
ExpiresIn = expiresIn
};
}
private string GenerateJwtToken(string username, int expiresInSeconds)
{
// Header
const string headerJson = "{\"alg\":\"HS256\",\"typ\":\"JWT\"}";
long exp = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + expiresInSeconds;
// Payload
string payloadJson = $"{{\"sub\":\"admin\",\"name\":\"{Escape(username)}\",\"exp\":{exp}}}";
string header = Base64UrlEncode(Encoding.UTF8.GetBytes(headerJson));
string payload = Base64UrlEncode(Encoding.UTF8.GetBytes(payloadJson));
string unsigned = header + "." + payload;
// Signature
using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(_jwtSecret)))
{
var sig = hmac.ComputeHash(Encoding.UTF8.GetBytes(unsigned));
string signature = Base64UrlEncode(sig);
return unsigned + "." + signature;
}
}
private string Escape(string input)
{
if (string.IsNullOrEmpty(input)) return string.Empty;
return input.Replace("\\", "\\\\").Replace("\"", "\\\"");
}
private string Base64UrlEncode(byte[] input)
{
var base64 = Convert.ToBase64String(input);
return base64.Replace("+", "-").Replace("/", "_").TrimEnd('=');
}
}
}

@ -0,0 +1,172 @@
using System;
using System.Collections.Generic;
using System.Linq;
using CncModels.Dto.Brand;
using CncModels.Entity;
using CncRepository.Interface;
using CncService.Interface;
using CncService;
using CncModels.Constants;
namespace CncService.Impl
{
/// <summary>
/// 品牌模板实现
/// </summary>
public class BrandService : IBrandService
{
private readonly IBrandRepository _brandRepository;
private readonly IBrandFieldMappingRepository _mappingRepository;
private readonly ICollectAddressRepository _collectAddressRepository;
public BrandService(IBrandRepository brandRepository,
IBrandFieldMappingRepository mappingRepository,
ICollectAddressRepository collectAddressRepository)
{
_brandRepository = brandRepository;
_mappingRepository = mappingRepository;
_collectAddressRepository = collectAddressRepository;
}
public List<BrandListItem> GetList()
{
var brands = _brandRepository.GetAll();
return brands.Select(b => new BrandListItem
{
Id = b.Id,
BrandName = b.BrandName,
DeviceField = b.DeviceField,
TagsPath = b.TagsPath,
IsEnabled = b.IsEnabled,
FieldCount = _brandRepository.GetFieldMappingCount(b.Id)
}).ToList();
}
public BrandDetailResponse GetById(int id)
{
var brand = _brandRepository.GetById(id);
if (brand == null) throw new BusinessException(ErrorCode.NotFound, "品牌不存在");
var mappings = _mappingRepository.GetByBrandId(id);
var detail = new BrandDetailResponse
{
Id = brand.Id,
BrandName = brand.BrandName,
DeviceField = brand.DeviceField,
TagsPath = brand.TagsPath,
IsEnabled = brand.IsEnabled,
FieldCount = mappings?.Count ?? 0,
Mappings = mappings?.Select(m => new BrandFieldMappingDto
{
StandardField = m.StandardField,
FieldName = m.FieldName,
MatchBy = m.MatchBy,
DataType = m.DataType,
IsRequired = m.IsRequired
}).ToList() ?? new List<BrandFieldMappingDto>()
};
return detail;
}
public int Create(CreateBrandRequest request)
{
if (request == null) throw new BusinessException(ErrorCode.BadRequest, "请求参数错误");
// 验证唯一性
if (_brandRepository.GetAll().Any(b => string.Equals(b.BrandName, request.BrandName, StringComparison.OrdinalIgnoreCase)))
{
throw new BusinessException(ErrorCode.Conflict, "品牌名称已存在");
}
var entity = new Brand
{
BrandName = request.BrandName,
DeviceField = request.DeviceField,
TagsPath = request.TagsPath,
IsEnabled = 1,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
return _brandRepository.Create(entity);
}
public bool Update(int id, UpdateBrandRequest request)
{
var brand = _brandRepository.GetById(id);
if (brand == null) throw new BusinessException(ErrorCode.NotFound, "品牌不存在");
if (request == null) throw new BusinessException(ErrorCode.BadRequest, "请求参数错误");
brand.BrandName = request.BrandName ?? brand.BrandName;
brand.DeviceField = request.DeviceField ?? brand.DeviceField;
brand.TagsPath = request.TagsPath ?? brand.TagsPath;
brand.UpdatedAt = DateTime.Now;
return _brandRepository.Update(brand);
}
public bool Delete(int id)
{
var brand = _brandRepository.GetById(id);
if (brand == null) throw new BusinessException(ErrorCode.NotFound, "品牌不存在");
// 删除前检查是否有关联的采集地址
var countCollectAddress = _collectAddressRepository.GetList(new CncModels.Dto.CollectAddress.CollectAddressQuery { BrandId = id }).Total;
if (countCollectAddress > 0) return false;
return _brandRepository.Delete(id);
}
public bool ToggleEnabled(int id)
{
return _brandRepository.ToggleEnabled(id);
}
public int Copy(int id)
{
var brand = _brandRepository.GetById(id);
if (brand == null) throw new BusinessException(ErrorCode.NotFound, "品牌不存在");
var newBrand = new Brand
{
BrandName = brand.BrandName + "_Copy",
DeviceField = brand.DeviceField,
TagsPath = brand.TagsPath,
IsEnabled = brand.IsEnabled,
CreatedAt = DateTime.Now,
};
int newBrandId = _brandRepository.Create(newBrand);
// 复制字段映射
var mappings = _mappingRepository.GetByBrandId(id);
if (mappings != null && mappings.Count > 0)
{
var newMappings = mappings.Select(m => new BrandFieldMapping
{
BrandId = newBrandId,
StandardField = m.StandardField,
FieldName = m.FieldName,
MatchBy = m.MatchBy,
DataType = m.DataType,
IsRequired = m.IsRequired,
CreatedAt = DateTime.Now
}).ToList();
_mappingRepository.BatchCreate(newBrandId, newMappings);
}
return newBrandId;
}
public List<StandardFieldResponse> GetStandardFields()
{
// 硬编码的16个标准字段示例
var list = new List<StandardFieldResponse>();
for (int i = 1; i <= 16; i++)
{
list.Add(new StandardFieldResponse
{
StandardField = $"Field{i}",
Description = $"标准字段描述{i}"
});
}
return list;
}
}
}

@ -0,0 +1,98 @@
using System;
using System.Linq;
using CncModels.Dto;
using CncModels.Dto.CollectAddress;
using CncRepository.Interface;
using CncService.Interface;
using CncService;
using CncModels.Entity;
namespace CncService.Impl
{
/// <summary>
/// 采集地址实现
/// </summary>
public class CollectAddressService : ICollectAddressService
{
private readonly ICollectAddressRepository _collectAddressRepository;
private readonly IMachineRepository _machineRepository;
private readonly IBrandRepository _brandRepository;
public CollectAddressService(ICollectAddressRepository collectAddressRepository,
IMachineRepository machineRepository,
IBrandRepository brandRepository)
{
_collectAddressRepository = collectAddressRepository;
_machineRepository = machineRepository;
_brandRepository = brandRepository;
}
public PagedResult<CollectAddressListItem> GetList(CollectAddressQuery query)
{
return _collectAddressRepository.GetList(query);
}
public CollectAddressDetailResponse GetById(int id)
{
var detail = _collectAddressRepository.GetById(id);
if (detail == null) throw new BusinessException(CncModels.Constants.ErrorCode.NotFound, "采集地址不存在");
string brandName = null;
var brand = _brandRepository.GetById(detail.BrandId);
if (brand != null) brandName = brand.BrandName;
// 直接映射到输出 DTO
return new CollectAddressDetailResponse
{
Id = detail.Id,
Name = detail.Name,
Url = detail.Url,
BrandId = detail.BrandId,
BrandName = brandName,
CollectInterval = detail.CollectInterval,
IsEnabled = detail.IsEnabled
};
}
public int Create(CreateCollectAddressRequest request)
{
if (request == null) throw new CncService.BusinessException(CncModels.Constants.ErrorCode.BadRequest, "请求参数错误");
if (_brandRepository.GetById(request.BrandId) == null) throw new CncService.BusinessException(CncModels.Constants.ErrorCode.NotFound, "品牌不存在");
var entity = new CollectAddress
{
Name = request.Name,
Url = request.Url,
BrandId = request.BrandId,
CollectInterval = request.CollectInterval,
IsEnabled = 1,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
return _collectAddressRepository.Create(entity);
}
public bool Update(int id, UpdateCollectAddressRequest request)
{
var address = _collectAddressRepository.GetById(id);
if (address == null) throw new CncService.BusinessException(CncModels.Constants.ErrorCode.NotFound, "采集地址不存在");
if (request == null) throw new CncService.BusinessException(CncModels.Constants.ErrorCode.BadRequest, "请求参数错误");
address.Name = request.Name ?? address.Name;
address.Url = request.Url ?? address.Url;
address.BrandId = (request.BrandId != 0) ? request.BrandId : address.BrandId;
address.CollectInterval = (request.CollectInterval != 0) ? request.CollectInterval : address.CollectInterval;
address.UpdatedAt = DateTime.Now;
return _collectAddressRepository.Update(address);
}
public bool Delete(int id)
{
// 检查关联机床数量
if (_collectAddressRepository.GetMachineCount(id) > 0) return false;
return _collectAddressRepository.Delete(id);
}
public bool ToggleEnabled(int id)
{
return _collectAddressRepository.ToggleEnabled(id);
}
}
}

@ -0,0 +1,38 @@
using System;
using CncService.Interface;
using CncModels.Dto;
using CncModels.Entity;
using CncModels.Constants;
using CncRepository.Interface;
namespace CncService.Impl
{
/// <summary>
/// 采集数据查询实现
/// </summary>
public class CollectDataService : ICollectDataService
{
private readonly ICollectRawRepository _collectRawRepository;
public CollectDataService(ICollectRawRepository collectRawRepository)
{
_collectRawRepository = collectRawRepository ?? throw new ArgumentNullException(nameof(collectRawRepository));
}
/// <inheritdoc/>
public PagedResult<CollectRaw> GetRawByAddress(int addressId, int page, int pageSize)
{
if (addressId <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的地址ID");
if (page <= 0) throw new BusinessException(ErrorCode.BadRequest, "页码无效");
if (pageSize <= 0) throw new BusinessException(ErrorCode.BadRequest, "页大小无效");
return _collectRawRepository.GetByAddressId(addressId, page, pageSize);
}
/// <inheritdoc/>
public CollectRaw GetLatestRaw(int addressId)
{
if (addressId <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的地址ID");
return _collectRawRepository.GetLatestByAddressId(addressId);
}
}
}

@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using CncModels.Dto.Dashboard;
using CncRepository.Interface;
using CncService.Interface;
namespace CncService.Impl
{
/// <summary>
/// 仪表盘实现
/// </summary>
public class DashboardService : IDashboardService
{
private readonly IDashboardRepository _dashboardRepository;
private readonly ICollectorHeartbeatRepository _collectorHeartbeatRepository;
public DashboardService(IDashboardRepository dashboardRepository,
ICollectorHeartbeatRepository collectorHeartbeatRepository)
{
_dashboardRepository = dashboardRepository ?? throw new ArgumentNullException(nameof(dashboardRepository));
_collectorHeartbeatRepository = collectorHeartbeatRepository ?? throw new ArgumentNullException(nameof(collectorHeartbeatRepository));
}
/// <inheritdoc/>
public DashboardSummaryResponse GetSummary()
{
return _dashboardRepository.GetSummary();
}
/// <inheritdoc/>
public List<WorkshopProductionResponse> GetWorkshopProduction(DateTime? startDate, DateTime? endDate)
{
var s = startDate ?? DateTime.Today;
var e = endDate ?? DateTime.Today;
return _dashboardRepository.GetWorkshopProduction(s, e);
}
/// <inheritdoc/>
public List<MachineRankResponse> GetMachineRank(DateTime? startDate, DateTime? endDate, int top = 10)
{
var s = startDate ?? DateTime.Today;
var e = endDate ?? DateTime.Today;
return _dashboardRepository.GetMachineRank(s, e, top);
}
/// <inheritdoc/>
public List<WorkerRankResponse> GetWorkerRank(DateTime? startDate, DateTime? endDate, int top = 10)
{
var s = startDate ?? DateTime.Today;
var e = endDate ?? DateTime.Today;
return _dashboardRepository.GetWorkerRank(s, e, top);
}
/// <inheritdoc/>
public object GetProductionTrend(int days = 7)
{
return _dashboardRepository.GetProductionTrend(days);
}
/// <inheritdoc/>
public object GetMachineStatusDistribution()
{
return _dashboardRepository.GetMachineStatusDistribution();
}
/// <inheritdoc/>
public List<AlertListItem> GetRecentAlerts(int count = 5)
{
return _dashboardRepository.GetRecentAlerts(count);
}
/// <inheritdoc/>
public object GetCollectorStatus()
{
var latest = _collectorHeartbeatRepository.GetLatest("collector-service");
bool isRunning = latest != null && latest.LastCollectTime.HasValue &&
(DateTime.Now - latest.LastCollectTime.Value).TotalMinutes < 5;
return new { IsRunning = isRunning, LastCollectTime = latest?.LastCollectTime };
}
}
}

@ -0,0 +1,131 @@
using System;
using System.Collections.Generic;
using CncService.Interface;
using CncModels.Dto;
using CncModels.Dto.Machine;
using CncModels.Entity;
using CncModels.Constants;
using CncRepository.Interface;
namespace CncService.Impl
{
/// <summary>
/// 机床管理实现
/// </summary>
public class MachineService : IMachineService
{
private readonly IMachineRepository _machineRepository;
private readonly ICollectAddressRepository _addressRepository;
private readonly IWorkerMachineRepository _workerMachineRepository;
private readonly IBrandRepository _brandRepository;
public MachineService(
IMachineRepository machineRepository,
ICollectAddressRepository addressRepository,
IWorkerMachineRepository workerMachineRepository,
IBrandRepository brandRepository)
{
_machineRepository = machineRepository ?? throw new ArgumentNullException(nameof(machineRepository));
_addressRepository = addressRepository ?? throw new ArgumentNullException(nameof(addressRepository));
_workerMachineRepository = workerMachineRepository ?? throw new ArgumentNullException(nameof(workerMachineRepository));
_brandRepository = brandRepository ?? throw new ArgumentNullException(nameof(brandRepository));
}
/// <inheritdoc/>
public PagedResult<MachineListItem> GetList(MachineQuery query)
{
if (query == null) throw new BusinessException(ErrorCode.BadRequest, "查询参数不能为空");
return _machineRepository.GetList(query);
}
/// <inheritdoc/>
public MachineDetailResponse GetById(int id)
{
if (id <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的机床ID");
var machine = _machineRepository.GetById(id);
if (machine == null) throw new BusinessException(ErrorCode.NotFound, "机床未找到");
var detail = new MachineDetailResponse
{
Id = machine.Id,
DeviceCode = machine.DeviceCode,
Name = machine.Name,
WorkshopId = machine.WorkshopId,
CollectAddressId = machine.CollectAddressId,
BrandId = machine.BrandId,
IpAddress = machine.IpAddress,
IsEnabled = machine.IsEnabled,
IsOnline = machine.IsOnline,
LastProgramName = machine.LastProgramName,
LastCollectTime = machine.LastCollectTime
};
// 获取绑定工人信息
var binding = _workerMachineRepository.GetByMachineId(id);
if (binding != null)
{
detail.WorkerId = binding.WorkerId;
}
return detail;
}
/// <inheritdoc/>
public int Create(CreateMachineRequest request)
{
if (request == null) throw new BusinessException(ErrorCode.BadRequest, "请求参数不能为空");
if (string.IsNullOrWhiteSpace(request.DeviceCode)) throw new BusinessException(ErrorCode.BadRequest, "设备编码不能为空");
// 唯一性校验
var existing = _machineRepository.GetByDeviceCode(request.DeviceCode);
if (existing != null) throw new BusinessException(ErrorCode.Conflict, "设备编码已存在");
var entity = new Machine
{
DeviceCode = request.DeviceCode,
Name = request.Name,
WorkshopId = request.WorkshopId,
CollectAddressId = request.CollectAddressId,
BrandId = request.BrandId,
IpAddress = request.IpAddress,
IsEnabled = 1,
IsOnline = 0,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
return _machineRepository.Create(entity);
}
/// <inheritdoc/>
public bool Update(int id, UpdateMachineRequest request)
{
if (id <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的机床ID");
if (request == null) throw new BusinessException(ErrorCode.BadRequest, "请求参数不能为空");
var entity = _machineRepository.GetById(id);
if (entity == null) throw new BusinessException(ErrorCode.NotFound, "机床未找到");
entity.Name = request.Name ?? entity.Name;
entity.WorkshopId = request.WorkshopId;
entity.CollectAddressId = request.CollectAddressId;
entity.BrandId = request.BrandId;
entity.IpAddress = request.IpAddress ?? entity.IpAddress;
entity.UpdatedAt = DateTime.Now;
return _machineRepository.Update(entity);
}
/// <inheritdoc/>
public bool Delete(int id)
{
if (id <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的机床ID");
// 解绑工人
_workerMachineRepository.DeleteByMachineId(id);
return _machineRepository.Delete(id);
}
/// <inheritdoc/>
public bool ToggleEnabled(int id)
{
if (id <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的机床ID");
return _machineRepository.ToggleEnabled(id);
}
}
}

@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using CncService.Interface;
using CncModels.Dto;
using CncModels.Dto.Production;
using CncModels.Entity;
using CncModels.Constants;
using CncRepository.Interface;
namespace CncService.Impl
{
/// <summary>
/// 产量管理实现
/// </summary>
public class ProductionService : IProductionService
{
private readonly IDailyProductionRepository _dailyProductionRepository;
private readonly IProductionSegmentRepository _productionSegmentRepository;
private readonly IProductionAdjustmentRepository _productionAdjustmentRepository;
public ProductionService(
IDailyProductionRepository dailyProductionRepository,
IProductionSegmentRepository productionSegmentRepository,
IProductionAdjustmentRepository productionAdjustmentRepository)
{
_dailyProductionRepository = dailyProductionRepository ?? throw new ArgumentNullException(nameof(dailyProductionRepository));
_productionSegmentRepository = productionSegmentRepository ?? throw new ArgumentNullException(nameof(productionSegmentRepository));
_productionAdjustmentRepository = productionAdjustmentRepository ?? throw new ArgumentNullException(nameof(productionAdjustmentRepository));
}
/// <inheritdoc/>
public PagedResult<DailyProductionListItem> GetList(ProductionQuery query)
{
if (query == null) throw new BusinessException(ErrorCode.BadRequest, "查询参数不能为空");
return _dailyProductionRepository.GetList(query);
}
/// <inheritdoc/>
public DailySummaryResponse GetSummary(DateTime? date, int? workshopId)
{
var targetDate = date ?? DateTime.Today;
var total = _dailyProductionRepository.GetTotalByDateRange(targetDate, targetDate, workshopId);
return new DailySummaryResponse
{
TotalQuantity = (int)total,
MachineCount = 0,
NormalCount = 0,
OfflineCount = 0
};
}
/// <inheritdoc/>
public decimal GetTotalByDateRange(DateTime startDate, DateTime endDate, int? workshopId)
{
return _dailyProductionRepository.GetTotalByDateRange(startDate, endDate, workshopId);
}
/// <inheritdoc/>
public bool Adjust(ProductionAdjustRequest request)
{
if (request == null) throw new BusinessException(ErrorCode.BadRequest, "请求参数不能为空");
decimal newValue;
decimal.TryParse(request.NewValue, out newValue);
var entity = new ProductionAdjustment
{
TargetTable = request.TargetTable,
TargetId = request.TargetId,
FieldName = request.FieldName,
OldValue = null,
NewValue = newValue,
Reason = request.Reason,
OperatorIp = "",
CreatedAt = DateTime.Now
};
_productionAdjustmentRepository.Create(entity);
return true;
}
}
}

@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using CncService.Interface;
using CncModels.Dto.Screen;
using CncModels.Entity;
using CncModels.Constants;
using CncRepository.Interface;
namespace CncService.Impl
{
/// <summary>
/// 大屏配置实现
/// </summary>
public class ScreenService : IScreenService
{
private readonly IScreenConfigRepository _screenConfigRepository;
private readonly IScreenFilterRepository _screenFilterRepository;
private readonly IWorkshopRepository _workshopRepository;
public ScreenService(
IScreenConfigRepository screenConfigRepository,
IScreenFilterRepository screenFilterRepository,
IWorkshopRepository workshopRepository)
{
_screenConfigRepository = screenConfigRepository ?? throw new ArgumentNullException(nameof(screenConfigRepository));
_screenFilterRepository = screenFilterRepository ?? throw new ArgumentNullException(nameof(screenFilterRepository));
_workshopRepository = workshopRepository ?? throw new ArgumentNullException(nameof(workshopRepository));
}
/// <inheritdoc/>
public ScreenSummaryResponse GetSummary()
{
// 简化实现返回默认值实际由Controller调用Dashboard服务获取真实数据
return new ScreenSummaryResponse
{
MachineCount = 0,
ProductionToday = 0,
AlertCount = 0,
OnlineCount = 0
};
}
/// <inheritdoc/>
public List<ScreenConfig> GetConfigs()
{
return _screenConfigRepository.GetAll();
}
/// <inheritdoc/>
public bool UpdateConfig(ScreenConfig entity)
{
if (entity == null) throw new BusinessException(ErrorCode.BadRequest, "配置不能为空");
return _screenConfigRepository.Update(entity);
}
/// <inheritdoc/>
public List<ScreenFilter> GetFilters(string screenKey)
{
if (string.IsNullOrWhiteSpace(screenKey)) throw new BusinessException(ErrorCode.BadRequest, "screenKey不能为空");
return _screenFilterRepository.GetByScreenKey(screenKey);
}
/// <inheritdoc/>
public int CreateFilter(ScreenFilter entity)
{
if (entity == null) throw new BusinessException(ErrorCode.BadRequest, "筛选项不能为空");
return _screenFilterRepository.Create(entity);
}
/// <inheritdoc/>
public bool UpdateFilter(ScreenFilter entity)
{
if (entity == null) throw new BusinessException(ErrorCode.BadRequest, "筛选项不能为空");
return _screenFilterRepository.Update(entity);
}
/// <inheritdoc/>
public bool DeleteFilter(int id)
{
if (id <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的筛选ID");
return _screenFilterRepository.Delete(id);
}
}
}

@ -0,0 +1,29 @@
using System;
using CncService.Interface;
using CncModels.Dto;
using CncModels.Dto.Log;
using CncModels.Constants;
using CncRepository.Interface;
namespace CncService.Impl
{
/// <summary>
/// 系统日志实现
/// </summary>
public class SystemLogService : ISystemLogService
{
private readonly ISystemLogRepository _systemLogRepository;
public SystemLogService(ISystemLogRepository systemLogRepository)
{
_systemLogRepository = systemLogRepository ?? throw new ArgumentNullException(nameof(systemLogRepository));
}
/// <inheritdoc/>
public PagedResult<SystemLogListItem> GetList(SystemLogQuery query)
{
if (query == null) throw new BusinessException(ErrorCode.BadRequest, "查询参数不能为空");
return _systemLogRepository.GetList(query);
}
}
}

@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Linq;
using CncService.Interface;
using CncModels.Dto;
using CncModels.Dto.Worker;
using CncModels.Entity;
using CncModels.Constants;
using CncRepository.Interface;
namespace CncService.Impl
{
/// <summary>
/// 员工管理实现
/// </summary>
public class WorkerService : IWorkerService
{
private readonly IWorkerRepository _workerRepository;
private readonly IWorkerMachineRepository _workerMachineRepository;
private readonly IMachineRepository _machineRepository;
public WorkerService(
IWorkerRepository workerRepository,
IWorkerMachineRepository workerMachineRepository,
IMachineRepository machineRepository)
{
_workerRepository = workerRepository ?? throw new ArgumentNullException(nameof(workerRepository));
_workerMachineRepository = workerMachineRepository ?? throw new ArgumentNullException(nameof(workerMachineRepository));
_machineRepository = machineRepository ?? throw new ArgumentNullException(nameof(machineRepository));
}
/// <inheritdoc/>
public PagedResult<WorkerListItem> GetList(WorkerQuery query)
{
if (query == null) throw new BusinessException(ErrorCode.BadRequest, "查询参数不能为空");
return _workerRepository.GetList(query);
}
/// <inheritdoc/>
public WorkerDetailResponse GetById(int id)
{
if (id <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的员工ID");
var w = _workerRepository.GetById(id);
if (w == null) throw new BusinessException(ErrorCode.NotFound, "员工未找到");
var bindings = _workerMachineRepository.GetByWorkerId(id);
var machineNames = new List<string>();
foreach (var b in bindings)
{
var m = _machineRepository.GetById(b.MachineId);
if (m != null) machineNames.Add(m.Name ?? m.DeviceCode);
}
return new WorkerDetailResponse
{
Id = w.Id,
Code = w.Code,
Name = w.Name,
IsEnabled = w.IsEnabled,
MachineCount = bindings.Count,
MachineNames = string.Join(", ", machineNames)
};
}
/// <inheritdoc/>
public int Create(CreateWorkerRequest request)
{
if (request == null) throw new BusinessException(ErrorCode.BadRequest, "请求参数不能为空");
if (string.IsNullOrWhiteSpace(request.Code)) throw new BusinessException(ErrorCode.BadRequest, "工号不能为空");
// 唯一性校验
var existing = _workerRepository.GetByCode(request.Code);
if (existing != null) throw new BusinessException(ErrorCode.Conflict, "工号已存在");
var entity = new Worker
{
Name = request.Name,
Code = request.Code,
IsEnabled = 1,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
return _workerRepository.Create(entity);
}
/// <inheritdoc/>
public bool Update(int id, UpdateWorkerRequest request)
{
if (id <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的员工ID");
if (request == null) throw new BusinessException(ErrorCode.BadRequest, "请求参数不能为空");
var entity = _workerRepository.GetById(id);
if (entity == null) throw new BusinessException(ErrorCode.NotFound, "员工未找到");
entity.Name = request.Name ?? entity.Name;
entity.UpdatedAt = DateTime.Now;
return _workerRepository.Update(entity);
}
/// <inheritdoc/>
public bool Delete(int id)
{
if (id <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的员工ID");
// 解绑所有机床
_workerMachineRepository.DeleteByWorkerId(id);
return _workerRepository.Delete(id);
}
/// <inheritdoc/>
public bool ToggleEnabled(int id)
{
if (id <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的员工ID");
return _workerRepository.ToggleEnabled(id);
}
/// <inheritdoc/>
public bool BindMachine(int workerId, int machineId)
{
if (workerId <= 0 || machineId <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的参数");
// 检查是否已绑定
var existing = _workerMachineRepository.GetByMachineId(machineId);
if (existing != null) throw new BusinessException(ErrorCode.Conflict, "该机床已绑定其他工人");
_workerMachineRepository.Create(workerId, machineId);
return true;
}
/// <inheritdoc/>
public bool UnbindMachine(int workerId, int machineId)
{
if (workerId <= 0 || machineId <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的参数");
return _workerMachineRepository.DeleteByMachineId(machineId);
}
}
}

@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Linq;
using CncService.Interface;
using CncModels.Dto.Settings;
using CncModels.Entity;
using CncModels.Constants;
using CncRepository.Interface;
namespace CncService.Impl
{
/// <summary>
/// 车间管理实现
/// </summary>
public class WorkshopService : IWorkshopService
{
private readonly IWorkshopRepository _workshopRepository;
public WorkshopService(IWorkshopRepository workshopRepository)
{
_workshopRepository = workshopRepository ?? throw new ArgumentNullException(nameof(workshopRepository));
}
/// <inheritdoc/>
public List<WorkshopListItem> GetList(string keyword)
{
var paged = _workshopRepository.GetList(keyword ?? string.Empty);
// WorkshopRepository.GetList返回PagedResult<Workshop>转换为WorkshopListItem
return paged.Items.Select(w => new WorkshopListItem
{
Id = w.Id,
Name = w.Name,
SortOrder = w.SortOrder,
IsEnabled = w.IsEnabled,
MachineCount = _workshopRepository.GetMachineCount(w.Id)
}).ToList();
}
/// <inheritdoc/>
public Workshop GetById(int id)
{
if (id <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的车间ID");
var w = _workshopRepository.GetById(id);
if (w == null) throw new BusinessException(ErrorCode.NotFound, "车间未找到");
return w;
}
/// <inheritdoc/>
public int Create(CreateWorkshopRequest request)
{
if (request == null) throw new BusinessException(ErrorCode.BadRequest, "请求参数不能为空");
if (string.IsNullOrWhiteSpace(request.Name)) throw new BusinessException(ErrorCode.BadRequest, "车间名称不能为空");
// 唯一性检查
var existing = _workshopRepository.GetList(request.Name);
if (existing.Items.Any(w => string.Equals(w.Name, request.Name, StringComparison.OrdinalIgnoreCase)))
throw new BusinessException(ErrorCode.Conflict, "车间名称已存在");
var entity = new Workshop
{
Name = request.Name,
SortOrder = request.SortOrder,
IsEnabled = 1,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
return _workshopRepository.Create(entity);
}
/// <inheritdoc/>
public bool Update(int id, UpdateWorkshopRequest request)
{
if (id <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的车间ID");
if (request == null) throw new BusinessException(ErrorCode.BadRequest, "请求参数不能为空");
var entity = _workshopRepository.GetById(id);
if (entity == null) throw new BusinessException(ErrorCode.NotFound, "车间未找到");
entity.Name = request.Name ?? entity.Name;
entity.SortOrder = request.SortOrder;
entity.UpdatedAt = DateTime.Now;
return _workshopRepository.Update(entity);
}
/// <inheritdoc/>
public bool Delete(int id)
{
if (id <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的车间ID");
int machineCount = _workshopRepository.GetMachineCount(id);
if (machineCount > 0) throw new BusinessException(ErrorCode.DataReferenced, "车间下有机床,无法删除");
return _workshopRepository.Delete(id);
}
/// <inheritdoc/>
public bool ToggleEnabled(int id)
{
if (id <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的车间ID");
return _workshopRepository.ToggleEnabled(id);
}
/// <inheritdoc/>
public int GetMachineCount(int workshopId)
{
if (workshopId <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的车间ID");
return _workshopRepository.GetMachineCount(workshopId);
}
}
}

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using CncModels.Dto.Alert;
using CncModels.Dto;
namespace CncService.Interface
{
public interface IAlertService
{
PagedResult<AlertListItem> GetList(AlertQuery query);
bool Resolve(long id);
int BatchResolve(List<long> ids);
AlertStatisticsResponse GetStatistics();
}
}

@ -0,0 +1,17 @@
using CncModels.Dto.Login;
namespace CncService.Interface
{
/// <summary>
/// 登录认证服务接口
/// </summary>
public interface IAuthService
{
/// <summary>
/// 管理员登录
/// </summary>
/// <param name="request">登录请求参数</param>
/// <returns>登录响应(包含 JWT Token 及有效期)</returns>
LoginResponse Login(LoginRequest request);
}
}

@ -0,0 +1,20 @@
using System.Collections.Generic;
using CncModels.Dto.Brand;
namespace CncService.Interface
{
/// <summary>
/// 品牌模板服务接口
/// </summary>
public interface IBrandService
{
List<BrandListItem> GetList();
BrandDetailResponse GetById(int id);
int Create(CreateBrandRequest request);
bool Update(int id, UpdateBrandRequest request);
bool Delete(int id);
bool ToggleEnabled(int id);
int Copy(int id);
List<StandardFieldResponse> GetStandardFields();
}
}

@ -0,0 +1,17 @@
using CncModels.Dto;
using CncModels.Dto.CollectAddress;
namespace CncService.Interface
{
/// <summary>
/// 采集地址服务接口
/// </summary>
public interface ICollectAddressService
{
PagedResult<CollectAddressListItem> GetList(CollectAddressQuery query);
CollectAddressDetailResponse GetById(int id);
int Create(CreateCollectAddressRequest request);
bool Update(int id, UpdateCollectAddressRequest request);
bool Delete(int id);
bool ToggleEnabled(int id);
}
}

@ -0,0 +1,17 @@
using CncModels.Dto;
using CncModels.Entity;
namespace CncService.Interface
{
/// <summary>
/// 采集数据查询服务接口
/// </summary>
public interface ICollectDataService
{
/// <summary>按地址ID分页查询原始采集记录</summary>
PagedResult<CollectRaw> GetRawByAddress(int addressId, int page, int pageSize);
/// <summary>获取最新采集记录</summary>
CollectRaw GetLatestRaw(int addressId);
}
}

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using CncModels.Dto.Dashboard;
namespace CncService.Interface
{
/// <summary>
/// 仪表盘服务接口
/// </summary>
public interface IDashboardService
{
DashboardSummaryResponse GetSummary();
List<WorkshopProductionResponse> GetWorkshopProduction(DateTime? startDate, DateTime? endDate);
List<MachineRankResponse> GetMachineRank(DateTime? startDate, DateTime? endDate, int top = 10);
List<WorkerRankResponse> GetWorkerRank(DateTime? startDate, DateTime? endDate, int top = 10);
object GetProductionTrend(int days = 7);
object GetMachineStatusDistribution();
List<AlertListItem> GetRecentAlerts(int count = 5);
object GetCollectorStatus();
}
}

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using CncModels.Dto.Machine;
using CncModels.Dto;
namespace CncService.Interface
{
/// <summary>
/// 机床管理服务接口
/// </summary>
public interface IMachineService
{
/// <summary>
/// 分页查询机床列表
/// </summary>
/// <param name="query">查询条件</param>
/// <returns>分页结果</returns>
PagedResult<MachineListItem> GetList(MachineQuery query);
/// <summary>
/// 根据ID获取机床详情
/// </summary>
/// <param name="id">机床ID</param>
/// <returns>机床详情</returns>
MachineDetailResponse GetById(int id);
/// <summary>
/// 新增机床
/// </summary>
/// <param name="request">创建参数</param>
/// <returns>新建机床的ID</returns>
int Create(CreateMachineRequest request);
/// <summary>
/// 编辑机床信息
/// </summary>
/// <param name="id">机床ID</param>
/// <param name="request">修改参数</param>
/// <returns>是否更新成功</returns>
bool Update(int id, UpdateMachineRequest request);
/// <summary>
/// 删除机床并解绑相关工人
/// </summary>
/// <param name="id">机床ID</param>
/// <returns>是否删除成功</returns>
bool Delete(int id);
/// <summary>
/// 启用或禁用机床
/// </summary>
/// <param name="id">机床ID</param>
/// <returns>是否切换成功</returns>
bool ToggleEnabled(int id);
}
}

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using CncModels.Dto;
using CncModels.Dto.Production;
namespace CncService.Interface
{
/// <summary>
/// 产量管理服务接口
/// </summary>
public interface IProductionService
{
/// <summary>分页查询产量记录</summary>
PagedResult<DailyProductionListItem> GetList(ProductionQuery query);
/// <summary>获取日汇总统计</summary>
DailySummaryResponse GetSummary(DateTime? date, int? workshopId);
/// <summary>获取日期范围总产量</summary>
decimal GetTotalByDateRange(DateTime startDate, DateTime endDate, int? workshopId);
/// <summary>产量修正</summary>
bool Adjust(ProductionAdjustRequest request);
}
}

@ -0,0 +1,34 @@
using System.Collections.Generic;
using CncModels.Dto.Screen;
using CncModels.Dto.ScreenConfig;
using CncModels.Entity;
namespace CncService.Interface
{
/// <summary>
/// 大屏配置服务接口
/// </summary>
public interface IScreenService
{
/// <summary>获取大屏汇总数据</summary>
ScreenSummaryResponse GetSummary();
/// <summary>获取所有卡片配置</summary>
List<ScreenConfig> GetConfigs();
/// <summary>更新卡片配置</summary>
bool UpdateConfig(ScreenConfig entity);
/// <summary>获取筛选项列表</summary>
List<ScreenFilter> GetFilters(string screenKey);
/// <summary>创建筛选项</summary>
int CreateFilter(ScreenFilter entity);
/// <summary>更新筛选项</summary>
bool UpdateFilter(ScreenFilter entity);
/// <summary>删除筛选项</summary>
bool DeleteFilter(int id);
}
}

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using CncModels.Dto.Log;
using CncModels.Dto;
namespace CncService.Interface
{
public interface ISystemLogService
{
PagedResult<SystemLogListItem> GetList(SystemLogQuery query);
}
}

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using CncModels.Dto.Worker;
using CncModels.Dto;
namespace CncService.Interface
{
public interface IWorkerService
{
PagedResult<WorkerListItem> GetList(WorkerQuery query);
WorkerDetailResponse GetById(int id);
int Create(CreateWorkerRequest request);
bool Update(int id, UpdateWorkerRequest request);
bool Delete(int id);
bool ToggleEnabled(int id);
bool BindMachine(int workerId, int machineId);
bool UnbindMachine(int workerId, int machineId);
}
}

@ -0,0 +1,33 @@
using System.Collections.Generic;
using CncModels.Dto.Settings;
using CncModels.Entity;
namespace CncService.Interface
{
/// <summary>
/// 车间管理服务接口
/// </summary>
public interface IWorkshopService
{
/// <summary>获取车间列表</summary>
List<WorkshopListItem> GetList(string keyword);
/// <summary>按ID获取车间</summary>
Workshop GetById(int id);
/// <summary>新增车间</summary>
int Create(CreateWorkshopRequest request);
/// <summary>编辑车间</summary>
bool Update(int id, UpdateWorkshopRequest request);
/// <summary>删除车间</summary>
bool Delete(int id);
/// <summary>启停车间</summary>
bool ToggleEnabled(int id);
/// <summary>获取车间下机床数量</summary>
int GetMachineCount(int workshopId);
}
}
Loading…
Cancel
Save