|
|
@@ -6,12 +6,15 @@ using NPOI.SS.Formula.Functions;
|
|
|
using NPOI.SS.UserModel;
|
|
|
using OASystem.API.OAMethodLib;
|
|
|
using OASystem.API.OAMethodLib.HunYuanAPI;
|
|
|
+using OASystem.API.OAMethodLib.QiYeWeChatAPI;
|
|
|
using OASystem.API.OAMethodLib.QiYeWeChatAPI.AppNotice;
|
|
|
using OASystem.Domain.AesEncryption;
|
|
|
using OASystem.Domain.Dtos.Groups;
|
|
|
using OASystem.Domain.Entities.Groups;
|
|
|
using OASystem.Domain.ViewModels.Groups;
|
|
|
+using OASystem.Domain.ViewModels.QiYeWeChat;
|
|
|
using OASystem.Infrastructure.Repositories.Groups;
|
|
|
+using Quartz.Logging;
|
|
|
using Quartz.Util;
|
|
|
using System.ComponentModel.DataAnnotations;
|
|
|
using System.Data;
|
|
|
@@ -19,6 +22,7 @@ using System.Diagnostics;
|
|
|
using TencentCloud.Common;
|
|
|
using static OASystem.API.OAMethodLib.GeneralMethod;
|
|
|
using static OASystem.API.OAMethodLib.JWTHelper;
|
|
|
+using static OpenAI.GPT3.ObjectModels.Models;
|
|
|
|
|
|
namespace OASystem.API.Controllers
|
|
|
{
|
|
|
@@ -31,9 +35,10 @@ namespace OASystem.API.Controllers
|
|
|
{
|
|
|
private readonly IMapper _mapper;
|
|
|
private readonly ILogger<ResourceController> _logger;
|
|
|
- private readonly SqlSugarClient _sqlSugar;
|
|
|
private readonly IConfiguration _config;
|
|
|
private readonly IHunyuanService _hunyuanService;
|
|
|
+ private readonly IQiYeWeChatApiService _qiYeWeChatApiService;
|
|
|
+ private readonly SqlSugarClient _sqlSugar;
|
|
|
private readonly CarDataRepository _carDataRep;
|
|
|
private readonly LocalGuideDataRepository _localGuideDataRep;
|
|
|
private readonly ThreeCodeRepository _ThreeCodeRep;
|
|
|
@@ -72,6 +77,7 @@ namespace OASystem.API.Controllers
|
|
|
IConfiguration config,
|
|
|
ILogger<ResourceController> logger,
|
|
|
IHunyuanService hunyuanService,
|
|
|
+ IQiYeWeChatApiService qiYeWeChatApiService,
|
|
|
SqlSugarClient sqlSugar,
|
|
|
CarDataRepository carDataRep,
|
|
|
LocalGuideDataRepository localGuideDataRep,
|
|
|
@@ -100,6 +106,7 @@ namespace OASystem.API.Controllers
|
|
|
_config = config;
|
|
|
_logger = logger;
|
|
|
_hunyuanService = hunyuanService;
|
|
|
+ _qiYeWeChatApiService = qiYeWeChatApiService;
|
|
|
_sqlSugar = sqlSugar;
|
|
|
_carDataRep = carDataRep;
|
|
|
_localGuideDataRep = localGuideDataRep;
|
|
|
@@ -2327,7 +2334,7 @@ Inner Join Sys_Department as d With(Nolock) On u.DepId=d.Id Where m.Id={0} ", _m
|
|
|
info.InvName,
|
|
|
info.GroupId,
|
|
|
info.AiCrawledDetails,
|
|
|
- Entry = info.Entries,
|
|
|
+ Entry = info.EntryInfo,
|
|
|
};
|
|
|
return Ok(JsonView(true, $"查询成功!", view));
|
|
|
}
|
|
|
@@ -2342,12 +2349,12 @@ Inner Join Sys_Department as d With(Nolock) On u.DepId=d.Id Where m.Id={0} ", _m
|
|
|
{
|
|
|
var invAiInfo = await _sqlSugar.Queryable<Res_InvitationAI>().Where(x => x.Id == dto.Id).FirstAsync();
|
|
|
|
|
|
- if (invAiInfo == null || invAiInfo?.Entries == null)
|
|
|
+ if (invAiInfo == null || invAiInfo?.EntryInfo == null)
|
|
|
{
|
|
|
return Ok(JsonView(false, "请先设置关键字信息!"));
|
|
|
}
|
|
|
|
|
|
- var entryInfo = invAiInfo.Entries;
|
|
|
+ var entryInfo = invAiInfo.EntryInfo;
|
|
|
|
|
|
// 当前企业领域
|
|
|
string industryFocus = string.Empty;
|
|
|
@@ -2548,7 +2555,7 @@ Inner Join Sys_Department as d With(Nolock) On u.DepId=d.Id Where m.Id={0} ", _m
|
|
|
invAiInfo.InvName,
|
|
|
invAiInfo.GroupId,
|
|
|
invAiInfo.AiCrawledDetails,
|
|
|
- Entry = invAiInfo.Entries
|
|
|
+ Entry = invAiInfo.EntryInfo
|
|
|
}));
|
|
|
}
|
|
|
|
|
|
@@ -2603,7 +2610,7 @@ Inner Join Sys_Department as d With(Nolock) On u.DepId=d.Id Where m.Id={0} ", _m
|
|
|
{
|
|
|
InvName = groupInfo?.TeamName ?? invName, //默认团组名称
|
|
|
GroupId = dto.GroupId,
|
|
|
- Entries = entryInfo,
|
|
|
+ EntryInfo = entryInfo,
|
|
|
CreateUserId = dto.CurrUserId
|
|
|
};
|
|
|
|
|
|
@@ -2616,7 +2623,7 @@ Inner Join Sys_Department as d With(Nolock) On u.DepId=d.Id Where m.Id={0} ", _m
|
|
|
else
|
|
|
{
|
|
|
// 3.2 数据存在 则更新数据(覆盖原有的AI爬取详情,保留原有的其他字段不变)
|
|
|
- dataInfo.Entries = entryInfo;
|
|
|
+ dataInfo.EntryInfo = entryInfo;
|
|
|
|
|
|
var update = await _sqlSugar.Updateable(dataInfo).ExecuteCommandAsync();
|
|
|
if (update < 1)
|
|
|
@@ -2632,12 +2639,54 @@ Inner Join Sys_Department as d With(Nolock) On u.DepId=d.Id Where m.Id={0} ", _m
|
|
|
dataInfo.Id,
|
|
|
dataInfo.InvName,
|
|
|
dataInfo.GroupId,
|
|
|
- Entry = dataInfo.Entries,
|
|
|
+ Entry = dataInfo.EntryInfo,
|
|
|
}));
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// 商邀资料AI 单条资料删除
|
|
|
+ /// 商邀资料AI 保存
|
|
|
+ /// </summary>
|
|
|
+ /// <returns></returns>
|
|
|
+ [HttpPost]
|
|
|
+ [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
|
|
|
+ public async Task<IActionResult> InvitationAISave(InvitationAISaveDto dto)
|
|
|
+ {
|
|
|
+ if (dto.Id < 1) return Ok(JsonView(false, "请选择保存的团组"));
|
|
|
+ if (dto.CurrUserId < 1) return Ok(JsonView(false, "请传入用户Id"));
|
|
|
+ if (dto.AiCrawledDetail == null) return Ok(JsonView(false, "请选择保存的邀请方详情"));
|
|
|
+
|
|
|
+ var invAiInfo = await _sqlSugar.Queryable<Res_InvitationAI>()
|
|
|
+ .Where(x => x.Id == dto.Id)
|
|
|
+ .FirstAsync();
|
|
|
+
|
|
|
+ if (invAiInfo == null) return Ok(JsonView(false, "数据信息为空"));
|
|
|
+
|
|
|
+ var dataList = invAiInfo?.AiCrawledDetails;
|
|
|
+
|
|
|
+ if (dataList == null) return Ok(JsonView(false, "邀请方数据信息为空"));
|
|
|
+
|
|
|
+ var datas = dataList.Where(x => x.Guid != dto.AiCrawledDetail.Guid).ToList();
|
|
|
+
|
|
|
+ var editInfo = dto.AiCrawledDetail;
|
|
|
+
|
|
|
+ var opUserName = await _sqlSugar.Queryable<Sys_Users>().Where(x => x.IsDel == 0 && x.Id == dto.CurrUserId).Select(x => x.CnName).FirstAsync() ?? "-";
|
|
|
+ editInfo.Operator = opUserName;
|
|
|
+ editInfo.OperatedAt = DateTime.Now;
|
|
|
+
|
|
|
+ datas.Add(editInfo);
|
|
|
+ invAiInfo.AiCrawledDetails = datas;
|
|
|
+
|
|
|
+ var editUpd = await _sqlSugar.Updateable(invAiInfo).UpdateColumns(x => x.AiCrawledDetails).ExecuteCommandAsync();
|
|
|
+ if (editUpd < 1)
|
|
|
+ {
|
|
|
+ return Ok(JsonView(true, "保存失败"));
|
|
|
+ }
|
|
|
+
|
|
|
+ return Ok(JsonView(true, "保存成功"));
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 商邀资料AI 单条删除
|
|
|
/// </summary>
|
|
|
/// <returns></returns>
|
|
|
[HttpPost]
|
|
|
@@ -2683,12 +2732,12 @@ Inner Join Sys_Department as d With(Nolock) On u.DepId=d.Id Where m.Id={0} ", _m
|
|
|
{
|
|
|
var invAiInfo = await _sqlSugar.Queryable<Res_InvitationAI>().Where(x => x.Id == dto.Id).FirstAsync();
|
|
|
|
|
|
- if (invAiInfo == null || invAiInfo?.Entries == null)
|
|
|
+ if (invAiInfo == null || invAiInfo?.EntryInfo == null)
|
|
|
{
|
|
|
return Ok(JsonView(false, "请先设置关键字信息!"));
|
|
|
}
|
|
|
|
|
|
- var entryInfo = invAiInfo.Entries;
|
|
|
+ var entryInfo = invAiInfo.EntryInfo;
|
|
|
|
|
|
// 已搜索出的数据(AI 数据源)
|
|
|
var hunyuanAIDatas = invAiInfo.AiCrawledDetails.Where(x => x.Source == 1).ToList() ?? new List<InvitationAIInfo>();
|
|
|
@@ -3102,6 +3151,325 @@ Inner Join Sys_Department as d With(Nolock) On u.DepId=d.Id Where m.Id={0} ", _m
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// 商邀资料AI 生成邮件
|
|
|
+ /// </summary>
|
|
|
+ /// <returns></returns>
|
|
|
+ [HttpPost]
|
|
|
+ [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
|
|
|
+ public async Task<IActionResult> InvitationAIGenerateEmail(InvitationAIGenerateEmailDto dto)
|
|
|
+ {
|
|
|
+ if (dto.Id < 1) return Ok(JsonView(false, "请选择保存的团组"));
|
|
|
+ if (dto.CurrUserId < 1) return Ok(JsonView(false, "请传入用户Id"));
|
|
|
+ if (dto.Guids == null || !dto.Guids.Any()) return Ok(JsonView(false, "请传入需要生成邮件的单位列表"));
|
|
|
+
|
|
|
+ var invAiInfo = await _sqlSugar.Queryable<Res_InvitationAI>()
|
|
|
+ .Where(x => x.Id == dto.Id)
|
|
|
+ .FirstAsync();
|
|
|
+
|
|
|
+ if (invAiInfo == null) return Ok(JsonView(false, "数据信息为空"));
|
|
|
+
|
|
|
+ var dataList = invAiInfo?.AiCrawledDetails;
|
|
|
+
|
|
|
+ if (dataList == null) return Ok(JsonView(false, "邀请方数据信息为空"));
|
|
|
+
|
|
|
+ var clientInfoSources = dataList.Where(x => dto.Guids.Contains(x.Guid)).ToList();
|
|
|
+
|
|
|
+ var clientInfos = clientInfoSources.Select(x => new AICreateEmailInfo()
|
|
|
+ {
|
|
|
+ Guid = x.Guid,
|
|
|
+ NameCn = x.NameCn,
|
|
|
+ Scope = x.Scope,
|
|
|
+ Subject = "",
|
|
|
+ Content = ""
|
|
|
+ }).ToList();
|
|
|
+
|
|
|
+ if (clientInfos == null || !clientInfos.Any()) return Ok(JsonView(false, "邀请单位信息为空"));
|
|
|
+
|
|
|
+ var groupInfo = await _sqlSugar.Queryable<Grp_DelegationInfo>()
|
|
|
+ .Where(x => x.IsDel == 0 && x.Id == invAiInfo.GroupId)
|
|
|
+ .Select(x => new { x.TeamName, x.VisitPurpose,x.VisitDate})
|
|
|
+ .FirstAsync();
|
|
|
+ string operatorName = await _sqlSugar.Queryable<Sys_Users>().Where(x => x.IsDel == 0 && x.Id == dto.CurrUserId).Select(x => x.CnName).FirstAsync() ?? "-";
|
|
|
+ string pormpt = $@"
|
|
|
+# Role
|
|
|
+你是一位精通国际政企关系的【首席联络官】。你具备极强的行业分析能力,能通过 [SourceEntity] 的名称自动检索并推导其行政职能与行业地位,并以此撰写具有战略高度、语调优雅的正式商务邮件。
|
|
|
+
|
|
|
+# Intelligence Task: Source Profiling
|
|
|
+在生成邮件前,请先执行以下逻辑:
|
|
|
+1. **职能推导**:基于 [SourceEntity] 的名称,自动识别其在所属领域的具体行政职能与政策影响力。
|
|
|
+2. **战略对齐**:将其职能与全球大趋势(如:Sustainable Urbanization, Digital Transformation, Carbon Neutrality)挂钩,作为邮件第二段的叙事背景。
|
|
|
+
|
|
|
+# Reference Model (Few-Shot Example)
|
|
|
+在生成时,请严格参考以下范例的【语气】、【五段式结构】和【外交辞令】:
|
|
|
+- Subject: Official Study Visit Inquiry – [Delegation Name] ([Date])
|
|
|
+- Para 1: ""On behalf of [SourceEntity], I am writing to formally propose...""
|
|
|
+- Para 2: ""As a pivotal megacity [AI根据Source地位补全]... we recognize [Target]’s leadership in...""
|
|
|
+- Para 3: ""We are particularly keen to explore... [基于Target经营范围推导的3个技术点]...""
|
|
|
+- Para 4: ""We propose a 2–3 day visit... during the week of [Logistics]...""
|
|
|
+- Para 5: ""Enclosed please find the [PascalCase_File]...""
|
|
|
+
|
|
|
+# Task
|
|
|
+根据 [TargetList] 中每个单位的【经营范围】,为 [SourceEntity] 生成独立的英文访问请求邮件。
|
|
|
+
|
|
|
+# Inputs
|
|
|
+- [SourceEntity]: [{invAiInfo.EntryInfo?.OriginUnit ?? ""}]
|
|
|
+- [VisitPurpose]: [{groupInfo?.VisitPurpose}]
|
|
|
+- [TargetList]: [{JsonConvert.SerializeObject(clientInfos)}]
|
|
|
+- [Logistics]: [{groupInfo?.VisitDate.ToString("yyyy-MM-dd")}]
|
|
|
+
|
|
|
+# Execution Logic (Chain of Thought)
|
|
|
+1. **Scope-to-Focus Analysis**:
|
|
|
+ - 深入分析每个 Target 的【经营范围】。
|
|
|
+ - 自动推导 3 个与 [VisitPurpose] 高度对齐的专业考察点(如:Policy Frameworks, Technical Standards, Operational Case Studies)。
|
|
|
+2. **Modular Drafting**:
|
|
|
+ - 必须严格遵循参考范例的 5 段式逻辑。
|
|
|
+ - 针对不同职能属性(监管/技术/运营)动态微调邮件主题(Subject)。
|
|
|
+3. **No Personnel Reference**: 严禁提及“名单、人数、成员、审核中”等任何具体人员信息,保持机构对等对话的高度。
|
|
|
+
|
|
|
+# Constraints & Standards
|
|
|
+- **Tone**: Formal, Strategic, and Executive (庄重、具战略高度、执行力强)。
|
|
|
+- **Naming Protocol**: 附件引用统一使用 `JointVisitAgenda`, `StrategicInquiryBrief` (PascalCase)。
|
|
|
+- **Output Format**: 仅输出一个合规的 JSON 数组,不包含任何解释性文字。
|
|
|
+- **Field Mapping**:
|
|
|
+ - `Guid`: 原样保留。
|
|
|
+ - `NameCn`: 原样保留。
|
|
|
+ - `Scope`: 原样保留。
|
|
|
+ - `Subject`: 动态生成的英文主题。
|
|
|
+ - `Content`: 包含 Markdown 格式符号(`###`, `**`, `*`, `---`)的英文正文。
|
|
|
+
|
|
|
+# JSON Structure Template
|
|
|
+[
|
|
|
+ {{
|
|
|
+ ""Guid"": ""ID"",
|
|
|
+ ""NameCn"": ""Entity Name"",
|
|
|
+ ""Scope"": ""Scope Description"",
|
|
|
+ ""Subject"": ""Dynamic English Subject"",
|
|
|
+ ""Content"": ""### Subject\n\n**Dear...**\n\nBody content with *Markdown*...\n\n--- \n\nClosing.""
|
|
|
+ }}
|
|
|
+]";
|
|
|
+
|
|
|
+ string response = string.Empty;
|
|
|
+ try
|
|
|
+ {
|
|
|
+ response = await _hunyuanService.ChatCompletionsHunyuan_t1_latestAsync(pormpt);
|
|
|
+ }
|
|
|
+ catch (HttpRequestException ex)
|
|
|
+ {
|
|
|
+ _logger.LogError(ex, "网络连接失败,无法调用混元API");
|
|
|
+ return Ok(JsonView(false, $"网络连接异常,请检查网络后重试!Ex:{ex.Message}"));
|
|
|
+ }
|
|
|
+ catch (TaskCanceledException ex)
|
|
|
+ {
|
|
|
+ _logger.LogError(ex, "请求超时");
|
|
|
+ return Ok(JsonView(false, $"AI 响应超时,请稍后再试!Ex:{ex.Message}"));
|
|
|
+ }
|
|
|
+ catch (TencentCloudSDKException ex)
|
|
|
+ {
|
|
|
+ // 记录完整日志
|
|
|
+ _logger.LogError(ex, "腾讯云API调用失败 | 错误码: {ErrorCode} | 请求ID: {RequestId}", ex.ErrorCode, ex.RequestId);
|
|
|
+
|
|
|
+ // 根据错误码做差异化处理
|
|
|
+ if (ex.ErrorCode != null && ex.ErrorCode == "AuthFailure.SignatureExpire")
|
|
|
+ {
|
|
|
+ return Ok(JsonView(false, $"系统时间异常,请同步时间后重试!Ex:{ex.Message}"));
|
|
|
+ }
|
|
|
+ else if (ex.ErrorCode != null && ex.ErrorCode.StartsWith("Unauthorized"))
|
|
|
+ {
|
|
|
+ return Ok(JsonView(false, $"当前账号无权限访问该服务!Ex:{ex.Message}"));
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return Ok(JsonView(false, $"腾讯云服务调用失败,请稍后重试!Ex:{ex.Message}"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ _logger.LogError(ex, "未知错误,调用混元API失败");
|
|
|
+ return Ok(JsonView(false, $"未知错误,调用混元API失败!Ex:{ex.Message}"));
|
|
|
+ }
|
|
|
+
|
|
|
+ var hunyuanAIEmailDatas = new List<AICreateEmailInfo>();
|
|
|
+
|
|
|
+ if (!string.IsNullOrWhiteSpace(response))
|
|
|
+ {
|
|
|
+ // 预处理:过滤 AI 可能返回的 Markdown 标记
|
|
|
+ string cleanJson = response.Trim();
|
|
|
+ if (cleanJson.StartsWith("```json"))
|
|
|
+ cleanJson = cleanJson.Substring(7, cleanJson.Length - 10).Trim();
|
|
|
+ else if (cleanJson.StartsWith("```"))
|
|
|
+ cleanJson = cleanJson.Substring(3, cleanJson.Length - 6).Trim();
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ // 解析并注入 Source 标识
|
|
|
+ hunyuanAIEmailDatas = JsonConvert.DeserializeObject<List<AICreateEmailInfo>>(cleanJson);
|
|
|
+
|
|
|
+ }
|
|
|
+ catch (JsonException ex)
|
|
|
+ {
|
|
|
+ // 记录日志并考虑 fallback 策略
|
|
|
+ _logger.LogError(ex, "Hunyuan AI 响应解析失败。原始数据:{Response}", response);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (hunyuanAIEmailDatas == null || !hunyuanAIEmailDatas.Any())
|
|
|
+ {
|
|
|
+ return Ok(JsonView(false, "Hunyuan AI 生成的邮件为空"));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 数据邮件存储在数据库
|
|
|
+ StringBuilder msgSb = new StringBuilder();
|
|
|
+
|
|
|
+ foreach (var client in clientInfoSources)
|
|
|
+ {
|
|
|
+ var aiEmailInfo = hunyuanAIEmailDatas.Where(x => x.Guid == client.Guid).FirstOrDefault();
|
|
|
+ if (aiEmailInfo == null)
|
|
|
+ {
|
|
|
+ msgSb.AppendLine($"{client.NameCn}: 邮件AI生成失败!");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ client.EmailInfo.Status = 2; // AI已生成
|
|
|
+ client.EmailInfo.EmailTitle = aiEmailInfo.Subject;
|
|
|
+ client.EmailInfo.EmailContent = aiEmailInfo.Content;
|
|
|
+ client.EmailInfo.Operator = operatorName;
|
|
|
+ client.EmailInfo.OperatedAt = DateTime.Now;
|
|
|
+ msgSb.AppendLine($"{client.NameCn}: 邮件AI生成成功!");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 先删除原有数据中对应的单位数据
|
|
|
+ invAiInfo.AiCrawledDetails = invAiInfo.AiCrawledDetails.Where(x => !dto.Guids.Contains(x.Guid)).ToList();
|
|
|
+ // 再添加AI生成的邮件数据
|
|
|
+ invAiInfo.AiCrawledDetails.AddRange(clientInfoSources);
|
|
|
+
|
|
|
+ var update = await _sqlSugar.Updateable(invAiInfo).UpdateColumns(x => x.AiCrawledDetails).ExecuteCommandAsync();
|
|
|
+ if (update < 1)
|
|
|
+ {
|
|
|
+ return Ok(JsonView(false, $"邮件AI生成,数据更新失败!"));
|
|
|
+ }
|
|
|
+
|
|
|
+ return Ok(JsonView(true, msgSb.ToString()));
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 商邀资料AI 邮件保存
|
|
|
+ /// </summary>
|
|
|
+ /// <returns></returns>
|
|
|
+ [HttpPost]
|
|
|
+ [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
|
|
|
+ public async Task<IActionResult> InvitationAIEmailSave(InvitationAIEmailSaveDto dto)
|
|
|
+ {
|
|
|
+ if (dto.Id < 1) return Ok(JsonView(false, "请选择保存的团组"));
|
|
|
+ if (dto.CurrUserId < 1) return Ok(JsonView(false, "请传入用户Id"));
|
|
|
+ if (string.IsNullOrEmpty(dto.Guid)) return Ok(JsonView(false, "请传入Guid"));
|
|
|
+ if (string.IsNullOrEmpty(dto.EmailTitle)) return Ok(JsonView(false, "请传入邮件标题"));
|
|
|
+ if (string.IsNullOrEmpty(dto.EmailContent)) return Ok(JsonView(false, "请传入邮件内容"));
|
|
|
+
|
|
|
+ var invAiInfo = await _sqlSugar.Queryable<Res_InvitationAI>()
|
|
|
+ .Where(x => x.Id == dto.Id)
|
|
|
+ .FirstAsync();
|
|
|
+
|
|
|
+ if (invAiInfo == null) return Ok(JsonView(false, "数据信息为空"));
|
|
|
+
|
|
|
+ var dataList = invAiInfo?.AiCrawledDetails;
|
|
|
+
|
|
|
+ if (dataList == null) return Ok(JsonView(false, "邀请方数据信息为空"));
|
|
|
+
|
|
|
+ var editInfo = dataList.Where(x => x.Guid == dto.Guid).FirstOrDefault();
|
|
|
+
|
|
|
+ if (editInfo == null) return Ok(JsonView(false, "邀请方信息为空"));
|
|
|
+
|
|
|
+ var opUserName = await _sqlSugar.Queryable<Sys_Users>().Where(x => x.IsDel == 0 && x.Id == dto.CurrUserId).Select(x => x.CnName).FirstAsync() ?? "-";
|
|
|
+
|
|
|
+ editInfo.EmailInfo.EmailTitle = dto.EmailTitle;
|
|
|
+ editInfo.EmailInfo.EmailContent = dto.EmailContent;
|
|
|
+ editInfo.EmailInfo.Operator = opUserName;
|
|
|
+ editInfo.EmailInfo.OperatedAt = DateTime.Now;
|
|
|
+
|
|
|
+ var datas = dataList.Where(x => x.Guid != dto.Guid).ToList();
|
|
|
+ datas.Add(editInfo);
|
|
|
+ invAiInfo.AiCrawledDetails = datas;
|
|
|
+
|
|
|
+ var editUpd = await _sqlSugar.Updateable(invAiInfo).UpdateColumns(x => x.AiCrawledDetails).ExecuteCommandAsync();
|
|
|
+ if (editUpd < 1)
|
|
|
+ {
|
|
|
+ return Ok(JsonView(true, "邮件失败"));
|
|
|
+ }
|
|
|
+
|
|
|
+ return Ok(JsonView(true, "邮件成功"));
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 商邀资料AI 发送邮件(默认当前用户企业微信邮件)
|
|
|
+ /// </summary>
|
|
|
+ /// <returns></returns>
|
|
|
+ [HttpPost]
|
|
|
+ [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
|
|
|
+ public async Task<IActionResult> InvitationAISeedEmail(InvitationAISeedEmailDto dto)
|
|
|
+ {
|
|
|
+ if (dto.Id < 1) return Ok(JsonView(false, "请选择发送邮件的团组"));
|
|
|
+ if (dto.CurrUserId < 1) return Ok(JsonView(false, "请传入用户Id"));
|
|
|
+ if (dto.Guids == null || dto.Guids.Count < 1) return Ok(JsonView(false, "请选择发送邮件的单位"));
|
|
|
+
|
|
|
+ var invAiInfo = await _sqlSugar.Queryable<Res_InvitationAI>()
|
|
|
+ .Where(x => x.Id == dto.Id)
|
|
|
+ .FirstAsync();
|
|
|
+
|
|
|
+ if (invAiInfo == null) return Ok(JsonView(false, "数据信息为空"));
|
|
|
+
|
|
|
+ var dataList = invAiInfo?.AiCrawledDetails;
|
|
|
+
|
|
|
+ if (dataList == null) return Ok(JsonView(false, "邀请方数据信息为空"));
|
|
|
+
|
|
|
+ var seedInvInfos = dataList.Where(x => dto.Guids.Contains(x.Guid)).ToList();
|
|
|
+
|
|
|
+ if (seedInvInfos == null) Ok(JsonView(false, "发送邮件的邀请方信息为空"));
|
|
|
+
|
|
|
+ var userInfo = await _sqlSugar.Queryable<Sys_Users>().Where(x => x.IsDel == 0 && x.Id == dto.CurrUserId).Select(x => new { x.Email, x.CnName }).FirstAsync();
|
|
|
+
|
|
|
+ if (string.IsNullOrEmpty(userInfo.Email)) Ok(JsonView(false, "当前账号未配置邮箱,请先配置邮箱!"));
|
|
|
+
|
|
|
+ var seedEmails = new List<EmailInfo>();
|
|
|
+ #region 发送邮件
|
|
|
+
|
|
|
+ foreach (var item in seedInvInfos)
|
|
|
+ {
|
|
|
+ int seedStatus = 4;
|
|
|
+
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var req = new EmailRequestDto()
|
|
|
+ {
|
|
|
+ ToEmails = new List<string> { "johnny.yang@pan-american-intl.com" },
|
|
|
+ CcEmails = new List<string> { userInfo.Email },
|
|
|
+ BccEmails = new List<string> { userInfo.Email },
|
|
|
+ Subject = "测试邮件 - 来自企业微信API",
|
|
|
+ Body = "这是一封通过企业微信API发送的测试邮件,包含附件。",
|
|
|
+ Files = Array.Empty<IFormFile>()
|
|
|
+ };
|
|
|
+ var response = await _qiYeWeChatApiService.EmailSendAsync(req);
|
|
|
+ if (response.errcode == 0)
|
|
|
+ {
|
|
|
+ seedStatus = 3;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ _logger.LogError(ex, "商邀AI调用企业微信邮件API失败。");
|
|
|
+ }
|
|
|
+
|
|
|
+ seedEmails.Add(new() { EmailContent = "邮件内容", Status = seedStatus, OperatedAt = DateTime.Now, Operator = userInfo.CnName });
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ return Ok(JsonView(true, "发送成功"));
|
|
|
+ }
|
|
|
+
|
|
|
#endregion
|
|
|
|
|
|
#region 公务出访
|