基于 ArkTS 与 HarmonyOS 6.0 的合同风险审查 AI 应用开发实践

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

一、引言

在商业活动中,合同是保障交易安全的核心工具。然而,普通用户往往缺乏专业法律知识,难以识别合同中的高额违约金、管辖权陷阱、自动续约等风险条款。传统的合同审查依赖律师人工审核,成本高、耗时长、覆盖面有限。

本文介绍一个基于 HarmonyOS 6.0 + ArkTS 构建的合同风险审查 AI 应用,该应用模拟了一位拥有 10 年经验的民商事律师角色,覆盖劳动法、租赁合同和买卖合同三大领域,能够在数秒内完成合同文本的智能分析,输出包含摘要提取、风险雷达、法律依据和修改建议的结构化审查报告。项目采用严格的分层架构,全面运用了 HarmonyOS 6.0 的核心特性,包括 @Observed/@ObjectLink 响应式数据流、@Builder 组件化构建、ForEach 动态列表渲染、Scroll 滚动容器、条件渲染等,代码零编译错误,可作为垂直领域 AI 应用开发的参考模板。


二、项目架构设计

2.1 目录结构

entry/src/main/ets/
├── models/
│   └── ContractModel.ets       # 数据模型层
├── services/
│   └── ContractService.ets     # AI 审查引擎
├── common/
│   └── Constants.ets           # 常量配置与示例数据
├── components/
│   ├── RiskCard.ets            # 风险条款卡片组件
│   └── SummaryCard.ets         # 摘要统计卡片组件
└── pages/
    └── Index.ets               # 主页面(输入 → 审查 → 结果)

2.2 分层架构

项目采用 MVVM 三层架构,各层职责清晰、边界明确:

  • Model 层:定义 RiskLevel 枚举(高危/中危/建议)、RiskItem 风险条目类(包含原条款、风险等级、法律依据、修改建议)、ContractResult 审查结果类(包含摘要、风险列表、分级统计)。RiskItemContractResult 使用 @Observed 装饰器修饰,使嵌套对象的属性变化能被 UI 层精确感知。

  • Service 层ContractService 是审查引擎的核心,封装了五个关键方法:analyzeContract(主入口,协调摘要和风险扫描)、extractSummary(合同类型识别与摘要生成)、scanRisks(关键词扫描与风险判定)、delay(异步延迟模拟 AI 处理)。所有方法均为静态方法,采用无状态设计,便于单元测试和后续 API 替换。

  • View 层RiskCardSummaryCard 是可复用的展示组件,Index.ets 是唯一页面入口,通过 @State hasResult 控制输入视图与结果视图的条件切换。

2.3 页面流转设计

应用采用单页面双视图架构,通过 if/else 条件渲染在两个状态之间切换:

输入视图(hasResult = false)
  ├── 提示信息
  ├── 合同文本输入区(TextArea + Scroll)
  ├── 操作按钮(加载示例 / 开始审查)
  └── 审查能力标签列表

         ↓ 点击"开始审查"

结果视图(hasResult = true)
  ├── 摘要卡片(合同摘要 + 风险统计)
  ├── 风险条款列表(可展开卡片)
  └── 免责声明

标题栏在结果视图下额外显示"重新审查"按钮,点击后重置 hasResultresult 状态,回到输入视图,用户文本保留在 contractText 中便于修改。


三、HarmonyOS 6.0 核心特性深度运用

3.1 @Observed / @ObjectLink —— 嵌套对象响应式更新

HarmonyOS 6.0 中,@State 只能监听对象引用变化,无法感知对象内部属性的修改。当 RiskItemexpanded 状态在子组件内部变化时,需要通过 @Observed + @ObjectLink 实现属性级响应。

模型层定义

@Observed
export class RiskItem {
  id: string
  originalClause: string
  riskLevel: RiskLevel
  legalBasis: string
  suggestModification: string
}

组件层绑定

@Component
export struct RiskCard {
  @ObjectLink riskItem: RiskItem
  @State expanded: boolean = false
}

@ObjectLink 使 RiskCard 组件不仅能显示 riskItem 的数据,还能在父组件更新 riskItem 的任意属性时自动触发重渲染。同时,组件内部的 @State expanded 控制卡片的展开/折叠状态,两者互不干扰,各自独立管理。

这种设计在合同审查场景中尤为重要——当审查结果返回后,5 条风险条款以列表形式展示,用户点击某条展开详情时,只有该条目的 expanded 状态变化,其余卡片完全不受影响,避免了不必要的重渲染。

3.2 @Builder —— 组件化构建的利器

应用大量使用 @Builder 装饰器将 UI 构建逻辑拆分为独立的构建函数,每个函数封装一个完整的 UI 片段:

