Lyyyi před 1 dnem
rodič
revize
1eccd7b6a7

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 829 - 129
OASystem/OASystem.Api/Controllers/GroupsController.cs


+ 218 - 0
OASystem/OASystem.Api/OAMethodLib/VisaFormExtractor.cs

@@ -0,0 +1,218 @@
+using Aspose.Words;
+using Aspose.Words.Tables;
+
+/// <summary>
+/// 签证文件识别服务类
+/// </summary>
+public class VisaFormExtractor
+{
+    public VisaApplicationData ExtractData(string filePath)
+    {
+        var data = new VisaApplicationData();
+        data.FileName = System.IO.Path.GetFileName(filePath);
+
+        // 加载文档
+        Document doc = new Document(filePath);
+
+        // 获取文档中的所有表格
+        Table[] tables = doc.GetChildNodes(NodeType.Table, true).ToArray().Cast<Table>().ToArray();
+
+        // 根据文件名或内容判断签证类型,并分发给不同的提取器
+        // 这里是一个简单的判断逻辑
+        if (data.FileName.Contains("澳新") || data.FileName.Contains("澳大利亚") || data.FileName.Contains("新西兰"))
+        {
+            data.VisaType = "ANZ";
+            ExtractANZData(tables, data.ExtractedFields);
+        }
+        else if (data.FileName.Contains("美国"))
+        {
+            data.VisaType = "USA";
+            ExtractUSAData(tables, data.ExtractedFields);
+        }
+        else if (data.FileName.Contains("申根"))
+        {
+            data.VisaType = "Schengen";
+            ExtractSchengenData(doc, data.ExtractedFields); // 申根表可能不全是表格
+        }
+        // ... 添加其他签证类型的判断和提取方法
+
+        return data;
+    }
+
+    /// <summary>
+    /// 提取澳新签证表数据
+    /// </summary>
+    private void ExtractANZData(Table[] tables, Dictionary<string, string> fields)
+    {
+        // 遍历所有表格,寻找关键字段
+        foreach (Table table in tables)
+        {
+            foreach (Row row in table.Rows)
+            {
+                for (int cellIndex = 0; cellIndex < row.Cells.Count; cellIndex++)
+                {
+                    Cell cell = row.Cells[cellIndex];
+                    string cellText = cell.GetText().Trim().Replace("\a", ""); // 获取单元格文本并清理
+
+                    // 1. 识别“姓名”字段
+                    if (cellText.Contains("姓名") && !cellText.Contains("曾用名") && !cellText.Contains("拼音"))
+                    {
+                        // 假设“姓名”的值在同行下一个单元格
+                        string value = GetAdjacentCellValue(row, cellIndex, isNext: true);
+                        if (!string.IsNullOrEmpty(value))
+                        {
+                            fields["姓名"] = value;
+                        }
+                    }
+
+                    // 2. 识别“出生日期”字段
+                    if (cellText.Contains("出生日期"))
+                    {
+                        // 假设“出生日期”的值在同行下一个单元格
+                        string value = GetAdjacentCellValue(row, cellIndex, isNext: true);
+                        if (!string.IsNullOrEmpty(value))
+                        {
+                            // 使用正则表达式验证并清理日期格式
+                            var dateMatch = Regex.Match(value, @"\d{4}年\d{1,2}月\d{1,2}日");
+                            if (dateMatch.Success)
+                            {
+                                fields["出生日期"] = dateMatch.Value;
+                            }
+                            else
+                            {
+                                fields["出生日期"] = value; // 如果不是标准格式,也先存下来
+                            }
+                        }
+                    }
+
+                    // 3. 识别“性别”字段 (处理带有选项的情况)
+                    if (cellText.Contains("性 别") || cellText.Contains("性别"))
+                    {
+                        // 这个单元格里可能直接是“男”或“女”,也可能在后面的Run里有带格式的文本
+                        string value = GetTextFromCellOrFollowingRuns(cell);
+                        // 清理文本,只取“男”或“女”
+                        if (value.Contains("男")) fields["性别"] = "男";
+                        else if (value.Contains("女")) fields["性别"] = "女";
+                    }
+
+                    // 4. 识别“婚姻状况”字段 (处理打勾的情况)
+                    if (cellText.Contains("婚姻状况"))
+                    {
+                        // 方法1: 在单元格内查找被勾选的项 (例如 R已婚 中的 R 可能代表被选中)
+                        string innerText = cell.GetText();
+                        if (innerText.Contains("R已婚")) fields["婚姻状况"] = "已婚";
+                        else if (innerText.Contains("R未婚")) fields["婚姻状况"] = "未婚";
+                        // ... 其他选项
+
+                        // 方法2: 如果勾选框是符号,可能需要检查特定的Run的字体
+                    }
+                    // ... 根据您的“澳新表”模板,继续添加更多字段的提取逻辑
+                    // 例如:现单位名称、月收入、家庭成员、教育经历等。
+                    // 这些字段可能需要更复杂的逻辑,比如跨行、跨表格读取。
+                }
+            }
+        }
+    }
+
+    /// <summary>
+    /// 提取美国签证表数据 (逻辑类似,但映射规则不同)
+    /// </summary>
+    private void ExtractUSAData(Table[] tables, Dictionary<string, string> fields)
+    {
+        foreach (Table table in tables)
+        {
+            foreach (Row row in table.Rows)
+            {
+                for (int cellIndex = 0; cellIndex < row.Cells.Count; cellIndex++)
+                {
+                    Cell cell = row.Cells[cellIndex];
+                    string cellText = cell.GetText().Trim().Replace("\a", "");
+
+                    // 美国表的“姓名”字段可能直接就是“姓名:”
+                    if (cellText.StartsWith("姓名:"))
+                    {
+                        // 值可能在同一个单元格的“:”后面,也可能在下一个单元格
+                        string value = cellText.Split(':').Last().Trim();
+                        if (string.IsNullOrEmpty(value))
+                        {
+                            value = GetAdjacentCellValue(row, cellIndex, isNext: true);
+                        }
+                        fields["姓名"] = value;
+                    }
+
+                    if (cellText.StartsWith("出生日期:"))
+                    {
+                        string value = cellText.Split(':').Last().Trim();
+                        if (string.IsNullOrEmpty(value))
+                        {
+                            value = GetAdjacentCellValue(row, cellIndex, isNext: true);
+                        }
+                        fields["出生日期"] = value;
+                    }
+                    // ... 添加美国表特有的字段,如“护照号码”、“DS160确认号”、“美国联系人”等。
+                }
+            }
+        }
+    }
+
+    /// <summary>
+    /// 提取申根表数据 (可能包含段落)
+    /// </summary>
+    private void ExtractSchengenData(Document doc, Dictionary<string, string> fields)
+    {
+        // 申根表可能不完全是表格,需要结合段落(Paragraph)来搜索
+        NodeCollection paragraphs = doc.GetChildNodes(NodeType.Paragraph, true);
+
+        foreach (Paragraph para in paragraphs)
+        {
+            string paraText = para.GetText().Trim();
+
+            if (paraText.StartsWith("1.姓名:"))
+            {
+                // 提取姓名,可能在冒号后面,也可能在下一行或下一个Run
+                fields["姓名"] = paraText.Replace("1.姓名:", "").Trim();
+            }
+            // ... 处理其他申根表字段
+        }
+
+        // 同时也检查表格
+        Table[] tables = doc.GetChildNodes(NodeType.Table, true).ToArray().Cast<Table>().ToArray();
+        // ... 类似ANZ和USA的方法,但使用申根表的映射规则
+    }
+
+    // --- 辅助方法 ---
+
+    /// <summary>
+    /// 获取同一行中相邻单元格的值
+    /// </summary>
+    /// <param name="row">当前行</param>
+    /// <param name="currentCellIndex">当前单元格索引</param>
+    /// <param name="isNext">true=下一个单元格,false=上一个单元格</param>
+    /// <returns>相邻单元格的文本</returns>
+    private string GetAdjacentCellValue(Row row, int currentCellIndex, bool isNext = true)
+    {
+        int targetIndex = isNext ? currentCellIndex + 1 : currentCellIndex - 1;
+        if (targetIndex >= 0 && targetIndex < row.Cells.Count)
+        {
+            return row.Cells[targetIndex].GetText().Trim().Replace("\a", "");
+        }
+        return null;
+    }
+
+    /// <summary>
+    /// 从一个单元格及其子Run中提取文本,有助于获取格式化的文本(如被勾选的项)
+    /// </summary>
+    private string GetTextFromCellOrFollowingRuns(Cell cell)
+    {
+        // 这是一个简化的版本,实际中可能需要递归遍历所有子节点
+        return cell.GetText().Trim().Replace("\a", "");
+    }
+
+    // 这是一个通用模型,您需要为每种签证类型定义更详细的模型
+    public class VisaApplicationData
+    {
+        public string FileName { get; set; }
+        public string VisaType { get; set; } // 可根据文件名判断
+        public Dictionary<string, string> ExtractedFields { get; set; } = new Dictionary<string, string>();
+    }
+}

