Git Commit Message 规范 + 自动化:别再写"fix bug"了

引言

打开你们项目的 git log,大概率是这样:

a3f2b1c fix bug
7c8d9e1 update
2b4f5a6 修改
9e1c3d7 xxx
5d6a7b8 修复订单问题
3f8c9d0 2024-01-15 update

这种 commit message 有什么问题?

  • "fix bug" —— 修的哪个 bug?怎么修的?
  • "update" —— 更新了什么?文档还是代码?
  • "修改" —— 改的哪里?
  • "xxx" —— ?

半年后排查线上问题,需要 git bisect 定位是哪次提交引入的 bug,看到一堆 "fix bug" 直接崩溃。

commit message 不是写给自己的,是写给团队和未来的自己看的

这篇文章从规范到自动化,讲透一套完整的 commit message 方案:

  • Conventional Commits 规范:feat/fix/chore 等类型含义
  • commitlint:自动校验,不规范不让提交
  • husky:把校验挂到 git hook
  • commitizen:交互式生成规范 message
  • standard-version / changesets:自动生成 CHANGELOG

配完整配置文件,复制粘贴就能用。


一、Conventional Commits 规范详解

1.1 规范格式

<type>(<scope>): <subject>

<body>

<footer>
  • type:提交类型(必填)
  • scope:影响范围(可选,如模块名)
  • subject:简短描述(必填,<= 50 字符)
  • body:详细说明(可选)
  • footer:关闭 issue、标记 BREAKING CHANGE(可选)

1.2 完整示例

feat(order): 新增订单导出功能

- 支持按时间范围批量导出
- 使用 SXSSF 流式写 Excel 避免大文件 OOM
- 1000 万条数据内存稳定在 200MB

Closes #123

1.3 type 类型详解

核心类型(最常用 6 个)

type含义示例
feat新功能feat(order): 新增订单导出功能
fixbug 修复fix(auth): 修复 token 过期未跳登录的问题
docs文档变更docs: 更新 README 部署说明
style代码格式(不影响功能)style: 格式化 import 顺序
refactor重构(既不是 feat 也不是 fix)refactor(user): 抽取用户校验逻辑
test测试相关test(order): 补充订单导出的单元测试

其他类型

type含义示例
chore构建/工具/依赖变更chore: 升级 Spring Boot 到 3.2.0
perf性能优化perf(query): 给订单表加联合索引
build构建系统或外部依赖build: 修改 Maven 打包配置
ciCI 配置变更ci: 添加 GitHub Actions 自动化测试
revert回滚提交revert: feat(order) 导出功能(导致 OOM)

1.4 type 选择决策树

这次提交是什么?
├── 新增功能 → feat
├── 修了 bug → fix
├── 改文档 → docs
├── 改格式(空格/分号/import 顺序)→ style
├── 改测试 → test
├── 重构(不改功能)→ refactor
├── 性能优化 → perf
├── 改构建/依赖 → chore 或 build
├── 改 CI → ci
└── 回滚 → revert

1.5 scope(范围)

scope 表示影响的模块,可选:

feat(order): 新增订单导出       ← 影响订单模块
fix(auth): 修复登录 bug          ← 影响鉴权模块
refactor(user): 重构用户服务     ← 影响用户模块
docs: 更新 README                ← 不填 scope(影响范围广)
chore: 升级依赖                  ← 不填 scope

1.6 subject(描述)规则

  • 用祈使句、现在时:"add" 不是 "added" 也不是 "adds"
  • 首字母小写(中文无此问题)
  • 结尾不加句号
  • <= 50 字符
✅ feat(order): 新增订单导出功能
❌ feat(order): 新增订单导出功能。       ← 多余句号
❌ feat(order): 新增了订单导出功能         ← "了"多余
❌ feat(order): 新增订单导出功能并修复了导出时内存溢出的问题同时优化了性能  ← 太长

1.7 body(正文)

详细说明"为什么"和"怎么做",每行 < 72 字符:

fix(auth): 修复 refresh token 过期未自动跳登录