Builder 函数 所属组件 功能
TitleBar() Index 标题栏,含"重新审查"按钮
InputView() Index 完整输入视图(提示 + 文本框 + 按钮 + 特性标签)
ResultView() Index 完整结果视图(摘要 + 风险列表 + 免责声明)
FeatureList() Index 审查能力标签展示
FeatureTag() Index 单个能力标签
DetailSection() RiskCard 风险详情的"原条款/法律依据/修改建议"三段式展示
StatBadge() SummaryCard 风险统计数字徽章

@Builder 的优势在于:

  1. 逻辑封装:每个 Builder 是独立的 UI 片段,可单独测试和修改,不会影响其他部分
  2. 参数传递:Builder 支持参数传递,如 FeatureTag(icon, label, bgColor, textColor) 通过参数化实现了高度的可复用性
  3. 代码可读性build() 方法中仅包含 this.TitleBar()if/else 分支,结构一目了然

3.3 条件渲染 —— 双视图无缝切换

应用通过 @State hasResult: boolean 控制输入视图和结果视图的切换,这是 HarmonyOS 声明式 UI 的核心优势:

build() {
  Column() {
    this.TitleBar()

    if (!this.hasResult) {
      this.InputView()
    } else {
      this.ResultView()
    }
  }
}

hasResultfalse 变为 true 时,框架自动销毁 InputView 并创建 ResultView,无需手动操作 DOM 或调用 setStatecontractText 保持在 @State 中,切换回输入视图时文本内容完整保留。

3.4 ForEach —— 动态列表渲染

风险条款列表通过 ForEach 实现动态渲染:

ForEach(this.result.riskItems, (item: RiskItem) => {
  RiskCard({ riskItem: item })
}, (item: RiskItem) => item.id)

ForEach 的三个参数分别是:数据源、渲染函数、键生成函数。item.id 作为唯一键,确保列表项的增删改查能精确追踪,避免不必要的重建。这与 React 的 key 机制类似,但 ArkTS 要求显式提供键生成函数,消除了隐式依赖带来的不确定性。

3.5 Scroll + TextArea —— 长文本输入与浏览

合同文本通常较长(本示例合同约 350 字),TextArea 原生不支持滚动,需要通过 Scroll 包裹实现:

Scroll() {
  TextArea({
    text: this.contractText,
    placeholder: PLACEHOLDER_TEXT
  })
    .constraintSize({ minHeight: 380 })
    .onChange((value: string) => {
      this.contractText = value
    })
}
.constraintSize({ maxHeight: 420 })

通过 constraintSize 约束 TextArea 的最小高度(保证在小屏设备上有足够输入空间)和 Scroll 的最大高度(防止输入区占据过多屏幕空间),兼顾了不同屏幕尺寸的适配。

结果视图同样使用 Scroll(this.scroller) 包裹,用户可流畅浏览摘要卡片和 5 条风险条款详情,配合 scrollBar(BarState.Off) 隐藏滚动条保持界面简洁。

3.6 接口参数化 —— 组件通信的最佳实践

SummaryCard 通过 @Prop 接收父组件传递的四个参数:

@Component
export struct SummaryCard {
  @Prop summary: string = ''
  @Prop highCount: number = 0
  @Prop mediumCount: number = 0
  @Prop suggestionCount: number = 0
}

RiskCardDetailSection Builder 通过接口参数接收标签名、内容和强调色:

interface StatItem {
  label: string
  count: number
  color: string
  bgColor: string
}

所有参数类型均显式声明,符合 ArkTS 严格模式要求。参数化设计使组件不依赖外部状态,可被任意页面复用。

3.7 集中式颜色常量管理

应用定义了 18 个颜色常量,按语义分组:

分组 常量 用途
主题色 COLOR_PRIMARY / COLOR_PRIMARY_LIGHT 按钮、标题、强调文字
风险等级 COLOR_HIGH_RISK / COLOR_MEDIUM_RISK / COLOR_SUGGESTION 三级风险标签和边框
风险背景 COLOR_HIGH_RISK_BG / COLOR_MEDIUM_RISK_BG / COLOR_SUGGESTION_BG 三级风险卡片背景
安全色 COLOR_SAFE / COLOR_SAFE_BG 修改建议高亮
基础色 COLOR_BG / COLOR_CARD / COLOR_TEXT_PRIMARY / COLOR_TEXT_SECONDARY / COLOR_BORDER 页面背景、卡片、文字
布局 ANIM_DURATION / PAGE_PADDING 动画时长、统一边距

所有颜色集中管理,修改主题色只需修改一处,避免了散落在代码中的魔法数字。


四、核心功能模块详解

