Explorar o código

Merge branch 'develop' of http://132.232.92.186:3000/yuanrf/OA2023 into develop

yuanrf hai 2 semanas
pai
achega
90a6ddc066

+ 70 - 69
OASystem/EntitySync/Program.cs

@@ -94,8 +94,8 @@ db.CodeFirst.SetStringDefaultLength(50).BackupTable().InitTables(new Type[]
     //typeof(Bus_ConfItemListInfo)
     //typeof(Res_CountryFeeCost)
     //typeof(Res_MemoInfo),
-    //typeof(Fin_DailyFeePayment),       // 财务 - 日付申请
-    //typeof(Fin_DailyFeePaymentContent),// 财务 - 日付申请详细类
+    //typeof(Fin_DailyFeePayment),       // 财务-日付申请
+    //typeof(Fin_DailyFeePaymentContent),// 财务-日付申请详细类
     //typeof(Grp_GroupsTaskAssignment),
     //typeof(Grp_AirTicketReservations),
     //typeof(Grp_GroupCostParameter),
@@ -103,87 +103,88 @@ db.CodeFirst.SetStringDefaultLength(50).BackupTable().InitTables(new Type[]
     //typeof(Fin_ForeignReceivables),    // 对外收款账单
     //typeof(Fin_ProceedsReceived),      // 已收款项
     //typeof(Fin_OtherPrice),            // 其他款项
-    //typeof(Res_OfficialActivities)     //公务出访
-    //typeof(Res_AskData),//请示数据库
+    //typeof(Res_OfficialActivities)     // 公务出访
+    //typeof(Res_AskData),// 请示数据库
     //typeof(Grp_DelegationEnData),
     //typeof(Grp_EnterExitCost),
     //typeof(Grp_DayAndCost), 
     //typeof(Grp_NationalTravelFee),
     //typeof(Air_TicketBlackCode),
-    //typeof(Pm_WageSheet)       //人事模块 工资表单
+    //typeof(Pm_WageSheet)       // 人事模块 工资表单
     //typeof(Grp_VisaProgressCustomer),
     //typeof(Grp_VisaProgressCustomerPicture)
-    //typeof(Grp_CheckBoxs)  //成本选中
-    //typeof(Grp_GroupCost) //成本数据信息
-    //typeof(Grp_CostTypeHotelNumber)//分段房间数量
+    //typeof(Grp_CheckBoxs) // 成本选中
+    //typeof(Grp_GroupCost) // 成本数据信息
+    //typeof(Grp_CostTypeHotelNumber)// 分段房间数量
     //typeof(Grp_VisaInfo)
     //typeof(Grp_CarTouristGuideGroundReservations),
     //typeof(Grp_CarTouristGuideGroundReservationsContent),//
     //typeof(Fin_PaymentRefundAndOtherMoney),
     //typeof(Grp_InsuranceCost),
-    //typeof(Grp_TourClientList), //接团客户名单
-    //typeof(Dis_Intercontinental), //洲
-    //typeof(Dis_Country), //国家
-    //typeof(Dis_City), //城市 
-    //typeof(Fin_GroupExtraCost), //超支费用 
-    //typeof(Pm_TaskAllocation), //任务分配 
-    //typeof(Pm_TaskRelevanceUser) //任务分配关联人员(完成者) 
-    //typeof(Pm_TaskJobRelevancy), //任务分配关联人员(完成者) 
-    //typeof(Crm_TableOperationRecord), //表操作记录 
-    //typeof(Grp_InvertedList), //倒推表 
-    //typeof(Grp_InvertedListVisaCountry), //倒推表签证国家 
-    //typeof(Grp_VisaFeeInfo),  //签证费用info 
-    //typeof(Grp_HotelInquiry),  //酒店询价 
-    //typeof(Grp_HotelReservationsContent),  //酒店费用子表
-    //typeof(Grp_VisitingClients),  //拜访团组客户
-    //typeof(Grp_ApprovalTravelDetails),  //公务出访表
-    //typeof(Sys_Countries),  //洲
-    //typeof(Sys_Continent),  //国家
-    //typeof(Sys_Cities),  //城市
-    //typeof(Grp_GroupModelFile),//团组文件 
-    //typeof(Grp_VisaCommission),//签证费用录入 
-    //typeof(Pm_GoodsReceive),//物品领用表 
-    //typeof(Pm_GoodsStorage),//物品入库表 
-    //typeof(Pm_GoodsInfo),//物品详细表 
-    //typeof(Grp_Opinionaire),//团组接待意见调查 
-    //typeof(Sys_ExchangeRateRecord),//团组接待意见调查 
-    //typeof(Res_TranslatorLibrary),//翻译人员库 
-    //typeof(Grp_OfficialDutyLinkTranslator),//公务信息关联翻译人员 
+    //typeof(Grp_TourClientList), // 接团客户名单
+    //typeof(Dis_Intercontinental), // 
+    //typeof(Dis_Country),  // 国家
+    //typeof(Dis_City),     // 城市 
+    //typeof(Fin_GroupExtraCost), // 超支费用 
+    //typeof(Pm_TaskAllocation),  // 任务分配 
+    //typeof(Pm_TaskRelevanceUser) // 任务分配关联人员(完成者) 
+    //typeof(Pm_TaskJobRelevancy), // 任务分配关联人员(完成者) 
+    //typeof(Crm_TableOperationRecord), // 表操作记录 
+    //typeof(Grp_InvertedList), // 倒推表 
+    //typeof(Grp_InvertedListVisaCountry), // 倒推表签证国家 
+    //typeof(Grp_VisaFeeInfo),   // 签证费用info 
+    //typeof(Grp_HotelInquiry),  // 酒店询价 
+    //typeof(Grp_HotelReservationsContent),  // 酒店费用子表
+    //typeof(Grp_VisitingClients),  // 拜访团组客户
+    //typeof(Grp_ApprovalTravelDetails),  // 公务出访表
+    //typeof(Sys_Countries),  // 
+    //typeof(Sys_Continent),  // 国家
+    //typeof(Sys_Cities),     // 城市
+    //typeof(Grp_GroupModelFile),// 团组文件 
+    //typeof(Grp_VisaCommission),// 签证费用录入 
+    //typeof(Pm_GoodsReceive),// 物品领用表 
+    //typeof(Pm_GoodsStorage),// 物品入库表 
+    //typeof(Pm_GoodsInfo),// 物品详细表 
+    //typeof(Grp_Opinionaire),// 团组接待意见调查 
+    //typeof(Sys_ExchangeRateRecord),// 团组接待意见调查 
+    //typeof(Res_TranslatorLibrary),// 翻译人员库 
+    //typeof(Grp_OfficialDutyLinkTranslator),// 公务信息关联翻译人员 
     //typeof(Sys_DeviceToken),
-    //typeof(Res_MediaSuppliers),//策划部供应商资料 
-    //typeof(Res_BasicInsuranceCost)//保险国家基础费用 
-    //typeof(Grp_EnterExitCostPermission),//团组 - 出入境费用 --> 操作权限
-    //typeof(Grp_EnterExitCostDraftPermission)//团组 - 出入境费用 --> 操作权限-草稿
+    //typeof(Res_MediaSuppliers),// 策划部供应商资料 
+    //typeof(Res_BasicInsuranceCost)// 保险国家基础费用 
+    //typeof(Grp_EnterExitCostPermission),// 团组-出入境费用 --> 操作权限
+    //typeof(Grp_EnterExitCostDraftPermission)// 团组-出入境费用 --> 操作权限-草稿
     //typeof(Grp_RestaurantInfo),
-    //ypeof(Grp_EnterExitCostQuote), //团组 - 出入境费用报价表
-    //ypeof(Grp_EnterExitCostQuoteItem), //团组 - 出入境费用报价表
-    //typeof(Sys_FormTemp), //表单模板
-    //typeof(Grp_OrderPreInfo), //团组下单前信息
-    //typeof(Grp_OrderPreItem), //团组下单前信息Item
-    //typeof(Grp_GroupShareFile),//团组共享文件 
-    //typeof(Pm_GoodsAudit),//物品确认审核表
-    //typeof(Pm_GoodsReceiveDetails),//物品领用明细表 
+    //ypeof(Grp_EnterExitCostQuote), // 团组 - 出入境费用报价表
+    //ypeof(Grp_EnterExitCostQuoteItem), // 团组 - 出入境费用报价表
+    //typeof(Sys_FormTemp), // 表单模板
+    //typeof(Grp_OrderPreInfo), // 团组下单前信息
+    //typeof(Grp_OrderPreItem), // 团组下单前信息Item
+    //typeof(Grp_GroupShareFile),// 团组共享文件 
+    //typeof(Pm_GoodsAudit),// 物品确认审核表
+    //typeof(Pm_GoodsReceiveDetails),// 物品领用明细表 
     /*审核流程相关表*/
-    //typeof(Sys_AuditFlow),//审核流程表 
-    //typeof(Sys_AuditRecord),//审核记录表 
-    //typeof(Sys_AuditTemplate),//审核模板表 
-    //typeof(Sys_AuditTemplateNode),//审核模板节点表 
-    //typeof(Sys_AuditTemplateNodeUser),//审核模板节点人员表 
-    //typeof(Fin_ApplicationLinkGoods),//日服申请单物品关联表 
-    //typeof(Grp_GamesBudgetMaster‌),//世运会成本预算明细 
-    //typeof(Res_VisaFeeStandard),//签证费用标准 
-    //typeof(Res_VisaFeeStandardDetails),//签证费用标准详情 
-    //typeof(Grp_ProcessOverview),//团组流程总览表
-    //typeof(Grp_ProcessNode),//流程节点
-    //typeof(Grp_VisaProcessSteps_Log),//流程节点 
-    //typeof(Grp_ProcessLog),//流程节点 
-    //typeof(Grp_FeeEntryAcknowledge),//团组费用录入通知记录表 
-    //typeof(Res_OverseaVehicle),//境外用车信息 
-    //typeof(Res_OverseaVehicleTypePrice),//境外用车类型价格信息   
-    //typeof(Pm_CompanyDailyKpi),//财务部绩效考核    
-    //typeof(Grp_ConfProcessOverview),//会务流程总览    
-    //typeof(Grp_ConfProcessNode),//会务流程节点     
-    //typeof(Grp_ConfProcessLog),//团组会务流程总览操作日志  
-    typeof(Res_InvitationAI),//团组会务流程总览操作日志  
+    //typeof(Sys_AuditFlow),// 审核流程表 
+    //typeof(Sys_AuditRecord),// 审核记录表 
+    //typeof(Sys_AuditTemplate),// 审核模板表 
+    //typeof(Sys_AuditTemplateNode),// 审核模板节点表 
+    //typeof(Sys_AuditTemplateNodeUser),// 审核模板节点人员表 
+    //typeof(Fin_ApplicationLinkGoods),// 日服申请单物品关联表 
+    //typeof(Grp_GamesBudgetMaster‌),// 世运会成本预算明细 
+    //typeof(Res_VisaFeeStandard),// 签证费用标准 
+    //typeof(Res_VisaFeeStandardDetails),// 签证费用标准详情 
+    //typeof(Grp_ProcessOverview),// 团组流程总览表
+    //typeof(Grp_ProcessNode),// 流程节点
+    //typeof(Grp_VisaProcessSteps_Log),// 流程节点 
+    //typeof(Grp_ProcessLog),// 流程节点 
+    //typeof(Grp_FeeEntryAcknowledge),// 团组费用录入通知记录表 
+    //typeof(Res_OverseaVehicle),// 境外用车信息 
+    //typeof(Res_OverseaVehicleTypePrice),// 境外用车类型价格信息   
+    //typeof(Pm_CompanyDailyKpi),// 财务部绩效考核    
+    //typeof(Grp_ConfProcessOverview),// 会务流程总览    
+    //typeof(Grp_ConfProcessNode),// 会务流程节点     
+    //typeof(Grp_ConfProcessLog),// 团组会务流程总览操作日志  
+    //typeof(Res_InvitationAI),// 商邀公务AI  
+    typeof(Res_InvitationAI_NoGroup),// 商邀公务AI-无团组版  
 });
 Console.WriteLine("数据库结构同步完成!");