问题:前端发现用户在页面停留超过 1 小时后,请求返回 401 但没跳登录页。
根因:拦截器只判断了 access token 过期,refresh token 过期时直接抛 401。
修复:refresh token 也过期时,清除本地登录态并跳转登录页。
影响:所有停留超过 1 小时的用户会重新登录一次。

1.8 footer(脚注)

关闭 issue

fix(auth): 修复 refresh token 过期未自动跳登录

...

Closes #123, #456

标记 BREAKING CHANGE

feat(auth): 重构鉴权模块

将 token 从 JWT 改为 OAuth2,签名算法变更。

BREAKING CHANGE: 所有调用 /api/auth/* 的客户端需要更新鉴权方式

1.9 特殊场景:BREAKING CHANGE 简写

在 type 后加 !

feat(auth)!: 重构鉴权模块,token 改为 OAuth2

BREAKING CHANGE: 所有调用 /api/auth/* 的客户端需要更新鉴权方式

等价于:

feat(auth): 重构鉴权模块,token 改为 OAuth2

BREAKING CHANGE: 所有调用 /api/auth/* 的客户端需要更新鉴权方式

二、commitlint 自动校验

2.1 为什么需要 commitlint

光有规范没用,靠自觉迟早有人不遵守。commitlint 可以在提交时自动校验,不规范的 commit 直接被拒绝。

2.2 安装

# 安装 commitlint + 官方推荐规则集
npm install --save-dev @commitlint/cli @commitlint/config-conventional

# 或用 pnpm
pnpm add -D @commitlint/cli @commitlint/config-conventional

2.3 配置文件

在项目根目录创建 commitlint.config.js

module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    // type 枚举
    'type-enum': [
      2,
      'always',
      [
        'feat',      // 新功能
        'fix',       // bug 修复
        'docs',      // 文档
        'style',     // 代码格式
        'refactor',  // 重构
        'test',      // 测试
        'chore',     // 构建/工具
        'perf',      // 性能优化
        'build',     // 构建系统
        'ci',        // CI 配置
        'revert',    // 回滚
      ],
    ],
    // type 不能为空
    'type-empty': [2, 'never'],
    // subject 不能为空
    'subject-empty': [2, 'never'],
    // subject 不超过 80 字符
    'subject-max-length': [2, 'always', 80],
    // subject 不以句号结尾
    'subject-full-stop': [2, 'never', '.'],
    // subject 大小写(中文可关)
    'subject-case': [0],
    // body 每行不超过 100 字符
    'body-max-line-length': [2, 'always', 100],
    // footer 每行不超过 100 字符
    'footer-max-line-length': [2, 'always', 100],
  },
};

2.4 规则级别

每条规则配置 [level, condition, value]

level含义行为
0disable不校验
1warning警告但不阻止
2error报错并阻止提交
// 强制 type 是规定枚举
'type-enum': [2, 'always', [...]],

// subject 为空时警告
'subject-empty': [1, 'never'],

// 不校验大小写
'subject-case': [0],

2.5 测试 commitlint

# 手动测试一条 commit message
echo "feat: 测试功能" | npx commitlint

# 测试不规范的
echo "fix bug" | npx commitlint
# 输出:
# ⧗   input: fix bug
# ✖   subject may not be empty        ← "bug" 被当成 type,subject 为空
# ✖   type must be one of [...]       ← "fix bug" 不是合法 type
# ✖   found 2 problems, 0 warnings

三、husky 配置 git hooks

3.1 为什么需要 husky

commitlint 装了,但每次提交都要手动跑 npx commitlint?没人会这么做。

husky 的作用:把 commitlint 挂到 git 的 commit-msg hook,git commit 时自动触发校验。

3.2 安装

npm install --save-dev husky

# 初始化 husky
npx husky init

执行后:

  • 项目根目录多出 .husky/ 文件夹
  • package.json 里多了 "prepare": "husky" 脚本

3.3 配置 commit-msg hook

# 创建 commit-msg hook
echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg

# 赋予执行权限
chmod +x .husky/commit-msg

.husky/commit-msg 内容:

#!/usr/bin/env sh
npx --no -- commitlint --edit $1

3.4 配置 pre-commit hook(可选:提交前跑 lint)

echo "npm run lint" > .husky/pre-commit
chmod +x .husky/pre-commit

.husky/pre-commit

#!/usr/bin/env sh
npm run lint          # 跑代码 lint
# npm test           # 也可以跑测试(慢的话别加)

3.5 完整的 .husky 目录

.husky/
├── _/
│   └── husky.sh           ← husky 内部脚本
├── commit-msg             ← commit message 校验
├── pre-commit             ← 提交前跑 lint
└── pre-push               ← push 前跑测试(可选)

3.6 测试 hook 生效

# 尝试一个不规范的 commit
git add .
git commit -m "fix bug"

输出:

⧗ input: fix bug
✖ subject may not be empty
✖ type must be one of [feat, fix, docs, style, refactor, test, chore, perf, build, ci, revert]

✖ found 2 problems, 0 warnings
husky - commit-msg hook exited with code 1 (error)

提交被拒绝。

# 改成规范的
git commit -m "fix(auth): 修复 token 过期未跳登录"
# [main abc1234] fix(auth): 修复 token 过期未跳登录
# 提交成功

四、commitizen 交互式生成

4.1 为什么需要 commitizen

记不住 type 枚举?scope 写不清楚?body 不会写?

commitizen 提供一个交互式命令行,问什么填什么,最后自动生成规范 message。

4.2 安装

# 安装 commitizen + 常用适配器
npm install --save-dev commitizen cz-conventional-changelog

4.3 配置 package.json

{
  "scripts": {
    "commit": "cz"
  },
  "config": {
    "commitizen": {
      "path": "cz-conventional-changelog"
    }
  }
}

4.4 使用

git add .
npm run commit    # 或 npx cz

进入交互式界面:

? Select the type of change that you're committing: (Use arrow keys)
❯ feat:     A new feature
  fix:      A bug fix
  docs:     Documentation only changes
  style:    Changes that do not affect the meaning of the code
  refactor: A code change that neither fixes a bug nor adds a feature
  perf:     A code change that improves performance
  test:     Adding missing tests or correcting existing tests
  chore:    Changes to the build process or auxiliary tools
  revert:   Revert to a commit

一步步引导:

? Select the type of change that you're committing: feat
? What is the scope of this change (e.g. component or file name): order
? Write a short, imperative mood description of the change:
  新增订单导出功能
? Provide a longer description of the change: (press enter to skip)
  - 支持按时间范围批量导出
  - 使用 SXSSF 流式写 Excel 避免 OOM
? Are there any breaking changes? No
? Does this change affect any open issues? Yes
? Add issue prefixes: #123

[main abc1234] feat(order): 新增订单导出功能
 2 files changed, 100 insertions(+), 0 deletions(-)

生成的 commit message

feat(order): 新增订单导出功能

- 支持按时间范围批量导出
- 使用 SXSSF 流式写 Excel 避免 OOM

Closes #123

4.5 自定义 commitizen 适配器(中文)

默认适配器是英文。可以装中文版:

npm install --save-dev cz-conventional-changelog-zh

package.json

{
  "config": {
    "commitizen": {
      "path": "cz-conventional-changelog-zh"
    }
  }
}

交互界面变中文:

? 请选择变更类型: (使用方向键)
❯ feat:     新功能
  fix:      Bug 修复
  docs:     文档变更
  style:    代码格式(不影响功能)
  refactor: 重构
  test:     测试相关
  chore:    构建/工具变更

4.6 可选适配器

适配器特点
cz-conventional-changelog标准英文,最常用
cz-conventional-changelog-zh中文版
cz-customizable高度自定义,可加自定义问题
cz-emoji-conventional支持 emoji(✨ feat:

五、CHANGELOG 自动生成

5.1 为什么需要 CHANGELOG

版本发布时,用户/前端想知道"这次发了什么"。手动写 CHANGELOG 太累还容易漏。

工具从规范的 commit message 自动生成 CHANGELOG。

5.2 方案 1:standard-version

最简单,适合单包项目:

npm install --save-dev standard-version

package.json

{
  "scripts": {
    "release": "standard-version",
    "release:minor": "standard-version --release-as minor",
    "release:major": "standard-version --release-as major"
  }
}

执行发布:

npm run release

会自动:

  1. 根据 commit 决定版本号:
    • BREAKING CHANGE! → major(如 1.0.0 → 2.0.0)
    • feat → minor(如 1.0.0 → 1.1.0)
    • 只有 fix → patch(如 1.0.0 → 1.0.1)
  2. 生成 CHANGELOG.md
  3. 创建 git tag
  4. 修改 package.json 版本号

生成的 CHANGELOG.md

# Changelog

## [1.1.0] (2026-08-08)

### Features

- **order:** 新增订单导出功能 ([abc1234](https://github.com/.../commit/abc1234))

### Bug Fixes

- **auth:** 修复 token 过期未跳登录 ([def5678](https://github.com/.../commit/def5678))

### Performance Improvements

- **query:** 给订单表加联合索引 ([ghi9012](https://github.com/.../commit/ghi9012))

5.3 方案 2:changesets(适合 monorepo)

复杂项目用 changesets:

npm install --save-dev @changesets/cli
npx changeset init

发布流程:

# 1. 开发完,提交变更集
npx changeset

# 交互式:
? Which packages would you like to include?
❯ ◯ @myrepo/core
  ◯ @myrepo/utils
? Which packages should have a major bump?
? Summary: 新增订单导出功能
? Is this a breaking change? No

# 生成 .changeset/xxx.md 文件,包含变更描述

# 2. 合并 PR 后,自动消费 changeset 并发版
npx changeset version
# 自动更新 CHANGELOG.md 和 package.json 版本号

# 3. 发布
npx changeset publish

适合:monorepo(pnpm workspaces)、多包项目。

5.4 方案 3:conventional-changelog-cli

最灵活,纯生成 CHANGELOG:

npm install -g conventional-changelog-cli

# 生成完整 CHANGELOG
conventional-changelog -p angular -i CHANGELOG.md -s

# 只生成本次版本
conventional-changelog -p angular -u

适合:自定义发布流程、集成到 CI。


六、Spring Boot 项目实战配置

6.1 项目结构

假设是一个 Spring Boot + Maven 项目:

order-service/
├── src/
├── pom.xml
├── package.json          ← 为 commit 工具准备
├── commitlint.config.js
├── .husky/
│   ├── commit-msg
│   └── pre-commit
└── CHANGELOG.md

6.2 package.json(最小配置)

{
  "name": "order-service",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "commit": "cz",
    "release": "standard-version",
    "release:minor": "standard-version --release-as minor",
    "release:major": "standard-version --release-as major",
    "prepare": "husky"
  },
  "devDependencies": {
    "@commitlint/cli": "^19.0.0",
    "@commitlint/config-conventional": "^19.0.0",
    "commitizen": "^4.3.0",
    "cz-conventional-changelog": "^3.3.0",
    "husky": "^9.0.0",
    "standard-version": "^9.5.0"
  },
  "config": {
    "commitizen": {
      "path": "cz-conventional-changelog"
    }
  }
}

6.3 commitlint.config.js(带中文提示)

module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [
      2,
      'always',
      ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore', 'perf', 'build', 'ci', 'revert'],
    ],
    'type-empty': [2, 'never'],
    'subject-empty': [2, 'never'],
    'subject-max-length': [2, 'always', 80],
    'subject-full-stop': [0],
    'subject-case': [0],
    'body-max-line-length': [2, 'always', 100],
    'footer-max-line-length': [2, 'always', 100],
  },
};

6.4 .husky/commit-msg

#!/usr/bin/env sh
npx --no -- commitlint --edit $1

6.5 .husky/pre-commit

Java 项目可以不配 lint,用简单脚本:

#!/usr/bin/env sh

# 检查是否包含大文件提交(避免误提交 jar 包)
LARGE_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(jar|war|zip)$')
if [ -n "$LARGE_FILES" ]; then
  echo "❌ 检测到大文件提交:"
  echo "$LARGE_FILES"
  echo "请使用 .gitignore 排除二进制文件"
  exit 1
fi

# 检查是否包含敏感信息
SENSITIVE=$(git diff --cached --name-only | xargs grep -l -E "(password|secret|api[_-]?key)\s*=" 2>/dev/null | head -3)
if [ -n "$SENSITIVE" ]; then
  echo "⚠️  可能包含敏感信息:"
  echo "$SENSITIVE"
  echo "请确认后再提交(5 秒后继续)"
  sleep 5
fi

6.6 集成到 Maven build

让 Maven 也知道版本号(用 versions-maven-plugin):

<build>
    <plugins>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>versions-maven-plugin</artifactId>
            <version>2.16.0</version>
        </plugin>
    </plugins>
</build>

发布脚本 release.sh

#!/bin/bash

# 1. 跑 standard-version 自动升版本 + 生成 CHANGELOG
npm run release

# 2. 提取新版本号
VERSION=$(node -p "require('./package.json').version")
echo "新版本:$VERSION"

# 3. 同步 Maven 版本
mvn versions:set -DnewVersion=$VERSION -DgenerateBackupPoms=false

# 4. 提交 Maven 版本变更
git add pom.xml
git commit --amend --no-edit

# 5. 推送 + tag
git push --follow-tags origin main

# 6. 触发 CI 构建
gh workflow run deploy.yml --ref v$VERSION

echo "✅ 发布完成:v$VERSION"

执行:

chmod +x release.sh
./release.sh

七、完整工作流

7.1 日常开发流程

# 1. 开发功能
git checkout -b feat/order-export
... 写代码 ...

# 2. 暂存
git add .

# 3. 交互式提交(推荐)
npm run commit

# 或手动提交(熟了可以省)
git commit -m "feat(order): 新增订单导出功能

- 支持 SXSSF 流式写 Excel
- 内存稳定在 200MB

Closes #123"

# 4. push
git push origin feat/order-export

7.2 不规范提交被拦截

git commit -m "fix bug"
# ⧗ input: fix bug
# ✖ type must be one of [feat, fix, ...]
# husky - commit-msg hook exited with code 1

被 commitlint 拦截,强制重写。

7.3 发布流程

# 1. 合并 PR 到 main
git checkout main
git merge feat/order-export

# 2. 自动生成版本和 CHANGELOG
npm run release
# 自动:
#   - 看历史 commit 决定版本号
#   - 生成 CHANGELOG.md
#   - 创建 tag v1.1.0
#   - 修改 package.json

# 3. 推送
git push --follow-tags origin main

# 4. 触发 CI/CD
gh workflow run deploy.yml --ref v1.1.0

八、CI/CD 集成

8.1 GitHub Actions 校验 PR 的 commit

.github/workflows/commitlint.yml

name: Commitlint

on:
  pull_request:
    types: [opened, edited, synchronize]

jobs:
  commitlint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # 拉取完整历史

      - uses: actions/setup-node@v4
        with:
          node-version: 20

      - run: npm ci

      - name: 校验 PR 的 commit message
        run: |
          # 取 PR 的所有 commit,校验每一条
          npx commitlint --from ${{ github.event.pull_request.base.sha }} \
                         --to ${{ github.event.pull_request.head.sha }} \
                         --verbose

不规范的 PR 直接 CI 失败,不允许合并。

8.2 自动生成 release notes

GitHub 原生支持 Conventional Commits 自动生成 Release Notes:

仓库 Settings → General → Pull Requests → 勾选 "Automatically generate release notes"。

或用 GitHub Action 自动发 release:

name: Release

on:
  push:
    tags:
      - 'v*'

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: 生成 Release Notes
        uses: softprops/action-gh-release@v1
        with:
          generate_release_notes: true
          body: |
            ## What's Changed

            ${{ steps.changelog.outputs.content }}

九、团队推广策略

9.1 渐进式落地

一次性给团队上全套规范阻力大,建议分阶段:

阶段 1(1 周内):只装 commitlint + husky

# 不规范就拦截
git commit -m "fix bug"
# ❌ 被 commitlint 拦截

让团队先习惯规范的 type。

阶段 2(1 个月后):加 commitizen

npm run commit
# 引导式生成规范 message

降低写规范 message 的心智负担。

阶段 3(2 个月后):加 standard-version

npm run release
# 自动生成 CHANGELOG

让团队体会到规范带来的收益——发版不用手动写 release notes。

9.2 处理老项目

老项目 commit 历史不规范怎么办?

不要重写历史——会造成 PR 冲突。从现在开始规范即可,旧 commit 留着。

可以打一个分界 tag:

git tag legacy-before-conventional-commits
# 之后所有 commit 都按规范

9.3 处理"我已经不规范了"的提交

git commit -m "fix bug"  # 已经提交了
git commit --amend       # 修改最近一次
# 进入编辑器改 message

或重置:

git reset HEAD~1
git commit -m "fix(auth): 修复登录 bug"

9.4 团队培训要点

3 个要点让团队快速上手:

  1. 记住 6 个 type:feat / fix / docs / style / refactor / test
  2. 格式口诀type(scope): subject
  3. 实在不会写就用 commitizennpm run commit

十、常见问题

10.1 commitlint 校验失败如何跳过

某些场景必须跳过校验(紧急 hotfix、自动化脚本):

git commit -m "fix: 紧急 hotfix" --no-verify

强烈建议:只在紧急情况用,CI 应该监控 --no-verify 的使用频率。

10.2 commit message 中带 emoji

git commit -m "feat(order): ✨ 新增订单导出"
# ✅ 合法,commitlint 不拦 emoji

但注意 subject-max-length 要算 emoji 占用的字符。

10.3 多行 commit message

git commit -m "feat(order): 新增订单导出" \
           -m "- 支持批量导出" \
           -m "- SXSSF 流式写" \
           -m "Closes #123"

每个 -m 是一段,自动用空行分隔。

10.4 amend 后丢失 commitlint 校验

git commit --amend 会重新触发 commit-msg hook,正常工作。

但如果用 git rebase -i 修改老 commit,可能不会触发 hook。可以在 rebase 后跑一次:

npx commitlint --from HEAD~10 --to HEAD --verbose

10.5 monorepo 的 scope 怎么写

monorepo 里 scope 用包名:

feat(core): 新增日志模块
fix(utils): 修复时间转换 bug
docs(docs): 更新文档

commitlint 可以配置自动校验 scope:

// commitlint.config.js
module.exports = {
  rules: {
    'scope-enum': [2, 'always', ['core', 'utils', 'docs', 'cli', 'api']],
  },
};

10.6 中文 commit message 的坑

commitlint 默认配置对中文友好,但要注意:

// subject 大小写校验对中文无意义,关掉
'subject-case': [0],

// 长度校验时,中文字符按 1 个算
'subject-max-length': [2, 'always', 80],
// 80 个中文字符 = 80 个英文,但中文 80 字其实挺长

十一、总结

完整方案速查

工具作用必装
Conventional Commits规范定义
commitlint自动校验 message
husky把校验挂到 git hook
commitizen交互式生成 message推荐
standard-version自动生成 CHANGELOG + 版本号推荐
changesetsmonorepo 发版工具monorepo 必装

配置文件清单

最小化配置 4 个文件:

package.json              ← 依赖 + scripts
commitlint.config.js      ← commitlint 规则
.husky/commit-msg         ← git hook
.husky/pre-commit         ← 可选,提交前检查

完整工作流

开发 → git add → npm run commit(交互式)→ 自动校验 → 提交
            ↓
PR → CI 自动校验 commit message → 不规范 CI 红
            ↓
合并 main → npm run release → 自动生成 CHANGELOG + tag
            ↓
push tag → 触发 CI/CD → 自动发布 + Release Notes

收益

维度改造前改造后
commit message"fix bug"fix(auth): 修复 token 过期
排查问题看不懂历史一眼定位变更模块
发版说明手写 release notes自动生成 CHANGELOG
版本号手动改自动根据 commit 决定
团队协作各写各的统一规范
自动化全手动全自动

一句话

规范的 commit message 不是"多此一举",而是给未来的自己、给团队、给自动化流水线留的礼物。

投资半小时配置,未来每次提交、每次发版都受益。

互动话题:你们项目的 commit message 规范吗?有没有被 "fix bug" 坑过?欢迎留言吐槽!


参考资料


标题:Git Commit Message 规范 + 自动化:别再写"fix bug"了
作者:jiangyi
地址:http://www.jiangyi.space/articles/2026/08/13/1786163995363.html
公众号:服务端技术精选
    评论
    0 评论
avatar

取消