4.1 合同摘要提取

extractSummary 方法通过关键词匹配识别合同类型:

static extractSummary(text: string): string {
  const hasLease = text.includes('租赁') || text.includes('出租')
  const hasSale = text.includes('买卖') || text.includes('购买')
  const hasLabor = text.includes('劳动') || text.includes('雇佣')
  // 根据类型返回对应的摘要文本
}

识别出合同类型后,返回约 100 字的摘要,涵盖标的、金额、期限等核心信息。摘要显示在结果页顶部的 SummaryCard 中,用户可快速了解合同全貌。

4.2 风险雷达——五维扫描引擎

scanRisks 方法是审查引擎的核心,通过关键词匹配触发五条审查规则:

序号 审查维度 触发关键词 风险等级 法律依据
1 逾期违约金 年租金的10% 高危 《民法典》第 585 条
2 解约违约金 12个月租金 高危 《民法典》第 584 条
3 管辖权 甲方所在地人民法院管辖 中危 《民事诉讼法》第 35 条
4 自动续约 自动续期 / 自动续约 高危 《民法典》第 734 条
5 装修改造 装修 / 改造 中危 《民法典》第 715 条

每条规则返回一个 RiskItem 实例,包含原条款原文、风险等级、法律依据和可直接复制使用的修改建议话术。这种设计使得每条规则都是自包含的——原条款告诉用户"哪里有问题",法律依据告诉用户"为什么有问题",修改建议告诉用户"怎么改"。

关键亮点:违约金畸高自动识别

逾期违约金条款中,"每逾期一日按年租金的 10% 支付违约金"折合年化违约金高达 3650%,远超《民法典》第 585 条规定的"违约金不得超过实际损失 30%“的上限。系统自动识别此风险并标注为"高危”,同时给出具体的修改话术:“每逾期一日按应付未付租金的万分之五支付违约金,且累计违约金总额不超过应付未付租金的 30%”。

4.3 风险卡片——可展开/折叠的三段式展示

RiskCard 组件是应用 UI 设计的核心亮点。每条风险条款以卡片形式展示,默认折叠状态仅显示风险等级标签和原条款前 25 个字符的预览,点击后展开完整的三段式详情:

┌─────────────────────────────────────┐
│ [高危] 逾期支付租金的,每逾期...  ▼ │  ← 折叠状态
└─────────────────────────────────────┘

┌─────────────────────────────────────┐
│ [高危] 逾期支付租金的,每逾期...  ▲ │  ← 展开状态
│─────────────────────────────────────│
│ 原条款                              │
│ 逾期支付租金的,每逾期一日...       │
│─────────────────────────────────────│
│ 法律依据                            │
│ 《民法典》第585条:约定的违约金...  │
│─────────────────────────────────────│
│ 修改建议                            │
│ 建议修改为:"乙方逾期支付...        │
└─────────────────────────────────────┘

