news 2026/8/1 12:17:11

【4. 搭建基础设施】:创建目录结构、数据库初始化脚本、配置管理模块、.gitignore,安装测试框架 gtest,实现日志封装、数据库连接池

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【4. 搭建基础设施】:创建目录结构、数据库初始化脚本、配置管理模块、.gitignore,安装测试框架 gtest,实现日志封装、数据库连接池

目录

1. 创建项目目录结构

2. 创建数据库初始化脚本

3. 创建配置管理模块

3.1 yaml

4. 测试框架 gtest

4.1 认识“单元测试”

4.2 安装 gtest

4.3 生成单元测试

5. 配置 .gitignore

6. 日志封装

7. 数据库连接池实现


1. 创建项目目录结构

@SPEC.md 请按照文档中 "Phase 1" 描述,先进行 "项目目录结构搭建" 操作。具体的目录结构请 按照 "6 项目目录结构" 的描述来构建。此处只是创建目录结构和空文件,暂时不编写文件的具体 内容。

此时,opencode 就会按照SPEC.md 中描述的目录结构,进行创建。

  • 通过mkdir 创建一系列的目录
  • 通过touch 创建一系列的空文件

当opencode 完成创建后,还会自主使用ls 命令检查当前的目录结构是否正确。

完成之后,和之前一样,记得提交一个git版本并推送。

每完成一个Phase,都可以提示AI修改SPEC.md,把对应的任务标记成完成。

2. 创建数据库初始化脚本

@SPEC.md 请按照文档中 Phase 1 中的描述,进行完成 "MySQL 数据库初始化脚本" 工作。具体需要创建的表结构,参考 "## 4. 数据库 Schema " 章节。

AI根据提示词,先编写init.sql 脚本

