From fd4047527108f726c440dcc5bd2920aa40528fdb Mon Sep 17 00:00:00 2001
From: haoliang <821644@qq.com>
Date: Tue, 28 Apr 2026 18:59:57 +0800
Subject: [PATCH] =?UTF-8?q?feat(service):=20CncService=E5=B1=82=E5=AE=8C?=
=?UTF-8?q?=E6=95=B4=E5=AE=9E=E7=8E=B0=20-=2012=E6=8E=A5=E5=8F=A3+12?=
=?UTF-8?q?=E5=AE=9E=E7=8E=B0=EF=BC=8C=E7=BC=96=E8=AF=91=E9=80=9A=E8=BF=87?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 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包
---
src/CncService/CncService.csproj | 5 +
src/CncService/Impl/AlertService.cs | 50 +++++
src/CncService/Impl/AuthService.cs | 101 ++++++++++
src/CncService/Impl/BrandService.cs | 172 ++++++++++++++++++
src/CncService/Impl/CollectAddressService.cs | 98 ++++++++++
src/CncService/Impl/CollectDataService.cs | 38 ++++
src/CncService/Impl/DashboardService.cs | 81 +++++++++
src/CncService/Impl/MachineService.cs | 131 +++++++++++++
src/CncService/Impl/ProductionService.cs | 79 ++++++++
src/CncService/Impl/ScreenService.cs | 84 +++++++++
src/CncService/Impl/SystemLogService.cs | 29 +++
src/CncService/Impl/WorkerService.cs | 132 ++++++++++++++
src/CncService/Impl/WorkshopService.cs | 106 +++++++++++
src/CncService/Interface/IAlertService.cs | 15 ++
src/CncService/Interface/IAuthService.cs | 17 ++
src/CncService/Interface/IBrandService.cs | 20 ++
.../Interface/ICollectAddressService.cs | 17 ++
.../Interface/ICollectDataService.cs | 17 ++
src/CncService/Interface/IDashboardService.cs | 28 +++
src/CncService/Interface/IMachineService.cs | 56 ++++++
.../Interface/IProductionService.cs | 25 +++
src/CncService/Interface/IScreenService.cs | 34 ++++
src/CncService/Interface/ISystemLogService.cs | 12 ++
src/CncService/Interface/IWorkerService.cs | 19 ++
src/CncService/Interface/IWorkshopService.cs | 33 ++++
25 files changed, 1399 insertions(+)
create mode 100644 src/CncService/Impl/AlertService.cs
create mode 100644 src/CncService/Impl/AuthService.cs
create mode 100644 src/CncService/Impl/BrandService.cs
create mode 100644 src/CncService/Impl/CollectAddressService.cs
create mode 100644 src/CncService/Impl/CollectDataService.cs
create mode 100644 src/CncService/Impl/DashboardService.cs
create mode 100644 src/CncService/Impl/MachineService.cs
create mode 100644 src/CncService/Impl/ProductionService.cs
create mode 100644 src/CncService/Impl/ScreenService.cs
create mode 100644 src/CncService/Impl/SystemLogService.cs
create mode 100644 src/CncService/Impl/WorkerService.cs
create mode 100644 src/CncService/Impl/WorkshopService.cs
create mode 100644 src/CncService/Interface/IAlertService.cs
create mode 100644 src/CncService/Interface/IAuthService.cs
create mode 100644 src/CncService/Interface/IBrandService.cs
create mode 100644 src/CncService/Interface/ICollectAddressService.cs
create mode 100644 src/CncService/Interface/ICollectDataService.cs
create mode 100644 src/CncService/Interface/IDashboardService.cs
create mode 100644 src/CncService/Interface/IMachineService.cs
create mode 100644 src/CncService/Interface/IProductionService.cs
create mode 100644 src/CncService/Interface/IScreenService.cs
create mode 100644 src/CncService/Interface/ISystemLogService.cs
create mode 100644 src/CncService/Interface/IWorkerService.cs
create mode 100644 src/CncService/Interface/IWorkshopService.cs
diff --git a/src/CncService/CncService.csproj b/src/CncService/CncService.csproj
index 2296ca9..2423637 100644
--- a/src/CncService/CncService.csproj
+++ b/src/CncService/CncService.csproj
@@ -13,6 +13,11 @@
+
+
+
+
+
diff --git a/src/CncService/Impl/AlertService.cs b/src/CncService/Impl/AlertService.cs
new file mode 100644
index 0000000..03d3d60
--- /dev/null
+++ b/src/CncService/Impl/AlertService.cs
@@ -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
+{
+ ///
+ /// 告警管理实现
+ ///
+ public class AlertService : IAlertService
+ {
+ private readonly IAlertRepository _alertRepository;
+
+ public AlertService(IAlertRepository alertRepository)
+ {
+ _alertRepository = alertRepository ?? throw new ArgumentNullException(nameof(alertRepository));
+ }
+
+ ///
+ public PagedResult GetList(AlertQuery query)
+ {
+ if (query == null) throw new BusinessException(ErrorCode.BadRequest, "查询参数不能为空");
+ return _alertRepository.GetList(query);
+ }
+
+ ///
+ public bool Resolve(long id)
+ {
+ if (id <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的告警ID");
+ return _alertRepository.Resolve(id);
+ }
+
+ ///
+ public int BatchResolve(List ids)
+ {
+ if (ids == null || ids.Count == 0) throw new BusinessException(ErrorCode.BadRequest, "无效的ID列表");
+ return _alertRepository.BatchResolve(ids);
+ }
+
+ ///
+ public AlertStatisticsResponse GetStatistics()
+ {
+ return _alertRepository.GetStatistics();
+ }
+ }
+}
diff --git a/src/CncService/Impl/AuthService.cs b/src/CncService/Impl/AuthService.cs
new file mode 100644
index 0000000..e7cdd75
--- /dev/null
+++ b/src/CncService/Impl/AuthService.cs
@@ -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
+{
+ ///
+ /// 登录认证实现
+ ///
+ 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));
+ }
+
+ ///
+ 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('=');
+ }
+ }
+}
diff --git a/src/CncService/Impl/BrandService.cs b/src/CncService/Impl/BrandService.cs
new file mode 100644
index 0000000..5d8b17f
--- /dev/null
+++ b/src/CncService/Impl/BrandService.cs
@@ -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
+{
+ ///
+ /// 品牌模板实现
+ ///
+ 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 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()
+ };
+ 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 GetStandardFields()
+ {
+ // 硬编码的16个标准字段示例
+ var list = new List();
+ for (int i = 1; i <= 16; i++)
+ {
+ list.Add(new StandardFieldResponse
+ {
+ StandardField = $"Field{i}",
+ Description = $"标准字段描述{i}"
+ });
+ }
+ return list;
+ }
+ }
+}
diff --git a/src/CncService/Impl/CollectAddressService.cs b/src/CncService/Impl/CollectAddressService.cs
new file mode 100644
index 0000000..0f94f91
--- /dev/null
+++ b/src/CncService/Impl/CollectAddressService.cs
@@ -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
+{
+ ///
+ /// 采集地址实现
+ ///
+ 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 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);
+ }
+ }
+}
diff --git a/src/CncService/Impl/CollectDataService.cs b/src/CncService/Impl/CollectDataService.cs
new file mode 100644
index 0000000..fd97ef2
--- /dev/null
+++ b/src/CncService/Impl/CollectDataService.cs
@@ -0,0 +1,38 @@
+using System;
+using CncService.Interface;
+using CncModels.Dto;
+using CncModels.Entity;
+using CncModels.Constants;
+using CncRepository.Interface;
+
+namespace CncService.Impl
+{
+ ///
+ /// 采集数据查询实现
+ ///
+ public class CollectDataService : ICollectDataService
+ {
+ private readonly ICollectRawRepository _collectRawRepository;
+
+ public CollectDataService(ICollectRawRepository collectRawRepository)
+ {
+ _collectRawRepository = collectRawRepository ?? throw new ArgumentNullException(nameof(collectRawRepository));
+ }
+
+ ///
+ public PagedResult 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);
+ }
+
+ ///
+ public CollectRaw GetLatestRaw(int addressId)
+ {
+ if (addressId <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的地址ID");
+ return _collectRawRepository.GetLatestByAddressId(addressId);
+ }
+ }
+}
diff --git a/src/CncService/Impl/DashboardService.cs b/src/CncService/Impl/DashboardService.cs
new file mode 100644
index 0000000..e7f0394
--- /dev/null
+++ b/src/CncService/Impl/DashboardService.cs
@@ -0,0 +1,81 @@
+using System;
+using System.Collections.Generic;
+using CncModels.Dto.Dashboard;
+using CncRepository.Interface;
+using CncService.Interface;
+
+namespace CncService.Impl
+{
+ ///
+ /// 仪表盘实现
+ ///
+ 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));
+ }
+
+ ///
+ public DashboardSummaryResponse GetSummary()
+ {
+ return _dashboardRepository.GetSummary();
+ }
+
+ ///
+ public List GetWorkshopProduction(DateTime? startDate, DateTime? endDate)
+ {
+ var s = startDate ?? DateTime.Today;
+ var e = endDate ?? DateTime.Today;
+ return _dashboardRepository.GetWorkshopProduction(s, e);
+ }
+
+ ///
+ public List GetMachineRank(DateTime? startDate, DateTime? endDate, int top = 10)
+ {
+ var s = startDate ?? DateTime.Today;
+ var e = endDate ?? DateTime.Today;
+ return _dashboardRepository.GetMachineRank(s, e, top);
+ }
+
+ ///
+ public List GetWorkerRank(DateTime? startDate, DateTime? endDate, int top = 10)
+ {
+ var s = startDate ?? DateTime.Today;
+ var e = endDate ?? DateTime.Today;
+ return _dashboardRepository.GetWorkerRank(s, e, top);
+ }
+
+ ///
+ public object GetProductionTrend(int days = 7)
+ {
+ return _dashboardRepository.GetProductionTrend(days);
+ }
+
+ ///
+ public object GetMachineStatusDistribution()
+ {
+ return _dashboardRepository.GetMachineStatusDistribution();
+ }
+
+ ///
+ public List GetRecentAlerts(int count = 5)
+ {
+ return _dashboardRepository.GetRecentAlerts(count);
+ }
+
+ ///
+ 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 };
+ }
+ }
+}
diff --git a/src/CncService/Impl/MachineService.cs b/src/CncService/Impl/MachineService.cs
new file mode 100644
index 0000000..b89afde
--- /dev/null
+++ b/src/CncService/Impl/MachineService.cs
@@ -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
+{
+ ///
+ /// 机床管理实现
+ ///
+ 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));
+ }
+
+ ///
+ public PagedResult GetList(MachineQuery query)
+ {
+ if (query == null) throw new BusinessException(ErrorCode.BadRequest, "查询参数不能为空");
+ return _machineRepository.GetList(query);
+ }
+
+ ///
+ 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;
+ }
+
+ ///
+ 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);
+ }
+
+ ///
+ 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);
+ }
+
+ ///
+ public bool Delete(int id)
+ {
+ if (id <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的机床ID");
+ // 解绑工人
+ _workerMachineRepository.DeleteByMachineId(id);
+ return _machineRepository.Delete(id);
+ }
+
+ ///
+ public bool ToggleEnabled(int id)
+ {
+ if (id <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的机床ID");
+ return _machineRepository.ToggleEnabled(id);
+ }
+ }
+}
diff --git a/src/CncService/Impl/ProductionService.cs b/src/CncService/Impl/ProductionService.cs
new file mode 100644
index 0000000..5bcfe62
--- /dev/null
+++ b/src/CncService/Impl/ProductionService.cs
@@ -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
+{
+ ///
+ /// 产量管理实现
+ ///
+ 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));
+ }
+
+ ///
+ public PagedResult GetList(ProductionQuery query)
+ {
+ if (query == null) throw new BusinessException(ErrorCode.BadRequest, "查询参数不能为空");
+ return _dailyProductionRepository.GetList(query);
+ }
+
+ ///
+ 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
+ };
+ }
+
+ ///
+ public decimal GetTotalByDateRange(DateTime startDate, DateTime endDate, int? workshopId)
+ {
+ return _dailyProductionRepository.GetTotalByDateRange(startDate, endDate, workshopId);
+ }
+
+ ///
+ 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;
+ }
+ }
+}
diff --git a/src/CncService/Impl/ScreenService.cs b/src/CncService/Impl/ScreenService.cs
new file mode 100644
index 0000000..415724b
--- /dev/null
+++ b/src/CncService/Impl/ScreenService.cs
@@ -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
+{
+ ///
+ /// 大屏配置实现
+ ///
+ 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));
+ }
+
+ ///
+ public ScreenSummaryResponse GetSummary()
+ {
+ // 简化实现,返回默认值(实际由Controller调用Dashboard服务获取真实数据)
+ return new ScreenSummaryResponse
+ {
+ MachineCount = 0,
+ ProductionToday = 0,
+ AlertCount = 0,
+ OnlineCount = 0
+ };
+ }
+
+ ///
+ public List GetConfigs()
+ {
+ return _screenConfigRepository.GetAll();
+ }
+
+ ///
+ public bool UpdateConfig(ScreenConfig entity)
+ {
+ if (entity == null) throw new BusinessException(ErrorCode.BadRequest, "配置不能为空");
+ return _screenConfigRepository.Update(entity);
+ }
+
+ ///
+ public List GetFilters(string screenKey)
+ {
+ if (string.IsNullOrWhiteSpace(screenKey)) throw new BusinessException(ErrorCode.BadRequest, "screenKey不能为空");
+ return _screenFilterRepository.GetByScreenKey(screenKey);
+ }
+
+ ///
+ public int CreateFilter(ScreenFilter entity)
+ {
+ if (entity == null) throw new BusinessException(ErrorCode.BadRequest, "筛选项不能为空");
+ return _screenFilterRepository.Create(entity);
+ }
+
+ ///
+ public bool UpdateFilter(ScreenFilter entity)
+ {
+ if (entity == null) throw new BusinessException(ErrorCode.BadRequest, "筛选项不能为空");
+ return _screenFilterRepository.Update(entity);
+ }
+
+ ///
+ public bool DeleteFilter(int id)
+ {
+ if (id <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的筛选ID");
+ return _screenFilterRepository.Delete(id);
+ }
+ }
+}
diff --git a/src/CncService/Impl/SystemLogService.cs b/src/CncService/Impl/SystemLogService.cs
new file mode 100644
index 0000000..df20865
--- /dev/null
+++ b/src/CncService/Impl/SystemLogService.cs
@@ -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
+{
+ ///
+ /// 系统日志实现
+ ///
+ public class SystemLogService : ISystemLogService
+ {
+ private readonly ISystemLogRepository _systemLogRepository;
+
+ public SystemLogService(ISystemLogRepository systemLogRepository)
+ {
+ _systemLogRepository = systemLogRepository ?? throw new ArgumentNullException(nameof(systemLogRepository));
+ }
+
+ ///
+ public PagedResult GetList(SystemLogQuery query)
+ {
+ if (query == null) throw new BusinessException(ErrorCode.BadRequest, "查询参数不能为空");
+ return _systemLogRepository.GetList(query);
+ }
+ }
+}
diff --git a/src/CncService/Impl/WorkerService.cs b/src/CncService/Impl/WorkerService.cs
new file mode 100644
index 0000000..75b2b8d
--- /dev/null
+++ b/src/CncService/Impl/WorkerService.cs
@@ -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
+{
+ ///
+ /// 员工管理实现
+ ///
+ 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));
+ }
+
+ ///
+ public PagedResult GetList(WorkerQuery query)
+ {
+ if (query == null) throw new BusinessException(ErrorCode.BadRequest, "查询参数不能为空");
+ return _workerRepository.GetList(query);
+ }
+
+ ///
+ 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();
+ 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)
+ };
+ }
+
+ ///
+ 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);
+ }
+
+ ///
+ 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);
+ }
+
+ ///
+ public bool Delete(int id)
+ {
+ if (id <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的员工ID");
+ // 解绑所有机床
+ _workerMachineRepository.DeleteByWorkerId(id);
+ return _workerRepository.Delete(id);
+ }
+
+ ///
+ public bool ToggleEnabled(int id)
+ {
+ if (id <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的员工ID");
+ return _workerRepository.ToggleEnabled(id);
+ }
+
+ ///
+ 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;
+ }
+
+ ///
+ public bool UnbindMachine(int workerId, int machineId)
+ {
+ if (workerId <= 0 || machineId <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的参数");
+ return _workerMachineRepository.DeleteByMachineId(machineId);
+ }
+ }
+}
diff --git a/src/CncService/Impl/WorkshopService.cs b/src/CncService/Impl/WorkshopService.cs
new file mode 100644
index 0000000..a5b74ae
--- /dev/null
+++ b/src/CncService/Impl/WorkshopService.cs
@@ -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
+{
+ ///
+ /// 车间管理实现
+ ///
+ public class WorkshopService : IWorkshopService
+ {
+ private readonly IWorkshopRepository _workshopRepository;
+
+ public WorkshopService(IWorkshopRepository workshopRepository)
+ {
+ _workshopRepository = workshopRepository ?? throw new ArgumentNullException(nameof(workshopRepository));
+ }
+
+ ///
+ public List GetList(string keyword)
+ {
+ var paged = _workshopRepository.GetList(keyword ?? string.Empty);
+ // WorkshopRepository.GetList返回PagedResult,转换为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();
+ }
+
+ ///
+ 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;
+ }
+
+ ///
+ 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);
+ }
+
+ ///
+ 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);
+ }
+
+ ///
+ 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);
+ }
+
+ ///
+ public bool ToggleEnabled(int id)
+ {
+ if (id <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的车间ID");
+ return _workshopRepository.ToggleEnabled(id);
+ }
+
+ ///
+ public int GetMachineCount(int workshopId)
+ {
+ if (workshopId <= 0) throw new BusinessException(ErrorCode.BadRequest, "无效的车间ID");
+ return _workshopRepository.GetMachineCount(workshopId);
+ }
+ }
+}
diff --git a/src/CncService/Interface/IAlertService.cs b/src/CncService/Interface/IAlertService.cs
new file mode 100644
index 0000000..6038279
--- /dev/null
+++ b/src/CncService/Interface/IAlertService.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using CncModels.Dto.Alert;
+using CncModels.Dto;
+
+namespace CncService.Interface
+{
+ public interface IAlertService
+ {
+ PagedResult GetList(AlertQuery query);
+ bool Resolve(long id);
+ int BatchResolve(List ids);
+ AlertStatisticsResponse GetStatistics();
+ }
+}
diff --git a/src/CncService/Interface/IAuthService.cs b/src/CncService/Interface/IAuthService.cs
new file mode 100644
index 0000000..0b36f87
--- /dev/null
+++ b/src/CncService/Interface/IAuthService.cs
@@ -0,0 +1,17 @@
+using CncModels.Dto.Login;
+
+namespace CncService.Interface
+{
+ ///
+ /// 登录认证服务接口
+ ///
+ public interface IAuthService
+ {
+ ///
+ /// 管理员登录
+ ///
+ /// 登录请求参数
+ /// 登录响应(包含 JWT Token 及有效期)
+ LoginResponse Login(LoginRequest request);
+ }
+}
diff --git a/src/CncService/Interface/IBrandService.cs b/src/CncService/Interface/IBrandService.cs
new file mode 100644
index 0000000..d6226f3
--- /dev/null
+++ b/src/CncService/Interface/IBrandService.cs
@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+using CncModels.Dto.Brand;
+
+namespace CncService.Interface
+{
+ ///
+ /// 品牌模板服务接口
+ ///
+ public interface IBrandService
+ {
+ List 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 GetStandardFields();
+ }
+}
diff --git a/src/CncService/Interface/ICollectAddressService.cs b/src/CncService/Interface/ICollectAddressService.cs
new file mode 100644
index 0000000..ac2b27c
--- /dev/null
+++ b/src/CncService/Interface/ICollectAddressService.cs
@@ -0,0 +1,17 @@
+using CncModels.Dto;
+using CncModels.Dto.CollectAddress;
+namespace CncService.Interface
+{
+ ///
+ /// 采集地址服务接口
+ ///
+ public interface ICollectAddressService
+ {
+ PagedResult 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);
+ }
+}
diff --git a/src/CncService/Interface/ICollectDataService.cs b/src/CncService/Interface/ICollectDataService.cs
new file mode 100644
index 0000000..a5636d9
--- /dev/null
+++ b/src/CncService/Interface/ICollectDataService.cs
@@ -0,0 +1,17 @@
+using CncModels.Dto;
+using CncModels.Entity;
+
+namespace CncService.Interface
+{
+ ///
+ /// 采集数据查询服务接口
+ ///
+ public interface ICollectDataService
+ {
+ /// 按地址ID分页查询原始采集记录
+ PagedResult GetRawByAddress(int addressId, int page, int pageSize);
+
+ /// 获取最新采集记录
+ CollectRaw GetLatestRaw(int addressId);
+ }
+}
diff --git a/src/CncService/Interface/IDashboardService.cs b/src/CncService/Interface/IDashboardService.cs
new file mode 100644
index 0000000..17d19c2
--- /dev/null
+++ b/src/CncService/Interface/IDashboardService.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using CncModels.Dto.Dashboard;
+
+namespace CncService.Interface
+{
+ ///
+ /// 仪表盘服务接口
+ ///
+ public interface IDashboardService
+ {
+ DashboardSummaryResponse GetSummary();
+
+ List GetWorkshopProduction(DateTime? startDate, DateTime? endDate);
+
+ List GetMachineRank(DateTime? startDate, DateTime? endDate, int top = 10);
+
+ List GetWorkerRank(DateTime? startDate, DateTime? endDate, int top = 10);
+
+ object GetProductionTrend(int days = 7);
+
+ object GetMachineStatusDistribution();
+
+ List GetRecentAlerts(int count = 5);
+
+ object GetCollectorStatus();
+ }
+}
diff --git a/src/CncService/Interface/IMachineService.cs b/src/CncService/Interface/IMachineService.cs
new file mode 100644
index 0000000..113f8f7
--- /dev/null
+++ b/src/CncService/Interface/IMachineService.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using CncModels.Dto.Machine;
+using CncModels.Dto;
+
+namespace CncService.Interface
+{
+ ///
+ /// 机床管理服务接口
+ ///
+ public interface IMachineService
+ {
+ ///
+ /// 分页查询机床列表
+ ///
+ /// 查询条件
+ /// 分页结果
+ PagedResult GetList(MachineQuery query);
+
+ ///
+ /// 根据ID获取机床详情
+ ///
+ /// 机床ID
+ /// 机床详情
+ MachineDetailResponse GetById(int id);
+
+ ///
+ /// 新增机床
+ ///
+ /// 创建参数
+ /// 新建机床的ID
+ int Create(CreateMachineRequest request);
+
+ ///
+ /// 编辑机床信息
+ ///
+ /// 机床ID
+ /// 修改参数
+ /// 是否更新成功
+ bool Update(int id, UpdateMachineRequest request);
+
+ ///
+ /// 删除机床并解绑相关工人
+ ///
+ /// 机床ID
+ /// 是否删除成功
+ bool Delete(int id);
+
+ ///
+ /// 启用或禁用机床
+ ///
+ /// 机床ID
+ /// 是否切换成功
+ bool ToggleEnabled(int id);
+ }
+}
diff --git a/src/CncService/Interface/IProductionService.cs b/src/CncService/Interface/IProductionService.cs
new file mode 100644
index 0000000..74dcab8
--- /dev/null
+++ b/src/CncService/Interface/IProductionService.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using CncModels.Dto;
+using CncModels.Dto.Production;
+
+namespace CncService.Interface
+{
+ ///
+ /// 产量管理服务接口
+ ///
+ public interface IProductionService
+ {
+ /// 分页查询产量记录
+ PagedResult GetList(ProductionQuery query);
+
+ /// 获取日汇总统计
+ DailySummaryResponse GetSummary(DateTime? date, int? workshopId);
+
+ /// 获取日期范围总产量
+ decimal GetTotalByDateRange(DateTime startDate, DateTime endDate, int? workshopId);
+
+ /// 产量修正
+ bool Adjust(ProductionAdjustRequest request);
+ }
+}
diff --git a/src/CncService/Interface/IScreenService.cs b/src/CncService/Interface/IScreenService.cs
new file mode 100644
index 0000000..19fb7a5
--- /dev/null
+++ b/src/CncService/Interface/IScreenService.cs
@@ -0,0 +1,34 @@
+using System.Collections.Generic;
+using CncModels.Dto.Screen;
+using CncModels.Dto.ScreenConfig;
+using CncModels.Entity;
+
+namespace CncService.Interface
+{
+ ///
+ /// 大屏配置服务接口
+ ///
+ public interface IScreenService
+ {
+ /// 获取大屏汇总数据
+ ScreenSummaryResponse GetSummary();
+
+ /// 获取所有卡片配置
+ List GetConfigs();
+
+ /// 更新卡片配置
+ bool UpdateConfig(ScreenConfig entity);
+
+ /// 获取筛选项列表
+ List GetFilters(string screenKey);
+
+ /// 创建筛选项
+ int CreateFilter(ScreenFilter entity);
+
+ /// 更新筛选项
+ bool UpdateFilter(ScreenFilter entity);
+
+ /// 删除筛选项
+ bool DeleteFilter(int id);
+ }
+}
diff --git a/src/CncService/Interface/ISystemLogService.cs b/src/CncService/Interface/ISystemLogService.cs
new file mode 100644
index 0000000..aee89e7
--- /dev/null
+++ b/src/CncService/Interface/ISystemLogService.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using CncModels.Dto.Log;
+using CncModels.Dto;
+
+namespace CncService.Interface
+{
+ public interface ISystemLogService
+ {
+ PagedResult GetList(SystemLogQuery query);
+ }
+}
diff --git a/src/CncService/Interface/IWorkerService.cs b/src/CncService/Interface/IWorkerService.cs
new file mode 100644
index 0000000..fc2f89a
--- /dev/null
+++ b/src/CncService/Interface/IWorkerService.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using CncModels.Dto.Worker;
+using CncModels.Dto;
+
+namespace CncService.Interface
+{
+ public interface IWorkerService
+ {
+ PagedResult 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);
+ }
+}
diff --git a/src/CncService/Interface/IWorkshopService.cs b/src/CncService/Interface/IWorkshopService.cs
new file mode 100644
index 0000000..00a4316
--- /dev/null
+++ b/src/CncService/Interface/IWorkshopService.cs
@@ -0,0 +1,33 @@
+using System.Collections.Generic;
+using CncModels.Dto.Settings;
+using CncModels.Entity;
+
+namespace CncService.Interface
+{
+ ///
+ /// 车间管理服务接口
+ ///
+ public interface IWorkshopService
+ {
+ /// 获取车间列表
+ List GetList(string keyword);
+
+ /// 按ID获取车间
+ Workshop GetById(int id);
+
+ /// 新增车间
+ int Create(CreateWorkshopRequest request);
+
+ /// 编辑车间
+ bool Update(int id, UpdateWorkshopRequest request);
+
+ /// 删除车间
+ bool Delete(int id);
+
+ /// 启停车间
+ bool ToggleEnabled(int id);
+
+ /// 获取车间下机床数量
+ int GetMachineCount(int workshopId);
+ }
+}