+ 3 - 0
OASystem/OASystem.Domain/Entities/Customer/Crm_CustomerCompany.cs

@@ -27,16 +27,19 @@ namespace OASystem.Domain.Entities.Customer
         /// </summary>
         [SugarColumn(IsNullable = true, ColumnDataType = "varchar(200)")]
         public string? Address { get; set; }
+
         /// <summary>
         /// 邮政编码
         /// </summary>
         [SugarColumn(IsNullable = true, ColumnDataType = "varchar(80)")]
         public string? PostCodes { get; set; }
+
         /// <summary>
         /// 最后操作人
         /// </summary>
         [SugarColumn(IsNullable = true, ColumnDataType = "int")]
         public int LastedOpUserId { get; set; }
+
         /// <summary>
         /// 最后操作时间
         /// </summary>

+ 224 - 5
OASystem/OASystem.Domain/Entities/Customer/Crm_DeleClient.cs

@@ -216,12 +216,12 @@ namespace OASystem.Domain.Entities.Customer
         /// 美国抵达日期
         /// </summary>
         [SugarColumn(IsNullable = true, ColumnDataType = "DateTime")]
-        public DateTime USADate { get; set; }
+        public DateTime? USADate { get; set; }
         /// <summary>
         /// 美国停留天数
         /// </summary>
         [SugarColumn(IsNullable = true, ColumnDataType = "int")]
