2026.4.27-2026.5.3

1. 工作内容

        本周工作延续上周数据库环境搭建与 JPA 自动建表的基础,重点从“数据库能不能跑起来”推进到“业务数据模型能不能支撑智契通的核心功能”。我围绕合同、模板、风险分析、摘要、润色和用户权限六个模块,完成了核心业务实体类和 Repository 数据访问层的开发。

        这一周的工作表面上是补充 entity 和 repository,实际更重要的是把“智能合同平台”拆解成可以落库、可以追溯、可以扩展的业务对象。因为智契通不是普通的合同增删改查系统,它后续还要接入 AI 合同生成、风险审查、摘要提取、文本润色和版本追踪。如果一开始数据结构设计得过于简单,后面 AI 结果、人工复核、模板版本、合同附件都会混在一起,业务会很快变得难维护。因此我在设计实体类时重点考虑了两个问题:第一,哪些数据是合同本身的主数据;第二,哪些数据是 AI 或用户操作产生的过程数据和结果数据。

2. 实体类开发与业务建模思考(entity)

2.1 合同模块:把“合同”拆成主信息、字段值和附件

        合同模块包含 contract_info、contract_field_value、contract_attachment 三张表。最初如果只从页面展示角度考虑,很容易把合同名称、甲乙方、金额、正文、附件路径都放在一张表里。但我在设计时选择拆成三类对象:

        contract_info 作为合同主表,只保存合同编号、合同名称、合同分类、甲乙方、金额、起止日期、状态、创建人和摘要等核心元数据。它的职责是支撑合同列表、检索、状态流转和权限归属。金额字段使用 BigDecimal,并在数据库层设置 decimal(18,2),是为了避免合同金额使用 float/double 时出现精度误差。合同金额属于法律和财务敏感信息,哪怕是 0.01 的误差也不应该由数据类型带来。

        contract_field_value 用来保存模板字段对应的实际填写值。这个设计的意义在于让合同生成更灵活。不同合同模板需要的字段不完全一样,例如劳动合同关注岗位、薪资、试用期,租赁合同关注房屋地址、租期、押金。如果把所有字段都写死在 contract_info 里,表会越来越宽,而且每新增一种模板都可能要改表。通过“字段编码 + 字段名称 + 字段值”的方式,合同主表保持稳定,模板字段可以动态扩展,更适合后续接入 AI 生成 Prompt 时统一组装结构化参数。

        contract_attachment 单独存储附件元数据,包括文件名、文件类型、存储路径、文件大小、上传人和上传时间。这里我没有把文件二进制内容直接放进 MySQL,而是只保存 storage_path。我的理解是:数据库更适合保存结构化元数据,真实文件更适合交给对象存储或本地文件服务。这样做可以避免大文件拖慢数据库备份和查询,也方便后续扩展到 MinIO 或云 OSS。

2.2 模板模块:模板不是静态文本,而是合同生成能力的配置中心

        模板模块包含 contract_template、contract_template_field、contract_template_version 三张表。这个模块是智契通后续 AI 合同生成的入口,因此我没有只把模板当作一段固定正文保存,而是把它拆成“模板主体、字段配置、版本记录”三部分。

        contract_template 保存模板编码、模板名称、分类、模板正文、Prompt 模板、启用状态和当前版本号。这里比较关键的是 prompt_template 字段。它说明模板不仅是给用户看的合同格式,也是给 AI 使用的提示词骨架。后续生成合同时,系统可以把用户填写的字段值和模板 Prompt 拼接,让模型在明确约束下生成合同,而不是完全自由发挥。

        contract_template_field 保存模板需要用户填写的动态字段,包括字段编码、字段名称、字段类型、是否必填、默认值、校验规则和排序号。这个设计让我意识到,合同生成的难点不是简单调用 AI,而是如何在调用 AI 前把用户输入结构化、规范化。字段配置层相当于一个轻量级表单引擎:前端可以根据它动态生成输入项,后端也可以根据它做参数校验和 Prompt 组装。

        contract_template_version 用来保存模板历史版本。模板一旦用于生成合同,就不能随意覆盖旧内容,否则后续很难解释“某一份合同当时是根据哪个模板生成的”。因此我设计模板版本表保存 version_no、content、prompt_template、published_by、published_at。这样后续即使模板升级,也可以追溯旧合同对应的模板内容和 Prompt 版本,保证系统具备基本的可审计性。

2.3 风险模块:把 AI 审查拆成任务和结果

        风险模块包含 risk_analysis_task 和 risk_analysis_result。这里我没有把风险结果直接挂在合同表上,而是设计为“任务表 + 结果表”的结构。

        risk_analysis_task 记录一次风险分析的过程,包括合同 ID、版本 ID、任务状态、模型名称、Prompt 版本、开始时间、结束时间和发起人。这样设计是因为 AI 审查天然是一个异步任务:模型调用可能耗时,也可能失败,还可能因为 Prompt 版本不同得到不同结果。把任务过程记录下来,后续可以清楚知道一次审查是由谁发起、使用了哪个模型、用了哪个 Prompt、是否成功完成。

        risk_analysis_result 记录具体风险点,包括风险分类、子分类、条款编号、条款文本、风险等级、置信度、风险说明、修改建议和人工复核状态。这个表体现了我对“AI 审查”的理解:AI 给出的不是最终判决,而是可供人工确认的辅助意见。因此我加入 review_status 字段,为后续“待确认、已确认、已忽略”的人工闭环预留空间。这样系统不会把模型输出简单当成绝对正确,而是能支持法务人员复核和沉淀结果。