+ 1 - 0
OASystem/OASystem.Api/Controllers/MarketCustomerResourcesController.cs

@@ -274,6 +274,7 @@ namespace OASystem.API.Controllers
                 {
                     return Ok(JsonView(false, result.Msg));
                 }
+
                 #region 客户资料操作记录
                 //OperationEnum operationEnum = OperationEnum.NoOperation;
                 //if (dto.Status == 1)

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1806 - 1457
OASystem/OASystem.Api/Controllers/ResourceController.cs


+ 503 - 176
OASystem/OASystem.Api/OAMethodLib/GeneralMethod.cs

@@ -1,11 +1,10 @@
-
-using Aspose.Cells;
+using Aspose.Cells;
 using Aspose.Words;
 using Aspose.Words.Layout;
 using Aspose.Words.Saving;
 using Aspose.Words.Tables;
-using Humanizer;
 using Microsoft.AspNetCore.SignalR;
+using Microsoft.Graph.Models;
 using Microsoft.International.Converters.PinYinConverter;
 using NodaTime;
 using OASystem.API.OAMethodLib.File;
@@ -26,8 +25,6 @@ using OASystem.Domain.ViewModels.JuHeExchangeRate;
 using OASystem.Domain.ViewModels.PersonnelModule;
 using OASystem.Infrastructure.Repositories.CRM;
 using OASystem.Infrastructure.Repositories.Groups;
-using System.ComponentModel;
-using System.ComponentModel.DataAnnotations;
 using System.Data;
 using System.IdentityModel.Tokens.Jwt;
 using System.Security.Claims;
@@ -1335,7 +1332,477 @@ namespace OASystem.API.OAMethodLib
         }
         #endregion
 