-        public int USADays { get; set; } = 0;
+        public int? USADays { get; set; } = 0;
         /// <summary>
         /// 加拿大抵达日期
         /// </summary>
@@ -272,7 +272,7 @@ namespace OASystem.Domain.Entities.Customer
         /// 被拒时间
         /// </summary>
         [SugarColumn(IsNullable = true, ColumnDataType = "DateTime")]
-        public DateTime RejectedDate { get; set; }
+        public DateTime? RejectedDate { get; set; }
         /// <summary>
         /// 被拒地点
         /// </summary>
@@ -410,12 +410,12 @@ namespace OASystem.Domain.Entities.Customer
         /// 结婚日期
         /// </summary>
         [SugarColumn(IsNullable = true, ColumnDataType = "DateTime")]
-        public DateTime WeddingDate { get; set; }
+        public DateTime? WeddingDate { get; set; }
         /// <summary>
         /// 离婚日期
         /// </summary>
         [SugarColumn(IsNullable = true, ColumnDataType = "DateTime")]
-        public DateTime DivorceDate { get; set; }
+        public DateTime? DivorceDate { get; set; }
         /// <summary>
         /// 配偶姓名
         /// </summary>
@@ -476,6 +476,194 @@ namespace OASystem.Domain.Entities.Customer
         /// </summary>
         [SugarColumn(IsNullable = true, ColumnDataType = "varchar(500)")]
         public string PhD { get; set; }
