新增MES模块,包含供应商、客户、车辆和地磅数据记录管理功能,支持免密接口和数据同步。更新相关控制器、实体、服务和数据库配置,优化权限管理和数据字典支持,确保系统的灵活性和可扩展性。

This commit is contained in:
geht
2026-04-30 15:28:20 +08:00
parent 142a0bdaba
commit b03cbeff9b
121 changed files with 10540 additions and 424 deletions

View File

@@ -67,6 +67,21 @@ public class ConfigConst
/// </summary>
public const string SysRefreshTokenExpire = "sys_refresh_token_expire";
/// <summary>
/// 用户操作时会话剩余不足该分钟数则续期为「Token过期时间」整段桌面端
/// </summary>
public const string SysTokenIdleExtendMinutes = "sys_token_idle_extend_minutes";
/// <summary>
/// 登录状态检查间隔(分钟),用于检测是否过期
/// </summary>
public const string SysTokenCheckIntervalMinutes = "sys_token_check_interval_minutes";
/// <summary>
/// 是否启用永不过期(桌面端)
/// </summary>
public const string SysTokenNeverExpire = "sys_token_never_expire";
/// <summary>
/// 发送异常日志邮件
/// </summary>

View File

@@ -0,0 +1,11 @@
using Prism.Events;
namespace YY.Admin.Core.Events;
public class CustomerChangedPayload
{
public string Action { get; set; } = string.Empty;
public string? CustomerId { get; set; }
}
public class CustomerChangedEvent : PubSubEvent<CustomerChangedPayload> { }

View File

@@ -0,0 +1,11 @@
using Prism.Events;
namespace YY.Admin.Core.Events;
public class SupplierChangedPayload
{
public string Action { get; set; } = string.Empty;
public string? SupplierId { get; set; }
}
public class SupplierChangedEvent : PubSubEvent<SupplierChangedPayload> { }

View File

@@ -0,0 +1,13 @@
using Prism.Events;
namespace YY.Admin.Core.Events;
public class SyncConflictPayload
{
public string EntityName { get; set; } = string.Empty;
public int PushedCount { get; set; }
public int ConflictCount { get; set; }
public int NewRecordsPushed { get; set; }
}
public class SyncConflictEvent : PubSubEvent<SyncConflictPayload> { }

View File

@@ -0,0 +1,11 @@
using Prism.Events;
namespace YY.Admin.Core.Events;
public class VehicleChangedPayload
{
public string Action { get; set; } = string.Empty;
public string? VehicleId { get; set; }
}
public class VehicleChangedEvent : PubSubEvent<VehicleChangedPayload> { }

View File

@@ -0,0 +1,19 @@
using YY.Admin.Core.Entity;
namespace YY.Admin.Core.Services;
public interface ICustomerService
{
Task<CustomerPageResult> PageAsync(int pageNo, int pageSize,
string? customerCode = null, string? customerName = null,
string? status = null, string? customerRegion = null,
CancellationToken ct = default);
Task<MesXslCustomer?> GetByIdAsync(string id, CancellationToken ct = default);
Task<bool> AddAsync(MesXslCustomer customer, CancellationToken ct = default);
Task<bool> EditAsync(MesXslCustomer customer, CancellationToken ct = default);
Task<bool> DeleteAsync(string id, CancellationToken ct = default);
Task<bool> UpdateStatusAsync(string id, string status, CancellationToken ct = default);
}
public record CustomerPageResult(List<MesXslCustomer> Records, long Total, int PageNo, int PageSize);

View File

@@ -5,4 +5,9 @@ public interface INetworkMonitor
bool IsOnline { get; }
event Action<bool>? StatusChanged;
Task StartAsync(CancellationToken cancellationToken = default);
/// <summary>
/// 由 STOMP 设备通道在连接成功/断开时回写,与 HTTP 探活结果取或,统一 <see cref="IsOnline"/> 与网络事件。
/// </summary>
void SetStompTransportOnline(bool online);
}

View File

@@ -10,4 +10,9 @@ public interface ISignalRService
Task ConnectUnifiedDeviceChannelAsync(CancellationToken cancellationToken = default);
Task SendDeviceStatusAsync(object status, CancellationToken cancellationToken = default);
/// <summary>
/// 主动断开 STOMP 连接并停止重连。
/// </summary>
Task DisconnectAsync(CancellationToken cancellationToken = default);
}

View File