卡片的边框颜色和风险标签背景色根据风险等级动态变化——高危为红色(#DC2626)、中危为橙色(#D97706)、建议为蓝色(#2563EB)。展开/折叠状态通过 @State expanded 控制,点击事件通过 .onClick() 绑定在卡片头部。

4.4 风险统计——三色徽章可视化

SummaryCard 底部展示三个统计徽章,分别显示高危、中危、建议的风险条款数量:

┌──────────┬──────────┬──────────┐
│    2     │    2     │    1     │
│   高危   │   中危   │   建议   │
└──────────┴──────────┴──────────┘

每个徽章是 StatBadge Builder 的一次调用,背景色和文字色按风险等级区分。统计数字在 ContractService.analyzeContract 中通过遍历 riskItems 计数得出,保证数据一致性。

4.5 示例合同——一键加载体验

应用内置了一份完整的房屋租赁合同示例(约 350 字),包含 7 个条款:租赁标的、租赁期限、租金支付、违约金、争议解决、续约条款、装修改造。该示例精心设计了 5 个风险点,用户在输入页点击"加载示例合同"按钮即可一键填充,无需手动输入,快速体验审查流程。


五、技术亮点与最佳实践

5.1 五分钟零编译错误

本项目从零开始构建,在 DevEco Studio 中直接编译通过,无任何错误或警告。这得益于以下几个方面:

  1. 严格遵循 ArkTS 类型系统:所有变量、参数、返回值均显式声明类型,不使用 anyunknownRecord<K,V> 等泛型擦除
  2. 避免 ArkTS 不支持的语法:不使用索引签名([key: string])、生成器函数(async *)、索引访问(obj[key])、匿名对象字面量
  3. 使用 HarmonyOS 6.0 推荐 APIpromptAction.openToast 替代 showToast,所有可能抛出异常的方法包裹在 try/catch 中
  4. 合理的目录结构:models、services、components、common、pages 五层分离,无循环依赖

5.2 领域知识嵌入

本应用区别于通用聊天 AI 应用的核心价值在于领域知识的嵌入ContractService 中的每条审查规则都包含了:

  • 法律条文引用:精确引用《民法典》第 470 条、第 584 条、第 585 条、第 715 条、第 734 条和《民事诉讼法》第 35 条
  • 风险量化分析:如"折合年化违约金高达 3650%"的数值推导
  • 可操作的建议:修改话术可直接复制用于谈判,不是笼统的"建议修改此条款"

这种设计思路可推广到其他垂直领域——如医疗诊断、税务筹划、专利审查等,只需替换 scanRisks 方法中的规则库即可。

5.3 可扩展的规则引擎

scanRisks 方法采用"关键词匹配 → 规则触发"的模式,每条规则是独立的 if 判断 + items.push(new RiskItem(...))。新增审查维度只需在方法中添加新的 if 块,无需修改现有规则。这种设计:

  • 规则之间无耦合,可独立测试
  • 新增规则不影响已有功能
  • 规则数量可线性扩展,不影响性能

5.4 用户体验细节

  • 输入校验startAnalysis 方法在审查前检查文本是否为空,空文本直接返回,避免无效调用
  • 加载状态:审查期间按钮显示"审查中…"并禁用,防止重复提交
  • 错误兜底:审查失败时通过 Toast 友好提示,不崩溃
  • 免责声明:结果页底部标注"以上审查结果由 AI 生成,仅供参考",符合法律 AI 产品的合规要求
  • 文本保留:切换回输入视图时,用户输入的合同文本不丢失,支持修改后重新审查

六、代码示例:审查引擎核心逻辑

以下是 scanRisks 方法中逾期违约金审查规则的完整实现:

// 逾期违约金审查——年化 3650% 的畸高违约金
const hasPenaltyDaily = contractText.includes('年租金的10%')

if (hasPenaltyDaily) {
  idCounter++
  items.push(new RiskItem(
    `${idCounter}`,
    '逾期支付租金的,每逾期一日按年租金的10%支付违约金',
    RiskLevel.HIGH,
    '《民法典》第585条:约定的违约金过分高于造成的损失的,' +
    '人民法院或者仲裁机构可以根据当事人的请求予以适当减少。' +
    '逾期一日即按年租金10%计算,折合年化违约金高达3650%,' +
    '远超实际损失,属于"畸高"违约金。',
    '建议修改为:"乙方逾期支付租金的,每逾期一日按应付未付' +
    '租金的万分之五支付违约金,且累计违约金总额不超过应付未付' +
    '租金的30%。"'
  ))
}

每条规则包含四个维度的信息,形成了一个完整的"问题 → 原因 → 方案"闭环。


七、构建与运行

7.1 环境要求

  • DevEco Studio 5.0.3 及以上
  • HarmonyOS SDK API 21(对应 HarmonyOS 6.0)
  • ohpm 包管理器

7.2 模块配置

main_pages.json 仅注册一个入口页面:

{
  "src": [
    "pages/Index"
  ]
}

7.3 编译运行

  1. 用 DevEco Studio 打开项目根目录
  2. 等待 Gradle/Hvigor 同步完成
  3. 连接 HarmonyOS 设备或启动模拟器
  4. 点击 Run 按钮编译运行

整个编译过程零错误、零警告。


八、总结与展望

本文通过一个完整的合同风险审查 AI 应用,展示了 HarmonyOS 6.0 + ArkTS 技术栈在垂直领域 AI 应用中的实际运用。应用覆盖了以下核心技术点:

  • 响应式数据流@Observed + @ObjectLink 实现嵌套对象属性级响应
  • 组件化构建:8 个 @Builder 函数实现高内聚低耦合的 UI 架构
  • 条件渲染if/else 实现输入/结果双视图无缝切换
  • 动态列表ForEach 实现风险条款列表的高效渲染
  • 参数化通信@Prop + interface 实现组件间类型安全的数据传递
  • 领域知识嵌入:5 条民法典引用 + 量化风险分析 + 可操作修改建议

本项目代码结构清晰、注释完整、零编译错误,可作为 HarmonyOS 6.0 垂直领域 AI 应用开发的学习参考。后续可在此基础上扩展真实 AI 接口对接、合同 OCR 识别、多格式合同解析(PDF/Word)、历史审查记录管理、多用户协作等功能,打造一个功能完备的智能合同审查平台。


本文基于 HarmonyOS 6.0 API 21 + ArkTS 严格模式编写,所有代码均通过 DevEco Studio 编译验证。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