双11狂欢大促

盲盒赢 定制羽绒服

开启惊喜盲盒,抽取你的专属好礼

— 惊喜奖品抢先看 —

掌中生活——β冲刺代码规范

掌中生活 掌中生活 墨衍会员 · AI 创作全网分发 墨衍智能分发 通过墨衍一键同步至各平台 可同步平台 CSDN微信公众号微博知乎掘金百家号博客园抖音小红书 你的文章也可以这样分发 墨衍会员 ¥399起/年,写一次发全网 我也要 + 加入社区

掌中生活——代码规范 v2.0(β冲刺)

这个作业属于哪个课程202601福大-软件工程实践-W班
这个作业要求在哪里团队作业—Beta冲刺+事后诸葛亮
这个作业的目标在 α 规范 v1.0 基础上,补上安全、附件、通知、数据库迁移、测试和 Git 协作的强制条款,作为 β 冲刺的开发规范
其他参考文献阿里巴巴 Java 开发手册 v1.3.0;Kotlin 官方编码规范;Android Developers 官方文档;团队《代码规范与冲刺计划 v1.0》;《04-接口契约文档 v1.1》;《掌中生活_系统设计说明书 v1.0》

目录

  • 掌中生活——代码规范 v2.0(β冲刺)
  • 一、为什么升到 v2.0
  • 二、适用范围和分层约束
  • 三、命名、格式和 Kotlin 写法
  • 3.1 命名
  • 3.2 格式
  • 3.3 Kotlin 和 Android
  • 四、错误码和日志
  • 4.1 错误码
  • 4.2 日志
  • 五、安全编码(β 的重点)
  • 5.1 单一加密边界
  • 5.2 明文出口唯一
  • 5.3 账号和密码
  • 5.4 数据库和备份
  • 六、数据库和迁移
  • 七、附件
  • 八、通知
  • 九、界面
  • 十、测试和缺陷
  • 10.1 测试
  • 10.2 缺陷
  • 十一、Git 和协作
  • 11.1 分支
  • 11.2 提交
  • 11.3 MR 和 review
  • 十二、AI 生成代码
  • 十三、提交前检查清单
  • 十四、v2.0 和 v1.0 的差异
  • 十五、执行

一、为什么升到 v2.0

α 的 v1.0 主要解决命名、格式、注释、异常和 Git 提交,够用,但不够覆盖 β 要做的事。α 期间暴露了几个具体问题:

  • D-02 口令长度,SRS 写 820,契约写 20,代码常量 32,测试按 820 写,三处口径并存了五天;
  • 受保护记录的凭据字段在数据库里是密文,但代码里没有加密服务,只能把编辑入口锁掉;
  • 个别日志把字段值打了出来;
  • 远端 40 多个分支,合并后大多没删;
  • 缺陷"修了没合",测试在 dev 上跑还是红。

所以 v2.0 在原来的基础上加了六块:分层的硬约束、安全编码、数据库迁移、附件、通知、测试与缺陷管理。原来的命名、格式、注释、Git 提交规范继续有效,这里不重复全文,只写新增和变化的部分。

三条原则不变:

  1. 可读性优先。代码首先是给人读的,其次才是给机器跑的。
  2. 安全约束优先于写法自由。加密边界、掩码、日志脱敏是强制项,违反就是缺陷,不是风格问题。
  3. 约束尽量下沉到工具。能写进脚本、CHECK、触发器的,不靠自觉。

再加一条:可验证。做完的功能要能指出用什么命令、什么用例、什么截图证明它做完了。

二、适用范围和分层约束

规范适用于 app/src/main/java/com/lifemap/lifemap/ 下的 Kotlin 代码、res/ 资源、tools/ 脚本、数据库脚本、测试代码和提交说明。

项目是 Android 原生、完全离线、Room + SQLite,不申请网络权限。分层和依赖方向如下:

ui(Activity/Fragment/Adapter/ViewModel)
        ↓ 只能依赖
service(业务服务接口与实现)
        ↓