-        #region 商邀AI invName 整合
+        #region 公务 AI
+
+        #region 提示词构建
+
+        /// <summary>
+        /// 构建行业匹配提示词(含预筛选逻辑)
+        /// <param name="entryInfo">【必填】基础配置信息</param>
+        /// <param name="localData">【数据源】来自 SqlSugar 查询的本地候选数据集,需包含 Region 和 NameCn 字段</param>
+        /// <returns>返回经过结构化处理的 Markdown 格式 Prompt 字符串</returns>
+        /// </summary>
+        public static string BuildIndustryPrompt(EntryInfo entryInfo, IEnumerable<InvitationAIInfo> localData)
+        {
+            // 业务逻辑预处理:仅保留目标国家数据,大幅节省 Token 成本
+            var filteredData = localData
+                .Where(x => entryInfo.TargetCountry.Contains(x.Region))
+                .Select(x => new { x.Region, x.NameCn }) // 仅提取 AI 需要的字段
+                .Distinct()
+                .ToList();
+
+            // 序列化数据
+            var industryStandard = IndustryTree.Build().Select(x => x.NameCn).ToList();
+            string industryStandardStr = string.Join("、", industryStandard);
+            string countriesStr = string.Join(", ", entryInfo.TargetCountry);
+            string jsonData = JsonConvert.SerializeObject(filteredData);
+            string sourceUnit = entryInfo.OriginUnit ?? "未知单位";
+            string sourceIndustry = string.Join(", ", entryInfo.Industries);
+
+            // 将提示词模板定义为常量,方便维护和查阅
+            string promptTemplate = @"
+# Role
+你是一位精通全球产业结构与大数据清洗的【智能匹配专家】。
+
+# Standard Industry Categories (Dynamic)
+请将目标单位严格归类为以下指定的行业类别:
+{0}
+*注意:若无法完全匹配,请归类至语义最接近的一项。*
+
+# Situation (Dynamic Input)
+- **出访单位**:{1}
+- **其所属行业**:{2}
+- **目标出访国家**:{3}
+
+# Task
+分析【本地待匹配数据集】,执行以下逻辑:
+1. **实体一致性 (Critical)**:返回结果中的 TargetUnitName 必须与输入数据集中的 NameCn 完全一致,严禁修改。
+2. **语义归类**:分析业务关键词,并映射至上述标准行业分类。
+3. **关联度计算**:计算与出访单位的业务契合度 (ConfidenceScore: 0.0-1.0)。
+
+# Rules
+- **输出格式**:仅返回纯 JSON 数组,严禁任何解释性文字。
+- **命名规范**:JSON 键名严格使用 PascalCase。
+
+# Data Source (JSON Array)
+{4}
+
+# Output Format (Required JSON)
+[
+  {{
+    ""SourceUnitName"": ""{1}"",
+    ""TargetUnitName"": ""必须与输入数据原样一致"",
+    ""TargetCountry"": ""所在国家"",
+    ""MatchedIndustry"": ""必须匹配标准行业分类中的原词"",
+    ""ConfidenceScore"": 0.00,
+    ""MatchReason"": ""匹配理由""
+  }}
+]
+";
+            // 填充模板并返回
+            return string.Format(
+                promptTemplate,
+                industryStandardStr, // {0}
+                sourceUnit,          // {1}
+                sourceIndustry,      // {2}
+                countriesStr,        // {3}
+                jsonData             // {4}
+            );
+        }
+
+        #region 国家配额计算(根据 行业、规模)
+
+        public class TaskCoverageReport
+        {
+            public bool IsValid { get; set; }
+
+            public List<string> MissingRegions { get; set; } = new();
+            public List<string> MissingIndustries { get; set; } = new();
+            public List<string> MissingScales { get; set; } = new();
+
+            public string Summary =>
+                $"Valid={IsValid}, " +
+                $"MissingRegions={string.Join(",", MissingRegions)}, " +
+                $"MissingIndustries={string.Join(",", MissingIndustries)}, " +
+                $"MissingScales={string.Join(",", MissingScales)}";
+        }
+
+        public static class CountryToRegionMapper
+        {
+            /// <summary>
+            /// 直接返回中文国家名作为 Region
+            /// </summary>
+            public static string GetRegion(string country)
+            {
+                if (string.IsNullOrWhiteSpace(country))
+                    return "Other";
+
+                return country;
+            }
+        }
+
+        public record IndustryNode(
+            string Code,
+            string NameCn,
+            string NameEn,
+            double Weight = 1.0, // 1.0 代表主行业
+            List<IndustryNode>? Children = null
+        );
+
+        /// <summary>
+        ///  行业结构
+        /// </summary>
+        public static class IndustryTree
+        {
+            public static List<IndustryNode> Build() => new()
+            {
+                // 1. 工业与制造业
+                new("IND_MANU", "工业与制造业领域", "Industry & Manufacturing", 1.0,
+                    Children: new()
+                    {
+                        new("MANU_MACH", "机械制造", "Machinery Manufacturing", 0.9),
+                        new("MANU_AUTO", "汽车制造", "Automotive Manufacturing", 0.9,
+                            Children: new()
+                            {
+                                new("AUTO_NEV", "新能源汽车", "New Energy Vehicles", 0.9),
+                                new("AUTO_AD", "自动驾驶", "Autonomous Driving", 0.85),
+                                new("AUTO_PARTS", "汽车零部件", "Auto Parts", 0.7)
+                            }),
+                        new("MANU_SMART", "智能制造", "Smart Manufacturing", 0.85)
+                    }),
+
+                // 2. 能源与资源
+                new("ENERGY_RES", "能源与资源领域", "Energy & Resources", 1.0,
+                    Children: new()
+                    {
+                        new("ENERGY_NE", "新能源", "New Energy", 0.9),
+                        new("ENERGY_STORAGE", "储能技术", "Energy Storage", 0.85),
+                        new("ENERGY_GRID", "智慧电网", "Smart Grid", 0.8)
+                    }),
+
+                // 3. 交通运输与物流
+                new("TRANS_LOG", "交通运输与物流领域", "Transportation & Logistics", 1.0,
+                    Children: new()
+                    {
+                        new("LOG_SMART", "智慧物流", "Smart Logistics", 0.9),
+                        new("TRANS_PORT", "港口自动化", "Port Automation", 0.85),
+                        new("LOG_COLD", "冷链物流", "Cold Chain Logistics", 0.8)
+                    }),
+
+                // 4. 城乡建设与规划
+                new("URBAN_RURAL", "城乡建设与规划领域", "Urban & Rural Construction", 1.0,
+                    Children: new()
+                    {
+                        new("URBAN_SMART", "智慧城市", "Smart City", 0.9),
+                        new("URBAN_GREEN", "绿色建筑", "Green Building", 0.85),
+                        new("URBAN_UPDATE", "城市更新", "Urban Renewal", 0.8)
+                    }),
+
+                // 5. 生态环境与水务
+                new("ECO_ENV", "生态环境与水务领域", "Ecology & Water Affairs", 1.0,
+                    Children: new()
+                    {
+                        new("ENV_WATER", "水处理", "Water Treatment", 0.9),
+                        new("ENV_SOLID", "固废处理", "Solid Waste Treatment", 0.85),
+                        new("ENV_MONITOR", "环境监测", "Environmental Monitoring", 0.8)
+                    }),
+
+                // 6. 农业与食品
+                new("AGRI_FOOD", "农业与食品领域", "Agriculture & Food", 1.0,
+                    Children: new()
+                    {
+                        new("AGRI_SMART", "智慧农业", "Smart Agriculture", 0.9),
+                        new("FOOD_PROC", "农产品加工", "Food Processing", 0.85),
+                        new("AGRI_SEED", "种业技术", "Seed Technology", 0.8)
+                    }),
+
+                // 7. 金融与商贸
+                new("FIN_TRADE", "金融与商贸领域", "Finance & Commerce", 1.0,
+                    Children: new()
+                    {
+                        new("FIN_TECH", "金融科技", "FinTech", 0.9),
+                        new("TRADE_E", "跨境电商", "Cross-border E-commerce", 0.85),
+                        new("FIN_SUPPLY", "供应链金融", "Supply Chain Finance", 0.8)
+                    }),
+
+                // 8. 医药健康与康养
+                new("MED_HEALTH", "医药健康与康养领域", "Medicine & Health", 1.0,
+                    Children: new()
+                    {
+                        new("MED_BIO", "生物医药", "Biopharma", 0.9),
+                        new("MED_DEVICE", "医疗器械", "Medical Devices", 0.85),
+                        new("HEALTH_CARE", "康复养老", "Rehabilitation & Elderly Care", 0.8)
+                    }),
+
+                // 9. 文化旅游与体育
+                new("CULT_TOUR", "文化旅游与体育领域", "Culture, Tourism & Sports", 1.0,
+                    Children: new()
+                    {
+                        new("TOUR_DIGITAL", "数字文旅", "Digital Culture & Tourism", 0.9),
+                        new("SPORT_EVENT", "体育赛事运营", "Sports Event Management", 0.85),
+                        new("CULT_CREATIVE", "文创设计", "Cultural Creativity", 0.8)
+                    }),
+
+                // 10. 信息科技与数字经济
+                new("IT_DIGITAL", "信息科技与数字经济领域", "IT & Digital Economy", 1.0,
+                    Children: new()
+                    {
+                        new("IT_SOFT", "软件", "Software", 1.0,
+                            Children: new()
+                            {
+                                new("SOFT_SAAS", "SaaS", "SaaS", 0.9),
+                                new("IT_AI", "AI", "Artificial Intelligence", 0.9),
+                                new("IT_BIGDATA", "大数据", "Big Data", 0.85),
+                                new("IT_CLOUD", "云计算", "Cloud Computing", 0.6)
+                            }),
+                        new("IT_IOT", "工业互联网", "Industrial Internet", 0.85)
+                    }),
+
+                // 11. 教育与科技
+                new("EDU_TECH", "教育与科技领域", "Education & Technology", 1.0,
+                    Children: new()
+                    {
+                        new("EDU_ONLINE", "在线教育", "Online Education", 0.9),
+                        new("EDU_VOC", "职业教育", "Vocational Education", 0.85),
+                        new("EDU_INFO", "教育信息化", "Education Informatization", 0.8)
+                    }),
+
+                // 12. 社会服务与公共管理
+                new("SOC_SERVICE", "社会服务与公共管理领域", "Social Services", 1.0,
+                    Children: new()
+                    {
+                        new("GOV_SMART", "智慧政务", "Smart Government", 0.9),
+                        new("COMMUNITY", "社区治理", "Community Governance", 0.85),
+                        new("NGO", "公益慈善", "Charity & NGO", 0.7)
+                    }),
+
+                // 13. 其他重点领域
+                new("OTHER_KEY", "其他重点领域", "Other Key Fields", 1.0,
+                    Children: new()
+                    {
+                        new("EMERGENCY", "应急管理", "Emergency Management", 0.9),
+                        new("GEO_SURVEY", "地质勘察", "Geological Survey", 0.85),
+                        new("IP_SERVICE", "知识产权服务", "IP Services", 0.8)
+                    })
+            };
+        }
+
+        public static class IndustryExpander
+        {
+            public static List<string> Expand(
+                List<string> baseIndustries,
+                List<IndustryNode> tree)
+            {
+                var result = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
+
+                foreach (var baseIndName in baseIndustries)
+                {
+                    var node = tree.FirstOrDefault(n =>
+                        n.NameCn.Equals(baseIndName, StringComparison.OrdinalIgnoreCase));
+
+                    if (node == null) continue;
+
+                    // 添加主行业
+                    result.Add(node.NameCn);
+
+                    // 递归添加子行业(按权重降序)
+                    if (node.Children != null)
+                    {
+                        AddChildrenRecursive(node.Children, result);
+                    }
+                }
+
+                return result.ToList();
+            }
+
+            private static void AddChildrenRecursive(
+                List<IndustryNode> nodes,
+                HashSet<string> result)
+            {
+                foreach (var child in nodes.OrderByDescending(n => n.Weight))
+                {
+                    result.Add(child.NameCn);
+                    if (child.Children != null)
+                    {
+                        AddChildrenRecursive(child.Children, result);
+                    }
+                }
+            }
+        }
+
+        public static class QuotaScheduler
+        {
+            /// <summary>
+            /// 国家配额计算(根据 行业、规模)
+            /// </summary>
+            /// <param name="countryTasks"></param>
+            /// <param name="industries"></param>
+            /// <param name="scales"></param>
+            /// <returns></returns>
+            public static List<AITaskItem> GenerateTasks(
+                List<CountryAIPormptInfo> countryTasks,
+                List<string> industries,
+                List<string> scales)
+            {
+                if (!countryTasks.Any() || !industries.Any() || !scales.Any())
+                    return new List<AITaskItem>();
+
+                var rnd = new Random();
+
+                // 1. 行业延伸
+                var expandedIndustries = IndustryExpander.Expand(
+                    industries,
+                    IndustryTree.Build()
+                );
+
+                // 2. Country -> Region (中文)
+                var regionGroups = countryTasks
+                    .Select(ct => new
+                    {
+                        Region = CountryToRegionMapper.GetRegion(ct.Country),
+                        ct.Count
+                    })
+                    .GroupBy(x => x.Region)
+                    .ToList();
+
+                int totalRequested = regionGroups.Sum(g => g.Sum(x => x.Count));
+
+                var tasks = new List<AITaskItem>();
+
+                int totalTaskCount = countryTasks.Sum(x => x.Count);
+
+                // 3. 按权重生成(Region 30%)
+                foreach (var group in regionGroups)
+                {
+                    double weight = (double)group.Sum(x => x.Count) / totalRequested;
+                    int quota = (int)Math.Round(weight * totalTaskCount * 0.3);
+
+                    for (int i = 0; i < quota; i++)
+                    {
+                        tasks.Add(new AITaskItem
+                        {
+                            Region = group.Key, // 中文国家名
+                            Industry = expandedIndustries[rnd.Next(expandedIndustries.Count)],
+                            Scale = scales[rnd.Next(scales.Count)]
+                        });
+                    }
+                }
+
+                return tasks
+                    .OrderBy(_ => rnd.Next())
+                    .ToList();
+            }
+        }
+
+        #endregion
+
+        /// <summary>
+        /// 混元提示词构建(高度定制化,适配商邀资料场景)
+        /// </summary>
+        /// <param name="tasks">包含国家及 Count 数量的任务列表</param>
+        /// <param name="countryTasks"></param>
+        /// <param name="entryInfo">包含规则基础信息</param>
+        /// <returns>最终构建的 System Prompt 字符串</returns>
+        public static string BuildHunyuanPrompt(List<CountryAIPormptInfo> tasks, List<AITaskItem> countryTasks, EntryInfo entryInfo)
+        {
+            // 行业信息,用于提示词硬约束
+            string industryEnum = string.Join("、", IndustryTree.Build().Select(x => x.NameCn).ToList());
+
+            var businessConstraints = new StringBuilder();
+            if (entryInfo.Industries.Any()) businessConstraints.AppendLine($" - 行业信息: {string.Join("、", entryInfo.Industries)}");
+            if (entryInfo.ScaleTypes.Any()) businessConstraints.AppendLine($" - 单位规模: {string.Join("、", entryInfo.ScaleTypes)}");
+            if (entryInfo.IsBackground) businessConstraints.AppendLine($" - 单位是否包含华人背景: 是");
+            if (!string.IsNullOrEmpty(entryInfo.OrgLevel) && !entryInfo.OrgLevel.Equals("全部")) businessConstraints.AppendLine($" - 公司层级: {entryInfo.OrgLevel}");
+            if (!string.IsNullOrEmpty(entryInfo.OtherConstraints)) businessConstraints.AppendLine($" - 其他规则: {entryInfo.OtherConstraints}");
+            if (businessConstraints == null || businessConstraints.Length == 0) businessConstraints.AppendLine("无");
+
+            return $@"
+# [SYSTEM_ROLE]
+你是精通全球实时经贸情报的【顶级商务咨询顾问】。
+你具备资深 .NET 6 架构思维,输出 JSON 必须 100% 兼容强类型反序列化。遵循“无存证不输出、官网优先溯源、AB管道证伪、零虚构熔断”的最高数据纯度准则。
+
+# [CONTEXT_ANALYSIS]
+• 当前核查基准时间 (CurrentDate): {DateTime.Now:yyyy-MM-dd} (锁定动态年份为绝对时空锚点)
+• 发起单位 (OriginUnit): {entryInfo.OriginUnit}
+• 业务硬性约束 (BusinessConstraints): {businessConstraints}
+• 核心驱动配置 (CountryTasks): {JsonConvert.SerializeObject(countryTasks)}
+• 任务配额分配 (Tasks): {JsonConvert.SerializeObject(tasks)}
+
+# [REALITY_CHECK_RULES - 严禁虚构与存证审计标准]
+
+## 1. 企业存在性“三位一体”核验 (Anti-Hallucination)
+- **核心原则**:严禁推测、编造、组合不存在的企业名称。必须严格按照 `CountryTasks` 定义的区域执行检索。
+- **验证链条**:
+  1. **官网验证**:SiteUrl 必须真实可访问,且内容与企业业务高度吻合。
+  2. **管道 A (官方公示)**:必须在目标国工商注册局、证监会或经商处有备案。
+  3. **管道 B (公正存证)**:必须在 LinkedIn (官方认证页)、Bloomberg 或 Crunchbase 有实时动态。
+- **熔断判定**:若以上三项均无法交叉证实企业真实存在性,该条目必须立即废弃。
+
+## 2. 软 404 与“FoundPage”假死内容熔断
+- **实质内容审计**:即便 URL 响应 200,若页面包含以下特征,必须判定为**死链**并熔断:
+  - **特征词**:`404 Not Found`, `Page Not Found`, `Oops`, `Seite nicht gefunden`, `foundpage`, `链接不存在`.
+- **主站熔断**:若 `SiteUrl` 触碰报错特征,该企业条目整体作废;若仅 `PostUrl` 触碰,则动态字段返回 `[]`。
+
+## 3. 信息溯源:官网优先与 AB 管道补位
+- **首要来源**:新闻 (PostUrl)、联系人 (Contact) 与邮件 (Email) 必须优先从企业官网提取。
+- **补位机制**:仅当官网无法获取联系信息时,允许从 AB 管道(管道B认证页)提取 {DateTime.Now.AddYears(-3).Year}-{DateTime.Now.Year} 年间的实时活跃数据。
+
+## 4. 属地物理地址强校验 (GEO_ADDRESS_STRICTNESS)
+- **物理属地锁定原则**:企业必须是在目标国家**实际运营、注册并拥有实体办公场所**的主体,严禁接受虚拟办公室、共享注册地址或仅用于税务/法律注册的空壳地址。
+- **地址真实性审计流程**:
+  1. **地理一致性校验**:`Address` 字段必须位于 `CountryTasks` 指定的国家境内,且与官网“Contact / Imprint / About Us”页面披露的实体地址完全一致。
+  2. **地图实体验证(AB 管道)**:必须通过 Google Maps / OpenStreetMap 等权威地理服务验证该地址对应真实建筑,且建筑内确实挂有该企业标识(或街景证据)。
+  3. **虚拟地址熔断**:若地址被识别为虚拟办公、信箱服务(P.O. Box)或商业中心,该条目**整条作废**。
+- **输出规范**:`Address` 必须返回**可直接复制粘贴到 Google Maps、百度地图、Apple 地图等主流地图服务中进行搜索和导航的完整物理地址**。格式要求:街道地址 + 城市 + 州/省 + 邮政编码 + 国家。地址中必须包含可被地图服务准确识别的**邮政编码**。
+
+# [BUSINESS_CONSTRAINTS_SCHEMA]
+- **零虚构原则**:绝对禁止编造任何不存在的企业、人名、链接或动态。
+- **行业约束**:Industry 必须严格锁定在业务定义的范围内,严禁跨类延伸。
+- **规模匹配**:Scale 必须严格匹配“单位规模”集合定义。
+- **英文命名清洗规则 (NAMING_CONVENTION_EN_CLEAN)**:在 `NameEn` 字段中,**严禁出现任何法律实体后缀标识**。包括但不限于:`PTY LTD`, `LTD`, `LIMITED`, `LLC`, `INC`, `CORP`, `PLC`, `GMBH`, `AG`, `SAS`, `SARL`, `BV`, `NV` 等。`NameEn` 应仅保留企业**核心品牌名或商号 (Trading Name)**,确保与官网首页主 Title 保持一致。若清洗后 `NameEn` 为空或仅剩通用词,该条目视为**无效命名并整体废弃**。
+
+# [QUOTA_SCHEDULING - 动态再分配算法]
+- **名额平移**:若 `CountryTasks` 指定的某国家因“官网失效/无法证伪”导致有效条目不足,允许实际输出数 < 理论配额。
+- **禁止凑数**:名额自动向质量更高、官网更活跃的国家转移,严禁为了填满 JSON 而降低审计标准。
+
+# [THOUGHT_PROCESS_LOGIC]
+1. 解析 `CountryTasks` 与 `BusinessConstraints` -> 2. 执行 {DateTime.Now.Year} 实时检索 -> 3. **执行存在性三位一体核验(证伪熔断)** -> 4. **扫描官网内容(识别并过滤软404)** -> 5. **属地物理地址强校验** -> 6. **英文命名清洗** -> 7. **以官网为核心、AB管道为备份提取字段** -> 8. 生成纯净 JSON。
+
+# [STRICT_DATA_CONTRACT]
+属性命名遵循 PascalCase,禁止新增字段:
+[
+  {{
+    ""Region"": ""string"",
+    ""Industry"": ""string"",
+    ""Scale"": ""string"",
+    ""NameCn"": ""string"",
+    ""NameEn"": ""string"",
+    ""Address"": ""string"",
+    ""Scope"": ""string"",
+    ""Contact"": ""string"",
+    ""Phone"": ""string"",
+    ""Email"": ""string"",
+    ""SiteUrl"": ""string"",
+    ""PostUrl"": [ {{ ""Date"": ""yyyy-MM-dd"", ""Description"": ""string"", ""Url"": ""string"" }} ],
+    ""RecLevel"": ""Core|Backup"",
+    ""IntgAdvice"": ""string""
+  }}
+]
+
+# [HUNYUAN_OUTPUT_GUARDRAILS]
+你是一个严格的 JSON 生成器。
+1. 最高禁令:绝对禁止编造任何不存在的企业、人名、链接或动态。
+2. 输出洁癖:不输出 Markdown 标记、不输出任何解释文字或思考过程。
+3. 边界控制:首字符 [,末字符 ]。结果为空输出 []。
+
+# [EXECUTION]
+立即执行:以动态年份为锚点,执行严苛的企业真实性审计与软 404 熔断逻辑,严禁虚构任何信息,按契约输出纯净 JSON。
+";
+        }
+
+        #endregion
+
+        #region 基础数据 整合
 
         /// <summary>
         /// 商邀AI invName 整合
