尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

SpringBoot集成ofdrw实现PDF/OFD转换与SM2签名实战指南

发布时间:2026/9/29 15:24:43

资讯中心
01
ARTICLE

SpringBoot集成ofdrw实现PDF/OFD转换与SM2签名实战指南

SpringBoot集成ofdrw实现PDF/OFD转换与SM2签名实战指南
最近一个电子档案项目让我在 SpringBoot 里把 OFD 相关能力整了个遍新归档文件必须是 OFD 版式历史存量还有上百万份 PDF 要转成 OFD归档前还得用国密 SM2 做数字签名。当时第一反应是找现成方案结果发现国内能打的 Java 开源库基本绕不开ofdrw。这篇文章把这套集成的关键路径、代码和踩坑记录整理出来给同样要做 PDF/OFD 转换和 SM2 签署 OFD 的朋友做个参考。1. 为什么业务系统要自己处理 OFD 和 SM21.1 从实际需求看 OFD 的不可替代性先说 OFD。很多人第一反应是这不就是个国产 PDF 吗但只要真做过电子档案、电子合同、电子票据这类项目就会明白 OFD 不是替代 PDF而是补上了 PDF 在版式文档管理上的短板。OFD 采用 XML 作为底层描述每个页面、每个资源、每个对象都有明确的目录结构文件内部自带容器化和资源描述机制。这意味着系统可以精确控制文档的哪个区域属于哪个业务对象能够直接对文档局部进行操作而不是像 PDF 那样把内容揉成一团路径和对象的集合。以档案系统为例我们要求归档文件必须满足版式固定、来源清晰、内容不可篡改。PDF 虽然也能做这些事但要在 PDF 上实现高可信度的电子签章依赖的是一个封闭的私有规范不同厂商封装层次不同互认成本很高。OFD 在这方面有天然优势结构开放、元数据与资源分离、签名模块独立并且它的签名机制从设计之初就考虑了国密系列算法。我接手项目时甲方给出的验收条件很明确——文件的存储格式必须是 OFD电子签名必须支持 SM2 算法验签结果要能被第三方 OFD 阅读器识别。这不是拍脑门的需求很多行业的归档和电子凭证规范里已经明确把 OFD 写进了标准。所以做系统改造时OFD 是绕不开的硬性条件。1.2 SM2 签名在归档场景里解决了什么SM2 属于国密非对称算法曲线参数固定为 256 位密钥长度相较 RSA 有明显优势。SM2 不是单独解决签名问题的它通常和 SM3 搭配构成SM3 摘要 SM2 签名的组合。归档文件需要防抵赖、防篡改而 SM2 签名能够把文件内容与签名实体绑定一旦内容变化验签立刻失败。这和我们常见的 MD5 RSA 是同一套思路区别在于 SM2 的算法参数是国家商用密码标准的一部分在特定行业内有强制的合规要求。关于 SM2 的具体算法细节比如椭圆曲线参数、签名过程、密钥派生方式建议直接看 GM/T 0003 标准原文。这里我强调的是集成层面的认识SM2 签名的输入是一段摘要值输出的是一段二进制签名数据最后再把签名数据、签名时间、签名者证书、签名区域等信息封装进 OFD 的签名结构中。真正复杂的部分并不是密码运算而是 OFD 文件怎么把签名信息安放进去这就是 ofdrw 要替我们解决的重点。1.3 ofdrw 在项目里的定位ofdrw 是一个基于 Java 的 OFD 处理工具包提供了解析、生成、转换、签名和验签的整套能力。项目层面它不是一个单一的 jar而是按功能拆了 core、reader、converter、sign、gm 等模块。这意味着我们可以按需引入不用全量带进工程。从我实际使用体验来看ofdrw 的 API 虽然有点文档跟不上代码的感觉但胜在覆盖场景足够全真正缺失的部分也方便自行扩展。选型时我对比过几个方案一是调用商业 OFD 控件的接口二是购买中间件产品三是直接用开源库改造。商业控件胜在稳定但价格不便宜而且权限管理和服务化集成并不灵活中间件产品适合做独立服务如果只是想在现有 SpringBoot 系统里增加转换和签名接口反而显得重。最终选择 ofdrw核心原因只有一条——它能把 OFD 的读写、转换、签名这几个核心能力全部塞进 Java 进程内我只需要封装成服务接口即可。2. 引入 ofdrwSpringBoot 工程改造的关键细节2.1 核心模块与版本锁定ofdrw 的模块划分非常细初次接触容易眼花缭乱。我实际用到的模块是这四个ofdrw-core、ofdrw-reader、ofdrw-converter、ofdrw-sign。如果你的项目里只需要读取和校验 OFD那加ofdrw-reader就够ofdrw-core是基础依赖会自动带入。如果要做 PDF 与 OFD 互转必须引入ofdrw-converter。这个模块核心工作是解析 OFD 的页面结构然后通过 PDFBox 把渲染结果输出成 PDF反过来也能处理 PDF 到 OFD 的转换。如果要做 SM2 签名那要引ofdrw-sign它内部会调用ofdrw-gm或者依赖 BouncyCastle 提供的国密算法实现。下面是我项目里精简后的 pom 依赖dependency groupIdorg.ofdrw/groupId artifactIdofdrw-core/artifactId version1.27.1/version /dependency dependency groupIdorg.ofdrw/groupId artifactIdofdrw-reader/artifactId version1.27.1/version /dependency dependency groupIdorg.ofdrw/groupId artifactIdofdrw-converter/artifactId version1.27.1/version /dependency dependency groupIdorg.ofdrw/groupId artifactIdofdrw-sign/artifactId version1.27.1/version /dependency dependency groupIdorg.bouncycastle/groupId artifactIdbcprov-jdk18on/artifactId version1.78.1/version /dependency dependency groupIdorg.bouncycastle/groupId artifactIdbcpkix-jdk18on/artifactId version1.78.1/version /dependency必须强调一句版本一定要锁死。ofdrw 的 API 变动非常频繁不同版本之间类名和方法名都可能不同我遇到过升级一个小版本后PdfConverter构造方式直接变了的情况。如果项目里有多个模块同时使用 ofdrw建议在 dependencyManagement 里统一管理版本避免 maven 依赖冲突。2.2 一个非常容易忽略的线程安全问题很多人把 ofdrw 当成普通工具类直接用然后在并发环境下发现各种奇怪异常。ofdrw 中的OFDReader、PdfConverter这些核心对象不是线程安全的每个线程同时处理不同的 OFD 文件时复用同一个 reader 对象会导致页面解析状态错乱。我在项目实施初期就在并发测试时遇到过一次 NPE排查半天最后定位到是全局单例对象的问题。稳妥的做法是在 Service 层每次调用时创建独立的 reader 和 converter用完之后立刻关闭。虽然频繁创建对象会带来一点性能开销但相比数据错乱和排查成本这点开销完全可以接受。如果你希望复用对象至少要确保一个 OFDReader 实例只被一个线程持有并且处理完文件后调用 close 释放资源。2.3 在 SpringBoot 里把 ofdrw 封装成服务我不建议直接在 Controller 里写 ofdrw 的调用逻辑因为转换和签名涉及大量 IO 操作、临时文件管理和异常边界处理这些都放到 Service 层更合适。项目里我建了一个OfdDocService专门对上层提供转换和签名接口。这样做的另一个好处是 Controller 层不用感知 ofdrw 的 API 变化以后升级依赖时只需要改 Service 内部。Service 里还要统一处理临时文件的清理策略。OFD 转换和签名都会产生中间文件如果 JVM 异常退出或者程序没有及时清理临时目录会堆积大量垃圾文件。我一般把中间文件放在一个固定的临时目录并在 finally 块中删除如果服务异常退出启动时也会清扫该目录。3. 转换层怎么选型OFD 转 PDF 与 PDF 转 OFD 是两回事3.1 OFD 转 PDF 是硬需求但要注意渲染差异很多系统场景是归档用 OFD预览用 PDF所以 OFD 转 PDF 几乎是标配功能。ofdrw-converter 模块里提供了现成的PdfConverter它做的事情是把 OFD 页面中的矢量图形、文字、图片按布局关系重新绘制到 PDF 页面上。大致调用方式是public void ofdToPdf(Path srcOfd, Path destPdf) throws Exception { try (OFDReader reader new OFDReader(srcOfd)) { PdfConverter converter new PdfConverter(reader, destPdf.toFile()); converter.convert(); } }这里有三个细节非常影响转换质量。第一个是字体。OFD 内部通常记录了字体名称但不会把字体文件完整嵌进文档如果转换环境里没有对应字体PDF 输出的文字会直接跳过或者替换成默认字体版面就可能乱掉。生产服务器上至少要把常用中文字体宋体、黑体、仿宋、楷体装好并且让 JVM 能找到它们。Linux 服务器尤其容易踩这个坑很多精简镜像里一个中文字体都没有。第二个是透明度与渐变。部分阅读器生成的 OFD 用到了透明度叠加效果PDFBox 对透明度处理不够精细转换出来的 PDF 会出现色差或者图形缺失。遇到这类文档我会先转换一版对比视觉效果如果差异明显就把该文档走图片化兜底流程具体做法在 3.2 小节说明。第三个是加密 OFD。带权限控制的 OFD 文件直接转换可能会失败需要先做权限校验或者解密后再转业务上要提前定好这个规则。3.2 PDF 转 OFD不建议硬碰矢量解析优先按页图片化PDF 转 OFD 比反向转换复杂得多。PDF 内部可能包含复杂字体、多重嵌套裁剪区、透明对象、渐变填充想把这些内容百分之百等价翻译成 OFD 的矢量描述工作量不亚于重写一个 PDF 解析器。ofdrw 官方在 PDF 转 OFD 方向上给出的参考实现也比较谨慎重点走的是PDF 渲染成图片再写入 OFD这条路子。我自己的经验是同样建议走图片化方案把每一页 PDF 按指定 DPI 渲染成图片然后作为 OFD 页面里的图片对象一页页写进去。这样做的好处是转换速度快、版面完全一致、兼容各种稀奇古怪的 PDF 内部结构缺点是生成的文件不可做文字检索和复制而且文件体积会比矢量方式的 OFD 大一些。对于业务场景是历史归档备份和电子凭证存档的系统图片化完全够用因为归档文件的核心诉求是不失真和可签署并不依赖文字提取。如果系统确实需要 OFD 文件里的文本可供检索那就要用 ofdrw 的解析能力逐页读取 PDF 文本和坐标信息再映射到 OFD 的文本对象上。这个方案工程量大得多我在项目里只针对特定客户做了后面会单独写一篇详细讲。用图片化方案时PDF 渲染的 DPI 建议不低于 144。DPI 太低文字边缘会有明显锯齿太高则文件体积膨胀。实际测下来 150 到 200 是一个比较平衡的区间。下面是核心代码public Path pdfToOfd(Path srcPdf, String outFileName) throws Exception { Path destOfd Files.createTempFile(convert_, .ofd); try (PDDocument doc PDDocument.load(srcPdf.toFile()); OFDDoc ofdDoc new OFDDoc(destOfd.toFile())) { PDFRenderer renderer new PDFRenderer(doc); int pageSize doc.getNumberOfPages(); for (int i 0; i pageSize; i) { BufferedImage image renderer.renderImageWithDPI(i, 150, ImageType.RGB); ImageData imageData ImageDataUtil.create(image, ImageType.PNG); // 在 OFD 页面中添加图片对象 ofdDoc.addPage(new PageLayout(PageSize.pageOf(PapeSizeType.A4)), imageData); } return destOfd; } catch (IOException e) { throw new OfdConvertException(PDF转OFD失败: srcPdf, e); } }实际项目中还需要处理页面尺寸映射。PDF 页面可能是 A4、A3 或者自定义尺寸如果 OFD 页面全部固定为 A4最终会拉伸变形。正确做法是读取每个 PDF 页面的 MediaBox动态设置 OFD 页面尺寸。这个适配逻辑不复杂但非常影响专业性甲方验证的时候往往会直接拿很规整的打印样张来看。3.3 转换失败时的备用链路正常文档的转换成功率很高但真实业务环境里总有例外PDF 本身加密、PDF 嵌入字体损坏、页面尺寸异常、图片流格式不标准等。这些异常会让图片化方案也会失败。我们在项目里设计了备用链路当 OFD 转 PDF 失败时自动降级成只提取 OFD 中的文本和图片摘要输出成预览文件PDF 转 OFD 失败时则把 PDF 首页转成图片配合提示信息交给业务方人工确认。这条备用链路不是技术上的完美方案但它保证了接口不会因为个别异常文件直接中断业务方也认可这种处理。如果你的系统对出错率要求很敏感很建议在转换模块外面包一层降级策略。4. SM2 签名 OFD从密钥生成到验签闭环4.1 签名前必须理解 OFD 的签名封装结构SM2 签名 OFD 不是简单地算个摘要、签个字、塞到文件尾部就完事。OFD 的签名机制在标准里有专门结构描述签名区域、签名值、签名时间、签名者证书、签名算法等等必须按照规范组织成标准数据包后续验签程序和第三方阅读器才能正确识别。整体流程可以这样理解先把要签名的 OFD 文件做一次预处理计算文件本体的摘要值用 SM3 算法得到摘要然后用签名者的 SM2 私钥对摘要做签名生成签名值最后把签名值、证书信息、签名时间、签名范围打包成 OFD 签名列表写回 OFD 文件。任何一步的顺序或者编码有误都可能导致签名文件在第三方软件里显示无效签名。4.2 生成和加载 SM2 密钥的细节SM2 密钥对的生成在 Java 里需要依赖 BouncyCastle。首先要确保 BouncyCastle Provider 已经注册到 JVM否则后续所有国密算法调用都会报 No such algorithm。static { if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) null) { Security.addProvider(new BouncyCastleProvider()); } } public KeyPair createSm2KeyPair() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { KeyPairGenerator kpg KeyPairGenerator.getInstance(EC, BouncyCastleProvider.PROVIDER_NAME); ECGenParameterSpec sm2Spec new ECGenParameterSpec(sm2p256v1); kpg.initialize(sm2Spec, new SecureRandom()); return kpg.generateKeyPair(); }密钥长度不需要额外指定SM2 规定的曲线本身就是 256 位。实际生产环境私钥不应该生成后放在内存里裸用通常需要对接硬件密码机或者使用加密机导出的密钥容器。我在项目里最开始是在本地写了个工具类生成密钥对来做联调上线前才对接客户的密码机。密码机对接方案里私钥不需要离开密码机应用侧只需要持有公钥和会话句柄签名操作远程调用密码机完成。这也是 OFD 签名集成里最容易被低估的一项工作量。4.3 核心签名流程实现签名最终调用 ofdrw-sign 模块的能力。我这里给出一个完整度比较高的加入签名的伪代码并补充一些细节说明public void signOfd(Path srcOfd, Path destOfd, PrivateKey privateKey, X509Certificate cert, String signerName) throws Exception { // 1. 打开待签名文件 try (OFDReader reader new OFDReader(srcOfd)) { // 2. 构建签名配置签名区域、签名人、签名图片、签名算法 SignConfig config new SignConfig.Builder() .setUserName(signerName) .setSignatureTime(new Date()) .setSignAlgorithm(SM3withSM2) .setSignArea(new PageRange(1), new SignLocation( new BigDecimal(100), new BigDecimal(100), new BigDecimal(100), new BigDecimal(60))) .build(); // 3. 调用签名器执行签名 OFDSigner signer new OFDSigner(reader); // 设置签名者证书、私钥、签名配置 signer.sign(config, privateKey, cert, destOfd); } }需要注意几个点签名区域不能压盖正文文字否则验签时视觉上会遮挡内容签名时间建议统一使用 ISO 8601 字符串格式不同阅读器的解析宽容度不同签名算法名称到底写SM3withSM2还是SM2withSM3取决于 ofdrw 这个版本内部声明的映射名我项目里两个字符串都用过最后以官方 KeyStore 工具类默认值为准。ofdrw-sign 的 API 在不同版本之间变化很大我上面的代码只能表达功能流程实际动工时一定要以所引入版本的源码和示例为准。建议直接到 ofdrw 的官方仓库搜ofdrw-sign下的 example 代码对着自己版本看一遍再动手。4.4 验签流程与第三方阅读器兼容性签名做完了验证才是真正的验收。验签分为两层第一层是密码学层面用证书里的公钥对签名值做verify操作第二层是结构层面验证 OFD 签名包里的信息是否和文件内容匹配。实际项目里甲方通常会用数字阅读器、数科 OFD、福昕 OFD 等第三方工具来验证签名有效性所以兼容性测试必不可少。我用代码做验签时参考了下面这个思路Signature verifier Signature.getInstance(SM3withSM2, BouncyCastleProvider.PROVIDER_NAME); verifier.initVerify(publicKey); verifier.update(originalData); boolean valid verifier.verify(signValue);验签失败时先不要怀疑密码学算法优先检查下面几个问题一是摘要算法是否一致如果签名时用的是 SM3 摘要验签时用了 SM3withSM2 则没有问题但如果文档在签名后被其他程序改动过验签必失败二是签名值是否被 Base64 编解码处理过很多签名程序会不经意地多转一次编码导致原始签名值被破坏三是证书链是否可信验签环境是否能追溯到根证书。第三方阅读器验签不通过的最常见原因是签名包里的证书、签名时间等属性没有被正确置入或者说签名区域与实际坐标有冲突。这种问题靠代码层面很难完全规避最好的做法是准备一批测试文档分别用不同阅读器验证把不通过的反馈记录下来逐项调整。5. 实际应用中的六个坑与解决办法5.1 中文字体和 PDFBox 字体目录问题前面简单提过字体问题这里展开说。ofdrw 转换时默认的字处理逻辑依赖 Font 类解析系统字体Windows 服务器一般没什么问题Linux 服务器上如果没有 ttf 字体文件中文字符会变成方框或者直接消失。解决办法分三层。第一层是服务器上安装字体包例如fonts-wqy-microhei、fonts-wqy-zenhei或者直接丢一套中文 ttf 到$JAVA_HOME/lib/fonts。第二层是在代码里显式注册字体目录让 ofdrw 使用指定字体文件渲染文字。第三层是在字体缺失时提前中断转换而不是生成一个版面错乱的文档这样业务方不会把坏文件存进归档系统。5.2 大量并发转换导致的内存溢出我在做完第一版联调后做了一轮并发压测100 个线程同时转 OFD 转 PDFJVM 直接撑爆了。ofdrw 解析处理内部会有大量的图片对象和中间流对象如果每个任务都叠加几份大文件内存很容易被打满。稳妥的方案是给转换任务设置一个单独的线程池限制最大并发数并且使用有界的任务队列。我当前的配置是核心线程 4、最大线程 8、队列容量 100超出队列容量的任务走快速失败策略。如果你对性能有更高要求可以考虑分页流式转换但那样代码复杂度会直线上升一般业务系统没必要。5.3 大文件转换时的临时目录堆积OFD 转 PDF 的过程中ofdrw 会先把 OFD 的结构解析到内存同时也会在临时目录写一些中间产物。如果文件数量多临时目录很快就会被撑满。我在服务器上单独划了一个挂载盘作为转换临时目录同时在 Service 层做定时清理每次任务完成后删除本次产生的临时文件每天凌晨再清扫一次超过 24 小时未使用的临时文件。5.4 PDF 加密和图片化 PDF 的兼容性带密码的 PDF 直接转换会抛异常。业务上要做一次统一判断有密码的 PDF 先通过密码打开再转换如果密码错误则返回明确的错误码不要把异常直接甩给前端。很多历史扫描 PDF 内部实际上是一个大图片转换速度虽然快但转出来的 OFD 文件体积偏大。这类文件在归档时如果要求控制体积就需要做图片压缩处理。5.5 SM2 签名后的 OFD 再被 PDF 转换器处理签名和转换不要混用同一个 OFDReader 实例我踩过一次很隐蔽的坑签名后的 OFD 文件再去做 OFD 转 PDF转换结果里签名区域和印章图片错位了。原因是签名后的文件新增了签名资源和签名对象转换器在解析时如果没有正确处理新增资源页面坐标计算会偏差。解决方法是签名后的文件在进行任何后续处理前重新读取一次文件流并明确告诉转换器这个文件是带签名结构的。5.6 ofdrw 版本升级带来的 API 破坏我在这次项目中前后经历了三个小版本升级每次都遇到了 API 变更比较烦恼。这里分享一个最简单实用的方法不要升级依赖版本除非新增功能确实需要。项目稳定运行后升级 ofdrw 之前一定要先在独立分支上跑一遍全量转换和签名用例对比输出结果后再考虑合并。我在 pom 里锁死当前验证过的一个稳定版本后续新需求如果确实需要新版 API我再单独评估。6. 可以直接改用的 Controller 模板和测试方法6.1 接口层只暴露功能不暴露文件细节设计接口时建议直接使用文件路径或者字节数组的对外输入输出让调用方不用关心 OFD 底层结构。我的 Controller 写法是下面这种形式上传 PDF 后返回一个 OFD 下载链接也支持在线预览。RestController RequestMapping(/api/ofd) public class OfdFileController { private final OfdConvertService convertService; public OfdFileController(OfdConvertService convertService) { this.convertService convertService; } PostMapping(/pdf-to-ofd) public ResponseEntityString pdfToOfd(RequestParam(file) MultipartFile file) { try { Path src Files.createTempFile(upload_, .pdf); file.transferTo(src); Path result convertService.pdfToOfd(src, file.getOriginalFilename()); return ResponseEntity.ok(result.getFileName().toString()); } catch (IOException e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(转换失败); } } PostMapping(/ofd-sign) public ResponseEntityString signOfd(RequestParam(file) MultipartFile file) { ... } }Controller 里不要直接依赖 ofdrw 的类这样后续替换实现比较容易。6.2 服务层的完整组织方式Service 层我把转换和签名拆成了两个方法同时保留了组合调用的方法方便业务方一步完成PDF 转 OFD 后立刻签名的场景。组合方法内需要注意签名一定是基于转换得到的 OFD 文件重新读取后执行不要把转换过程中的 reader 对象直接传给签名器。Service public class OfdConvertService { public Path pdfToOfd(Path srcPdf, String originalName) throws Exception { Path result Files.createTempFile(ofd_, .ofd); // 转换逻辑省略参见 3.2 小节 return result; } public Path signOfd(Path srcOfd, SignCertInfo certInfo) throws Exception { Path dest Files.createTempFile(signed_, .ofd); // 签名逻辑省略参见 4.3 小节 return dest; } public Path pdfToSignedOfd(Path srcPdf, SignCertInfo certInfo) throws Exception { Path ofd pdfToOfd(srcPdf, null); return signOfd(ofd, certInfo); } }这个结构简单但很实用后续不管是用消息队列异步处理还是改成 RPC 远程调用都不用动底层逻辑。6.3 端到端测试建议项目交付前我强烈建议做三类测试功能测试、兼容性测试、并发测试。功能测试重点验证转换不丢页、不丢内容、签名后能够被有效验证。我在测试阶段准备了一个包含表格、图片、混合字体、多级目录的复杂文档每轮改动后先跑这个文档。兼容性测试需要准备多款 OFD 阅读器至少要覆盖数字 OFD、福昕 OFD、浏览器插件这三类。对于签名文件还要用阅读器打开检查证书链信息是否正确显示。并发测试按生产预估值抬高 20% 的压力跑观察 JVM 内存曲线和临时目录文件数。建议在测试环境中直接使用和线上一致的 Linux 环境和字体配置避免出现字体验证通过但线上字体缺失的尴尬情况。7. 让我反复折腾后最想提醒的一件事这套 OFD 集成方案跑通之后我最深的感受是技术难点从来不在 ofdrw 的 API 怎么调用而在文件流转边界和异常处理边界。PDF 转换、OFD 签名、SM2 验签每个环节都有大量的中间文件、对象生命周期和编码细节任何一个边界没处理好生产环境就会出现难以排查的偶发故障。如果你也准备在 SpringBoot 项目里接 ofdrw建议先把文件处理流程画到一张纸上从上传落盘到中间转换再到签名输出最后到临时文件清理把每一步的资源和异常都列出来。然后再去写代码。我最初跳过了这一步结果就是在并发测试和夜间定时任务里不断遇到文件句柄泄漏和临时文件残留的问题后来回头补全了处理流程稳定性才真正提上来。另外SM2 的密钥管理部分如果业务上有条件用硬件密码机尽量一开始就对接不要等系统上线后再补。我在项目后期改造密钥管理方式时踩的坑比写 ofdrw 业务代码多得多。提前规划好密钥存储、安全访问和证书轮换后面对接其他业务系统也会顺畅很多。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。