domain(纯模型、规则、状态机、掩码投影)
        ↑
data(Room 实体/DAO/Repository 实现)
        ↓
core(Result、错误码、时间、安全、定位器)

硬约束:

层可以做不可以做
ui绑定 ViewModel、渲染 UiState、发事件、跳转写 SQL、直接调 CryptoBox、直接 new Repository、手写掩码
service编排用例、校验、事务、错误码转换持有 View/Context(Application 除外)、直接拼 SQL
domain纯函数、模型、规则、状态机、MaskProjector依赖 Android 框架、Room、Keystore
dataRoom 实体/DAO、Repository 实现、加解密字段读写做业务判断、抛裸异常给上层、直接弹 UI
coreResult、ErrorCode、时间、CryptoBox、ServiceLocator依赖 ui/service.impl(α 出现过 core 反向 import service.impl,属于违规)

ServiceLocator 是唯一的装配入口。新增服务时在这里加 by lazy,不要在 Activity/Fragment 里自己 new。装配顺序和依赖关系写进契约,不靠猜。

模块边界按 A~I:A 账号、B 记录、C 分类标签、D 附件、F 状态、G 日期、H 提醒、I 检索、Z 安全。跨模块调用走 service 接口,不直接调对方的 impl。

三、命名、格式和 Kotlin 写法

3.1 命名

元素规则例子
类/接口UpperCamelCaseRecordRepository、SensitivityPolicy
方法/变量lowerCamelCasegetRecordById(userId, id)
常量UPPER_SNAKEMAX_FAIL_COUNT = 5
资源文件全小写下划线activity_record_detail.xml
数据库对象小写下划线,主键 {表名}_idreminder_task、record_id
测试方法should_行为_when_条件should_lock_when_fail_five_times
布尔变量is/has/can/should 开头isProtected、hasAttachment

类别(category)和敏感级别(sensitivity)是两个正交维度,字段名不能混用。这是作业 3 就澄清过的口径。

3.2 格式

  • 缩进 4 空格,行宽 ≤120,import 按「平台 → 第三方 → 项目」分组,不用通配符;
  • 一个方法 ≤40 行,超了拆私有方法;一个文件一个公开类;
  • 公开 API 写 KDoc,说明职责、参数、返回和可能抛的错误码;
  • 注释解释为什么,不解释是什么。复杂取舍要写清楚,比如"reminder_task.user_id 冗余是为了避免三层 join,这是有意的反范式";
  • TODO 必须带 Issue:// TODO(#71192049): 接入 H 模块提醒生成;
  • 不提交被注释掉的代码,用 git 历史找回。

3.3 Kotlin 和 Android

  • 能不用可空类型就不用;必须可空时,调用处要处理,不滥 !!;
  • 数据类用 data class,状态用 sealed interface/class,不要用一堆布尔标志;
  • 异步统一 suspend + Dispatchers.IO;UI 层用 StateFlow 暴露状态,SharedFlow 发一次性事件;
  • ViewModel 不持有 View、Fragment、Activity 引用;Fragment 在 onDestroyView 里清 binding;
  • 资源字符串放 strings.xml,不硬编码中文;颜色、间距、圆角走设计令牌,不在布局里写魔法值;
  • 不在主线程做数据库、文件、加解密操作;
  • 广播、通知、WorkManager 的入口要判空、判权限,不能假设一定拿得到。

四、错误码和日志

4.1 错误码

统一用 LmResult<T> 返回,成功 Success(data),失败 Failure(ErrorCode)。错误码分两类:

  • 数据库侧沿用 DBAC-xx,由触发器 RAISE(ABORT) 携带;
  • 业务侧用 BIZ-xx,在契约的错误码表里登记,新增必须先改契约。

规则:

  • Repository 不把 LmResult 往上抛,也不把 SQLException 直接抛给 service;在边界处转成业务错误码;
  • service 层负责把底层错误翻译成用户能看懂的错误码,UI 只负责展示文案;
  • 不用异常做正常流程控制;
  • 捕获异常不允许静默吞掉。解密失败按"数据不可用"处理,不降级返回空串,防止用户覆盖真实数据。