2.4 摘要与润色模块:保留 AI 输出的上下文

        摘要模块 contract_summary 保存摘要类型、摘要内容、合同主体、金额、关键日期、违约责任和模型名称。这里不仅保存 summary_text,还保存 key_parties、key_amount、key_dates、key_liability 等结构化字段,是为了让摘要结果不仅能显示给用户看,也能被合同列表、搜索筛选、风险预览等功能复用。

  润色模块 contract_polish_result 保存 original_text、polished_text、polish_type、model_name 和创建人。这里同时保存原文和润色后文本,是因为合同文本修改不能只保留最终结果。对于法律文本来说,用户需要看到“改了哪里、为什么改、能不能回退”。保留原文可以支撑后续做对照展示,也能在用户不接受 AI 润色时恢复原内容。

2.5 用户权限模块:为后续 RBAC 做准备

        用户模块包含 sys_user、sys_role、sys_user_role、sys_permission、sys_role_permission 五张表。虽然本周重点是核心业务实体,但用户权限模块是后续团队系统不可缺少的基础。

        我采用用户、角色、权限和两张关联表的结构,本质上是在为 RBAC 权限模型做准备。不同用户可能有不同职责,例如管理员可以维护模板和用户,法务人员可以审查合同,普通业务人员只能生成和查看自己的合同。通过角色和权限拆分,后续不需要在代码里写死“某个用户能不能访问某个功能”,而是可以通过数据库配置来控制系统行为。

3. 数据访问层开发与 Repository 设计(repository)

        本周同步完成了 15 个 Repository 接口。所有 Repository 都继承 JpaRepository,让实体类天然具备基础的增删改查、分页和排序能力。在自定义查询方法上,我尽量使用 Spring Data JPA 的方法命名机制,例如 findByContractId、findByStatus、existsByContractNo、deleteByContractId 等。

        我对 Repository 层的定位是:它应该负责“清晰、稳定、单表维度的数据访问”,而不应该过早承载复杂业务判断。比如 ContractInfoRepository 提供按合同编号、状态、模板 ID、创建人查询的能力;RiskAnalysisResultRepository 提供按任务、合同、版本、风险等级、复核状态查询的能力;TemplateFieldRepository 提供按模板 ID 并按 sortNo 排序查询字段的能力。这些方法都紧贴业务查询入口,但没有把合同生成、风险评分、权限判断等复杂逻辑写进 Repository。

        这样划分的好处是边界清楚:Repository 只关心数据怎么取,Service 层再决定这些数据怎么组合、怎么校验、怎么流转。后续如果某些查询变复杂,可以再引入 @Query、Specification 或分页查询,而不会影响现有基础接口。

示例代码:

3.1.1 合同主表数据访问层(ContractInfoRepository)

3.1.2 合同字段值表数据访问层(ContractFieldValueRepository)

3.1.3 合同附件表数据访问层(ContractAttachmentRepository)

4. 本周开发成果统计

本周共完成 6个模块、15个实体类、15个Repository接口的开发,具体分布如下:

模块 实体类数量 Repository数量 核心功能
contract 3 3 合同主信息、字段值、附件管理
template 3 3 模板定义、字段配置、版本管理
risk 2 2 风险分析任务、结果记录
summary 1 1 合同摘要生成
polish 1 1 合同润色记录
user 5 5 用户认证、角色权限管理
合计 15 15 -

注:目前完成的是核心业务逻辑的数据模型和访问层,后续在项目骨架继续增加数据测试、Prompt 模板、合同历史版本、模型调用日志等分支后,还会进一步完善实体类字段、索引和关联查询能力。

5. 关键技术理解与设计取舍

5.1 JPA 自动建表提高效率,但不能代替数据库设计

        上周我主要验证了 ddl-auto: update 可以根据实体类自动建表。本周进一步实践后,我意识到 JPA 自动建表解决的是“从 Java 类到数据库表”的效率问题,但真正决定系统质量的仍然是实体边界设计。如果字段命名、表职责、业务关系没有想清楚,自动建表只会更快地生成一批不合理的表。

        所以我这周不是简单照着页面写实体,而是先对照项目总体架构和数据库设计文档,把核心业务拆分成合同、模板、AI 结果、用户权限四类数据。这样做可以让后续功能开发更顺:合同生成依赖模板和字段值,风险分析依赖合同和版本,摘要润色依赖合同和 AI 结果,权限控制依赖用户和角色。


