|
|
@@ -1,23 +1,23 @@
|
|
|
using Aspose.Cells;
|
|
|
using Aspose.Words;
|
|
|
-using Dm.util;
|
|
|
-using EyeSoft.Collections.Generic;
|
|
|
+using Aspose.Words.Tables;
|
|
|
using EyeSoft.Extensions;
|
|
|
-using Microsoft.IdentityModel.Tokens;
|
|
|
+using NPOI.SS.Formula.Functions;
|
|
|
using NPOI.SS.UserModel;
|
|
|
using OASystem.API.OAMethodLib;
|
|
|
using OASystem.API.OAMethodLib.HunYuanAPI;
|
|
|
using OASystem.API.OAMethodLib.QiYeWeChatAPI.AppNotice;
|
|
|
using OASystem.Domain.AesEncryption;
|
|
|
using OASystem.Domain.Dtos.Groups;
|
|
|
-using OASystem.Domain.Entities.Customer;
|
|
|
using OASystem.Domain.Entities.Groups;
|
|
|
using OASystem.Domain.ViewModels.Groups;
|
|
|
using OASystem.Infrastructure.Repositories.Groups;
|
|
|
+using Quartz.Util;
|
|
|
using System.ComponentModel.DataAnnotations;
|
|
|
using System.Data;
|
|
|
using System.Diagnostics;
|
|
|
using TencentCloud.Common;
|
|
|
+using static OASystem.API.OAMethodLib.GeneralMethod;
|
|
|
using static OASystem.API.OAMethodLib.JWTHelper;
|
|
|
|
|
|
namespace OASystem.API.Controllers
|
|
|
@@ -2283,41 +2283,20 @@ Inner Join Sys_Department as d With(Nolock) On u.DepId=d.Id Where m.Id={0} ", _m
|
|
|
{
|
|
|
var itemNames = await GeneralMethod.InvitationAIInvName();
|
|
|
|
|
|
- var invDatas = await _sqlSugar.Queryable<Crm_NewClientData>()
|
|
|
- .Where(x => x.IsDel == 0)
|
|
|
- .Where(x => !x.Client.Contains("-") && x.Client != null)
|
|
|
- .Select(x => x.Client)
|
|
|
- .ToListAsync();
|
|
|
-
|
|
|
- invDatas = invDatas
|
|
|
- .Where(c => !string.IsNullOrWhiteSpace(c)) // 确保非空
|
|
|
- .Select(c => c.Replace(" ", "")) // 去除所有空格
|
|
|
- .Distinct() // 炼金:去重
|
|
|
- .ToList();
|
|
|
+ var unitNames = await GeneralMethod.InvitationAIClientName();
|
|
|
|
|
|
- var countries = await _sqlSugar.Queryable<Sys_Countries>()
|
|
|
- .Where(x => x.IsDel == 0)
|
|
|
- .Select(x => x.Name_CN)
|
|
|
- .Distinct()
|
|
|
- .ToListAsync();
|
|
|
+ var countries = await GeneralMethod.InvitationAICountryName();
|
|
|
|
|
|
- // 解密与去重(使用 HashSet 提升性能)
|
|
|
- var unitNames = invDatas
|
|
|
- .Select(item => AesEncryptionHelper.Decrypt(item))
|
|
|
- .Where(val => !string.IsNullOrEmpty(val))
|
|
|
- .Distinct()
|
|
|
- .ToList();
|
|
|
-
|
|
|
- return Ok(new
|
|
|
+ return Ok(JsonView(true, $"查询成功!", new
|
|
|
{
|
|
|
itemNames,
|
|
|
unitNames,
|
|
|
countries
|
|
|
- });
|
|
|
+ }));
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// 商邀资料AI 细聊列表
|
|
|
+ /// 商邀资料AI 资料列表
|
|
|
/// </summary>
|
|
|
/// <returns></returns>
|
|
|
[HttpGet("{name}")]
|
|
|
@@ -2325,20 +2304,30 @@ Inner Join Sys_Department as d With(Nolock) On u.DepId=d.Id Where m.Id={0} ", _m
|
|
|
public async Task<IActionResult> InvitationAIItemByName(string name)
|
|
|
{
|
|
|
// 基础校验
|
|
|
- if (string.IsNullOrWhiteSpace(name) )
|
|
|
+ if (string.IsNullOrWhiteSpace(name))
|
|
|
return Ok(JsonView(false, "请传入有效的名称!"));
|
|
|
|
|
|
var info = await _sqlSugar.Queryable<Res_InvitationAI>()
|
|
|
.Where(x => x.IsDel == 0 && x.InvName == name)
|
|
|
.FirstAsync();
|
|
|
|
|
|
+ if (info == null)
|
|
|
+ {
|
|
|
+ return Ok(JsonView(true, $"暂无数据",new {
|
|
|
+ Id = 0,
|
|
|
+ GroupId= 0,
|
|
|
+ InvName = "",
|
|
|
+ AiCrawledDetails = new List<InvitationAIInfo>(),
|
|
|
+ }));
|
|
|
+ }
|
|
|
+
|
|
|
var view = new
|
|
|
{
|
|
|
info.Id,
|
|
|
info.InvName,
|
|
|
info.GroupId,
|
|
|
info.AiCrawledDetails,
|
|
|
- Entry = info.Entries.FirstOrDefault(),
|
|
|
+ Entry = info.Entries,
|
|
|
};
|
|
|
return Ok(JsonView(true, $"查询成功!", view));
|
|
|
}
|
|
|
@@ -2351,27 +2340,24 @@ Inner Join Sys_Department as d With(Nolock) On u.DepId=d.Id Where m.Id={0} ", _m
|
|
|
[ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
|
|
|
public async Task<IActionResult> InvitationAISearch(InvitationAISearchDto dto)
|
|
|
{
|
|
|
- // 基础校验
|
|
|
- if (string.IsNullOrWhiteSpace(dto.UnitName) || string.IsNullOrWhiteSpace(dto.Country))
|
|
|
- return Ok(JsonView(false, "请传入有效的单位名称和国家!"));
|
|
|
+ var invAiInfo = await _sqlSugar.Queryable<Res_InvitationAI>().Where(x => x.Id == dto.Id).FirstAsync();
|
|
|
+
|
|
|
+ if (invAiInfo == null || invAiInfo?.Entries == null)
|
|
|
+ {
|
|
|
+ return Ok(JsonView(false, "请先设置关键字信息!"));
|
|
|
+ }
|
|
|
+
|
|
|
+ var entryInfo = invAiInfo.Entries;
|
|
|
|
|
|
// 当前企业领域
|
|
|
string industryFocus = string.Empty;
|
|
|
|
|
|
// 动态计算 AI 需求缺口
|
|
|
- int totalTarget = 15;
|
|
|
+ int totalTarget = entryInfo.NeedCount;
|
|
|
|
|
|
// 词条
|
|
|
string question = string.Empty;
|
|
|
|
|
|
- var groupInfo = await _sqlSugar.Queryable<Grp_DelegationInfo>()
|
|
|
- .Where(x => x.IsDel == 0 && x.Id == dto.GroupId)
|
|
|
- .Select(x => new { x.TeamName, x.VisitPurpose })
|
|
|
- .FirstAsync();
|
|
|
-
|
|
|
- // 当前企业出访目的
|
|
|
- string objective = groupInfo?.VisitPurpose ?? "商务考察与合作对接";
|
|
|
-
|
|
|
string operatorName = await _sqlSugar.Queryable<Sys_Users>().Where(x => x.IsDel == 0 && x.Id == dto.CurrUserId).Select(x => x.CnName).FirstAsync() ?? "-";
|
|
|
|
|
|
var localInvDatas = new List<InvitationAIInfo>(); // 本地数据源(商邀资料)
|
|
|
@@ -2379,14 +2365,15 @@ Inner Join Sys_Department as d With(Nolock) On u.DepId=d.Id Where m.Id={0} ", _m
|
|
|
#region 本地数据源(商邀资料)
|
|
|
|
|
|
var rawData = await _sqlSugar.Queryable<Res_InvitationOfficialActivityData>()
|
|
|
- .Where(x => x.IsDel == 0 && x.Country.Equals(dto.Country))
|
|
|
+ .Where(x => x.IsDel == 0 && x.Country.Equals(entryInfo.TargetCountry))
|
|
|
.ToListAsync();
|
|
|
|
|
|
// 解密
|
|
|
localInvDatas = rawData.AsParallel().AsOrdered().Select(item => new InvitationAIInfo
|
|
|
{
|
|
|
+ Guid = Guid.NewGuid().ToString("N"),
|
|
|
Source = 0, // 明确来源标识
|
|
|
- Region = dto.UnitName,
|
|
|
+ Region = entryInfo.OriginUnit,
|
|
|
NameCn = AesEncryptionHelper.Decrypt(item.UnitName),
|
|
|
Address = AesEncryptionHelper.Decrypt(item.Address),
|
|
|
Scope = AesEncryptionHelper.Decrypt(item.Field),
|
|
|
@@ -2413,12 +2400,11 @@ Inner Join Sys_Department as d With(Nolock) On u.DepId=d.Id Where m.Id={0} ", _m
|
|
|
- Target Architecture: .NET 6 DTO Compatible Interface
|
|
|
|
|
|
# [INPUT_CONFIG]
|
|
|
-- OriginUnit: {{{dto.UnitName}}}
|
|
|
-- TargetCountry: {{{dto.Country}}}
|
|
|
-- Objective: {{{objective}}}
|
|
|
+- OriginUnit: {{{entryInfo.OriginUnit}}}
|
|
|
+- TargetCountry: {{{entryInfo.TargetCountry}}}
|
|
|
+- Objective: {{{entryInfo.Objective}}}
|
|
|
- DataCount: {{{aiNeedCount}}}
|
|
|
-- OtherConstraints: {{{dto.Remark}}}
|
|
|
-
|
|
|
+- OtherConstraints: {{{entryInfo.OtherConstraints}}}
|
|
|
# [ROLE_DEFINITION]
|
|
|
你是一位精通全球跨境经贸、海外园区政策及 {{TargetCountry}} 本地准入法规的【顶级商务咨询顾问】。你擅长通过“产业链对等原则(Chain-Parity Principle)”为跨国企业精准匹配具备落地价值的合作伙伴。
|
|
|
|
|
|
@@ -2518,9 +2504,11 @@ Inner Join Sys_Department as d With(Nolock) On u.DepId=d.Id Where m.Id={0} ", _m
|
|
|
|
|
|
if (hunyuanAIInvDatas != null)
|
|
|
{
|
|
|
- hunyuanAIInvDatas = hunyuanAIInvDatas.Select(x => {
|
|
|
+ hunyuanAIInvDatas = hunyuanAIInvDatas.Select(x =>
|
|
|
+ {
|
|
|
+ x.Guid = Guid.NewGuid().ToString("N");
|
|
|
x.Source = 1;
|
|
|
- x.Region = dto.Country;
|
|
|
+ x.Region = entryInfo.TargetCountry;
|
|
|
x.Operator = operatorName;
|
|
|
x.OperatedAt = DateTime.Now;
|
|
|
return x;
|
|
|
@@ -2542,14 +2530,48 @@ Inner Join Sys_Department as d With(Nolock) On u.DepId=d.Id Where m.Id={0} ", _m
|
|
|
// 数据整合 (高效合并)
|
|
|
var finalResult = localInvDatas.Concat(hunyuanAIInvDatas).ToList();
|
|
|
|
|
|
- // 词条信息
|
|
|
- var entries = new List<EntryInfo>() { new EntryInfo() {
|
|
|
- Details = question,
|
|
|
- Operator = operatorName,
|
|
|
- OperatedAt = DateTime.Now
|
|
|
- } };
|
|
|
+ string invName = $"{entryInfo.OriginUnit}拜访{entryInfo.TargetCountry}";
|
|
|
+
|
|
|
+ invAiInfo.AiCrawledDetails = finalResult;
|
|
|
|
|
|
- string invName = $"{dto.UnitName}拜访{dto.Country}";
|
|
|
+ var update = await _sqlSugar.Updateable(invAiInfo).ExecuteCommandAsync();
|
|
|
+ if (update < 1)
|
|
|
+ {
|
|
|
+ return Ok(JsonView(false, $"数据更新失败!"));
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ return Ok(JsonView(true, $"查询成功!", new
|
|
|
+ {
|
|
|
+ invAiInfo.Id,
|
|
|
+ invAiInfo.InvName,
|
|
|
+ invAiInfo.GroupId,
|
|
|
+ invAiInfo.AiCrawledDetails,
|
|
|
+ Entry = invAiInfo.Entries
|
|
|
+ }));
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 商邀资料AI 设置词条
|
|
|
+ /// </summary>
|
|
|
+ /// <returns></returns>
|
|
|
+ [HttpPost]
|
|
|
+ [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
|
|
|
+ public async Task<IActionResult> InvitationAISetPrompt(InvitationAISetPromptDto dto)
|
|
|
+ {
|
|
|
+ // 基础校验
|
|
|
+ if (string.IsNullOrWhiteSpace(dto.OriginUnit) || string.IsNullOrWhiteSpace(dto.TargetCountry))
|
|
|
+ return Ok(JsonView(false, "请传入有效的单位名称和国家!"));
|
|
|
+
|
|
|
+ var groupInfo = await _sqlSugar.Queryable<Grp_DelegationInfo>()
|
|
|
+ .Where(x => x.IsDel == 0 && x.Id == dto.GroupId)
|
|
|
+ .Select(x => new { x.TeamName, x.VisitPurpose })
|
|
|
+ .FirstAsync();
|
|
|
+
|
|
|
+ string invName = $"{dto.OriginUnit}拜访{dto.TargetCountry}";
|
|
|
+
|
|
|
+ #region 数据库操作
|
|
|
|
|
|
// 数据库信息获取方式
|
|
|
// 1.通过团组 GroupId 关联查询
|
|
|
@@ -2558,6 +2580,21 @@ Inner Join Sys_Department as d With(Nolock) On u.DepId=d.Id Where m.Id={0} ", _m
|
|
|
// 2.通过InvName 关联查询(考虑到可能存在同名情况,优先使用 GroupId 关联查询,确保数据准确性)
|
|
|
dataInfo ??= await _sqlSugar.Queryable<Res_InvitationAI>().Where(x => x.IsDel == 0 && x.InvName == invName).FirstAsync();
|
|
|
|
|
|
+ #region 词条信息
|
|
|
+
|
|
|
+ string operatorName = await _sqlSugar.Queryable<Sys_Users>().Where(x => x.IsDel == 0 && x.Id == dto.CurrUserId).Select(x => x.CnName).FirstAsync() ?? "-";
|
|
|
+
|
|
|
+ var entryInfo = new EntryInfo()
|
|
|
+ {
|
|
|
+ OriginUnit = dto.OriginUnit,
|
|
|
+ TargetCountry = dto.TargetCountry,
|
|
|
+ Objective = groupInfo?.VisitPurpose ?? "商务考察与合作对接",
|
|
|
+ OtherConstraints = dto.OtherConstraints,
|
|
|
+ Operator = operatorName,
|
|
|
+ OperatedAt = DateTime.Now
|
|
|
+ };
|
|
|
+ #endregion
|
|
|
+
|
|
|
// 3.如果以上两种方式都没有查询到数据,则说明是新数据,需要添加到数据库
|
|
|
if (dataInfo == null)
|
|
|
{
|
|
|
@@ -2566,41 +2603,505 @@ 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,
|
|
|
- AiCrawledDetails = finalResult,
|
|
|
- Entries = entries,
|
|
|
+ Entries = entryInfo,
|
|
|
CreateUserId = dto.CurrUserId
|
|
|
};
|
|
|
|
|
|
var insert = await _sqlSugar.Insertable(dataInfo).ExecuteCommandAsync();
|
|
|
if (insert < 1)
|
|
|
{
|
|
|
- return Ok(JsonView(false, $"数据新增失败!"));
|
|
|
+ return Ok(JsonView(false, $"词条信息新增失败!"));
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- // 3.1 数据存在 则更新数据(覆盖原有的AI爬取详情,保留原有的其他字段不变)
|
|
|
- dataInfo.AiCrawledDetails = finalResult;
|
|
|
+ // 3.2 数据存在 则更新数据(覆盖原有的AI爬取详情,保留原有的其他字段不变)
|
|
|
+ dataInfo.Entries = entryInfo;
|
|
|
|
|
|
var update = await _sqlSugar.Updateable(dataInfo).ExecuteCommandAsync();
|
|
|
if (update < 1)
|
|
|
{
|
|
|
- return Ok(JsonView(false, $"数据更新失败!"));
|
|
|
+ return Ok(JsonView(false, $"词条信息更新失败!"));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
- return Ok(JsonView(true, $"查询成功!", new
|
|
|
+ return Ok(JsonView(true, $"设置成功!", new
|
|
|
+ {
|
|
|
+ dataInfo.Id,
|
|
|
+ dataInfo.InvName,
|
|
|
+ dataInfo.GroupId,
|
|
|
+ Entry = dataInfo.Entries,
|
|
|
+ }));
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 商邀资料AI 单条资料删除
|
|
|
+ /// </summary>
|
|
|
+ /// <returns></returns>
|
|
|
+ [HttpPost]
|
|
|
+ [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
|
|
|
+ public async Task<IActionResult> InvitationAISingleDel(InvitationAISingleDelDto dto)
|
|
|
+ {
|
|
|
+ int id = dto.Id;
|
|
|
+ string guid = dto.Guid;
|
|
|
+ // 基础校验
|
|
|
+ if (id < 1 || string.IsNullOrWhiteSpace(guid))
|
|
|
+ return Ok(JsonView(false, "请传入有效的Id和Guid!"));
|
|
|
+
|
|
|
+ var dataInfo = await _sqlSugar.Queryable<Res_InvitationAI>().Where(x => x.IsDel == 0 && x.Id == id).FirstAsync();
|
|
|
+
|
|
|
+ if (dataInfo == null)
|
|
|
+ return Ok(JsonView(false, "当前数据信息不存在!"));
|
|
|
+
|
|
|
+ var newDataInfos = dataInfo.AiCrawledDetails.Where(x => x.Guid != guid).ToList();
|
|
|
+ dataInfo.AiCrawledDetails = newDataInfos;
|
|
|
+
|
|
|
+ var update = await _sqlSugar.Updateable(dataInfo).ExecuteCommandAsync();
|
|
|
+ if (update < 1)
|
|
|
+ {
|
|
|
+ return Ok(JsonView(false, $"删除失败!"));
|
|
|
+ }
|
|
|
+
|
|
|
+ return Ok(JsonView(true, $"删除成功!", new
|
|
|
{
|
|
|
dataInfo.Id,
|
|
|
dataInfo.InvName,
|
|
|
dataInfo.GroupId,
|
|
|
- dataInfo.AiCrawledDetails,
|
|
|
- Entry = dataInfo.Entries.FirstOrDefault(),
|
|
|
+ dataInfo.AiCrawledDetails
|
|
|
}));
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// 商邀资料AI 混元AI查询资料
|
|
|
+ /// </summary>
|
|
|
+ /// <returns></returns>
|
|
|
+ [HttpPost]
|
|
|
+ [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
|
|
|
+ public async Task<IActionResult> InvitationAICompleteText(InvitationAICompleteTextDto dto)
|
|
|
+ {
|
|
|
+ var invAiInfo = await _sqlSugar.Queryable<Res_InvitationAI>().Where(x => x.Id == dto.Id).FirstAsync();
|
|
|
+
|
|
|
+ if (invAiInfo == null || invAiInfo?.Entries == null)
|
|
|
+ {
|
|
|
+ return Ok(JsonView(false, "请先设置关键字信息!"));
|
|
|
+ }
|
|
|
+
|
|
|
+ var entryInfo = invAiInfo.Entries;
|
|
|
+
|
|
|
+ // 已搜索出的数据(AI 数据源)
|
|
|
+ var hunyuanAIDatas = invAiInfo.AiCrawledDetails.Where(x => x.Source == 1).ToList() ?? new List<InvitationAIInfo>();
|
|
|
+
|
|
|
+ // 当前企业领域
|
|
|
+ string industryFocus = string.Empty;
|
|
|
+
|
|
|
+ // 动态计算 AI 需求缺口
|
|
|
+ int totalTarget = entryInfo.NeedCount;
|
|
|
+
|
|
|
+ // 词条
|
|
|
+ string question = string.Empty;
|
|
|
+
|
|
|
+ string operatorName = await _sqlSugar.Queryable<Sys_Users>().Where(x => x.IsDel == 0 && x.Id == dto.CurrUserId).Select(x => x.CnName).FirstAsync() ?? "-";
|
|
|
+
|
|
|
+ #region 混元AI 获取商邀资料
|
|
|
+
|
|
|
+ var hunyuanAIInvDatas = new List<InvitationAIInfo>();
|
|
|
+
|
|
|
+ int aiNeedCount = Math.Max(0, totalTarget - hunyuanAIDatas.Count);
|
|
|
+
|
|
|
+ // 已存在数据名称整理
|
|
|
+ var existingNames = new HashSet<string>(hunyuanAIDatas.Select(x => x.NameCn));
|
|
|
+ entryInfo.OtherConstraints = $"请基于以下已存在的名称列表进行推荐,避免重复:{string.Join(", ", existingNames)}。{entryInfo.OtherConstraints}";
|
|
|
+
|
|
|
+ if (aiNeedCount > 0)
|
|
|
+ {
|
|
|
+ question = @$"# [SYSTEM_CONTEXT]
|
|
|
+- Role: Senior Business Consultant (Global Supply Chain & Cross-border Trade Expert)
|
|
|
+- Framework: S.P.A.R. (Situation-Problem-Action-Result)
|
|
|
+- Target Architecture: .NET 6 DTO Compatible Interface
|
|
|
+
|
|
|
+# [INPUT_CONFIG]
|
|
|
+- OriginUnit: {{{entryInfo.OriginUnit}}}
|
|
|
+- TargetCountry: {{{entryInfo.TargetCountry}}}
|
|
|
+- Objective: {{{entryInfo.Objective}}}
|
|
|
+- DataCount: {{{aiNeedCount}}}
|
|
|
+- OtherConstraints: {{{entryInfo.OtherConstraints}}}
|
|
|
+# [ROLE_DEFINITION]
|
|
|
+你是一位精通全球跨境经贸、海外园区政策及 {{TargetCountry}} 本地准入法规的【顶级商务咨询顾问】。你擅长通过“产业链对等原则(Chain-Parity Principle)”为跨国企业精准匹配具备落地价值的合作伙伴。
|
|
|
+
|
|
|
+# [BUSINESS_LOGIC_CoT]
|
|
|
+在生成结果前,请严格执行以下思维链拆解:
|
|
|
+1. **EntityProfiling**: 识别 {{OriginUnit}} 的核心生态位(如:Supply/Demand/Capital/Logistics)。
|
|
|
+2. **ParityMatching**: 检索 {{TargetCountry}} 境内业务闭环对等机构(例如:若 Origin 为分销,则 Target 为源头工厂/种植园)。
|
|
|
+3. **RegulatoryCheck**: 验证目标机构的合规性(如:GACC 备案、SPS 协议、或当地政府特许经营权)。
|
|
|
+4. **DataSynthesis**: 针对无法直接获取的动态(如 PostUrl),优先检索其官网 News 频道或 LinkedIn 企业号。
|
|
|
+
|
|
|
+# [CONSTRAINTS_&_STANDARDS]
|
|
|
+- **NamingConvention**: 所有 JSON Key 必须严格遵循 **PascalCase**(例如:`UnitNameCn` 而非 `unit_name_cn`)。
|
|
|
+- **DataIntegrity**:
|
|
|
+ - 必须输出精确的 {{DataCount}} 条记录。
|
|
|
+ - 优先级:Core (核心机构) > Backup (关联替代机构)。
|
|
|
+- **ValidationRules**:
|
|
|
+ - `Phone`: 必须包含 {{TargetCountry}} 国际区号(如 +856, +66 等)。
|
|
|
+ - `IntgAdvice`: 必须包含“对等性分析”,解释该机构如何与 {{OriginUnit}} 形成业务闭环(50-100字)。
|
|
|
+ - `Status`: 若字段确实无法获取,统一填充 'N/A',禁止编造。
|
|
|
+- **Safety**: 确保推荐机构不涉及敏感黑名单或已破产企业。
|
|
|
+
|
|
|
+# [INFORMATION_SCHEMA]
|
|
|
+请将结果填充至以下结构的 JSON 数组中:
|
|
|
+- NameCn: 单位名称(中文)
|
|
|
+- NameEn: 单位名称(英文)
|
|
|
+- Address: 详细地理位置(含省市区街道)
|
|
|
+- Scope: 经营范围(需强调其出口配额、生产能力或行业地位)
|
|
|
+- Contact: 联系人姓名及职务
|
|
|
+- Phone: 拨打全号(含区号)
|
|
|
+- Email: 商务联络邮箱
|
|
|
+- SiteUrl: 官方网站或权威社媒主页
|
|
|
+- PostUrl: 近一年内的商务动态/新闻链接
|
|
|
+- RecLevel: 推荐等级(枚举值:Core, Backup)
|
|
|
+- IntgAdvice: 对接深度建议(基于产业链交合、互补逻辑)
|
|
|
+
|
|
|
+# [OUTPUT_PROTOCOL]
|
|
|
+- 仅输出一个标准的 JSON Array 字符串。
|
|
|
+- 严禁任何 Markdown 说明文字、代码块之外的解释或开场白。
|
|
|
+- 确保 JSON 语法在 .NET `JsonSerializer.Deserialize` 下可直接解析。
|
|
|
+
|
|
|
+# [EXECUTION]
|
|
|
+根据以上配置,开始生成。";
|
|
|
+
|
|
|
+ string response = string.Empty;
|
|
|
+ try
|
|
|
+ {
|
|
|
+ response = await _hunyuanService.ChatCompletionsHunyuan_t1_latestAsync(question);
|
|
|
+ }
|
|
|
+ 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}"));
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 标识
|
|
|
+ hunyuanAIInvDatas = JsonConvert.DeserializeObject<List<InvitationAIInfo>>(cleanJson);
|
|
|
+
|
|
|
+ if (hunyuanAIInvDatas != null)
|
|
|
+ {
|
|
|
+ hunyuanAIInvDatas = hunyuanAIInvDatas.Select(x =>
|
|
|
+ {
|
|
|
+ x.Guid = Guid.NewGuid().ToString("N");
|
|
|
+ x.Source = 1;
|
|
|
+ x.Region = entryInfo.TargetCountry;
|
|
|
+ x.Operator = operatorName;
|
|
|
+ x.OperatedAt = DateTime.Now;
|
|
|
+ return x;
|
|
|
+ }).ToList();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (JsonException ex)
|
|
|
+ {
|
|
|
+ // 记录日志并考虑 fallback 策略
|
|
|
+ _logger.LogError(ex, "Hunyuan AI 响应解析失败。原始数据:{Response}", response);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ #region 数据库操作
|
|
|
+ invAiInfo.AiCrawledDetails.AddRange(hunyuanAIInvDatas);
|
|
|
+
|
|
|
+ var update = await _sqlSugar.Updateable(invAiInfo).ExecuteCommandAsync();
|
|
|
+ if (update < 1)
|
|
|
+ {
|
|
|
+ return Ok(JsonView(false, $"AI续写,数据更新失败!"));
|
|
|
+ }
|
|
|
+
|
|
|
+ return Ok(JsonView(true, $"AI续写成功!操作数据{hunyuanAIInvDatas.Count}条"));
|
|
|
+
|
|
|
+ #endregion
|
|
|
+ }
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ return Ok(JsonView(false, $"AI续写失败!"));
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 商邀资料AI 文件生成(基于混元AI已爬取数据进行格式化输出,供用户下载使用)
|
|
|
+ /// </summary>
|
|
|
+ /// <returns></returns>
|
|
|
+ [HttpGet("{id}")]
|
|
|
+ [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
|
|
|
+ public async Task<IActionResult> InvitationAIFileGenerator(int id)
|
|
|
+ {
|
|
|
+ var invAiInfo = await _sqlSugar.Queryable<Res_InvitationAI>()
|
|
|
+ .Where(x => x.Id == id)
|
|
|
+ .FirstAsync();
|
|
|
+
|
|
|
+ var dataList = invAiInfo?.AiCrawledDetails;
|
|
|
+
|
|
|
+ if (dataList == null)
|
|
|
+ return Ok(JsonView(false, "数据为空"));
|
|
|
+
|
|
|
+ Document doc = new Document();
|
|
|
+ DocumentBuilder builder = new DocumentBuilder(doc);
|
|
|
+
|
|
|
+ // A4页面设置
|
|
|
+ double usableWidth = 495;
|
|
|
+
|
|
|
+ foreach (Section sec in doc.Sections)
|
|
|
+ {
|
|
|
+ sec.PageSetup.PaperSize = Aspose.Words.PaperSize.A4;
|
|
|
+ sec.PageSetup.LeftMargin = 50;
|
|
|
+ sec.PageSetup.RightMargin = 50;
|
|
|
+ sec.PageSetup.TopMargin = 50;
|
|
|
+ sec.PageSetup.BottomMargin = 50;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 卡片背景
|
|
|
+ Color cardBg = Color.FromArgb(252, 252, 252);
|
|
|
+
|
|
|
+ string fontName = "微软雅黑";
|
|
|
+
|
|
|
+ int count = 1;
|
|
|
+
|
|
|
+ foreach (var item in dataList)
|
|
|
+ {
|
|
|
+ // 标题
|
|
|
+ builder.ParagraphFormat.ClearFormatting();
|
|
|
+ builder.Font.ClearFormatting();
|
|
|
+
|
|
|
+ builder.Font.Name = fontName;
|
|
|
+ builder.Font.Size = 16;
|
|
|
+ builder.Font.Bold = true;
|
|
|
+ builder.Font.Color = Color.Black;
|
|
|
+
|
|
|
+ builder.Write($"{count++}. {item.NameCn}");
|
|
|
+ builder.InsertParagraph();
|
|
|
+
|
|
|
+ // 英文名
|
|
|
+ builder.Font.Size = 10;
|
|
|
+ builder.Font.Bold = false;
|
|
|
+ builder.Font.Color = Color.FromArgb(120, 120, 120);
|
|
|
+ builder.Write(item.NameEn ?? "");
|
|
|
+
|
|
|
+ builder.InsertParagraph();
|
|
|
+ builder.InsertParagraph();
|
|
|
+
|
|
|
+ // ====== 卡片表格 ======
|
|
|
+ Aspose.Words.Tables.Table table = builder.StartTable();
|
|
|
+
|
|
|
+ builder.CellFormat.Shading.BackgroundPatternColor = cardBg;
|
|
|
+ builder.CellFormat.VerticalAlignment = CellVerticalAlignment.Center;
|
|
|
+ builder.CellFormat.WrapText = true;
|
|
|
+
|
|
|
+ builder.CellFormat.LeftPadding = 12;
|
|
|
+ builder.CellFormat.RightPadding = 12;
|
|
|
+ builder.CellFormat.TopPadding = 8;
|
|
|
+ builder.CellFormat.BottomPadding = 8;
|
|
|
+
|
|
|
+ double labelW = 120;
|
|
|
+
|
|
|
+ AddSingleRow(builder, fontName, labelW, usableWidth - labelW, "详细地址", item.Address);
|
|
|
+
|
|
|
+ AddSingleRow(builder, fontName, labelW, usableWidth - labelW, "联系人", item.Contact);
|
|
|
+
|
|
|
+ AddSingleRow(builder, fontName, labelW, usableWidth - labelW, "联系电话", item.Phone);
|
|
|
+
|
|
|
+ AddSingleRow(builder, fontName, labelW, usableWidth - labelW, "电子邮箱", item.Email);
|
|
|
+
|
|
|
+ AddSingleLinkRow(builder, fontName, labelW, usableWidth - labelW, "官方网站", item.SiteUrl);
|
|
|
+
|
|
|
+ AddSingleLinkRow(builder, fontName, labelW, usableWidth - labelW, "最新动态", item.PostUrl);
|
|
|
+
|
|
|
+ AddFullWidthRow(builder, fontName, labelW, usableWidth - labelW, "推荐等级", item.RecLevel);
|
|
|
+
|
|
|
+ AddFullWidthRow(builder, fontName, labelW, usableWidth - labelW, "经营范围", item.Scope);
|
|
|
+
|
|
|
+ AddFullWidthRow(builder, fontName, labelW, usableWidth - labelW, "对接建议", item.IntgAdvice);
|
|
|
+
|
|
|
+ // 表格样式
|
|
|
+ table.SetBorders(Aspose.Words.LineStyle.Single, 0.5, Color.FromArgb(230, 230, 230));
|
|
|
+ table.AutoFit(AutoFitBehavior.FixedColumnWidths);
|
|
|
+ table.AllowAutoFit = false;
|
|
|
+ table.PreferredWidth = PreferredWidth.FromPoints(usableWidth);
|
|
|
+
|
|
|
+ builder.EndTable();
|
|
|
+
|
|
|
+ builder.InsertParagraph();
|
|
|
+ builder.InsertParagraph();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存逻辑
|
|
|
+ string fileName = $"{invAiInfo.InvName}_Professional.docx";
|
|
|
+ string filePath = Path.Combine(AppSettingsHelper.Get("InvitationAIAssistBasePath"), fileName);
|
|
|
+ doc.Save(filePath, Aspose.Words.SaveFormat.Docx);
|
|
|
+
|
|
|
+ return Ok(JsonView(true, "生成成功", new { Url = $"{AppSettingsHelper.Get("WordBaseUrl")}/{AppSettingsHelper.Get("InvitationAIAssistFtpPath")}/{fileName}" }));
|
|
|
+ }
|
|
|
+
|
|
|
+ #region 15.12 适配助手方法
|
|
|
+
|
|
|
+ private void AddSingleRow(DocumentBuilder builder, string font, double labelW, double contentW, string label, string value)
|
|
|
+ {
|
|
|
+ // 标签
|
|
|
+ builder.InsertCell();
|
|
|
+ builder.CellFormat.Width = labelW;
|
|
|
+
|
|
|
+ builder.ParagraphFormat.Alignment = ParagraphAlignment.Right;
|
|
|
+
|
|
|
+ builder.Font.Name = font;
|
|
|
+ builder.Font.Size = 9;
|
|
|
+ builder.Font.Bold = false;
|
|
|
+ builder.Font.Color = Color.FromArgb(120, 120, 120);
|
|
|
+
|
|
|
+ builder.Write(label);
|
|
|
+
|
|
|
+ // 内容
|
|
|
+ builder.InsertCell();
|
|
|
+ builder.CellFormat.Width = contentW;
|
|
|
+ builder.CellFormat.HorizontalMerge = CellMerge.First;
|
|
|
+
|
|
|
+ builder.ParagraphFormat.Alignment = ParagraphAlignment.Left;
|
|
|
+
|
|
|
+ builder.Font.Size = 10;
|
|
|
+ builder.Font.Bold = false;
|
|
|
+ builder.Font.Color = Color.FromArgb(60, 60, 60);
|
|
|
+
|
|
|
+ builder.Write(string.IsNullOrEmpty(value) ? "—" : value);
|
|
|
+
|
|
|
+ // 合并剩余列
|
|
|
+ builder.InsertCell();
|
|
|
+ builder.CellFormat.HorizontalMerge = CellMerge.Previous;
|
|
|
+
|
|
|
+ builder.InsertCell();
|
|
|
+ builder.CellFormat.HorizontalMerge = CellMerge.Previous;
|
|
|
+
|
|
|
+ builder.EndRow();
|
|
|
+
|
|
|
+ builder.CellFormat.HorizontalMerge = CellMerge.None;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void AddSingleLinkRow(DocumentBuilder builder, string font, double labelW, double contentW, string label, string url)
|
|
|
+ {
|
|
|
+ builder.InsertCell();
|
|
|
+ builder.CellFormat.Width = labelW;
|
|
|
+
|
|
|
+ builder.ParagraphFormat.Alignment = ParagraphAlignment.Right;
|
|
|
+
|
|
|
+ builder.Font.Size = 9;
|
|
|
+ builder.Font.Bold = false;
|
|
|
+ builder.Font.Color = Color.FromArgb(120, 120, 120);
|
|
|
+
|
|
|
+ builder.Write(label);
|
|
|
+
|
|
|
+ builder.InsertCell();
|
|
|
+ builder.CellFormat.Width = contentW;
|
|
|
+ builder.CellFormat.HorizontalMerge = CellMerge.First;
|
|
|
+
|
|
|
+ builder.ParagraphFormat.Alignment = ParagraphAlignment.Left;
|
|
|
+
|
|
|
+ if (!string.IsNullOrEmpty(url) && url.StartsWith("http"))
|
|
|
+ {
|
|
|
+ builder.Font.Size = 10;
|
|
|
+ builder.Font.Color = Color.FromArgb(0, 102, 204);
|
|
|
+ builder.Font.Underline = Underline.None;
|
|
|
+
|
|
|
+ builder.InsertHyperlink(url, url, false);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ builder.Font.Color = Color.FromArgb(60, 60, 60);
|
|
|
+ builder.Write("—");
|
|
|
+ }
|
|
|
+
|
|
|
+ builder.InsertCell();
|
|
|
+ builder.CellFormat.HorizontalMerge = CellMerge.Previous;
|
|
|
+
|
|
|
+ builder.InsertCell();
|
|
|
+ builder.CellFormat.HorizontalMerge = CellMerge.Previous;
|
|
|
+
|
|
|
+ builder.EndRow();
|
|
|
+
|
|
|
+ builder.CellFormat.HorizontalMerge = CellMerge.None;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void AddFullWidthRow(DocumentBuilder builder, string font, double lw, double cw, string label, string value)
|
|
|
+ {
|
|
|
+ // 标签
|
|
|
+ builder.InsertCell();
|
|
|
+ builder.CellFormat.Width = lw;
|
|
|
+
|
|
|
+ builder.ParagraphFormat.Alignment = ParagraphAlignment.Right;
|
|
|
+
|
|
|
+ builder.Font.Size = 9;
|
|
|
+ builder.Font.Bold = false;
|
|
|
+ builder.Font.Color = Color.FromArgb(120, 120, 120);
|
|
|
+
|
|
|
+ builder.Write(label);
|
|
|
+
|
|
|
+ // 内容
|
|
|
+ builder.InsertCell();
|
|
|
+ builder.CellFormat.Width = cw;
|
|
|
+ builder.CellFormat.HorizontalMerge = CellMerge.First;
|
|
|
+
|
|
|
+ builder.ParagraphFormat.Alignment = ParagraphAlignment.Left;
|
|
|
+
|
|
|
+ builder.Font.Size = 10;
|
|
|
+ builder.Font.Bold = false;
|
|
|
+ builder.Font.Color = Color.FromArgb(60, 60, 60);
|
|
|
+
|
|
|
+ builder.Write(string.IsNullOrEmpty(value) ? "—" : value);
|
|
|
+
|
|
|
+ builder.InsertCell();
|
|
|
+ builder.CellFormat.HorizontalMerge = CellMerge.Previous;
|
|
|
+
|
|
|
+ builder.InsertCell();
|
|
|
+ builder.CellFormat.HorizontalMerge = CellMerge.Previous;
|
|
|
+
|
|
|
+ builder.EndRow();
|
|
|
+
|
|
|
+ builder.CellFormat.HorizontalMerge = CellMerge.None;
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
#endregion
|
|
|
|
|
|
#region 公务出访
|
|
|
@@ -3684,7 +4185,7 @@ Inner Join Sys_Department as d With(Nolock) On u.DepId=d.Id Where m.Id={0} ", _m
|
|
|
b.CreateTime
|
|
|
})
|
|
|
.ToList()
|
|
|
- .Where(x => !x.Client.IsNullOrWhiteSpace())
|
|
|
+ .Where(x => !string.IsNullOrWhiteSpace(x.Client))
|
|
|
.ToList();
|
|
|
|
|
|
if (Query_DB.Count > 0)
|