4.2 日志

强制条款:

  • 日志只记错误码、时间戳、字段名,不记字段值;
  • 不打印密码、凭据、金额、证件号、手机号,也不打印密文和密钥;
  • 不打印完整 SQL 参数;
  • Log.d 只在 BuildConfig.DEBUG 下用,release 不留调试日志;
  • 崩溃日志如果接了第三方,要先脱敏。

反面例子:Log.d("Auth", "login password=$pwd"),这条在 α 出现过,v2.0 起按缺陷处理。

五、安全编码(β 的重点)

5.1 单一加密边界

所有敏感字段和附件只能经过 CryptoBox 读写,明文不能出现在 SQL、日志、异常消息里。加密算法用 Android Keystore 托管密钥的 AES-256-GCM:

  • 密钥别名 lifemap_dek,由 Keystore 生成和保管,应用不持久化密钥明文;
  • 每次加密随机 12 字节 IV,和密文一起存;密文格式 v1:{iv}:{cipher},带版本号,方便以后换算法;
  • 解密失败返回"数据不可用"的错误码,不返回空串;
  • 安全区不可用时走显式降级,必须让用户确认,不能静默降级为明文。

敏感字段清单:record.amount、record.remark、credential.account_name、credential.password、credential.service_name、credential.expire_date,以及受保护记录的附件文件。

5.2 明文出口唯一

  • 所有展示统一走 MaskProjector,禁止在 View、Adapter、XML 里手写掩码;
  • 受保护内容默认掩码,验证通过后才显示明文;验证有效期默认 2 分钟,切后台、超时、点"隐藏"都立即恢复掩码;
  • 受保护页面设置 FLAG_SECURE,最近任务缩略图不能出现明文;
  • 剪贴板 30 秒自动清空,敏感字段不进入剪贴板历史;
  • 通知文案只允许出现记录名称、节点类型、剩余天数,金额、账号、密码、证件号一律不进通知;
  • 验证失败 5 次锁定 5 分钟,锁定期间正确凭据也不放行;验证和查看行为写 access_log,日志本身不含明文。

5.3 账号和密码

  • 口令 8~20 位,必须含字母和数字;上限、下限、正则三处口径必须一致(SRS、契约、代码常量、测试期望);
  • 密码只存 PBKDF2-HMAC-SHA256 哈希,20 万次迭代,16 字节随机 salt;不存明文,不存可逆加密;
  • "账号不存在"和"密码错误"统一返回 E_LOGIN_FAILED,防止枚举;
  • 登录失败计数和锁定时间以数据库为准,SharedPreferences 只做缓存,不能再出现两份真相;
  • 修改密码成功后旧会话立即失效;密码找回走密保问题或恢复码,重置后同样失效旧会话。

5.4 数据库和备份

  • 数据库文件、WAL、备份文件里都不能出现明文敏感值;提交前用 strings 或 Database Inspector 抽查;
  • 受保护记录改为明文、或反过来,要在同一个事务里完成,失败回滚,不允许出现"标记明文但数据还是密文";
  • 备份用口令派生密钥(PBKDF2)加 AES-GCM,备份文件不自动上传任何云端位置;
  • 恢复前校验完整性、版本和口令,失败回滚,不能破坏本机原有数据。

六、数据库和迁移

  • 表结构以 001_init.sql 和迁移脚本为准,Room 的 @Entity 只是映射,不替代 CHECK、触发器和外键;
  • Room 的 onCreate 要先 DROP 由脚本管理的表,再执行脚本重建,避免 CREATE TABLE IF NOT EXISTS 整条跳过导致 CHECK 丢失(α 踩过);
  • 数据库版本升级必须写迁移脚本和迁移测试,禁止在 release 用 fallbackToDestructiveMigration;
  • 迁移脚本要能在旧库上跑通,升级后记录、节点、状态日志、附件关联都在;
  • 查询按 user_id 隔离,所有列表、详情、搜索、统计都要带账号条件;
  • 多表写操作放同一个事务;状态变更先改 record.status 再插 status_log,顺序反了会被触发器 ABORT;
  • 常用查询字段建索引,新增索引要说明服务的查询场景,不为了保险给所有字段建索引;
  • 测试数据用合成数据,不把真实证件、手机号、凭据写进 SQL 夹具。