5.2 主表与结果表分离,保证 AI 能力可扩展

        智契通的 AI 功能会持续扩展,如果把风险、摘要、润色等结果都塞进 contract_info,合同主表会越来越臃肿,也不利于保存多次 AI 调用结果。因此本周我采用“合同主数据稳定,AI 结果独立扩展”的设计。

        这种设计的优点是,每个 AI 能力都有自己的结果表和查询入口。风险分析可以保存多条风险明细,摘要可以保存不同类型摘要,润色可以保存原文与润色结果。后续增加合同对比、模型调用日志、Prompt 回归测试时,也可以继续用类似方式扩展,而不需要频繁改动合同主表。


5.3 版本意识比功能实现更早出现

        合同类系统很重要的一点是可追溯。虽然本周还没有完整实现合同版本表,但在实体设计中已经多次预留 versionId、currentVersion、latestVersionId、promptVersion 等字段。这样做是为了避免后续补版本功能时大范围推翻已有结构。

        我理解的“版本意识”不只是保存旧文本,而是能回答几个问题:这份合同基于哪个模板版本生成?风险审查针对的是哪一个合同版本?摘要和润色结果对应的是修改前还是修改后的合同?如果这些问题没有在数据层留下线索,后面页面做得再好也很难真正可信。


5.4 Repository 方法要贴近业务场景,而不是为了数量好看

        这周写 Repository 时,我没有为每个字段都机械生成查询方法,而是优先写后续业务确实会用到的方法。例如合同需要按编号查重、按状态筛选、按创建人查询;模板需要按编码查重、按分类和状态筛选;风险结果需要按任务、合同版本、风险等级、复核状态查询。

        这让我对 Repository 的理解更清楚:它不是越多越好,而是要围绕业务动作设计。一个好的 Repository 方法应该能让 Service 层读起来接近业务语言,例如“查某个合同某个版本的风险结果”“查某个模板下按排序排列的字段”“判断合同编号是否已存在”。这样后续代码可读性会更强。


5.5 数据类型选择会影响后续业务可靠性

        本周有几个字段类型是我特别注意的。合同金额使用 BigDecimal,对应数据库 decimal(18,2),避免浮点误差。合同正文、Prompt、润色文本等长文本使用 LONGTEXT 或 TEXT,避免普通 varchar 长度不足。状态字段虽然现在用 String 或 Integer,但已经统一围绕 draft、reviewed、signed、archived、pending、running、success、failed 这些业务状态设计,后续可以进一步抽成枚举,减少魔法字符串。

        这些看起来是细节,但会直接影响系统稳定性。合同平台的数据不是临时数据,而是会被用户反复查看、审查和导出,所以一开始就要尽量避免金额精度、文本长度、状态混乱这类基础问题。

6. 遇到的问题与后续优化方向

        第一,当前实体类主要通过 Long 类型保存关联 ID,还没有使用 @ManyToOne、@OneToMany 等 JPA 关系映射。这样做的优点是简单、可控,不容易出现懒加载和循环序列化问题;缺点是跨表查询需要在 Service 层手动组合。后续如果业务查询复杂度上升,可以根据实际场景局部引入关联映射或 DTO 聚合查询。

        第二,目前部分字段还缺少唯一约束和索引。例如 contract_no、template_code、username、role_code 等字段在业务上应具备唯一性。后续需要在实体注解或数据库脚本中补充唯一索引,避免只依赖 Service 层校验。

        第三,软删除策略目前只是通过 status 字段预留,还没有形成统一规范。后续可以在合同、模板、用户等模块中明确启用、停用、归档、删除之间的区别,避免真实删除导致历史记录和 AI 分析结果失去上下文。

        第四,AI 相关实体目前已经保存了 modelName、promptVersion、confidenceScore 等关键字段,但还没有独立的模型调用日志表。后续如果要评估模型效果、调用成本和失败率,需要进一步加入 ai_model_call_log 和 Prompt 模板管理表。


7. 本周小结

        本周工作的核心收获是:后端实体类不是数据库字段的简单搬运,而是对业务理解的代码化表达。智契通作为一个基于大模型的智能合同平台,真正复杂的地方不只是“调用 AI”,而是如何让 AI 生成、审查、摘要、润色这些结果与合同业务本身形成可靠的数据关系。

        通过这次 entity 和 repository 的开发,我对 Spring Data JPA 的理解从“能自动建表、能写接口查询”推进到了“如何围绕业务边界设计数据模型”。合同主表负责稳定主数据,模板模块负责生成规则,风险/摘要/润色模块负责 AI 结果留痕,用户权限模块负责后续访问控制。这样的结构为下一阶段 Service 层开发、AI 接口接入、合同版本管理和团队协作打下了比较清晰的基础。

        后续我会继续补充数据测试和业务接口,在实际增删改查、合同生成和风险分析流程中验证这些实体设计是否足够合理。如果发现某些字段只是“看起来完整”但业务中用不上,或者某些查询在 Service 层组合成本太高,也会继续调整表结构和 Repository 方法,让数据模型真正服务于系统功能,而不是停留在文档层面。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