@@ -0,0 +1,26 @@
using YY.Admin.Core.Entity;
namespace YY.Admin.Core.Services;
public interface ISupplierService
{
Task<SupplierPageResult> PageAsync(int pageNo, int pageSize,
string? supplierCode = null, string? supplierName = null,
string? supplierShortName = null, string? erpCode = null,
string? status = null, CancellationToken ct = default);
Task<MesXslSupplier?> GetByIdAsync(string id, CancellationToken ct = default);
Task<bool> AddAsync(MesXslSupplier supplier, CancellationToken ct = default);
Task<bool> EditAsync(MesXslSupplier supplier, CancellationToken ct = default);
Task<bool> DeleteAsync(string id, CancellationToken ct = default);
Task<bool> UpdateStatusAsync(string id, string status, CancellationToken ct = default);
/// <summary>
/// 重连后将离线期间的本地改动推送到后端,并检测冲突。
/// </summary>
Task<PushPendingResult> PushPendingOnReconnectAsync(CancellationToken ct = default);
}
public record SupplierPageResult(List<MesXslSupplier> Records, long Total, int PageNo, int PageSize);
public record PushPendingResult(int PushedCount, int ConflictCount, int NewRecordsPushed);

View File

@@ -0,0 +1,13 @@
namespace YY.Admin.Core.Services;
/// <summary>
/// 将桌面端用户 CRUD 操作写入 Outbox异步反同步到 Jeecg 后端。
/// </summary>
public interface IUserSyncOutbox
{
Task EnqueueCreateAsync(string userId, string account, string? realName, int? sex, DateTime? birthday, string? phone, string? email, int status, string? updateBy, CancellationToken cancellationToken = default);
Task EnqueueUpdateAsync(string userId, string account, string? realName, int? sex, DateTime? birthday, string? phone, string? email, int status, string? updateBy, CancellationToken cancellationToken = default);
Task EnqueueToggleStatusAsync(string userId, int status, string? updateBy, CancellationToken cancellationToken = default);
Task EnqueueDeleteAsync(string userId, CancellationToken cancellationToken = default);
Task EnqueueBatchDeleteAsync(IReadOnlyList<string> userIds, CancellationToken cancellationToken = default);
}

View File

@@ -0,0 +1,16 @@
using YY.Admin.Core.Entity;
namespace YY.Admin.Core.Services;
public record VehiclePageResult(List<MesXslVehicle> Records, long Total, int Current, int Size);
public interface IVehicleService
{
Task<VehiclePageResult> PageAsync(int pageNo, int pageSize, string? plateNumber = null, string? vehicleBelong = null, string? status = null, CancellationToken ct = default);
Task<MesXslVehicle?> GetByIdAsync(string id, CancellationToken ct = default);
Task<bool> AddAsync(MesXslVehicle vehicle, CancellationToken ct = default);
Task<bool> EditAsync(MesXslVehicle vehicle, CancellationToken ct = default);
Task<bool> DeleteAsync(string id, CancellationToken ct = default);
Task<bool> DeleteBatchAsync(string ids, CancellationToken ct = default);
Task<bool> UpdateStatusAsync(string id, string status, CancellationToken ct = default);
}

View File

@@ -0,0 +1,16 @@
namespace YY.Admin.Core.Sync;
/// <summary>
/// 桌面→后端用户反同步在 Outbox 中的聚合类型常量。
/// 走 /sys/sync/batch 而非本地拉取路径。
/// </summary>
public static class SysUserSyncOutbox
{
public const string AggregateType = "SYS_USER";
public const string EventCreate = "CREATE";
public const string EventUpdate = "UPDATE";
public const string EventToggleStatus = "TOGGLE_STATUS";
public const string EventDelete = "DELETE";
public const string EventBatchDelete = "BATCH_DELETE";
}

View File

@@ -0,0 +1,25 @@
using System;
namespace YY.Admin.Core.Entity;
public class MesXslCustomer
{
public string? Id { get; set; }
public string? CustomerCode { get; set; }
public string? CustomerName { get; set; }
public string? CustomerShortName { get; set; }
public string? CustomerRegion { get; set; } // Dict: xslmes_customer_region
public string? ErpCode { get; set; }
public string? Status { get; set; } // Dict: xslmes_customer_status "0"启用 "1"停用
public int? IzEnable { get; set; } // 与 Status 联动,由服务端写入,本端只读
public string? CustomerDesc { get; set; }
public int? TenantId { get; set; }
public string? CreateBy { get; set; }
public DateTime? CreateTime { get; set; }
public string? UpdateBy { get; set; }
public DateTime? UpdateTime { get; set; }
public string? SysOrgCode { get; set; }
public int? Version { get; set; }
public string StatusText => Status == "1" ? "停用" : "启用";
}

