refactor:重构预警通知,用户中心只使用站内信功能,接入飞书第三方库,直接调用飞书调用发送接口

This commit is contained in:
hardstylewyl 2025-06-05 17:45:50 +08:00
parent ab348a0707
commit 0a4103abf5
9 changed files with 287 additions and 177 deletions

View File

@ -1,63 +0,0 @@
namespace SJZY.Track.Application.Hangfire;
// 发送消息接口参数
// https://centerapi.test.shijizhongyun.com/userApi/index.html?urls.primaryName=%E6%B6%88%E6%81%AF%E8%AE%A2%E9%98%85%E5%AE%9E%E4%BE%8B
/// MsgSubscribeInstance/SendMessageByTemplate
public class NotifyConfig
{
private const string ContentKey = "content"; //内容参数
public bool IsNeedToHandle { get; set; } = true; //是否需要处理(默认为未处理,传false为无需处理,针对站内信是否处理功能)
public int MsgSource { get; set; } = 1; // 消息来源:1.消息 2.任务 3.流程 4.消息提醒
public int SystemId { get; set; } = 9498; //子系统id 业务中台
public string TemplateCode { get; set; } = "TrackWarningNotify"; //模板code
public List<int> Channels { get; set; } //1:站内信 2.飞书 4.邮箱 8.短信 16.语音播报
public List<Variable> Variables { get; set; } //模板参数
public List<Receiver> Receivers { get; set; }
public sealed record Variable(string Key, string Value);
public sealed record Receiver(
int ReceiverId, //接收人id : 内部用户Id,外部用户Id,或只有邮箱跟短信时不需要接收人Id
int RecipientType = 1, //接收人类型 (1:内部用户 2.外部用户)
string? ReceiverName = null, //接收人姓名
string? Mail = null, //邮箱
string? MobilePhone = null //手机号
);
public static NotifyConfig CreateConfig(int userId, string message, List<int> notifyModes)
{
return new NotifyConfig
{
Channels = notifyModes,
Receivers = new List<Receiver>(1)
{
new Receiver(userId)
},
Variables = new List<Variable>(1)
{
new Variable(ContentKey, message)
}
};
}
public static NotifyConfig CreateConfig(int userId, string message)
{
return new NotifyConfig
{
Channels = new List<int>(2)
{
1,
2
},
Receivers = new List<Receiver>
{
new Receiver(userId)
},
Variables = new List<Variable>
{
new Variable(ContentKey, message)
}
};
}
}

View File