+
+
+        //----------增加字段------------
+        /// <summary>
+        /// 离婚原因
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(500)")]
+        public string DivorceRsn { get; set; }
+
+        /// <summary>
+        /// 美国驾照No
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")]
+        public string USA_DLNo { get; set; }
+        /// <summary>
+        /// 美国驾照签发地
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")]
+        public string USA_DLPlace { get; set; }
+
+        /// <summary>
+        /// 美国社会安全号或者纳税ID号
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")]
+        public string USA_SSNOrITIN { get; set; }
+
+        /// <summary>
+        /// 是否是其他国家的永久居民
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")]
+        public string Resident { get; set; }
+
+        /// <summary>
+        /// 近五年内是否使用过任何社交账号
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(200)")]
+        public string HasSocialAcc‌ { get; set; }
+
+        /// <summary>
+        /// 请提供美国详细住址
+        ///   赴美目的
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(200)")]
+        public string USAAddress { get; set; }
+
+        /// <summary>
+        /// 赴美目的
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(200)")]
+        public string PurposeUSA { get; set; }
+
+        /// <summary>
+        /// 美国联系人或组织
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")]
+        public string USAContact‌ { get; set; }
+
+        /// <summary>
+        /// 美国联系人电话
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(50)")]
+        public string USAContact‌Tel‌ { get; set; }
+        /// <summary>
+        /// 美国联系人邮箱
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(50)")]
+        public string USAContact‌Email { get; set; }
+        /// <summary>
+        /// 赴美的到达城市
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")]
+        public string USAArrivalCity‌ { get; set; }
+        /// <summary>
+        /// 赴美的离开城市
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")]
+        public string USADepartCity‌ { get; set; }
+        /// <summary>
+        /// 计划在美国访问的景点
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")]
+        public string USAVisitSites‌ { get; set; }
+        /// <summary>
+        /// 赴美同行人姓名
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")]
+        public string USACmpName‌ { get; set; }
+        /// <summary>
+        /// 赴美同行人关系
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")]
+        public string USACmpRelation‌ { get; set; }
+        /// <summary>
+        /// 过去5年内到过的国家
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")]
+        public string Cntry5Y‌ { get; set; }
+        /// <summary>
+        /// 签时能流利交流的语言
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")]
+        public string LangSkill‌ { get; set; }
+        /// <summary>
+        /// 曾被哪些国家拒签/拒签原因
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(300)")]
+        public string DenyNationRsn { get; set; }
+        /// <summary>
+        /// 曾抵达过美国时间
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(50)")]
+        public string USAHadArrival‌ { get; set; }
+        /// <summary>
+        /// 曾停留美国天数
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
+        public string USAHadDays‌ { get; set; }
+        /// <summary>
+        /// 美国移民签证时间
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "DateTime")]
+        public DateTime? USAImmVisa‌Date { get; set; }
+        /// <summary>
+        /// 美国移民签证地点
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(50)")]
+        public string USAImmVisa‌Place { get; set; }
+        /// <summary>
+        /// 美国移民签证种类
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(50)")]
+        public string USAImmVisa‌ { get; set; }
+
+        /// <summary>
+        /// 直系亲属在美国 姓名
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(50)")]
+        public string USAImmedFamName { get; set; }
+        /// <summary>
+        /// 直系亲属在美国 关系
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(50)")]
+        public string USAImmedFamRel { get; set; }
+        /// <summary>
+        /// 直系亲属在美国 身份
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(50)")]
+        public string USAImmedFamStat { get; set; }
+
+        /// <summary>
+        /// 旁系亲属在美国 姓名
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(50)")]
+        public string USAUncleFamName { get; set; }
+        /// <summary>
+        /// 旁系亲属在美国 关系
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(50)")]
+        public string USAUncleFamRel { get; set; }
+        /// <summary>
+        /// 旁系亲属在美国 身份
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(50)")]
+        public string USAUncleFamStat { get; set; }
+
+        /// <summary>
+        /// 父亲姓名
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(50)")]
+        public string FthName { get; set; }
+
+        /// <summary>
+        /// 父亲出生日期
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(50)")]
+        public DateTime? FthBirth { get; set; }
+
+        /// <summary>
+        /// 母亲姓名
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(50)")]
+        public string MthName { get; set; }
+
+        /// <summary>
+        /// 母亲出生日期
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(50)")]
+        public DateTime? MthBirth { get; set; }
     }
 
 
@@ -491,4 +679,35 @@ namespace OASystem.Domain.Entities.Customer
         [Encrypted]
         public string CompanyFullName { get; set; }
     }
