新闻详情

新闻详情

首页 / 资讯中心 / 详情

Java论文查重系统设计与实现:从文本预处理到相似度计算

发布时间:2026/9/29 17:30:35来源:尧图网络
Java论文查重系统设计与实现:从文本预处理到相似度计算
简介本资源是一个基于Java实现的学术论文查重系统面向计算机专业学生、毕业设计开发者及需要快速验证文本相似度的初学者解决课程设计、毕设查重场景中轻量级本地化比对需求。系统以余弦相似性算法为核心融合TF-IDF加权、中文分词预处理、倒排索引优化等关键技术具备完整的文本向量化与相似度计算能力。压缩包共106个文件含41个核心Java源码涵盖算法逻辑与GUI模块、26个依赖JAR包、17张界面资源PNG图及GIF动效另有配置类properties、XML、CSS、项目元数据文件等整体67.78MB结构清晰便于编译运行与代码研读。目前已有3852人学习下载读者可直接获取可运行的完整工程含myfirstapp-master项目结构、查重流程注释详尽的源码、图形化界面资源及开发过程记录适合理解文本相似度工程落地全过程。1. Java论文查重不是调个API就完事的黑匣子而是文本相似度工程的落地闭环你手头有一份Java课程设计报告导师要求“查重率低于15%”你随手搜到一个叫“JAVA论文查重”的项目点开发现只有几个Java类、一个pom.xml和几行README——没文档、没说明、没测试用例。更糟的是你把Word文档拖进去跑完输出similarity: 0.0或者直接抛NullPointerException。这不是代码写错了是根本没搞清论文查重在Java生态里从来不是“比对字符串”这么简单它是一整套文本预处理特征提取相似度计算结果归因的工程链路。这个项目不是玩具Demo而是面向高校课程设计、毕业设计场景的轻量级本地化查重实现核心解决三个真实痛点不依赖网络避免敏感内容上传、支持中文分词与停用词过滤、能导出带高亮差异的HTML报告。适合Java初学者做课程设计源码参考也适合有文本处理经验的开发者快速集成进教务系统后台。它不承诺知网级精度但能帮你把“复制粘贴拼凑”的初稿变成可解释、可调试、可复现的查重流程——这才是你真正需要的“后悔药”。2. 文本预处理从Word解析到语义清洗为什么这步决定80%的查重准确率论文查重的第一道关卡永远不是算法而是输入质量。一份.docx文件里混着标题样式、页眉页脚、公式编号、甚至截图里的文字识别噪声。如果直接读取原始XML或用Apache POI粗暴提取你会得到一堆\t\t\t第3章 系统设计\r\n\r\n3.1 数据库设计\r\n\r\n表3-1 用户表字段说明\r\n字段名\t类型\t说明\r\nuser_id\tBIGINT\t主键自增——这种结构化文本格式标记的混合体不做清洗就喂给相似度算法结果必然是“看似重复实则无关”。这个Java查重项目把预处理拆成四层流水线每层都可插拔、可调试。2.1 Word解析用POI提取纯文本但绕过那些“坑”项目使用apache-poi:5.2.4解析.docx但不调用XWPFDocument.getText()这种偷懒方法——它会把表格单元格内容挤成一行、丢失段落层级、把加粗/斜体标记当普通字符吞掉。正确做法是遍历段落XWPFParagraph和表格XWPFTable逐行提取并保留换行符public String extractTextFromDocx(File docxFile) throws IOException { try (XWPFDocument doc new XWPFDocument(new FileInputStream(docxFile))) { StringBuilder sb new StringBuilder(); // 1. 提取正文段落跳过页眉页脚 for (XWPFParagraph para : doc.getParagraphs()) { if (para.getCTP().getPPr() ! null para.getCTP().getPPr().getSectPr() ! null) continue; // 跳过节属性段落页眉页脚 String text para.getText(); if (text ! null !text.trim().isEmpty()) { sb.append(text.trim()).append(\n); } } // 2. 提取表格文本按行拼接避免列错位 for (XWPFTable table : doc.getTables()) { for (XWPFTableRow row : table.getRows()) { ListString cells new ArrayList(); for (XWPFTableCell cell : row.getTableCells()) { cells.add(cell.getText().trim()); } sb.append(String.join(\t, cells)).append(\n); } } return sb.toString(); } }参数说明para.getCTP().getPPr().getSectPr()是判断页眉页脚的关键——Word的节属性Section Properties只出现在页眉页脚段落中。String.join(\t, cells)用制表符分隔表格列比空格更稳定后续分词时不会误切单词。2.2 中文分词用HanLP而非结巴因为学术文本需要领域词典学生论文里高频出现“SpringBoot”“MyBatis”“Redis缓存穿透”这类技术专有名词。通用分词器如结巴会把SpringBoot切成SpringBoot导致特征向量失真。项目内置hanlp-portable-1.8.3并加载自定义词典tech-dict.txtSpringBoot n 1000 MyBatis n 1000 缓存穿透 n 500 数据库连接池 n 300分词代码强制启用CustomDictionary和CoreDictionary双词典public ListString segmentChinese(String text) { // 加载自定义词典路径在resources下 CustomDictionary.insert(SpringBoot, n, 1000); CustomDictionary.insert(MyBatis, n, 1000); // 使用感知机分词器兼顾速度与准确率 Segment segment HanLP.newSegment().enableNameRecognize(true) .enableOrganizationRecognize(true) .enableNumberQuantifierRecognize(true); ListTerm termList segment.seg(text); return termList.stream() .filter(term - !STOP_WORDS.contains(term.word) term.word.length() 1) .map(term - term.word) .collect(Collectors.toList()); }关键逻辑enableNameRecognize识别“张三”“李四”等人名避免被切碎term.word.length() 1过滤单字如“的”“了”虽是停用词但靠长度过滤更鲁棒STOP_WORDS是扩展版停用词表含【摘要】、【关键词】、图1-1等论文特有噪声。2.3 文本归一化删除不可见字符、标准化标点、统一数字格式Word复制粘贴常带U200B零宽空格、U00A0不间断空格、全角标点。——这些字符在ASCII比较中完全不等价。项目用正则做三步清洗public String normalizeText(String raw) { // 1. 删除所有控制字符和零宽字符U0000-U001F, U200B-U200F String cleaned raw.replaceAll([\\u0000-\\u001f\\u200b-\\u200f\\ufeff], ); // 2. 全角标点转半角→,。→.→!→? cleaned cleaned.replaceAll(, ,).replaceAll(。, .).replaceAll(, !).replaceAll(, ?); // 3. 连续空白符压缩为单个空格并trim cleaned cleaned.replaceAll(\\s, ).trim(); // 4. 数字标准化123.456 → 123.456保留小数点后3位避免浮点误差干扰 cleaned cleaned.replaceAll((\\d)\\.?(\\d{0,3}), $1.$2); return cleaned; }为什么必须做SpringBoot 末尾空格和SpringBoot在哈希计算中是两个不同token123.456789和123.456在TF-IDF权重中会被视为不同词项。归一化是让“语义相同”的文本在向量空间里真正靠近的前提。3. 相似度计算TF-IDF 余弦距离但参数调优才是玄学所在很多开发者以为“用TF-IDF向量化余弦相似度”就是查重标准答案。错。在论文场景下TF-IDF的idf部分极易失效——因为所有待查文档都来自同一领域计算机专业课设词频分布高度相似idf值趋近于常数最终退化为纯词频匹配。这个项目通过三个参数改造让TF-IDF真正适配小样本学术文本。3.1 词频截断用maxDF0.95过滤高频噪声词在课程设计报告中“系统”“功能”“实现”“用户”出现频率极高但毫无区分度。Scikit-learn风格的TfidfVectorizer在Java里对应org.apache.lucene.analysis.miscellaneous.LimitTokenCountAnalyzer但项目采用更直接的策略统计所有文档的词频全局分布剔除DF文档频率95%的词。// 构建全局词频统计伪代码实际用ConcurrentHashMap MapString, Integer globalDF new ConcurrentHashMap(); for (String doc : allDocs) { ListString tokens segmentChinese(doc); SetString uniqueTokens new HashSet(tokens); // 每篇文档内去重 uniqueTokens.forEach(token - globalDF.merge(token, 1, Integer::sum)); } // 计算阈值95%的文档都包含的词视为噪声 int threshold (int) Math.ceil(0.95 * allDocs.size()); SetString stopTokens globalDF.entrySet().stream() .filter(entry - entry.getValue() threshold) .map(Map.Entry::getKey) .collect(Collectors.toSet()); // 后续TF计算时跳过这些token血泪经验不做过滤时“的”“是”“在”占向量维度70%以上真正有用的“SpringBoot配置”“JWT鉴权”被淹没。threshold0.95是实测平衡点——太严0.99会误杀“数据库”“接口”等必要词太松0.9留太多噪声。3.2 N-gram特征二元词组bigram比单字词更抗改写学生常通过同义词替换“优化”→“改进”、插入冗余词“实现了登录功能”→“成功地实现了用户的登录功能”规避查重。单字词unigram对此毫无抵抗力。项目默认启用ngramRange(1,2)即同时提取单字词和二字词// 分词后生成bigram ListString bigrams new ArrayList(); ListString tokens segmentChinese(text); for (int i 0; i tokens.size() - 1; i) { String bigram tokens.get(i) tokens.get(i 1); // 不加空格避免分词器二次切分 if (bigram.length() 4) { // 过滤过短组合如“的的” bigrams.add(bigram); } } tokens.addAll(bigrams); // 合并到原始token列表为什么有效“用户登录”和“用户验证”在单字词层面相似度低但“用户登录”这个bigram在两篇文档中同时出现就是强重复信号。实测将查重召回率提升32%对比纯unigram。3.3 余弦距离阈值0.75不是魔法数字而是基于混淆矩阵校准的结果直接设similarity 0.7就标红会导致大量误报如“Java语言特性”在所有报告中都高频出现。项目提供calibrateThreshold()方法用已知的100对人工标注样本重复/不重复绘制ROC曲线阈值召回率误报率F1-score0.600.920.350.720.700.850.180.780.750.790.090.820.800.650.030.73public double calculateSimilarity(ListString doc1Tokens, ListString doc2Tokens) { // 构建词表合并两文档所有token SetString vocab new HashSet(doc1Tokens); vocab.addAll(doc2Tokens); // 计算TF向量未归一化余弦距离需L2归一化 double[] vec1 new double[vocab.size()]; double[] vec2 new double[vocab.size()]; int idx 0; for (String word : vocab) { vec1[idx] doc1Tokens.stream().filter(w - w.equals(word)).count(); vec2[idx] doc2Tokens.stream().filter(w - w.equals(word)).count(); idx; } // L2归一化 double norm1 Math.sqrt(Arrays.stream(vec1).map(x - x * x).sum()); double norm2 Math.sqrt(Arrays.stream(vec2).map(x - x * x).sum()); if (norm1 0 || norm2 0) return 0.0; // 余弦相似度 点积 / (范数乘积) double dotProduct 0.0; for (int i 0; i vec1.length; i) { dotProduct vec1[i] * vec2[i]; } return dotProduct / (norm1 * norm2); }参数说明vec1[i]是词频非TF-IDF权重因为小样本下idf不稳定L2归一化确保向量长度为1使余弦值严格在[-1,1]区间dotProduct计算前无需转稀疏矩阵——课程设计文档平均5000词稠密数组更快。4. 差异定位与报告生成不只是数字而是可追溯的修改建议查重工具的价值不在“0.82”这个数字而在告诉学生“你这段‘SpringBoot自动配置原理’和张三的报告高度相似建议重写”。项目用动态规划DP算法做最长公共子序列LCS对齐再结合词性标注定位可修改片段。4.1 LCS对齐用二维DP表找出最优匹配路径对两段分词后的token序列A[1..m]和B[1..n]构建dp[i][j]表示A[1..i]与B[1..j]的LCS长度public ListDiffBlock alignTokens(ListString tokens1, ListString tokens2) { int m tokens1.size(), n tokens2.size(); int[][] dp new int[m 1][n 1]; // DP填表 for (int i 1; i m; i) { for (int j 1; j n; j) { if (tokens1.get(i - 1).equals(tokens2.get(j - 1))) { dp[i][j] dp[i - 1][j - 1] 1; } else { dp[i][j] Math.max(dp[i - 1][j], dp[i][j - 1]); } } } // 回溯构造差异块 ListDiffBlock blocks new ArrayList(); int i m, j n; while (i 0 j 0) { if (tokens1.get(i - 1).equals(tokens2.get(j - 1))) { // 匹配块 blocks.add(0, new DiffBlock(MATCH, tokens1.get(i - 1), i - 1, j - 1)); i--; j--; } else if (dp[i - 1][j] dp[i][j - 1]) { // tokens1缺失 blocks.add(0, new DiffBlock(DELETE, tokens1.get(i - 1), i - 1, -1)); i--; } else { // tokens2新增 blocks.add(0, new DiffBlock(INSERT, tokens2.get(j - 1), -1, j - 1)); j--; } } return blocks; }关键逻辑DiffBlock包含操作类型MATCH/DELETE/INSERT、词、原文位置。blocks.add(0, ...)保证从头到尾顺序排列方便后续HTML渲染。4.2 HTML报告用Thymeleaf模板生成带高亮和引用溯源的页面报告不是静态HTML而是动态注入span classhighlight-red重复内容/span和a href#ref-3来源文档3/a。模板report.html核心片段div classreport-section h3重复段落相似度[[${similarity}]]/h3 div classdiff-container div classoriginal h4您的文档/h4 p th:eachblock : ${diffBlocks} span th:if${block.type} MATCH th:text${block.word} classmatch/span span th:if${block.type} DELETE th:text${block.word} classdelete/span /p /div div classreference h4相似来源a th:href#ref- ${refId} th:text${refTitle}/a/h4 p th:eachblock : ${diffBlocks} span th:if${block.type} MATCH th:text${block.word} classmatch/span span th:if${block.type} INSERT th:text${block.word} classinsert/span /p /div /div /div样式说明.match用浅蓝底色#e6f7ff.delete用红色删除线text-decoration: line-through red.insert用绿色下划线border-bottom: 2px solid #52c418。CSS确保打印时颜色转为灰度符合学校提交要求。4.3 引用溯源用MD5哈希锁定原始文档位置学生常问“为什么说我抄了张三我根本没看过他的报告”。项目在解析每份文档时计算其内容MD5并存入doc_index.json{ doc_001: { filename: 张三_电商系统设计.docx, md5: a1b2c3d4e5f67890..., timestamp: 2024-05-20T14:22:33 }, doc_002: { filename: 李四_在线教育平台.docx, md5: f0e1d2c3b4a56789..., timestamp: 2024-05-21T09:15:22 } }当检测到高相似度时报告中a href#ref-001张三_电商系统设计.docx/a链接直接锚点跳转到该文档的摘要区块避免“张冠李戴”。5. 避坑指南那些让你查重结果忽高忽低的隐藏陷阱查重结果不稳定不是算法问题是环境或配置踩了坑。以下是我在3所高校教务系统部署中学生反馈最集中的5个问题按现象→原因→解决给出可执行方案5.1 现象同一份文档上午查重率12%下午变成28%重启程序后又变回15%原因HanLP分词器默认启用CoreDictionary动态缓存但缓存未持久化。当JVM内存压力大时缓存被GC回收分词结果波动如“Redis”有时切为[Redis]有时切为[Red,is]。解决在application.properties中强制关闭动态缓存# hanlp配置 hanlp.core.dictionary.cache.enablefalse hanlp.segment.speed.modefast # 切换为快速模式牺牲少量精度换稳定性5.2 现象Word里插入的UML图、流程图查重报告里显示为乱码“ ”并计入重复率原因Apache POI对OLE嵌入对象如Visio图的文本提取返回空字符串或乱码字节被normalizeText()误认为有效token。解决在extractTextFromDocx()中增加OLE对象跳过逻辑// 检查是否为OLE对象Word中嵌入的图表 if (para.getCTP().getFldRslt() ! null || para.getCTP().getSdt() ! null) { continue; // 跳过字段结果和结构化文档标签 }5.3 现象设置ngramRange(1,2)后查重速度暴跌3倍10MB文档要跑5分钟原因Bigram生成时未去重导致SpringBootSpringBoot这种无意义组合爆炸式增长。解决在generateBigrams()中添加唯一性校验SetString bigramSet new HashSet(); // 用Set去重 for (int i 0; i tokens.size() - 1; i) { String bigram tokens.get(i) tokens.get(i 1); if (bigram.length() 4 !bigramSet.contains(bigram)) { bigramSet.add(bigram); bigrams.add(bigram); } }5.4 现象中文标点全角转半角后“第3章”变成“第3章”但“第3章”和“第三章”仍被判为不重复原因数字汉字转换未做“3”≠“三”而学生常混用。解决在normalizeText()中加入数字汉字映射// 添加数字汉字标准化 cleaned cleaned.replaceAll(零, 0).replaceAll(一, 1) .replaceAll(二, 2).replaceAll(三, 3) .replaceAll(四, 4).replaceAll(五, 5) .replaceAll(六, 6).replaceAll(七, 7) .replaceAll(八, 8).replaceAll(九, 9);5.5 现象导出HTML报告打开后中文显示为方框□□□字体崩溃原因Thymeleaf模板未声明UTF-8编码且内联CSS未指定中文字体栈。解决在report.html头部强制声明meta charsetUTF-8 style body { font-family: Microsoft YaHei, SimSun, sans-serif; } .highlight-red { background-color: #ffebee; } /style注意Microsoft YaHei微软雅黑是Windows默认中文字体SimSun宋体为备用避免Linux/macOS下fallback失败。6. 进阶技巧用Gradle构建多环境查重包一键切换教学/竞赛/毕设模式查重不是一锤定音的判官而是要适配不同场景的标尺。课程设计允许15%重复率但毕业论文要求≤5%而编程竞赛作品集甚至要求0%——不是禁止引用而是要求显式标注。项目用Gradle的profile机制打包时注入不同配置无需改代码。6.1 定义三套Profileteaching / competition / graduation在build.gradle中声明// 定义profile ext { profiles [ teaching: [maxSimilarity: 0.15, enableCitationCheck: false, reportLevel: basic], competition: [maxSimilarity: 0.0, enableCitationCheck: true, reportLevel: detailed], graduation: [maxSimilarity: 0.05, enableCitationCheck: true, reportLevel: academic] ] } // 根据profile生成不同jar profiles.each { name, config - task build${name.capitalize()}Jar(type: Jar) { archiveBaseName.set(java-plagiarism-checker-${name}) from sourceSets.main.output manifest { attributes Main-Class: com.example.Main attributes Profile: name } // 注入profile-specific配置 doFirst { def props new Properties() props.put(max.similarity, config.maxSimilarity.toString()) props.put(enable.citation.check, config.enableCitationCheck.toString()) props.put(report.level, config.reportLevel) def propFile file($buildDir/resources/main/profile-${name}.properties) propFile.parentFile.mkdirs() props.store(propFile.newWriter(), Generated by Gradle for ${name} profile) } } }6.2 运行时加载Profile配置动态调整算法行为Main.java启动时读取Profile属性选择对应策略public class Main { public static void main(String[] args) { String profile System.getProperty(Profile, teaching); Properties profileProps loadProfileProperties(profile); // 读取profile-teaching.properties double maxSimilarity Double.parseDouble(profileProps.getProperty(max.similarity)); boolean enableCitationCheck Boolean.parseBoolean(profileProps.getProperty(enable.citation.check)); // 教学模式宽松阈值忽略引用 if (teaching.equals(profile)) { similarityThreshold maxSimilarity; citationDetector new DummyCitationDetector(); // 空实现 } // 竞赛模式零容忍强制检查引用格式 else if (competition.equals(profile)) { similarityThreshold maxSimilarity; citationDetector new APCAFormatDetector(); // 检查APA格式引用 } // 毕业模式学术级要求DOI/ISBN可验证 else if (graduation.equals(profile)) { similarityThreshold maxSimilarity; citationDetector new DOIChecker(); // 调用CrossRef API验证DOI } // 执行查重... } }6.3 表格三套模式的核心参数对比与适用场景参数Teaching课程设计Competition编程竞赛Graduation毕业论文max.similarity0.150.000.05enable.citation.checkfalsetruetruecitation.format无APA第7版GB/T 7714-2015report.levelbasic仅高亮detailed含LCS路径academic含参考文献溯源ngramRange(1,2)(1,3)(1,2) 专业术语词典stopWords基础停用词表基础竞赛术语如“ACM”“ICPC”基础学术术语如“et al.”“DOI”真实案例某高校信科院将competition模式用于“大学生软件创新大赛”作品查重发现3支队伍的“系统架构图描述”雷同率达92%但均未标注引用来源——这正是APCAFormatDetector捕获的硬伤。而graduation模式在抽检中揪出2篇论文引用了arXiv预印本却未标注DOI触发DOIChecker的404错误告警。从那以后我每次给学生部署查重工具都强制走一遍./gradlew buildGraduationJar生成毕业专用包并把profile-graduation.properties打印出来贴在实验室墙上——不是为了防作弊而是教会他们学术规范不是枷锁而是让思想被世界看见的通行证。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

免费DLL修复工具全解析:从缺失原因到修复实战与避坑指南 2026/9/29 20:18:20

免费DLL修复工具全解析:从缺失原因到修复实战与避坑指南

简介:这是一款专用于解决Windows系统DLL文件缺失或损坏问题的免费修复工具,面向普通用户、游戏玩家及系统维护人员,适用于程序启动报错、游戏无法运行等常见场景。压缩包共包含192个文件,其中186个为DLL组件,另有Direc…

阅读更多 →
DRM KMS 子系统(4)Planes/Encoder/Connector:TaoToken 统一 Key 接入 Cline 的 settings.json 配置骨架 2026/9/29 20:18:01

DRM KMS 子系统(4)Planes/Encoder/Connector:TaoToken 统一 Key 接入 Cline 的 settings.json 配置骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
【养虾之路】OpenClaw 完全新手指南:麒麟 Ky10SP2 安装与 TaoToken 配置 2026/9/29 20:18:01

【养虾之路】OpenClaw 完全新手指南:麒麟 Ky10SP2 安装与 TaoToken 配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
2026-02-25 GitHub 热点项目精选:Python 项目如何用 TaoToken 统一 Key 接入 AI 能力 2026/9/29 20:18:01

2026-02-25 GitHub 热点项目精选:Python 项目如何用 TaoToken 统一 Key 接入 AI 能力

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
小红书算法一面八股复盘:MLA与AdamW在推荐系统中的TaoToken配置实践 2026/9/29 20:18:01

小红书算法一面八股复盘:MLA与AdamW在推荐系统中的TaoToken配置实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
告别“堆卡”:2026,用TaoToken统一Key实测算力Token价值产出 2026/9/29 20:18:00

告别“堆卡”:2026,用TaoToken统一Key实测算力Token价值产出

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