@ -1,6 +1,5 @@
using Microsoft.Extensions.Options;
using SJZY.Track.Application.Contract.Dtos.Warning;
using SJZY.Track.Application.Helpers;
using SJZY.Track.Application.Options;
using SJZY.Track.Application.Services.Warning.Model;
using SJZY.Track.Domain.Aggregates.KPIAreaAggregate;
@ -43,7 +42,7 @@ public sealed class WarningNotifyAggregator : ITransientDependency
// 2.以人的维度聚合通知信息,并生成配置参数NotifyConfig
// 3.构建并调度一个调用通知接口的立即执行任务
// 4.绑定这个任务id(可能是多个)和jobId用户跟踪任务状态
public async Task<(List<NotifyConfig> NotifyConfigs, HashSet<int> taskIds)> ScanNotifyAndAggregateAsync()
public async Task<(List<WarningNotifyParam> NotifyConfigs, HashSet<int> taskIds)> ScanNotifyAndAggregateAsync()
{
// 构建查询游标 todo:全量查询需要优化,直接存缓存?
// var options = _optionsMonitor.CurrentValue;
@ -210,7 +209,7 @@ public sealed class WarningNotifyAggregator : ITransientDependency
// 2.检查明细中为人工介入的状态
var detailsIdSet = filtered.Select(x => x.WarningDetailsId).ToHashSet();
var warningDetails = await _warningDetailsEsRepository.GetReturnWarningDetailsAsync(new GetReturnWarningDetailsVO()
var warningDetails = await _warningDetailsEsRepository.GetReturnWarningDetailsAsync(new GetReturnWarningDetailsVO
{
DetailsIds = detailsIdSet,
Status = new HashSet<int>(1)
@ -261,10 +260,9 @@ public sealed class WarningNotifyAggregator : ITransientDependency
return result;
}
private List<NotifyConfig> BuildNotifyConfigs(List<WarningNotifyTaskEntity> needNotifyTasks)
private List<WarningNotifyParam> BuildNotifyConfigs(List<WarningNotifyTaskEntity> needNotifyTasks)
{
var systemId = _optionsMonitor.CurrentValue.MiddleSystemId;
var result = new List<NotifyConfig>();
var result = new List<WarningNotifyParam>();
if (needNotifyTasks.Count == 0) return result;
// 1.以人的维度对不同类别的预警通知分组聚合
@ -293,109 +291,51 @@ public sealed class WarningNotifyAggregator : ITransientDependency
}
}
// 2.使用消息模板构建通知消息,并发送通知
// 2.使用消息模板构建通知参数,稍后排队发送通知
foreach (var (userId, typeNotifyTaskDict) in userIdNotifyTaskDict)
{
var notifyMessage = BuildNotifyMessage(typeNotifyTaskDict);
//聚合时出现 用户a
//接收回传预警,命中规则1,通知方式(飞书,站内信) ,命中规则2,通知方式(飞书)
//接收运输预警,命中规则1,通知方式(飞书,站内信) ,命中规则2,通知方式(飞书)
//接收完整预警,命中规则1,通知方式(飞书) ,命中规则2,通知方式(飞书,站内信)
//此时聚合后,消息为:总共6条预警消息,其中回传xx条,运输xx条,完整xx条
//同时使用飞书和站内信两种方式通知
var config = NotifyConfig.CreateConfig(userId, notifyMessage, new List<int>
{
1,
2
});
config.SystemId = systemId;
result.Add(config);
result.Add(BuildNotifyParam(userId, typeNotifyTaskDict));
}
return result;
}
private string BuildNotifyMessage(Dictionary<int, List<WarningNotifyTaskEntity>> typeNotifyTaskDict)
private WarningNotifyParam BuildNotifyParam(int userId, Dictionary<int, List<WarningNotifyTaskEntity>> typeNotifyTaskDict)
{
var options = _optionsMonitor.CurrentValue;
var hours = options.ScanWarningNotifyTaskHourInterval;
var transportCount = 0;
var transportLastNHoursCount = 0;
var returnCount = 0;
var returnLastNHoursCount = 0;
var completeCount = 0;
var completeLastNHoursCount = 0;
Func<WarningNotifyTaskEntity, bool> hoursNBefore = x => DateTime.Now - x.CreateTime <= TimeSpan.FromHours(hours);
var param = new WarningNotifyParam
{
UserId = userId,
Hours = hours
};
// tips:获取过去x小时的增量
Func<WarningNotifyTaskEntity, bool> hoursNBefore = x => DateTime.Now - x.CreateTime <= TimeSpan.FromHours(hours);
if (typeNotifyTaskDict.TryGetValue(WarningRuleType.运输时效预警.GetHashCode(), out var transportTimelinessNotifyTasks))
{
transportLastNHoursCount = transportTimelinessNotifyTasks.Count(hoursNBefore);
transportCount = transportTimelinessNotifyTasks.Count;
param.TransportLastNHoursCount = transportTimelinessNotifyTasks.Count(hoursNBefore);
param.TransportCount = transportTimelinessNotifyTasks.Count;
}
if (typeNotifyTaskDict.TryGetValue(WarningRuleType.回传时效预警.GetHashCode(), out var returnTimelinessNotifyTasks))
{
returnLastNHoursCount = returnTimelinessNotifyTasks.Count(hoursNBefore);
returnCount = returnTimelinessNotifyTasks.Count;
param.ReturnLastNHoursCount = returnTimelinessNotifyTasks.Count(hoursNBefore);
param.ReturnCount = returnTimelinessNotifyTasks.Count;
}
if (typeNotifyTaskDict.TryGetValue(WarningRuleType.完整预警.GetHashCode(), out var completeWarningNotifyTasks))
{
completeLastNHoursCount = completeWarningNotifyTasks.Count(hoursNBefore);
completeCount = completeWarningNotifyTasks.Count;
param.CompleteLastNHoursCount = completeWarningNotifyTasks.Count(hoursNBefore);
param.CompleteCount = completeWarningNotifyTasks.Count;
}
return NotifyMessageBuilder.Build(
hours,
transportCount,
transportLastNHoursCount,
returnCount,
returnLastNHoursCount,
completeCount,
completeLastNHoursCount
);
}
}
// 标题:预警通知
// 内容:尊敬的用户,截止到2024-12-13 10:00:01(UTC +8:00),轨迹中心监测到与您相关的预警风险信息共计299 条,请及时处理,详情如下:
// 运输时效预警共计100条,其中过去4小时内新增的预警10条,查看详情
// 回传时效预警共计99条,其中过去4小时内新增的预警10条,查看详情
// 轨迹完整预警共计100条,其中过去4小时内新增的预警10条,查看详情
public static class NotifyMessageBuilder
{
public const string Title = "预警通知";
public static string Build(int hours,
int transportCount, int transportLastNHoursCount,
int returnCount, int returnLastNHoursCount,
int completeCount, int completeLastNHoursCount)
{
var dateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
var totalCount = transportCount + returnCount + completeCount;
// todo:无法使用a标签填充(可能是为了安全考虑),需要具体给定一个样式
var url1 = "";
var url2 = "";
var url3 = "";
var message = $"{Title}\n尊敬的用户,截止到{dateTime}({BuildLocalTimezoneStr()}),轨迹中心监测到与您相关的预警风险信息共计{totalCount} 条,请及时处理,详情如下:";
if (transportCount > 0)
message += $"\n运输时效预警共计{transportCount}条,其中过去{hours}小时内新增的预警{transportLastNHoursCount}条";
if (returnCount > 0)
message += $"\n回传时效预警共计{returnCount}条,其中过去{hours}小时内新增的预警{returnLastNHoursCount}条";
if (completeCount > 0)
message += $"\n轨迹完整预警共计{completeCount}条,其中过去{hours}小时内新增的预警{completeLastNHoursCount}条";
return message;
}
// 构建本地时区字符串标识 e.g UTC +8:00
private static string BuildLocalTimezoneStr()
{
var offset = TimeZoneInfo.Local.BaseUtcOffset;
var flag = offset.Hours > 0 ? "+" : "";
var offsetStr = offset.ToString().RemovePostFix(":00");
return $"UTC {flag}{offsetStr}";
return param;
}
}

