2026-03-25 16:53:22 +08:00

366 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using YD_Event.Application.AppUser.Dtos;
using YD_Event.Application.Auth;
using YD_Event.Core.Entities;
using AppUserEntity = YD_Event.Core.Entities.AppUser;
using YD_Event.Core.Infrastructure.Aliyun.Ocr;
using YD_Event.Core.Enum;
using Mapster;
namespace YD_Event.Application.AppUser;
/// <summary>
/// 普通用户服务
/// </summary>
public class AppUserService : BaseService<AppUserEntity>, ITransient
{
private readonly ISqlSugarRepository<AppUserEntity> _appUserRep;
private readonly ISqlSugarClient _db;
private readonly AuthManager _authManager;
private readonly OcrService _ocrService;
public AppUserService(ISqlSugarRepository<AppUserEntity> appUserRep,
ISqlSugarClient db,
AuthManager authManager,
OcrService ocrService) : base(appUserRep)
{
_appUserRep = appUserRep;
_db = db;
_authManager = authManager;
_ocrService = ocrService;
}
/// <summary>
/// 分页查询普通用户
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("分页查询普通用户")]
[HttpGet("page")]
public async Task<PageResult<AppUserOutput>> Page([FromQuery] AppUserPageInput input)
{
var query = _appUserRep.AsQueryable()
.Where(u => u.PersonType == PersonType.User) // 仅查询普通用户
.WhereIF(!string.IsNullOrWhiteSpace(input.Name), u => u.Name.Contains(input.Name))
.WhereIF(input.Gender.HasValue, u => u.Gender == input.Gender)
.WhereIF(!string.IsNullOrWhiteSpace(input.Mobile), u => u.Mobile.Contains(input.Mobile))
.WhereIF(input.AuditStatus.HasValue, u => u.AuditStatus == input.AuditStatus)
.WhereIF(input.StartTime.HasValue, u => u.CreatedTime >= input.StartTime)
.WhereIF(input.EndTime.HasValue, u => u.CreatedTime <= input.EndTime)
.OrderByDescending(u => u.CreatedTime);
return await query.Select<AppUserOutput>().ToPagedListAsync(input);
}
/// <summary>
/// 获取普通用户详情
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[DisplayName("获取普通用户详情")]
[HttpGet("{id}")]
public async Task<AppUserOutput> Detail([FromRoute] long id)
{
return await _appUserRep.AsQueryable()
.Where(u => u.Id == id)
.Select<AppUserOutput>()
.FirstAsync();
}
/// <summary>
/// 审核普通用户
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("审核普通用户")]
[HttpPost("audit")]
[UnitOfWork]
public async Task Audit([FromBody] AuditInput input)
{
if (input.Ids == null || !input.Ids.Any()) return;
// 更新状态
await _appUserRep.UpdateAsync(u => new AppUserEntity
{
AuditStatus = input.Status,
AuditRemark = input.Remark,
UpdatedTime = DateTime.Now,
UpdatedUserId = _authManager.UserId
}, u => input.Ids.Contains(u.Id));
}
#region 统一成员管理 (领队/教练/运动员)
/// <summary>
/// 统一分页查询成员
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("member/page")]
[DisplayName("统一分页查询成员")]
public async Task<PageResult<MemberOutput>> MemberPage([FromQuery] MemberQueryInput input)
{
var (userId, merchantId) = await GetCurrentUserAndMerchantId();
var query = _appUserRep.AsQueryable()
.Where(x => x.PersonType == input.PersonType && !x.DeleteMark)
.WhereIF(!string.IsNullOrWhiteSpace(input.Name), x => x.Name.Contains(input.Name))
.WhereIF(input.Gender.HasValue, x => x.Gender == input.Gender)
.WhereIF(!string.IsNullOrWhiteSpace(input.Mobile), x => x.Mobile.Contains(input.Mobile))
.WhereIF(!string.IsNullOrWhiteSpace(input.IdCardNumber), u => u.IdCardNumber.Contains(input.IdCardNumber))
.WhereIF(input.PersonType == PersonType.Coach && !string.IsNullOrWhiteSpace(input.SkillLevel), x => x.SkillLevel == input.SkillLevel)
.WhereIF(input.PersonType == PersonType.Coach && input.CertificationStatus.HasValue, x => x.CertificationStatus == input.CertificationStatus)
.WhereIF(input.StartTime.HasValue, u => u.CreatedTime >= input.StartTime)
.WhereIF(input.EndTime.HasValue, u => u.CreatedTime <= input.EndTime)
.OrderByDescending(x => x.CreatedTime);
// 权限过滤 (管理员可看全部,这里简化的商户隔离逻辑)
if (merchantId.HasValue)
{
query = query.Where(x => x.MerchantId == merchantId.Value);
}
else
{
// 这里根据实际业务,如果普通用户只能看自己创建的,需要这个过滤
query = query.Where(x => x.CreatedUserId == userId && x.MerchantId == null);
}
var list = await query.ToPagedListAsync(input);
return list.Adapt<PageResult<MemberOutput>>();
}
/// <summary>
/// 统一新增/编辑成员
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("member/save")]
[DisplayName("统一新增/编辑成员")]
public async Task SaveUnifiedMember(CreateOrUpdateMemberInput input)
{
var (userId, merchantId) = await GetCurrentUserAndMerchantId();
// 校验唯一性 (手机号或身份证号,具体根据角色定)
// 运动员主要查身份证,领队教练查手机号。这里综合校验。
var queryPhone = _appUserRep.AsQueryable()
.Where(x => x.Mobile == input.Mobile && x.PersonType == input.PersonType && !x.DeleteMark);
var queryIdCard = _appUserRep.AsQueryable()
.Where(x => x.IdCardNumber == input.IdCardNumber && x.PersonType == input.PersonType && !x.DeleteMark);
if (input.Id.HasValue)
{
queryPhone = queryPhone.Where(x => x.Id != input.Id.Value);
queryIdCard = queryIdCard.Where(x => x.Id != input.Id.Value);
}
if (merchantId.HasValue)
{
queryPhone = queryPhone.Where(x => x.MerchantId == merchantId.Value);
queryIdCard = queryIdCard.Where(x => x.MerchantId == merchantId.Value);
}
else
{
queryPhone = queryPhone.Where(x => x.CreatedUserId == userId && x.MerchantId == null);
queryIdCard = queryIdCard.Where(x => x.CreatedUserId == userId && x.MerchantId == null);
}
// 只有领队和教练强校验手机号
if (input.PersonType == PersonType.Leader || input.PersonType == PersonType.Coach)
{
if (await queryPhone.AnyAsync()) throw Oops.Bah($"该手机号的成员已存在");
}
// 运动员强校验身份证
if (input.PersonType == PersonType.Athlete)
{
if (await queryIdCard.AnyAsync()) throw Oops.Bah($"该身份证号的成员已存在");
}
AppUserEntity entity;
if (input.Id.HasValue)
{
entity = await _appUserRep.GetByIdAsync(input.Id.Value);
if (entity == null || entity.DeleteMark) throw Oops.Bah("成员不存在");
await CheckPermission(entity);
entity.UpdatedUserId = _authManager.UserId;
entity.UpdatedTime = DateTime.Now;
}
else
{
entity = new AppUserEntity
{
Id = YitIdHelper.NextId(),
PersonType = input.PersonType,
MerchantId = merchantId,
CreatedUserId = userId,
CreatedTime = DateTime.Now,
Status = AvailabilityStatus.Enable,
AuditStatus = AuditStatus.Pass
};
}
entity.Name = input.Name;
entity.Gender = input.Gender;
entity.IdCardNumber = input.IdCardNumber;
entity.IdCardImageUrl = input.IdCardUrl;
entity.Mobile = input.Mobile;
entity.Remark = input.Remark;
entity.AuditRemark = "";
// 计算年龄逻辑
if (input.BirthDate.HasValue)
{
entity.BirthDate = input.BirthDate;
var today = DateTime.Today;
var age = today.Year - input.BirthDate.Value.Year;
if (input.BirthDate.Value.Date > today.AddYears(-age)) age--;
entity.Age = age;
}
else
{
entity.Age = input.Age;
}
if (input.PersonType == PersonType.Coach)
{
entity.SkillLevel = input.SkillLevel;
entity.CertificationStatus = input.CertificationStatus;
}
await _appUserRep.InsertOrUpdateAsync(entity);
}
#endregion
#region 领队/教练管理 (向下兼容)
private async Task<(long userId, long? merchantId)> GetCurrentUserAndMerchantId()
{
var userId = _authManager.UserId;
var merchant = await _db.Queryable<YD_Event.Core.Entities.Merchant>()
.FirstAsync(m => m.CreatedUserId == userId && !m.DeleteMark);
return (userId, merchant?.Id);
}
private async Task CheckPermission(AppUserEntity entity)
{
var (userId, merchantId) = await GetCurrentUserAndMerchantId();
if (merchantId.HasValue)
{
if (entity.MerchantId != merchantId.Value) throw Oops.Bah("无权操作该成员");
}
else
{
if (entity.CreatedUserId != userId || entity.MerchantId != null) throw Oops.Bah("无权操作该成员");
}
}
/// <summary>
/// 删除成员
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[DisplayName("删除成员")]
[HttpPost("member/delete")]
public async Task DeleteMember([FromBody] long id)
{
var entity = await _appUserRep.GetByIdAsync(id);
if (entity == null || entity.DeleteMark) throw Oops.Bah("成员不存在");
await CheckPermission(entity);
entity.DeleteMark = true;
entity.UpdatedUserId = _authManager.UserId;
entity.UpdatedTime = DateTime.Now;
await _appUserRep.UpdateAsync(entity);
}
/// <summary>
/// 批量删除成员
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
[HttpPost("member/batchDelete")]
[DisplayName("批量删除成员")]
public async Task BatchDeleteMember([FromBody] List<long> ids)
{
if (ids == null || !ids.Any()) throw Oops.Bah("请选择要删除的记录");
// 逻辑删除
await _appUserRep.UpdateAsync(x => new AppUserEntity { DeleteMark = true }, x => ids.Contains(x.Id));
}
/// <summary>
/// 变更成员状态
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("变更成员状态")]
[HttpPost("member/status")]
public async Task ChangeMemberStatus(ChangeMemberStatusInput input)
{
var entity = await _appUserRep.GetByIdAsync(input.Id);
if (entity == null || entity.DeleteMark) throw Oops.Bah("成员不存在");
await CheckPermission(entity);
entity.Status = input.Status;
entity.UpdatedUserId = _authManager.UserId;
entity.UpdatedTime = DateTime.Now;
await _appUserRep.UpdateAsync(entity);
}
/// <summary>
/// 识别身份证信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("member/recognize/idcard")]
[DisplayName("OCR识别身份证")]
public async Task<IdCardRecognitionResult> RecognizeIdCard([FromBody] AthleteOcrInput input)
{
// 输入验证
if (string.IsNullOrWhiteSpace(input.ImageUrl))
{
throw Oops.Bah("护照图片URL不能为空");
}
// URL格式验证(简单的格式检查)
if (!input.ImageUrl.StartsWith("http://") && !input.ImageUrl.StartsWith("https://"))
{
throw Oops.Bah("请输入有效的图片URL地址");
}
return await _ocrService.RecognizeIdCardAsync(input.ImageUrl);
}
/// <summary>
/// 识别护照信息
/// </summary>
/// <param name="input">护照图片URL输入</param>
/// <returns>护照识别结果</returns>
[HttpPost("member/recognize/passport")]
[DisplayName("OCR识别护照")]
public async Task<PassportRecognitionResult> RecognizePassport([FromBody] AthleteOcrInput input)
{
// 输入验证
if (string.IsNullOrWhiteSpace(input.ImageUrl))
{
throw Oops.Bah("护照图片URL不能为空");
}
// URL格式验证(简单的格式检查)
if (!input.ImageUrl.StartsWith("http://") && !input.ImageUrl.StartsWith("https://"))
{
throw Oops.Bah("请输入有效的图片URL地址");
}
return await _ocrService.RecognizePassportAsync(input.ImageUrl);
}
#endregion
}