七、附件

  • 来源支持拍照、相册、文件;类型支持 JPG/JPEG/PNG/HEIC/PDF;单文件 ≤10MB,单记录 ≤9 个;
  • 文件存应用私有目录 files/attachments/{recordId}/,文件名用 UUID,不用用户原始文件名;
  • 受保护记录的附件必须加密后落盘,缩略图在未验证时用遮挡块覆盖,预览前先验证;
  • 删除记录或替换附件时,要同时删文件和数据库记录,避免孤儿文件;
  • 文件被外部清理或损坏时,提示"文件已不可用",应用不能崩;
  • 图片预览支持缩放和翻页,PDF 支持翻页;打开大文件不能阻塞主线程;
  • 测试附件用自制样例,不用真实证件照片。

八、通知

  • 通知渠道 id、名称、重要性在代码里固定,渠道名称用中文;
  • Android 13+ 先申请 POST_NOTIFICATIONS,拒绝时保留待办,并在首页显示提示条,点击能跳系统设置;
  • 每日扫描用 WorkManager,触发时间默认 09:00,启动时补扫一次;同节点同一天只推一次;
  • 通知标题统一"掌中生活 · 到期提醒",正文模板"{记录名称} 还有 {剩余天数} 天到达{节点类型},请及时处理。";
  • 正文只允许名称、节点类型、剩余天数,不能出现金额、账号、密码、证件号;
  • 点击通知进对应记录详情;受保护记录仍要先验证;
  • 提供"锁屏时隐藏通知详情"开关。

九、界面

  • 用 Material 3 主题,颜色、字体、圆角、间距走设计令牌,不在布局里写死;
  • 间距用 4/8 的倍数,常用 8、12、16、24;触控目标不小于 48dp;
  • 每个页面都要有加载、空数据、错误三种状态,不能只画有数据的样子;
  • 受保护字段显示掩码,记录列表带锁标记;验证弹层说明验证方式、剩余次数、锁定倒计时;
  • 文案用简体中文,不出现占位文字、调试信息、英文按钮;
  • 320dp 小屏要能正常显示,不横向溢出;
  • 图片和图标给 contentDescription,纯装饰图设为 null;
  • 不做深色模式(P2),但颜色不要写死到只在浅色下能看。

十、测试和缺陷

10.1 测试

  • 单元测试覆盖正常路径、边界、空数据、异常、权限五类;命名 should_..._when_...,结构按 Arrange-Act-Assert;
  • 优先用 Fake,不滥用 Mockito;数据库测试尽量在真实 SQLite 上跑;
  • 新增业务方法必须有单测或明确的验收记录;UI 关键路径用 Robolectric,至少覆盖登录、掩码、验证弹层、附件遮挡;
  • 测试用例对应 FR/AC 编号,AC-38 一条一行,结果只填通过/不通过/阻塞;
  • 每个缺陷先写复现用例,修复后用例转绿进基线;D-01D-06 的探针转成 B-01B-06,每次回归先跑;
  • 测试数据用合成数据,不写真实个人信息。

10.2 缺陷

  • 缺陷 ID 从 B-01 起全局唯一,不再按天编号;
  • 状态只有打开、修复中、待验证、关闭;
  • "修了没合"不算关闭,必须合入 dev 并回归通过;
  • 缺陷清单单一来源 docs/测试/缺陷清单-beta.md,群里说的结论当天入库;
  • P0/P1 缺陷 48 小时内关闭,P2 可以带到总结里说明。

十一、Git 和协作