View File

@ -0,0 +1,122 @@
using FeishuNetSdk;
using FeishuNetSdk.Im;
using FeishuNetSdk.Im.Dtos;
using SJZY.UserCenter.SharedModel.Dtos.Message;
using SJZY.UserCenter.SharedModel.Enums;
namespace SJZY.Track.Application.Hangfire;
// 标题:预警通知
// 内容:尊敬的用户,截止到2024-12-13 10:00:01(UTC +8:00),轨迹中心监测到与您相关的预警风险信息共计299 条,请及时处理,详情如下:
// 运输时效预警共计100条,其中过去4小时内新增的预警10条,查看详情
// 回传时效预警共计99条,其中过去4小时内新增的预警10条,查看详情
// 轨迹完整预警共计100条,其中过去4小时内新增的预警10条,查看详情
public static class WarningNotifyBuilder
{
private const string Title = "预警通知";
// 构建站内信发消息请求体
public static SendMessageByTemplateInput BuildZhanNeiXinMessage(WarningNotifyParam param, int systemId)
{
const string templateCode = "TrackWarningNotify"; // 模板code
const string contentKey = "content"; //内容参数
var dateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
var totalCount = param.TransportCount + param.ReturnCount + param.CompleteCount;
// todo:无法使用a标签填充(可能是为了安全考虑),需要具体给定一个样式
var url1 = "";
var url2 = "";
var url3 = "";
var content = $"{Title}\n尊敬的用户,截止到{dateTime}({BuildLocalTimezoneStr()}),轨迹中心监测到与您相关的预警风险信息共计{totalCount} 条,请及时处理,详情如下:";
if (param.TransportCount > 0)
content += $"\n运输时效预警共计{param.TransportCount}条,其中过去{param.Hours}小时内新增的预警{param.TransportLastNHoursCount}条";
if (param.ReturnCount > 0)
content += $"\n回传时效预警共计{param.ReturnCount}条,其中过去{param.Hours}小时内新增的预警{param.ReturnLastNHoursCount}条";
if (param.CompleteCount > 0)
content += $"\n轨迹完整预警共计{param.CompleteCount}条,其中过去{param.Hours}小时内新增的预警{param.CompleteLastNHoursCount}条";
// 构建用户中心发消息请求体对象
var dto = new SendMessageByTemplateInput
{
IsNeedToHandle = true,
MsgSource = MsgSourceEnum.消息,
SystemId = systemId,
templateCode = templateCode,
channels = new List<ChannelEnum>(1)
{
ChannelEnum.站内信
},
variables = new List<VariableDto>(1)
{
new VariableDto
{
Key = contentKey,
Value = content
}
},
receivers = new List<MsgInstanceReceiverInput>(1)
{
new MsgInstanceReceiverInput
{
RecipientType = 1, //接收人类型 (1:内部用户 2.外部用户)
ReceiverId = param.UserId, //接收人id
ReceiverName = null, //接收人名称
Mail = null, // 邮箱
MobilePhone = null, // 手机号
}
},
};
return dto;
}
// 构建飞书卡片发消息请求体
public static PostImV1MessagesBodyDto BuildFeiShuCard(WarningNotifyParam param, string fsUserId)
{
var totalCount = param.TransportCount + param.ReturnCount + param.CompleteCount;
var body = new List<object>
{
new MarkdownElement(Content: $"尊敬的用户,截止到{DateTime.Now:yyyy-MM-dd HH:mm:ss}({BuildLocalTimezoneStr()}),轨迹中心监测到与您相关的预警风险信息共计{totalCount} 条,请及时处理,详情如下:", TextAlign: "left", TextSize: "normal_v2")
};
// todo:补充点击链接
var url1 = "";
var url2 = "";
var url3 = "";
if (param.TransportCount > 0)
body.Add(new MarkdownElement(Content: $"运输时效预警共计{param.TransportCount}条,其中过去{param.Hours}小时内新增的预警{param.TransportLastNHoursCount}条 [查看详情]({url1})", TextAlign: "left", TextSize: "normal_v2"));
if (param.ReturnCount > 0)
body.Add(new MarkdownElement(Content: $"轨迹回传时效预警共计{param.ReturnCount}条,其中过去{param.Hours}小时内新增的预警{param.ReturnLastNHoursCount}条 [查看详情]({url2})", TextAlign: "left", TextSize: "normal_v2"));
if (param.CompleteCount > 0)
body.Add(new MarkdownElement(Content: $"轨迹完整预警共计{param.CompleteCount}条,其中过去{param.Hours}小时内新增的预警{param.CompleteLastNHoursCount}条 [查看详情]({url3})", TextAlign: "left", TextSize: "normal_v2"));
var dto = new PostImV1MessagesBodyDto
{
MsgType = "interactive",
ReceiveId = fsUserId
};
dto.SetContent(new ElementsCardV2Dto
{
// 配置卡片全局行为
Config = new ElementsCardV2Dto.ConfigSuffix(),
Header = new ElementsCardV2Dto.HeaderSuffix()
{
Title = new HeaderTitleElement(Title),
Template = "red"
},
Body = new ElementsCardV2Dto.BodySuffix(body.ToArray()),
});
return dto;
}
// 构建本地时区字符串标识 e.g UTC +8:00
private static string BuildLocalTimezoneStr()
{
var offset = TimeZoneInfo.Local.BaseUtcOffset;
var flag = offset.Hours > 0 ? "+" : "";
var offsetStr = offset.ToString().RemovePostFix(":00");
return $"UTC {flag}{offsetStr}";
}
}