View File

@@ -0,0 +1,23 @@
using System;
namespace YY.Admin.Core.Entity;
public class MesXslSupplier
{
public string? Id { get; set; }
public string? SupplierCode { get; set; }
public string? SupplierName { get; set; }
public string? SupplierShortName { get; set; }
public string? ErpCode { get; set; }
public string? Remark { get; set; }
public string? Status { get; set; } // Dict: xslmes_supplier_status "0"启用 "1"停用
public int? TenantId { get; set; }
public string? CreateBy { get; set; }
public DateTime? CreateTime { get; set; }
public string? UpdateBy { get; set; }
public DateTime? UpdateTime { get; set; }
public string? SysOrgCode { get; set; }
public int? Version { get; set; }
public string StatusText => Status == "1" ? "停用" : "启用";
}

View File

@@ -0,0 +1,46 @@
namespace YY.Admin.Core.Entity;
public class MesXslVehicle
{
public string? Id { get; set; }
public string? PlateNumber { get; set; }
/// <summary>车辆归属1客户 2供应商 3本公司</summary>
public string? VehicleBelong { get; set; }
public decimal? TareWeightKg { get; set; }
public decimal? LoadCapacity { get; set; }
public string? UnitId { get; set; }
public string? LoadUnit { get; set; }
public string? CustomerIds { get; set; }
public string? CustomerShortName { get; set; }
public string? SupplierId { get; set; }
public string? SupplierName { get; set; }
public string? SupplierShortName { get; set; }
public decimal? VehicleLength { get; set; }
public decimal? VehicleWidth { get; set; }
public decimal? VehicleHeight { get; set; }
public string? DriverName { get; set; }
public string? DriverPhone { get; set; }
/// <summary>状态0启用 1停用</summary>
public string? Status { get; set; }
public int? TenantId { get; set; }
public string? CreateBy { get; set; }
public DateTime? CreateTime { get; set; }
public string? UpdateBy { get; set; }
public DateTime? UpdateTime { get; set; }
public string? SysOrgCode { get; set; }
public int? Version { get; set; }
public string VehicleBelongText => VehicleBelong switch
{
"1" => "客户",
"2" => "供应商",
"3" => "本公司",
_ => VehicleBelong ?? ""
};
public string StatusText => Status == "1" ? "停用" : "启用";
}

View File