11.1 分支

  • 只保留 dev(日常集成)、master(发布)和短期 feature/{模块}-{Issue号};
  • 功能分支活不过 3 天,MR 合并后当天删除;
  • 文档、博客、脚本直接提交 dev,不建分支;
  • 不 force push 到 dev 和 master;发布打 tag(v0.8.0-beta);
  • 远端已有的历史分支不再新增,β 结束前统一清理一次。

11.2 提交

格式:feat/fix/docs/test/refactor/chore(scope): 描述 (#Issue)。例子:

feat(crypto): 接入 Keystore AES-GCM 字段加密 (#71234567)
fix(record): 受保护记录编辑保存后回滚 (#71234568)
test(ac): 补 AC-14 掩码验证用例 (#71234569)

一次提交只做一件事,粒度控制在半天内能 review。提交前检查改动范围,不把 IDE 配置、构建产物、临时截图、本地数据库提交上去。

11.3 MR 和 review

合并前必须:

  1. 本地 assembleDebug 通过;
  2. 相关单测通过;
  3. git diff --check 没有空白错误;
  4. 至少一人 review;
  5. 入口可达,附一张运行截图;
  6. 接口、字段、错误码变更同步了契约和测试期望。

review 三查:规范符合度、缺陷密度(对照错误码)、文档同步。AI 生成的代码和人工代码同一套标准,不因为"AI 写的"就放宽。

十二、AI 生成代码

  • 提示词写清五件事:角色、输入材料、任务、约束、输出格式;
  • 喂给 AI 的材料要脱敏,真实手机号、证件号、凭据、密钥一律替换;
  • AI 只出初稿,不直接合入;合并前人工逐行读,标出采纳、修改、拒绝;
  • AI 不得修改契约外的接口、字段、依赖;不得生成真实密钥、绕过权限、关掉校验;
  • 生成代码要附规范自查表,说明对应哪条规范、怎么验证;
  • 每个任务记三个数:生成条数、人工采纳条数、发现缺陷数,写进每日随笔。

十三、提交前检查清单

  • 能编译,assembleDebug 通过;
  • 相关单测通过,新增方法有用例;
  • 没有明文敏感字段进数据库、日志、通知、异常消息;
  • 受保护内容展示走 MaskProjector,没有手写掩码;
  • 新页面有加载、空数据、错误三态;
  • 320dp 小屏不溢出;
  • 新增/修改的接口、字段、错误码同步了契约和测试;
  • 没有提交密钥、Token、真实个人信息、本地数据库、构建产物;
  • 提交说明关联 Issue,分支名合规;
  • 文档、截图、测试记录已归档。

十四、v2.0 和 v1.0 的差异

章节v1.0v2.0
分层约束有原则,没列表每层能做/不能做,core 反向依赖算违规
安全提了 CryptoService 和掩码补 Keystore、密文格式、明文出口、验证有效期、锁定、日志、通知、备份
数据库建表规范补 Room 与脚本顺序、迁移测试、禁止 destructive migration、事务和索引
附件无补来源、类型、大小、私有目录、加密、缩略图、清理
通知无补渠道、权限、文案、去重、点击跳转
测试有单测要求补 Robolectric、AC 映射、探针、缺陷 ID 和关闭口径
Git有分支和提交格式补分支收敛、合并即删、MR 六项检查
AI有三道关补提示词五要素、脱敏、禁止改契约、度量

十五、执行

  • 规范从 Day1 起执行,代码 review 按本规范检查;
  • 违反强制项(安全、明文、日志、迁移、分支)按缺陷登记,不按风格问题放过;
  • 规范条款有异议,站会提,改规范要记 CR,不在代码里各写各的;
  • 工具层面目前是 scripts/ci-check.sh(单测 + 数据库验收)加人工 review。ktlint/detekt 没有接入,Day1 有余力再补,不阻塞冲刺;
  • 本规范的解释权在团队,最后由组长确认。

附:相关文档

...全文
评论
收藏 已收藏
分享
返回顶部