View File

@ -0,0 +1,23 @@
namespace SJZY.Track.Application.Hangfire;
/// <summary>
/// 构建轨迹预警通知消息所需的参数
/// </summary>
public class WarningNotifyParam
{
public int UserId { get; set; }
public int Hours { get; set; }
public int TransportCount { get; set; }
public int TransportLastNHoursCount { get; set; }
public int ReturnCount { get; set; }
public int ReturnLastNHoursCount { get; set; }
public int CompleteCount { get; set; }
public int CompleteLastNHoursCount { get; set; }
}

View File

@ -1,3 +1,4 @@
using FeishuNetSdk;
using Hangfire;
using Microsoft.Extensions.Options;
using SJZY.Expand.ABP.Core.Logger;
@ -11,9 +12,8 @@ using SJZY.Track.Domain.Aggregates.WarningAggregate.Return;
using SJZY.Track.Domain.Dtos.Warning;
using SJZY.Track.Domain.Enums;
using SJZY.Track.Domain.Extensions;
using SJZY.Track.Domain.Queries;
using SJZY.UserCenter.HttpApi.Services.Message;
using SJZY.UserCenter.SharedModel.Dtos.Message;
using SJZY.UserCenter.SharedModel.Enums;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Client.DynamicProxying;
@ -21,8 +21,8 @@ namespace SJZY.Track.Application.Hangfire;
public interface IWarningTaskService
{
[AutomaticRetry(Attempts = 3)]
Task CallNotifyAsync(NotifyConfig config, CancellationToken ct);
[AutomaticRetry(Attempts = 1)]
Task CallNotifyAsync(WarningNotifyParam param, CancellationToken ct);
[AutomaticRetry(Attempts = 3, ExceptOn = new[] { typeof(BusinessException) })]
Task CallTrackRetryPushAsync(ReturnWarningRetryPushTaskEntity entity, CancellationToken ct);
@ -30,51 +30,57 @@ public interface IWarningTaskService
public class WarningTaskService : IWarningTaskService, ITransientDependency
{
private readonly IOptionsMonitor<InterfaceRelayParamsConfigOptions> _interfaceRelayOptions;
private readonly IOptionsMonitor<TrackWarningTaskOptions> _warningTaskOptions;
private readonly IHttpClientProxy<IRequestSendMessageService> _requestSendMessageService;
private readonly IHttpClientProxy<IInterfaceForwardServiceV2> _interfaceForwardServiceV2;
private readonly IOptionsMonitor<InterfaceRelayParamsConfigOptions> _options;
private readonly IFeishuTenantApi _feishuTenantApi;
private readonly IWarningDetailsEsRepository _warningDetailsEsRepository;
private readonly IWarningTaskRepository _warningTaskRepository;
private readonly IVFsUserQueries _vfsUserQueries;
public WarningTaskService(IHttpClientProxy<IInterfaceForwardServiceV2> interfaceForwardServiceV2, IOptionsMonitor<InterfaceRelayParamsConfigOptions> options, IHttpClientProxy<IRequestSendMessageService> requestSendMessageService, IWarningDetailsEsRepository warningDetailsEsRepository, IWarningTaskRepository warningTaskRepository)
public WarningTaskService(
IOptionsMonitor<InterfaceRelayParamsConfigOptions> interfaceRelayOptions,
IOptionsMonitor<TrackWarningTaskOptions> warningTaskOptions,
IHttpClientProxy<IInterfaceForwardServiceV2> interfaceForwardServiceV2,
IHttpClientProxy<IRequestSendMessageService> requestSendMessageService,
IFeishuTenantApi feishuTenantApi,
IWarningDetailsEsRepository warningDetailsEsRepository,
IWarningTaskRepository warningTaskRepository,
IVFsUserQueries vfsUserQueries)
{
_interfaceRelayOptions = interfaceRelayOptions;
_warningTaskOptions = warningTaskOptions;
_interfaceForwardServiceV2 = interfaceForwardServiceV2;
_requestSendMessageService = requestSendMessageService;
_feishuTenantApi = feishuTenantApi;
_warningDetailsEsRepository = warningDetailsEsRepository;
_warningTaskRepository = warningTaskRepository;
_options = options;
_vfsUserQueries = vfsUserQueries;
}
#region 用户中心调用,通知(一次)
[AutomaticRetry(Attempts = 3)]
public async Task CallNotifyAsync(NotifyConfig config, CancellationToken ct)
[AutomaticRetry(Attempts = 1)]
public async Task CallNotifyAsync(WarningNotifyParam param, CancellationToken ct)
{
ct.ThrowIfCancellationRequested();
var r = await _requestSendMessageService.Service.SendMessageByTemplateAsync(new SendMessageByTemplateInput
{
IsNeedToHandle = config.IsNeedToHandle,
MsgSource = (MsgSourceEnum) config.MsgSource,
SystemId = config.SystemId,
templateCode = config.TemplateCode,
channels = config.Channels.Select(x => (ChannelEnum) x).ToList(),
variables = config.Variables.Select(x => new VariableDto()
{
Key = x.Key,
Value = x.Value
}).ToList(),
receivers = config.Receivers.Select(x => new MsgInstanceReceiverInput()
{
ReceiverId = x.ReceiverId,
RecipientType = x.RecipientType
}).ToList(),
});
var options = _warningTaskOptions.CurrentValue;
// 发送站内信
// https://centerapi.test.shijizhongyun.com/userApi/index.html?urls.primaryName=%E6%B6%88%E6%81%AF%E8%AE%A2%E9%98%85%E5%AE%9E%E4%BE%8B
// MsgSubscribeInstance/SendMessageByTemplate
var r = await _requestSendMessageService.Service.SendMessageByTemplateAsync(WarningNotifyBuilder.BuildZhanNeiXinMessage(param, options.MiddleSystemId));
if (r.Code != GlobalConst.SuccessCode)
{
throw new BusinessException(message: $"用户中心调用失败:SendMessageByTemplateAsync" +
$"Msg:{r.Msg}");
}
throw new BusinessException(message: $"用户中心调用失败:SendMessageByTemplateAsync Msg:{r.Msg}");
// 发送FeiShu卡片信息
var fsUserId = await _vfsUserQueries.GetFirstAsync(x => x.UserCenterId == param.UserId, y => y.FsEmployeeId);
if (string.IsNullOrWhiteSpace(fsUserId))
throw new BusinessException(message: "查询用户中心失败:SendMessageByTemplateAsync Msg:未找到用户对应的飞书用户Id");
var res = await _feishuTenantApi.PostImV1MessagesAsync("user_id", WarningNotifyBuilder.BuildFeiShuCard(param, fsUserId));
if (res.Code != 0)
throw new BusinessException(message: $"飞书调用失败:SendMessageByTemplateAsync Msg:{res.Msg}");
}
#endregion
@ -107,8 +113,8 @@ public class WarningTaskService : IWarningTaskService, ITransientDependency
using (Track2InterfaceRelaySubSystemContext.Use(new Track2InterfaceRelaySubSystemContext(task.SubSystemCode)))
{
var upStreamCode = isAdd
? _options.CurrentValue.PushTrackInterfaceCode
: _options.CurrentValue.TrackUpdateInterfaceCode;
? _interfaceRelayOptions.CurrentValue.PushTrackInterfaceCode
: _interfaceRelayOptions.CurrentValue.TrackUpdateInterfaceCode;
var r = await _interfaceForwardServiceV2.Service.BatchForwardThenPushToQueueAsync(
upStreamCode: upStreamCode,

View File

@ -10,6 +10,7 @@
<ItemGroup>
<PackageReference Include="BSI.Stack.Rabbit" Version="2.1.4" />
<PackageReference Include="Confluent.Kafka" Version="2.10.0" />
<PackageReference Include="FeishuNetSdk" Version="2.4.6" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.18" />
<PackageReference Include="Hangfire.Redis.StackExchange" Version="1.9.4" />
<PackageReference Include="Savorboard.CAP.InMemoryMessageQueue" Version="8.2.1"/>

View File

@ -0,0 +1,7 @@
using SJZY.Track.Domain.VEntities;
namespace SJZY.Track.Domain.Queries;
public interface IVFsUserQueries : IBasicQuery<VFSUserEntity>
{
}

View File

@ -0,0 +1,65 @@
namespace SJZY.Track.Domain.VEntities;
/// <summary>
/// 飞书用户表,用户中心用户关联飞书用户
/// </summary>
[SugarTable("v_fs_user")]
public class VFSUserEntity : IEntity
{
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
/// <summary>
/// 用户中心id
/// </summary>
[SugarColumn(ColumnName = "user_center_id")]
public int UserCenterId { get; set; }
/// <summary>
/// 用户名
/// </summary>
[SugarColumn(ColumnName = "user_name")]
public string UserName { get; set; }
/// <summary>
/// 用户手机号
/// </summary>
[SugarColumn(ColumnName = "mobile")]
public string Mobile { get; set; }
/// <summary>
/// 飞书 union id
/// </summary>
[SugarColumn(ColumnName = "union_id")]
public string UnionId { get; set; }
/// <summary>
/// 飞书 user id
/// </summary>
[SugarColumn(ColumnName = "fs_employee_id")]
public string FsEmployeeId { get; set; }
/// <summary>
/// 飞书头像
/// </summary>
[SugarColumn(ColumnName = "avatar")]
public string Avatar { get; set; }
/// <summary>
/// 自建的飞书登录应用下的OpenId
/// </summary>
[SugarColumn(ColumnName = "fsloginApp_openid")]
public string FsLoginAppOpenId { get; set; }
/// <summary>
/// 创建时间
/// </summary>
[SugarColumn(ColumnName = "create_time")]
public DateTime CreateTime { get; set; }
/// <summary>
/// 更新时间
/// </summary>
[SugarColumn(ColumnName = "update_time")]
public DateTime UpdateTime { get; set; }
}

View File

@ -0,0 +1,9 @@
using SJZY.Track.Domain.Queries;
using SJZY.Track.Domain.VEntities;
namespace SJZY.Track.Repository.Queries;
public class VFsUserQueries: BasicQuery<VFSUserEntity>, IVFsUserQueries, ITransientDependency
{
}