基于 ArkTS 与 HarmonyOS 6.0 的合同风险审查 AI 应用开发实践
基于 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审查结果类(包含摘要、风险列表、分级统计)。RiskItem和ContractResult使用@Observed装饰器修饰,使嵌套对象的属性变化能被 UI 层精确感知。 -
Service 层:
ContractService是审查引擎的核心,封装了五个关键方法:analyzeContract(主入口,协调摘要和风险扫描)、extractSummary(合同类型识别与摘要生成)、scanRisks(关键词扫描与风险判定)、delay(异步延迟模拟 AI 处理)。所有方法均为静态方法,采用无状态设计,便于单元测试和后续 API 替换。 -
View 层:
RiskCard和SummaryCard是可复用的展示组件,Index.ets是唯一页面入口,通过@State hasResult控制输入视图与结果视图的条件切换。
2.3 页面流转设计
应用采用单页面双视图架构,通过 if/else 条件渲染在两个状态之间切换:
输入视图(hasResult = false)
├── 提示信息
├── 合同文本输入区(TextArea + Scroll)
├── 操作按钮(加载示例 / 开始审查)
└── 审查能力标签列表
↓ 点击"开始审查"
结果视图(hasResult = true)
├── 摘要卡片(合同摘要 + 风险统计)
├── 风险条款列表(可展开卡片)
└── 免责声明
标题栏在结果视图下额外显示"重新审查"按钮,点击后重置 hasResult 和 result 状态,回到输入视图,用户文本保留在 contractText 中便于修改。
三、HarmonyOS 6.0 核心特性深度运用
3.1 @Observed / @ObjectLink —— 嵌套对象响应式更新
HarmonyOS 6.0 中,@State 只能监听对象引用变化,无法感知对象内部属性的修改。当 RiskItem 的 expanded 状态在子组件内部变化时,需要通过 @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 的优势在于:
- 逻辑封装:每个 Builder 是独立的 UI 片段,可单独测试和修改,不会影响其他部分
- 参数传递:Builder 支持参数传递,如
FeatureTag(icon, label, bgColor, textColor)通过参数化实现了高度的可复用性 - 代码可读性:
build()方法中仅包含this.TitleBar()和if/else分支,结构一目了然
3.3 条件渲染 —— 双视图无缝切换
应用通过 @State hasResult: boolean 控制输入视图和结果视图的切换,这是 HarmonyOS 声明式 UI 的核心优势:
build() {
Column() {
this.TitleBar()
if (!this.hasResult) {
this.InputView()
} else {
this.ResultView()
}
}
}
当 hasResult 从 false 变为 true 时,框架自动销毁 InputView 并创建 ResultView,无需手动操作 DOM 或调用 setState。contractText 保持在 @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
}
RiskCard 的 DetailSection 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 中直接编译通过,无任何错误或警告。这得益于以下几个方面:
- 严格遵循 ArkTS 类型系统:所有变量、参数、返回值均显式声明类型,不使用
any、unknown、Record<K,V>等泛型擦除 - 避免 ArkTS 不支持的语法:不使用索引签名(
[key: string])、生成器函数(async *)、索引访问(obj[key])、匿名对象字面量 - 使用 HarmonyOS 6.0 推荐 API:
promptAction.openToast替代showToast,所有可能抛出异常的方法包裹在 try/catch 中 - 合理的目录结构: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 编译运行
- 用 DevEco Studio 打开项目根目录
- 等待 Gradle/Hvigor 同步完成
- 连接 HarmonyOS 设备或启动模拟器
- 点击 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 编译验证。
更多推荐



所有评论(0)