@@ -30,6 +30,9 @@ public class SysConfigSeedData : ISqlSugarEntitySeedData<SysConfig>
new SysConfig{ Id=1300000000172, Name="登录时隐藏租户", Code=ConfigConst.SysHideTenantLogin, Value="True", SysFlag=YesNoEnum.Y, Remark="登录时隐藏租户", OrderNo=90, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysConfig{ Id=1300000000181, Name="Token过期时间", Code=ConfigConst.SysTokenExpire, Value="30", SysFlag=YesNoEnum.Y, Remark="Token过期时间分钟", OrderNo=100, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysConfig{ Id=1300000000191, Name="RefreshToken过期时间", Code=ConfigConst.SysRefreshTokenExpire, Value="20160", SysFlag=YesNoEnum.Y, Remark="刷新Token过期时间分钟一般 refresh_token 的有效时间 > 2 * access_token 的有效时间)", OrderNo=110, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysConfig{ Id=1300000000192, Name="会话续期阈值", Code=ConfigConst.SysTokenIdleExtendMinutes, Value="20", SysFlag=YesNoEnum.Y, Remark="桌面端用户有操作时若会话剩余不足该分钟数则续期为「Token过期时间」整段", OrderNo=115, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysConfig{ Id=1300000000193, Name="登录状态检查间隔", Code=ConfigConst.SysTokenCheckIntervalMinutes, Value="1", SysFlag=YesNoEnum.Y, Remark="桌面端:定时检查登录是否过期的间隔(分钟)", OrderNo=118, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysConfig{ Id=1300000000194, Name="登录永不过期", Code=ConfigConst.SysTokenNeverExpire, Value="False", SysFlag=YesNoEnum.Y, Remark="桌面端:开启后不触发登录过期提示与自动踢回登录页", OrderNo=119, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysConfig{ Id=1300000000201, Name="发送异常日志邮件", Code=ConfigConst.SysErrorMail, Value="False", SysFlag=YesNoEnum.Y, Remark="是否发送异常日志邮件", OrderNo=120, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysConfig{ Id=1300000000211, Name="域登录验证", Code=ConfigConst.SysDomainLogin, Value="False", SysFlag=YesNoEnum.Y, Remark="是否开启域登录验证", OrderNo=130, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysConfig{ Id=1300000000221, Name="数据校验日志", Code=ConfigConst.SysValidationLog, Value="True", SysFlag=YesNoEnum.Y, Remark="是否数据校验日志", OrderNo=140, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },

View File

@@ -21,6 +21,20 @@ public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
// 建议此处Id范围之间放置具体业务应用菜单
#region
new SysMenu{ Id=1300150000101, Pid=0, Title="基础资料", Path="/base", Name="base", Component="Layout", Icon="&#xe7c4;", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=9000 },
// 车辆管理
new SysMenu{ Id=1300150010101, Pid=1300150000101, Title="车辆管理", Path="/xslmes/mesXslVehicle", Name="mesXslVehicle", Component="VehicleListView", Icon="&#xe7d2;", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
// 客户管理
new SysMenu{ Id=1300150010201, Pid=1300150000101, Title="客户管理", Path="/xslmes/mesXslCustomer", Name="mesXslCustomer", Component="CustomerListView", Icon="&#xe7ce;", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=101 },
// 供应商管理
new SysMenu{ Id=1300150010301, Pid=1300150000101, Title="供应商管理", Path="/xslmes/mesXslSupplier", Name="mesXslSupplier", Component="SupplierListView", Icon="&#xe7ce;", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=102 },
#endregion
#region
new SysMenu{ Id=1300200000101, Pid=0, Title="系统管理", Path="", Name="system", Component="Layout", Icon="&#xe7a3;", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=10000 },
@@ -43,6 +57,9 @@ public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
new SysMenu{ Id=1300200012011, Pid=1300200012001, Title="查询", Permission="sysDict:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
new SysMenu{ Id=1300200012021, Pid=1300200012001, Title="同步", Permission="sysDict:sync", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
// 登录设置(桌面端会话与检查间隔)
new SysMenu{ Id=1300200013001, Pid=1300200000101, Title="登录设置", Path="LoginSettingsView", Name="loginSettings", Component="LoginSettingsView", Icon="&#xe7c1;", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=107 },
// 角色管理
new SysMenu{ Id=1300200020101, Pid=1300200000101, Title="角色管理", Path="RoleManagementView", Name="sysRole", Component="/system/role/index", Icon="&#xe7e0;", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=110 },
new SysMenu{ Id=1300200020201, Pid=1300200020101, Title="查询", Permission="sysRole:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
@@ -114,7 +131,7 @@ public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
new SysMenu{ Id=1300300011201, Pid=1300300010101, Title="进入租管端", Permission="sysTenant:goTenant", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
// 菜单管理
new SysMenu{ Id=1300300030101, Pid=1300300000101, Title="菜单管理", Path="/platform/menu", Name="sysMenu", Component="/system/menu/index", Icon="&#xe8f1;", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=110 },
new SysMenu{ Id=1300300030101, Pid=1300300000101, Title="菜单管理", Path="MenuManagementView", Name="sysMenu", Component="MenuManagementView", Icon="&#xe8f1;", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=110 },
new SysMenu{ Id=1300300030201, Pid=1300300030101, Title="查询", Permission="sysMenu:list", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
new SysMenu{ Id=1300300030301, Pid=1300300030101, Title="编辑", Permission="sysMenu:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
new SysMenu{ Id=1300300030401, Pid=1300300030101, Title="增加", Permission="sysMenu:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },

View File

@@ -21,6 +21,9 @@ public class SysTenantMenuSeedData : ISqlSugarEntitySeedData<SysTenantMenu>
return new[]
{
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300100000101},
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300150000101},
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300150010101},
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300150010201},
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300200010701 },
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300300100601 },
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300200090401 },
@@ -42,6 +45,7 @@ public class SysTenantMenuSeedData : ISqlSugarEntitySeedData<SysTenantMenu>
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300300090201 },
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300200011101 },
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300200010101 },
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300200013001 },
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300500010101 },
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300500030101 },
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300300051301 },

View File

@@ -0,0 +1,11 @@
using Yitter.IdGenerator;
namespace YY.Admin.Core;
/// <summary>
/// 分布式雪花 Id依赖应用启动时 SqlSugarSetup 中对 YitIdHelper 的初始化)
/// </summary>
public static class SnowflakeId
{
public static long Next() => YitIdHelper.NextId();
}