@@ -1357,13 +1824,27 @@ namespace OASystem.API.OAMethodLib
             // 先进行去重逻辑处理
             var distinctQuery = _sqlSugar.Queryable(unionQuery)
                     .PartitionBy(it => it.Name)
-                    .OrderByDescending(it => it.SortTime) 
+                    .OrderByDescending(it => it.SortTime)
                     .Select(it => it);
 
             // 将去重后的结果包装,再进行全局
-            return await _sqlSugar.Queryable(distinctQuery) 
-                    .OrderByDescending(it => it.Source)     
-                    .OrderByDescending(it => it.SortTime)   
+            return await _sqlSugar.Queryable(distinctQuery)
+                    .OrderByDescending(it => it.Source)
+                    .OrderByDescending(it => it.SortTime)
+                    .ToListAsync();
+        }
+
+        /// <summary>
+        /// 商邀AI-无团组 invName 整合
+        /// </summary>
+        /// <returns></returns>
+        public static async Task<List<InvitationAIInvNameView>> InvitationAI_NoGroupInvName()
+        {
+            return await _sqlSugar.Queryable<Res_InvitationAI_NoGroup>()
+                    .Where(x => x.IsDel == 0 && !string.IsNullOrEmpty(x.InvName))
+                    .Select(x => new InvitationAIInvNameView { Id = x.Id, Name = x.InvName, Source = 2, SortTime = x.CreateTime })
+                    .OrderByDescending(it => it.Source)
+                    .OrderByDescending(it => it.SortTime)
                     .ToListAsync();
         }
 