-- 创建数据库 CREATE DATABASE IF NOT EXISTS oj_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE oj_system; -- 用户表:存储用户账号、密码哈希、角色 CREATE TABLE IF NOT EXISTS users ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password_hash VARCHAR(255) NOT NULL, role ENUM('user', 'admin') NOT NULL DEFAULT 'user', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_username (username) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 题目表:存储题目基本信息、难度、资源限制 CREATE TABLE IF NOT EXISTS problems ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, title VARCHAR(200) NOT NULL, description TEXT NOT NULL, difficulty ENUM('easy', 'medium', 'hard') NOT NULL DEFAULT 'medium', time_limit_ms INT UNSIGNED NOT NULL DEFAULT 1000, memory_limit_mb INT UNSIGNED NOT NULL DEFAULT 256, created_by BIGINT UNSIGNED NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_created_by (created_by), FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 测试用例表:存储题目输入输出、是否示例 CREATE TABLE IF NOT EXISTS testcases ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, problem_id BIGINT UNSIGNED NOT NULL, input TEXT NOT NULL, expected_output TEXT NOT NULL, is_sample BOOLEAN NOT NULL DEFAULT FALSE, INDEX idx_problem_id (problem_id), FOREIGN KEY (problem_id) REFERENCES problems(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 提交记录表:存储代码提交、判题状态、执行资源消耗 CREATE TABLE IF NOT EXISTS submissions ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL, problem_id BIGINT UNSIGNED NOT NULL, code TEXT NOT NULL, language VARCHAR(20) NOT NULL, status ENUM('pending', 'compiling', 'running', 'accepted', 'wrong_answer', 'time_limit_exceeded', 'memory_limit_exceeded', 'runtime_error', 'compile_error') NOT NULL DEFAULT 'pending', error_msg TEXT, execution_time_ms INT UNSIGNED, execution_memory_kb INT UNSIGNED, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_user_id (user_id), INDEX idx_problem_id (problem_id), INDEX idx_status (status), FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (problem_id) REFERENCES problems(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

注意,此时仅仅是生成了数据库脚本,并没有真正执行,此时的数据库也是空的数据库。我们可以另外起一个终端,登陆到数据库上检查。接下来,可以直接让opencode 来执行这个数据库初始化脚本。

这个过程中,opencode 执行的多个操作,均因为没有 root 权限而失败。
我们可以通过 root 用户运行 opencode 权限,或者把当前用户添加到 sudoers 中解决这个问题,但是这样做并不合适。LLM 返回的结果可能是“有幻觉”的。万一返回了一些危险命令(比如 rm -rf / ),并直接执行了,在 root 用户下造成的后果会非常严重。

完成上述配置后,我们可以让opencode 总结下数据库访问方式,以备后续开发中使用。

把当前数据库的访问方式(用户名密码) 总结写入到 @SPEC.md 中

## 3. 数据库配置 **连接方式**: ```bash mysql -u wsj -p'' ``` **数据库信息**: - 用户名:`wsj` - 密码:(空) - Socket:`/var/run/mysqld/mysqld.sock` - 数据库名:`oj_system` ---

3. 创建配置管理模块

@SPEC.md 请开发 Phase 1 这里的“配置管理”功能。相关的代码编写在 @src/utils/config.cc 中

此时 opencode 就会按照要求,完成下列工作:

  • 在 config.h 和 config.cc 中编写代码进行实现。
  • 修改 CMakeLists.txt,增加编译相关配置
  • 生成 config.yaml 作为配置模板。

生成的代码核心是 ConfigManager 单例类,以单例模式的方式进行配置的解析管理。

# OJ 系统配置文件 # 数据库配置 database: host: "localhost" username: "wsj" password: "" name: "oj_system" socket: "/var/run/mysqld/mysqld.sock" # 服务器配置 server: host: "0.0.0.0" port: 8080 # 判题配置 judger: # 代码存放目录 work_dir: "/tmp/oj-judger" # 默认时间限制(毫秒) default_time_limit_ms: 2000 # 默认内存限制(MB) default_memory_limit_mb: 256 # 日志配置 logger: # 日志级别: trace, debug, info, warn, error, critical level: "info" # 日志输出目录 dir: "logs" # 控制台输出 console: true

3.1 yaml

yaml 是当前一种流行的配置文件组织格式,格式以“缩进”方式来组织处一个“树形结构”,每一级缩进都相当于一个“子树”,每个树的节点都是一个“键值对”,形如下列形式:

server: host: 0.0.0.0 port: 8080 thread_pool_size: 4 database: host: localhost port: 3306 username: wsj password: database: oj_system max_connections: 10

4. 测试框架 gtest

当然,光靠肉眼来观察代码,不足以验证代码的正确性。我们还可以通过“单元测试”的方式,来对代码进行进一步验证。

4.1 认识“单元测试”

一个项目整体的“正确”,意味着项目中的每个模块,每个部分都是正确的。
与其等待整个项目开发完,再验证正确,不如每开发一个模块,都先验证下这个模块是否正确。
这个针对模块验证的过程,就是“单元测试”。这是在开发中一种常见的测试方式。


在 C++ 中,编写单元测试不必从头开始。google 提供了 google test(简称 gtest)框架,可以让我们更方便的完成单元测试。

4.2 安装 gtest

sudo apt install libgtest-dev

4.3 生成单元测试

针对上述配置管理功能的开发,基于 gtest 设计单元测试用例。相关代码存放在 @tests/unit/ 目录中。

此时,opencode 会进行下列操作:

  • 编写单元测试代码
  • 修改 CMakeLists.txt,添加针对单元测试程序的编译选项。
  • 编译,并修改编译报错。
  • 运行,并修改失败的用例。

AI虽然功能很强,但是也不是无所不能。我们仍然要对AI生成的结果进行检查验证,对于不符合要求的地方,要能够给出及时的提示和修正。

5. 配置 .gitignore

这一次进行提交 git 版本时,我们发现,要提交的内容突然变很多。

仔细观察发现,这些文件,都是 cmake 构建过程中,生成的临时文件。这些临时文件原则上是不需要提交到 git 中。

只要有 CMakeLists.txt 这一个配置文件,都是随时可以生成出上述临时文件的。

如何避免提交这些临时文件呢?可以通过 git 内置的功能,.gitignore 来实现。

.gitignore 是一个纯文本文件。我们可以在项目的根目录中创建出这个文件

文件的内容按行组织,每一行描述了要忽略一类文件,针对上述的临时文件来说,他们都是处于“build”目录下的,因此直接在 .gitignore 中编写

build/

此时,这些临时文件就不会被加入到 git 仓库中了。使用 .gitignore 是开发中的一个良好习惯。确保只提交项目真正关键的代码/配置文件即可。

6. 日志封装

日志是一个系统中的重要环节,是程序员定位问题的重要手段,相比于“断点调试”来说,日志的普适性是更强的,断点一旦把程序“断住”,其实影响到了程序原有的执行逻辑。在一个已经改变的逻辑上定位问题,本身已经未必“靠谱”了,相比之下,日志对于程序的侵入性更小,能更好的还原出现问题的线索。

提示词:

@SPEC.md 请按照“Phase 1”中“日志封装”这样的部分,来进行开发。代码放到 @backend/logger/logger.cpp 中。

AI 按照提示词,很快完成开发。封装好的日志会支持日志级别(ERROR、WARNING、INFO、DEBUG 等)。和前面类似,同样可以针对日志生成测试用例,进行验证。

此时,opencode 会按照要求,设计测试用例,编译,调试测试用例通过。

根据上述编写的代码,基于 gtest 设计测试用例。测试用例代码放到 @tests/unit/ 目录中。

7. 数据库连接池实现

opencode 会按照要求,开始进行开发,开发出来的连接池:

  • 对 mysql 原生 API 进行了封装,简化了使用
  • 提前创建好多个连接,后续代码需要使用数据库连接时直接从池子中获取
  • 考虑到了线程安全,在合理位置进行加锁

@SPEC.md 根据文档描述,完成“Phase1”中的“数据库连接池实现”,实现的代码放到 @backend/repository/db_pool.cpp 中。

开发完毕之后,和前面一样,让 opencode 编写单元测试。

对上述的数据库连接池,设计单元测试用例,基于 gtest 实现。代码放在 @tests/unit/ 中

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/1 12:17:07

TSB技能编辑器实战:可视化设计游戏角色技能与优化

那天下午,我正对着屏幕上一段飘忽不定的角色动作发愁。项目里的“漂泊带土”这个角色,常态下的技能效果始终达不到想要的那种流畅感——不是特效衔接生硬,就是打击反馈不够真实。尝试过调整动画帧、修改粒子参数,甚至重写了部分逻…

作者头像 李华
网站建设 2026/8/1 12:16:26

5款零代码AI工具推荐:非技术人员也能快速搭建智能助手

1. 为什么非技术人员也需要AI Agent工具? 三年前我帮一位服装店主搭建第一个聊天机器人时,她盯着代码编辑器的那种茫然眼神让我记忆犹新。如今零代码工具已经让AI应用开发变得像拼乐高一样简单。Harness Engineering(驾驭工程)这个…

作者头像 李华
网站建设 2026/8/1 12:15:53

如何与头部连锁商超高效对接?供应商须打通从订单到结算的数据链路

随着全渠道零售战略的深入推进,头部连锁商超的供应链早已不是供应商与采购方之间的简单线性通道,而是由数千家门店、线上商城、会员制卖场、履约中心、仓储节点及全球物流网络交织而成的庞大生态系统。许多供应商初入这一体系时,往往将其视作…

作者头像 李华
网站建设 2026/8/1 12:15:25

嵌入式视觉AI模块AT指令开发指南:从串口通信到人脸识别实战

1. 项目缘起:当“看得见”的模块遇上“说人话”的指令最近在捣鼓一个智能门禁的原型,核心需求是能识别门口的人脸,然后决定开不开门。硬件上,我选用了Seeed Studio的Grove Vision AI V2模块,这玩意儿集成了算力不错的视…

作者头像 李华
网站建设 2026/8/1 12:13:27

C++ vector迭代器失效全解析:从内存模型到安全编程实践

1. 项目概述:从一次诡异的崩溃说起如果你写过C,尤其是用过STL容器,那么“迭代器失效”这个词大概率不会陌生。它就像一个潜伏在代码深处的幽灵,平时运行得好好的,一旦触发特定条件,程序就可能崩溃、数据错乱…

作者头像 李华