2026-04-30 15:28:20 +08:00
|
|
|
|
using Microsoft.Extensions.Configuration;
|
2026-04-28 10:23:58 +08:00
|
|
|
|
using SqlSugar;
|
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
|
using YY.Admin.Core;
|
2026-04-30 15:28:20 +08:00
|
|
|
|
using YY.Admin.Core.Services;
|
2026-04-28 10:23:58 +08:00
|
|
|
|
using YY.Admin.Core.Session;
|
|
|
|
|
|
using YY.Admin.Core.Util;
|
|
|
|
|
|
|
|
|
|
|
|
namespace YY.Admin.Services.Service.User
|
|
|
|
|
|
{
|
|
|
|
|
|
public class SysUserService : ISysUserService, ISingletonDependency
|
|
|
|
|
|
{
|
|
|
|
|
|
private readonly ISqlSugarClient _dbContext;
|
2026-04-30 15:28:20 +08:00
|
|
|
|
private readonly IConfiguration _configuration;
|
|
|
|
|
|
private readonly IUserSyncOutbox _userSyncOutbox;
|
|
|
|
|
|
|
|
|
|
|
|
public SysUserService(ISqlSugarClient dbContext, IConfiguration configuration, IUserSyncOutbox userSyncOutbox)
|
2026-04-28 10:23:58 +08:00
|
|
|
|
{
|
|
|
|
|
|
_dbContext = dbContext;
|
2026-04-30 15:28:20 +08:00
|
|
|
|
_configuration = configuration;
|
|
|
|
|
|
_userSyncOutbox = userSyncOutbox;
|
2026-04-28 10:23:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async Task<List<SysUser>> GetUsersAsync()
|
|
|
|
|
|
{
|
2026-04-30 15:28:20 +08:00
|
|
|
|
await Task.CompletedTask;
|
2026-04-28 10:23:58 +08:00
|
|
|
|
return new List<SysUser>();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-30 15:28:20 +08:00
|
|
|
|
// ── 查询 ──────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
2026-04-28 10:23:58 +08:00
|
|
|
|
public async Task<SqlSugarPagedList<UserOutput>> PageAsync(PageUserInput input)
|
|
|
|
|
|
{
|
|
|
|
|
|
var sexFilter = input.Sex.HasValue ? (int?)input.Sex.Value : null;
|
|
|
|
|
|
var statusFilter = input.Status.HasValue ? (int?)input.Status.Value : null;
|
|
|
|
|
|
|
|
|
|
|
|
var query = _dbContext.Queryable<JeecgSysUser>().ClearFilter()
|
2026-04-30 15:28:20 +08:00
|
|
|
|
// 只显示未软删除的记录
|
|
|
|
|
|
.Where(u => u.DelFlag == null || u.DelFlag == 0)
|
2026-04-28 10:23:58 +08:00
|
|
|
|
.WhereIF(input.TenantId > 0, u => u.LoginTenantId == input.TenantId)
|
|
|
|
|
|
.WhereIF(!string.IsNullOrWhiteSpace(input.Account), u => u.Username != null && u.Username.Contains(input.Account))
|
|
|
|
|
|
.WhereIF(!string.IsNullOrWhiteSpace(input.RealName), u => u.Realname != null && u.Realname.Contains(input.RealName))
|
|
|
|
|
|
.WhereIF(!string.IsNullOrWhiteSpace(input.Phone), u => u.Phone != null && u.Phone.Contains(input.Phone))
|
|
|
|
|
|
.WhereIF(input.BeginTime.HasValue, u => u.CreateTime >= input.BeginTime)
|
|
|
|
|
|
.WhereIF(input.EndTime.HasValue, u => u.CreateTime <= input.EndTime)
|
|
|
|
|
|
.OrderBy(u => SqlFunc.Desc(u.CreateTime));
|
2026-04-30 15:28:20 +08:00
|
|
|
|
|
2026-04-28 10:23:58 +08:00
|
|
|
|
if (sexFilter.HasValue)
|
|
|
|
|
|
{
|
|
|
|
|
|
var sexValue = sexFilter.Value;
|
|
|
|
|
|
query = query.Where(u => u.Sex == sexValue);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (statusFilter.HasValue)
|
|
|
|
|
|
{
|
|
|
|
|
|
var statusValue = statusFilter.Value;
|
|
|
|
|
|
query = query.Where(u => u.Status == statusValue);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var pageData = await query.ToPagedListAsync(input.Page, input.PageSize);
|
2026-04-30 15:28:20 +08:00
|
|
|
|
var mapped = pageData.Items.Select(MapToOutput).ToList();
|
2026-04-28 10:23:58 +08:00
|
|
|
|
|
|
|
|
|
|
return new SqlSugarPagedList<UserOutput>
|
|
|
|
|
|
{
|
|
|
|
|
|
Page = pageData.Page,
|
|
|
|
|
|
PageSize = pageData.PageSize,
|
|
|
|
|
|
Items = mapped,
|
|
|
|
|
|
Total = pageData.Total,
|
|
|
|
|
|
TotalPages = pageData.TotalPages,
|
|
|
|
|
|
HasNextPage = pageData.HasNextPage,
|
|
|
|
|
|
HasPrevPage = pageData.HasPrevPage
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-30 15:28:20 +08:00
|
|
|
|
public async Task<bool> AccountExistsAsync(string account, long? excludeUserId = null)
|
2026-04-28 10:23:58 +08:00
|
|
|
|
{
|
2026-04-30 15:28:20 +08:00
|
|
|
|
var query = _dbContext.Queryable<JeecgSysUser>().ClearFilter()
|
|
|
|
|
|
.Where(u => (u.DelFlag == null || u.DelFlag == 0) && u.Username == account);
|
2026-04-28 10:23:58 +08:00
|
|
|
|
|
2026-04-30 15:28:20 +08:00
|
|
|
|
if (excludeUserId.HasValue && excludeUserId.Value != 0)
|
2026-04-28 10:23:58 +08:00
|
|
|
|
{
|
2026-04-30 15:28:20 +08:00
|
|
|
|
var excludeIdStr = excludeUserId.Value.ToString(CultureInfo.InvariantCulture);
|
|
|
|
|
|
query = query.Where(u => u.Id != excludeIdStr);
|
2026-04-28 10:23:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-30 15:28:20 +08:00
|
|
|
|
return await query.AnyAsync();
|
2026-04-28 10:23:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-30 15:28:20 +08:00
|
|
|
|
// ── 新增 ──────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
public async Task<int> CreateAsync(SysUser sysUser)
|
2026-04-28 10:23:58 +08:00
|
|
|
|
{
|
2026-04-30 15:28:20 +08:00
|
|
|
|
var defaultTenantId = (int?)_configuration.GetValue<long?>("JeecgIntegration:DefaultTenantId") ?? 1002;
|
|
|
|
|
|
var now = DateTime.Now;
|
2026-04-28 10:23:58 +08:00
|
|
|
|
|
2026-04-30 15:28:20 +08:00
|
|
|
|
var jeecgUser = new JeecgSysUser
|
|
|
|
|
|
{
|
|
|
|
|
|
// 用毫秒时间戳生成本地唯一 ID(数值型字符串,与 PageAsync 的 long.TryParse 兼容)
|
|
|
|
|
|
Id = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString(CultureInfo.InvariantCulture),
|
|
|
|
|
|
Username = sysUser.Account,
|
|
|
|
|
|
Realname = sysUser.RealName,
|
|
|
|
|
|
// 本地创建账号使用 CryptogramUtil 加密,无 salt;登录时走 CryptogramUtil 路径
|
|
|
|
|
|
Password = string.IsNullOrWhiteSpace(sysUser.Password)
|
|
|
|
|
|
? string.Empty
|
|
|
|
|
|
: CryptogramUtil.Encrypt(sysUser.Password),
|
|
|
|
|
|
Salt = null,
|
|
|
|
|
|
Sex = ToJeecgSex(sysUser.Sex),
|
|
|
|
|
|
Birthday = sysUser.Birthday,
|
|
|
|
|
|
Phone = sysUser.Phone,
|
|
|
|
|
|
Email = sysUser.Email,
|
|
|
|
|
|
Status = 1, // 默认启用
|
|
|
|
|
|
DelFlag = 0,
|
|
|
|
|
|
LoginTenantId = defaultTenantId,
|
|
|
|
|
|
CreateBy = AppSession.CurrentUser?.Account,
|
|
|
|
|
|
CreateTime = now,
|
|
|
|
|
|
UpdateTime = now,
|
|
|
|
|
|
};
|
2026-04-28 10:23:58 +08:00
|
|
|
|
|
2026-04-30 15:28:20 +08:00
|
|
|
|
var affected = await _dbContext.Insertable(jeecgUser).ExecuteCommandAsync();
|
|
|
|
|
|
if (affected > 0)
|
2026-04-28 10:23:58 +08:00
|
|
|
|
{
|
2026-04-30 15:28:20 +08:00
|
|
|
|
_ = _userSyncOutbox.EnqueueCreateAsync(
|
|
|
|
|
|
jeecgUser.Id,
|
|
|
|
|
|
jeecgUser.Username ?? string.Empty,
|
|
|
|
|
|
jeecgUser.Realname,
|
|
|
|
|
|
jeecgUser.Sex,
|
|
|
|
|
|
jeecgUser.Birthday,
|
|
|
|
|
|
jeecgUser.Phone,
|
|
|
|
|
|
jeecgUser.Email,
|
|
|
|
|
|
jeecgUser.Status ?? 1,
|
|
|
|
|
|
jeecgUser.CreateBy);
|
2026-04-28 10:23:58 +08:00
|
|
|
|
}
|
2026-04-30 15:28:20 +08:00
|
|
|
|
return affected;
|
2026-04-28 10:23:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-30 15:28:20 +08:00
|
|
|
|
// ── 修改 ──────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
public async Task<int> UpdateAsync(SysUser sysUser)
|
2026-04-28 10:23:58 +08:00
|
|
|
|
{
|
2026-04-30 15:28:20 +08:00
|
|
|
|
var idStr = sysUser.Id.ToString(CultureInfo.InvariantCulture);
|
|
|
|
|
|
var now = DateTime.Now;
|
|
|
|
|
|
var updater = AppSession.CurrentUser?.Account;
|
|
|
|
|
|
var jeecgStatus = sysUser.Status == StatusEnum.Enable ? 1 : 2;
|
|
|
|
|
|
var jeecgSex = ToJeecgSex(sysUser.Sex);
|
|
|
|
|
|
var account = (sysUser.Account ?? string.Empty).Trim();
|
|
|
|
|
|
|
|
|
|
|
|
var affected = await _dbContext.Updateable<JeecgSysUser>()
|
|
|
|
|
|
.SetColumns(u => new JeecgSysUser
|
|
|
|
|
|
{
|
|
|
|
|
|
Username = account,
|
|
|
|
|
|
Realname = sysUser.RealName,
|
|
|
|
|
|
Sex = jeecgSex,
|
|
|
|
|
|
Birthday = sysUser.Birthday,
|
|
|
|
|
|
Phone = sysUser.Phone,
|
|
|
|
|
|
Email = sysUser.Email,
|
|
|
|
|
|
Status = jeecgStatus,
|
|
|
|
|
|
UpdateBy = updater,
|
|
|
|
|
|
UpdateTime = now,
|
|
|
|
|
|
})
|
|
|
|
|
|
.Where(u => u.Id == idStr)
|
|
|
|
|
|
.ExecuteCommandAsync();
|
|
|
|
|
|
|
|
|
|
|
|
if (affected > 0)
|
2026-04-28 10:23:58 +08:00
|
|
|
|
{
|
2026-04-30 15:28:20 +08:00
|
|
|
|
_ = _userSyncOutbox.EnqueueUpdateAsync(idStr, account, sysUser.RealName, jeecgSex, sysUser.Birthday, sysUser.Phone, sysUser.Email, jeecgStatus, updater);
|
|
|
|
|
|
}
|
|
|
|
|
|
return affected;
|
|
|
|
|
|
}
|
2026-04-28 10:23:58 +08:00
|
|
|
|
|
2026-04-30 15:28:20 +08:00
|
|
|
|
// ── 状态切换 ──────────────────────────────────────────────────────────
|
2026-04-28 10:23:58 +08:00
|
|
|
|
|
2026-04-30 15:28:20 +08:00
|
|
|
|
public async Task<int> ToggleStatus(SysUser sysUser)
|
|
|
|
|
|
{
|
|
|
|
|
|
var idStr = sysUser.Id.ToString(CultureInfo.InvariantCulture);
|
|
|
|
|
|
// Jeecg 约定:1=正常,2=冻结
|
|
|
|
|
|
var jeecgStatus = sysUser.Status == StatusEnum.Enable ? 1 : 2;
|
|
|
|
|
|
var now = DateTime.Now;
|
|
|
|
|
|
var updater = AppSession.CurrentUser?.Account;
|
|
|
|
|
|
|
|
|
|
|
|
var affected = await _dbContext.Updateable<JeecgSysUser>()
|
|
|
|
|
|
.SetColumns(u => new JeecgSysUser
|
|
|
|
|
|
{
|
|
|
|
|
|
Status = jeecgStatus,
|
|
|
|
|
|
UpdateBy = updater,
|
|
|
|
|
|
UpdateTime = now,
|
|
|
|
|
|
})
|
|
|
|
|
|
.Where(u => u.Id == idStr)
|
|
|
|
|
|
.ExecuteCommandAsync();
|
|
|
|
|
|
|
|
|
|
|
|
if (affected > 0)
|
2026-04-28 10:23:58 +08:00
|
|
|
|
{
|
2026-04-30 15:28:20 +08:00
|
|
|
|
_ = _userSyncOutbox.EnqueueToggleStatusAsync(idStr, jeecgStatus, updater);
|
2026-04-28 10:23:58 +08:00
|
|
|
|
}
|
2026-04-30 15:28:20 +08:00
|
|
|
|
return affected;
|
2026-04-28 10:23:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-30 15:28:20 +08:00
|
|
|
|
// ── 删除 ──────────────────────────────────────────────────────────────
|
2026-04-28 10:23:58 +08:00
|
|
|
|
|
2026-04-30 15:28:20 +08:00
|
|
|
|
public async Task<int> DeleteAsync(long id)
|
|
|
|
|
|
{
|
|
|
|
|
|
var idStr = id.ToString(CultureInfo.InvariantCulture);
|
|
|
|
|
|
// 软删除:保留记录供审计,PageAsync 已过滤 del_flag=1
|
|
|
|
|
|
var affected = await _dbContext.Updateable<JeecgSysUser>()
|
|
|
|
|
|
.SetColumns(u => new JeecgSysUser { DelFlag = 1 })
|
|
|
|
|
|
.Where(u => u.Id == idStr)
|
|
|
|
|
|
.ExecuteCommandAsync();
|
|
|
|
|
|
|
|
|
|
|
|
if (affected > 0)
|
2026-04-28 10:23:58 +08:00
|
|
|
|
{
|
2026-04-30 15:28:20 +08:00
|
|
|
|
_ = _userSyncOutbox.EnqueueDeleteAsync(idStr);
|
2026-04-28 10:23:58 +08:00
|
|
|
|
}
|
2026-04-30 15:28:20 +08:00
|
|
|
|
return affected;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async Task<int> BatchDeleteAsync(List<long> ids)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ids == null || ids.Count == 0)
|
2026-04-28 10:23:58 +08:00
|
|
|
|
{
|
2026-04-30 15:28:20 +08:00
|
|
|
|
return 0;
|
2026-04-28 10:23:58 +08:00
|
|
|
|
}
|
2026-04-30 15:28:20 +08:00
|
|
|
|
var idStrings = ids.Select(i => i.ToString(CultureInfo.InvariantCulture)).ToList();
|
|
|
|
|
|
var affected = await _dbContext.Updateable<JeecgSysUser>()
|
|
|
|
|
|
.SetColumns(u => new JeecgSysUser { DelFlag = 1 })
|
|
|
|
|
|
.Where(u => idStrings.Contains(u.Id))
|
|
|
|
|
|
.ExecuteCommandAsync();
|
2026-04-28 10:23:58 +08:00
|
|
|
|
|
2026-04-30 15:28:20 +08:00
|
|
|
|
if (affected > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
_ = _userSyncOutbox.EnqueueBatchDeleteAsync(idStrings);
|
|
|
|
|
|
}
|
|
|
|
|
|
return affected;
|
2026-04-28 10:23:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-30 15:28:20 +08:00
|
|
|
|
// ── 辅助 ──────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
2026-04-28 10:23:58 +08:00
|
|
|
|
public async Task<long> ReadMaxIdAsync()
|
|
|
|
|
|
{
|
2026-04-30 15:28:20 +08:00
|
|
|
|
// jeecg_sys_user 使用字符串ID,此方法不再适用;保留签名兼容旧调用
|
|
|
|
|
|
await Task.CompletedTask;
|
|
|
|
|
|
return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
2026-04-28 10:23:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-30 15:28:20 +08:00
|
|
|
|
private static UserOutput MapToOutput(JeecgSysUser u)
|
2026-04-28 10:23:58 +08:00
|
|
|
|
{
|
2026-04-30 15:28:20 +08:00
|
|
|
|
long.TryParse(u.Id, NumberStyles.Integer, CultureInfo.InvariantCulture, out var id);
|
|
|
|
|
|
var sex = u.Sex == 1 ? GenderEnum.Male : u.Sex == 2 ? GenderEnum.Female : GenderEnum.Unknown;
|
|
|
|
|
|
var status = u.Status == 1 ? StatusEnum.Enable : StatusEnum.Disable;
|
|
|
|
|
|
return new UserOutput
|
2026-04-28 10:23:58 +08:00
|
|
|
|
{
|
2026-04-30 15:28:20 +08:00
|
|
|
|
Id = id,
|
|
|
|
|
|
Account = u.Username ?? string.Empty,
|
|
|
|
|
|
RealName = u.Realname ?? string.Empty,
|
|
|
|
|
|
NickName = string.IsNullOrWhiteSpace(u.Realname) ? (u.Username ?? string.Empty) : u.Realname,
|
|
|
|
|
|
Avatar = u.Avatar,
|
|
|
|
|
|
Sex = sex,
|
|
|
|
|
|
Birthday = u.Birthday,
|
|
|
|
|
|
Phone = u.Phone,
|
|
|
|
|
|
Email = u.Email,
|
|
|
|
|
|
OfficePhone = u.Telephone,
|
|
|
|
|
|
Status = status,
|
|
|
|
|
|
CreateTime = u.CreateTime,
|
|
|
|
|
|
OrgName = u.OrgCode ?? string.Empty,
|
|
|
|
|
|
PosName = u.PositionType ?? string.Empty,
|
|
|
|
|
|
RoleName = string.Empty,
|
|
|
|
|
|
AccountType = AccountTypeEnum.NormalUser
|
|
|
|
|
|
};
|
2026-04-28 10:23:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-30 15:28:20 +08:00
|
|
|
|
private static int? ToJeecgSex(GenderEnum? sex) => sex switch
|
2026-04-28 10:23:58 +08:00
|
|
|
|
{
|
2026-04-30 15:28:20 +08:00
|
|
|
|
GenderEnum.Male => 1,
|
|
|
|
|
|
GenderEnum.Female => 2,
|
|
|
|
|
|
_ => null
|
|
|
|
|
|
};
|
2026-04-28 10:23:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|