@@ -1378,7 +1859,7 @@ namespace OASystem.API.OAMethodLib
                .Select(x => x.Client)
                .ToListAsync();
 
-            // 2. 内存精炼:解密 -> 去空格 -> 排除横杠 -> 去重
+            // 解密 -> 去空格 -> 排除横杠 -> 去重
             var unitNames = rawEncryptedDatas
                 .Select(encryptedItem =>
                 {
@@ -1417,6 +1898,8 @@ namespace OASystem.API.OAMethodLib
 
         #endregion
 
+        #endregion
+
         #region 团组汇率
 
         /// <summary>
@@ -2660,7 +3143,7 @@ namespace OASystem.API.OAMethodLib
             }
 
             string pattern = @"\+\d+"; // 匹配“+”及其后的数字
-            Match match = Regex.Match(input, pattern);
+            System.Text.RegularExpressions.Match match = Regex.Match(input, pattern);
 
             if (match.Success)
             {
@@ -4402,7 +4885,7 @@ namespace OASystem.API.OAMethodLib
         /// <param name="fontSize"><字体大小/param>
         public static void AsposeWordSetFooter(DocumentBuilder builder, string footerLabel, string font, int fontSize)
         {
-            Section currentSection = builder.CurrentSection;
+            Aspose.Words.Section currentSection = builder.CurrentSection;
             var pageSetup = currentSection.PageSetup;
             pageSetup.DifferentFirstPageHeaderFooter = true;
 
@@ -5356,7 +5839,7 @@ namespace OASystem.API.OAMethodLib
                         string tempPath = (AppSettingsHelper.Get("WordBasePath") + "EnterExitCost/Temp/四川省商务厅出国经费财政先行审核表.xls");
                         //载入模板
                         WorkbookDesigner designer = new WorkbookDesigner();
-                        designer.Workbook = new Workbook(tempPath);
+                        designer.Workbook = new Aspose.Cells.Workbook(tempPath);
 
                         Dictionary<string, string> dic = new Dictionary<string, string>();
 
@@ -5547,7 +6030,7 @@ namespace OASystem.API.OAMethodLib
                         designer.SetDataSource("cellSum4", (enterExitCosts.OutsideJJPay + enterExitCosts.OutsideGWPay).ToString("#0.00"));
                         designer.SetDataSource("celllastStr", celllastStr);
 
-                        Workbook wb = designer.Workbook;
+                        Aspose.Cells.Workbook wb = designer.Workbook;
                         var sheet = wb.Worksheets[0];
 
                         //绑定datatable数据集
@@ -6190,7 +6673,7 @@ namespace OASystem.API.OAMethodLib
         #endregion
 
         #region Excel导出服务
-        
+
         #region 类
         public class FileExportSettings
         {
@@ -6269,165 +6752,9 @@ namespace OASystem.API.OAMethodLib
             }
         }
 
-        public static async Task<ExportResult> ExportWithTemplate<T>(IEnumerable<T> data, byte[] templateBytes, string fileName, string sheetName = "Sheet1")
-        {
-            try
-            {
-                using var templateStream = new MemoryStream(templateBytes);
-                using var workbook = new Workbook(templateStream);
-
-                var worksheet = workbook.Worksheets[sheetName] ?? workbook.Worksheets[0];
-
-                // 查找数据起始行(可以根据模板中的标记来定位)
-                int startRow = FindDataStartRow(worksheet);
-
-                // 填充数据
-                FillWorksheetWithData(worksheet, data, startRow);
-
-                using var outputStream = new MemoryStream();
-                workbook.Save(outputStream, Aspose.Cells.SaveFormat.Xlsx);
-                var excelBytes = outputStream.ToArray();
-
-                var filePath = await SaveFileAsync(excelBytes, fileName, "excel/templates");
-                var downloadUrl = $"wwwroot/exports/{filePath}";
-
-                return new ExportResult
-                {
-                    Success = true,
-                    FilePath = filePath,
-                    DownloadUrl = downloadUrl,
-                    FileName = Path.GetFileName(filePath),
-                    FileSize = excelBytes.Length,
-                    MimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
-                };
-            }
-            catch (Exception ex)
-            {
-                return new ExportResult
-                {
-                    Success = false,
-                    ErrorMessage = ex.Message
-                };
-            }
-        }
-
-        public static async Task<ExportResult> ExportMultipleSheets<T>(Dictionary<string, IEnumerable<T>> sheetsData, string fileName)
-        {
-            try
-            {
-                using var workbook = new Workbook();
-                workbook.Worksheets.Clear(); // 清除默认工作表
-
-                foreach (var sheet in sheetsData)
-                {
-                    var worksheet = workbook.Worksheets.Add(sheet.Key);
-                    var data = sheet.Value;
-
-                    if (data != null && data.Any())
-                    {
-                        FillWorksheetWithData(worksheet, data, 0);
-                        worksheet.AutoFitColumns();
-                    }
-                }
-
-                using var stream = new MemoryStream();
-                workbook.Save(stream, Aspose.Cells.SaveFormat.Xlsx);
-                var excelBytes = stream.ToArray();
-
-                var filePath = await SaveFileAsync(excelBytes, fileName, "excel/multi-sheets");
-                var downloadUrl = $"wwwroot/exports/{filePath}";
-
-                return new ExportResult
-                {
-                    Success = true,
-                    FilePath = filePath,
-                    DownloadUrl = downloadUrl,
-                    FileName = Path.GetFileName(filePath),
-                    FileSize = excelBytes.Length,
-                    MimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
-                };
-            }
-            catch (Exception ex)
-            {
-                return new ExportResult
-                {
-                    Success = false,
-                    ErrorMessage = ex.Message
-                };
-            }
-        }
-
-        public static async Task<ExportResult> ExportWithCustomHeaders<T>(IEnumerable<T> data, Dictionary<string, string> headers, string fileName, string sheetName = "Sheet1")
-        {
-            try
-            {
-                using var workbook = new Workbook();
-                var worksheet = workbook.Worksheets[0];
-                worksheet.Name = sheetName;
-
-                // 设置自定义表头
-                int colIndex = 0;
-                foreach (var header in headers)
-                {
-                    worksheet.Cells[0, colIndex].PutValue(header.Value);
-                    colIndex++;
-                }
-
-                // 填充数据
-                if (data != null && data.Any())
-                {
-                    int rowIndex = 1;
-                    var properties = typeof(T).GetProperties();
-
-                    foreach (var item in data)
-                    {
-                        colIndex = 0;
-                        foreach (var header in headers)
-                        {
-                            var property = properties.FirstOrDefault(p => p.Name == header.Key);
-                            if (property != null)
-                            {
-                                var value = property.GetValue(item);
-                                worksheet.Cells[rowIndex, colIndex].PutValue(value);
-                            }
-                            colIndex++;
-                        }
-                        rowIndex++;
-                    }
-                }
-
-                worksheet.AutoFitColumns();
-
-                using var stream = new MemoryStream();
-                workbook.Save(stream, Aspose.Cells.SaveFormat.Xlsx);
-                var excelBytes = stream.ToArray();
-
-                var filePath = await SaveFileAsync(excelBytes, fileName, "excel/custom-headers");
-                var downloadUrl = $"filePath";
-
-                return new ExportResult
-                {
-                    Success = true,
-                    FilePath = filePath,
-                    DownloadUrl = downloadUrl,
-                    FileName = Path.GetFileName(filePath),
-                    FileSize = excelBytes.Length,
-                    MimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
-                };
-            }
-            catch (Exception ex)
-            {
-                return new ExportResult
-                {
-                    Success = false,
-                    ErrorMessage = ex.Message
-                };
-            }
-        }
-
         private static byte[] GenerateDefaultExcel<T>(IEnumerable<T> data, string sheetName)
         {
-            using var workbook = new Workbook();
+            using var workbook = new Aspose.Cells.Workbook();
             var worksheet = workbook.Worksheets[0];
             worksheet.Name = sheetName;
 
@@ -6442,7 +6769,7 @@ namespace OASystem.API.OAMethodLib
 
         private static byte[] GenerateStyledExcel<T>(IEnumerable<T> data, string sheetName)
         {
-            using var workbook = new Workbook();
+            using var workbook = new Aspose.Cells.Workbook();
             var worksheet = workbook.Worksheets[0];
             worksheet.Name = sheetName;
 
@@ -6469,7 +6796,7 @@ namespace OASystem.API.OAMethodLib
 
         private static byte[] GenerateExcelWithFormulas<T>(IEnumerable<T> data, string sheetName)
         {
-            using var workbook = new Workbook();
+            using var workbook = new Aspose.Cells.Workbook();
             var worksheet = workbook.Worksheets[0];
             worksheet.Name = sheetName;
 
@@ -6537,7 +6864,7 @@ namespace OASystem.API.OAMethodLib
             return 1; // 默认从第2行开始
         }
 
-        private static byte[] SaveWorkbookToBytes(Workbook workbook)
+        private static byte[] SaveWorkbookToBytes(Aspose.Cells.Workbook workbook)
         {
             using var stream = new MemoryStream();
             workbook.Save(stream, Aspose.Cells.SaveFormat.Xlsx);

+ 22 - 16
OASystem/OASystem.Api/OAMethodLib/Quartz/Business/ProcessAndNotifySummary.cs

@@ -21,6 +21,7 @@ namespace OASystem.API.OAMethodLib.Quartz.Business
         private static readonly IDeepSeekService _deepSeekService = AutofacIocManager.Instance.GetService<IDeepSeekService>();
         private static readonly Microsoft.Extensions.Logging.ILogger _logger =AutofacIocManager.Instance.GetService<ILoggerFactory>().CreateLogger("ProcessAndNotifySummary");
 
+        private static List<string> _defaultEmails = new List<string>() { "johnny.yang@pan-american-intl.com", "ellisa.wang@pan-american-intl.com" };
         /// <summary>
         /// hotmail 邮件 汇总 发送企微邮件
         /// 时间范围 昨天
@@ -41,7 +42,6 @@ namespace OASystem.API.OAMethodLib.Quartz.Business
             var yesterdayStart = nowInCst.Date.AddDays(-1);
             var yesterdayEnd = yesterdayStart.AddDays(1).AddTicks(-1);
 
-
             _logger.LogInformation("Hotmail 定时发送邮件 -> 获取Hotmail邮件信息 -> 开始");
             // 获取邮件信息
             var emailInfos = await _hotmailService.GetMergedMessagesAsync(hotmails, yesterdayStart, yesterdayEnd);
@@ -112,18 +112,13 @@ namespace OASystem.API.OAMethodLib.Quartz.Business
                             finalBody = summary.TextBody;
                         }
 
-                        // 默认发送 至 杨俊z霄、王鸽 邮件
-                        string defualtEmail_yanjunxiao = "johnny.yang@pan-american-intl.com";
-                        string defualtEmail_wangge = "ellisa.wang@pan-american-intl.com";
-
                         _logger.LogInformation("Hotmail 定时发送邮件 -> DeepSeek AI 分析汇总邮件 -> 解析AI返回的json -> 发送腾讯邮件 -> {email} 开始", qwEmail);
+
+                        var toEmails = new List<string>() { qwEmail };
+                        toEmails.AddRange(_defaultEmails);
                         var seedRes = await _qiYeWeChatApiService.EmailSendAsync(new EmailRequestDto
                         {
-                            ToEmails = new List<string> { 
-                                qwEmail, 
-                                defualtEmail_yanjunxiao,
-                                defualtEmail_wangge
-                            },
+                            ToEmails = toEmails,
                             Subject = finalSubject,
                             Body = finalBody,
                         });
@@ -211,15 +206,26 @@ Now, output the JSON array based on the logic above. No prose, no chat, just the
 
         private static async Task NotifyEmptyEmails(List<int> userIds)
         {
-            var userEmails = await _sqlSugar.Queryable<Sys_Users>().Where(x => x.IsDel == 0 && userIds.Contains(x.Id)).Select(x => x.Email).ToListAsync();
+            var userEmails = await _sqlSugar.Queryable<Sys_Users>().Where(x => x.IsDel == 0 && userIds.Contains(x.Id)).Select(x => x.Email.Trim()).ToListAsync();
             if (userEmails.Any())
             {
-                await _qiYeWeChatApiService.EmailSendAsync(new EmailRequestDto
+                try
                 {
-                    ToEmails = userEmails,
-                    Subject = $"{DateTime.Now:yyyy-MM-dd} - 邮件汇总",
-                    Body = "昨日暂未收到有效邮件。"
-                });
+                    userEmails.AddRange(_defaultEmails);
+
+                    await _qiYeWeChatApiService.EmailSendAsync(new EmailRequestDto
+                    {
+                        ToEmails = userEmails,
+                        Subject = $"{DateTime.Now:yyyy-MM-dd} - 邮件汇总",
+                        Body = "昨日暂未收到有效邮件。"
+                    });
+                }
+                catch (Exception ex)
+                {
+                    _logger.LogInformation("Microsoft Hotmail 定时发送邮件 -> 获取Hotmail邮件信息 -> 发送无邮件情况 -> 发送失败!【MSG:{ErrorMsg}】", ex.Message);
+                }
+
+                _logger.LogInformation("Microsoft Hotmail 定时发送邮件 -> 获取Hotmail邮件信息 -> 发送无邮件情况 -> 发送成功!");
             }
         }
 

+ 117 - 0
OASystem/OASystem.Domain/Dtos/Resource/InvitationAI.cs

@@ -50,6 +50,11 @@ namespace OASystem.Domain.Dtos.Resource
         /// </summary>
         public bool IsBackground { get; set; } = false;
 
+        /// <summary>
+        /// 公司层级
+        /// </summary>
+        public string OrgLevel { get; set; }
+
         /// <summary>
         /// 备注信息
         /// </summary>
@@ -148,4 +153,116 @@ namespace OASystem.Domain.Dtos.Resource
     public class InvitationAIInsertResourceDto: InvitationAIGenerateEmailDto
     { }
 
+
+    #region 无团组版
+
+    public class InvitationAI_NoGroupSearchDto:InvitationAISearchDto
+    {
+    }
+
+    public class InvitationAI_NoGroupAddDto
+    {
+        public string InvName { get; set; }
+
+        public int CurrUserId { get; set; }
+    }
+
+    public class InvitationAI_NoGroupSetPromptDto 
+    {
+        public int Id { get; set; }
+
+        /// <summary>
+        /// 出访单位
+        /// </summary>
+        public string OriginUnit { get; set; }
+        /// <summary>
+        /// 出访国家
+        /// </summary>
+        public List<string> TargetCountry { get; set; }
+
+        /// <summary>
+        /// 出访时间(eg:2026-04-16)
+        /// </summary>
+        public string VisitDate { get; set; }
+
+        /// <summary>
+        /// 出访目的
+        /// </summary>
+        public string Objective { get; set; }
+
+        /// <summary>
+        /// 行业信息
+        /// 信息技术、金融与财会、工业制造、医疗保健、政府与公共服务、消费与贸易
+        /// </summary>
+        public List<string> Industries { get; set; } = new List<string>();
+
+        /// <summary>
+        /// 规模类型
+        /// </summary>
+        public List<string> ScaleTypes { get; set; } = new List<string>();
+
+        /// <summary>
+        /// 是否需要华人单位背景
+        /// </summary>
+        public bool IsBackground { get; set; } = false;
+
+        /// <summary>
+        /// 公司层级
+        /// </summary>
+        public string OrgLevel { get; set; }
+
+        /// <summary>
+        /// 备注信息
+        /// </summary>
+        public string OtherConstraints { get; set; }
+
+        public int CurrUserId { get; set; }
+    }
+
+    public class InvitationAI_NoGroupSetCheckedDto : InvitationAISetCheckedDto
+    {
+    }
+
+    public class InvitationAI_NoGroupSaveDto : InvitationAISaveDto
+    {
+        public new InvitationAI_NoGroupInfo AiCrawledDetail { get; set; }
+    }
+
+    public class InvitationAI_NoGroupSingleDelDto : InvitationAISingleDelDto
+    {
+    }
+
+    public class InvitationAI_NoGroupInsertResourceDto : InvitationAIInsertResourceDto
+    {
+    }
+
+    public class InvitationAI_NoGroupCompleteTextDto : InvitationAICompleteTextDto
+    {
+    }
+
+    public class InvitationAI_NoGroupGenerateEmailDto : InvitationAIGenerateEmailDto
+    {
+    }
+
+    public class InvitationAI_NoGroupEmailSaveDto : InvitationAIEmailSaveDto
+    {
+    }
+
+    public class InvitationAI_NoGroupFileSaveDto:InvitationAIFileSaveDto
+    {
+        
+    }
+
+    public class InvitationAI_NoGroupFileDelDto : InvitationAIFileDelDto
+    {
+
+    }
+
+    public class InvitationAI_NoGroupSeedEmailDto : InvitationAISeedEmailDto
+    {
+    }
+
+
+    #endregion
+
 }

+ 0 - 1
OASystem/OASystem.Domain/Entities/Customer/Crm_DeleClient.cs

@@ -94,7 +94,6 @@ namespace OASystem.Domain.Entities.Customer
         /// </summary>
         [SugarColumn(IsNullable = true, ColumnDataType = "varchar(300)")]
         public string RoomPref { get; set; }
-
         /// <summary>
         /// 家庭联系方式
         /// </summary>

+ 11 - 10
OASystem/OASystem.Domain/Entities/Resource/Res_InvitationAI.cs

@@ -1,10 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.Linq;
-using System.Text;
-using System.Text.Json.Serialization;
-using System.Threading.Tasks;
+using System.Text.Json.Serialization;
 
 namespace OASystem.Domain.Entities.Resource
 {
@@ -12,7 +6,7 @@ namespace OASystem.Domain.Entities.Resource
     /// 商邀公务AI
     /// </summary>
     [SugarTable("Res_InvitationAI", "商邀公务AI")]
-    public class Res_InvitationAI: EntityBase
+    public class Res_InvitationAI : EntityBase
     {
         /// <summary>
         /// 名称
@@ -175,6 +169,13 @@ namespace OASystem.Domain.Entities.Resource
         /// </summary>
         public bool IsBackground { get; set; } = false;
 
+        /// <summary>
+        /// 公司类型
+        /// 全部、总部、分公司
+        /// 默认:总部
+        /// </summary>
+        public string OrgLevel { get; set; } = "总部";
+
         /// <summary>
         /// 出访目的
         /// </summary>
@@ -237,7 +238,6 @@ namespace OASystem.Domain.Entities.Resource
 
     public class AICreateEmailInfo
     {
-
         public string Guid { get; set; }
         public string NameCn { get; set; }
         public string Scope { get; set; }
@@ -245,7 +245,8 @@ namespace OASystem.Domain.Entities.Resource
         public string Content { get; set; }
     }
 
-    public class CountryAIPormptInfo {
+    public class CountryAIPormptInfo
+    {
         public string Country { get; set; }
         public int Count { get; set; }
     }

+ 43 - 0
OASystem/OASystem.Domain/Entities/Resource/Res_InvitationAI_NoGroup.cs

@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OASystem.Domain.Entities.Resource
+{
+    /// <summary>
+    /// 商邀公务AI-无团组
+    /// </summary>
+    [SugarTable("Res_InvitationAI_NoGroup", "商邀公务AI-无团组")]
+    public class Res_InvitationAI_NoGroup : EntityBase
+    {
+        /// <summary>
+        /// 名称
+        /// </summary>
+        [SugarColumn(ColumnName = "InvName", ColumnDescription = "名称", IsNullable = true, ColumnDataType = "varchar(150)")]
+        public string InvName { get; set; }
+
+        /// <summary>
+        /// ai 抓取信息存储(含本地数据信息)
+        /// </summary>
+        [SugarColumn(ColumnName = "AiCrawledDetails", ColumnDescription = "ai 抓取信息存储(含本地数据信息)", IsJson = true, IsNullable = true, ColumnDataType = "varchar(max)")]
+        public List<InvitationAI_NoGroupInfo> AiCrawledDetails { get; set; } = new List<InvitationAI_NoGroupInfo>();
+
+        /// <summary>
+        /// 词条信息
+        /// </summary>
+        [SugarColumn(ColumnName = "Entries", ColumnDescription = "词条信息", IsJson = true, IsNullable = true, ColumnDataType = "varchar(max)")]
+        public Entry_NoGroupInfo EntryInfo { get; set; } = new Entry_NoGroupInfo();
+    }
+
+    public class InvitationAI_NoGroupInfo : InvitationAIInfo { }
+
+    public class Entry_NoGroupInfo : EntryInfo 
+    {
+        /// <summary>
+        /// 出访时间
+        /// </summary>
+        public string VisitDate { get; set; }
+    }
+}