+
+    /// <summary>
+    /// 军事信息
+    /// </summary>
+    public class MilitaryInfo
+    {
+        /// <summary>
+        /// 军种(陆军/海军/空军等)
+        /// </summary>
+        public string MilBranch { get; set; }
+
+        /// <summary>
+        /// 军衔(列兵/上校等)
+        /// </summary>
+        public string MilRank { get; set; }
+
+        /// <summary>
+        /// 军事特长(狙击/医疗等)
+        /// </summary>
+        public string MilSkill { get; set; }
+
+        /// <summary>
+        /// 服役时间(单位:xxxx年xx月xx日) 起
+        /// </summary>
+        public DateTime? ServeTimeBegin { get; set; }
+        /// <summary>
+        /// 服役时间(单位:xxxx年xx月xx日)止
+        /// </summary>
+        public DateTime? ServeTimeEnd { get; set; }
+
+    }
 }

+ 1 - 1
OASystem/OASystem.Domain/Entities/Customer/Crm_VisaCustomerSchool.cs

@@ -18,7 +18,7 @@ namespace OASystem.Domain.Entities.Customer
         [SugarColumn(IsNullable =true,ColumnDataType ="int")]
         public int DcId { get; set; }
         /// <summary>
-        /// 学校名称和学历
+        /// 学校名称
         /// </summary>
         [SugarColumn(IsNullable =true,ColumnDataType = "varchar(100)")]
         public string School { get; set; }

+ 3 - 0
OASystem/OASystem.Infrastructure/Repositories/BaseRepository.cs

@@ -1,5 +1,6 @@
 using OASystem.Domain.Entities;
 using OASystem.Domain.Enums;
+using System.Reflection;
 
 namespace OASystem.Infrastructure.Repositories
 {
@@ -322,5 +323,7 @@ namespace OASystem.Infrastructure.Repositories
             return result > 0;
         }
         #endregion
+
+        
     }
 }

+ 65 - 0
OASystem/OASystem.Infrastructure/Tools/CommonFun.cs

@@ -873,4 +873,69 @@ public static class CommonFun
         return index >= 0 && index < list.Count ? list[index] : null;
     }
 
+
+    #region 姓名 取姓和名
+    /// <summary>
+    /// 取姓
+    /// </summary>
+    public static string GetLastName(this string fullName)
+    {
+        if (string.IsNullOrWhiteSpace(fullName)) return string.Empty;
+
+        // 1. 英文/拼音:有逗号
+        var comma = fullName.IndexOf(',');
+        if (comma >= 0) return fullName[..comma].Trim();
+
+        // 2. 英文/拼音:无逗号,按最后一个空格切
+        var lastSpace = fullName.LastIndexOf(' ');
+        if (lastSpace >= 0) return fullName[..lastSpace].Trim();
+
+        // 3. 中文:默认首字为姓
+        return fullName.Length switch
+        {
+            0 => string.Empty,
+            1 => fullName,
+            _ => fullName[..1]   // 复姓可改成 ..2
+        };
+    }
+
+    /// <summary>
+    /// 取名(去掉姓)
+    /// </summary>
+    public static string GetFirstName(this string fullName)
+    {
+        if (string.IsNullOrWhiteSpace(fullName)) return string.Empty;
+
+        var comma = fullName.IndexOf(',');
+        if (comma >= 0) return fullName[(comma + 1)..].Trim();
+
+        var lastSpace = fullName.LastIndexOf(' ');
+        if (lastSpace >= 0) return fullName[(lastSpace + 1)..].Trim();
+
+        // 中文:去掉首字
+        return fullName.Length <= 1 ? string.Empty : fullName[1..];
+    }
+    #endregion
+
+    /// <summary>
+    /// 从地址字符串取出省份和城市
+    /// </summary>
+    public static (string province, string city) SplitProvinceCity(string src)
+    {
+        if (string.IsNullOrWhiteSpace(src)) return ("", "");
+
+        ReadOnlySpan<char> s = src.AsSpan();
+
+        // 1. 省
+        int pEnd = s.IndexOf("省");
+        if (pEnd < 0) pEnd = s.IndexOf("自治区");
+        string province = pEnd < 0 ? "" : s[..pEnd].ToString();
+
+        // 2. 市
+        int cStart = pEnd + (pEnd >= 0 ? (province.EndsWith("自治区") ? 3 : 1) : 0);
+        int cEnd = s[cStart..].IndexOf("市");
+        string city = cEnd < 0 ? "" : s.Slice(cStart, cEnd).ToString();
+
+        return (province, city);
+    }
 }