feat: implement hybrid database architecture and frontend encryption

- Add PostgreSQL + SQLite hybrid database support with automatic switching
- Implement frontend AES-GCM + RSA-OAEP encryption for sensitive data
- Add comprehensive DatabaseInterface with all required methods
- Fix compilation issues with interface consistency
- Update all database method signatures to use DatabaseInterface
- Add missing UpdateTraderInitialBalance method to PostgreSQL implementation
- Integrate RSA public key distribution via /api/config endpoint
- Add frontend crypto service with proper error handling
- Support graceful degradation between encrypted and plaintext transmission
- Add directory creation for RSA keys and PEM parsing fixes
- Test both SQLite and PostgreSQL modes successfully

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: tinkle-community <tinklefund@gmail.com>
This commit is contained in:
icy
2025-11-06 01:50:06 +08:00
parent aecc5e58a1
commit 65053518d6
104 changed files with 16864 additions and 4152 deletions

135
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1,135 @@
# CODEOWNERS
#
# This file defines code ownership and automatic reviewer assignment.
# When a PR touches files matching these patterns, the listed users/teams
# will be automatically requested for review.
#
# 此文件定义代码所有权和自动 reviewer 分配。
# 当 PR 涉及匹配这些模式的文件时,列出的用户/团队将自动被请求审查。
#
# Syntax | 语法:
# pattern @username @org/team-name
#
# More specific patterns override less specific ones
# 更具体的模式会覆盖不太具体的模式
#
# Documentation: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
# =============================================================================
# Global Owners | 全局所有者
# These users will be requested for review on ALL pull requests
# 这些用户将被请求审查所有 PR
# =============================================================================
* @hzb1115 @Icyoung @tangmengqiu @xqliu @SkywalkerJi
# =============================================================================
# Specific Component Owners | 特定组件所有者
# Additional reviewers based on file paths (in addition to global owners)
# 基于文件路径的额外 reviewers在全局 owners 之外)
# =============================================================================
# Backend / Go Code | 后端 / Go 代码
# Go files and backend logic
*.go @xqliu @SkywalkerJi @hzb1115 @Icyoung @tangmengqiu
go.mod @xqliu @SkywalkerJi @hzb1115 @Icyoung @tangmengqiu
go.sum @xqliu @SkywalkerJi @hzb1115 @Icyoung @tangmengqiu
# Frontend / Web | 前端 / Web
# React/TypeScript frontend code
/web/ @0xEmberZz @hzb1115 @xqliu @tangmengqiu
/web/src/ @0xEmberZz @hzb1115 @xqliu @tangmengqiu
*.tsx @0xEmberZz @hzb1115 @xqliu @tangmengqiu
*.ts @0xEmberZz @hzb1115 @xqliu @tangmengqiu (frontend TypeScript only)
*.jsx @0xEmberZz @hzb1115 @xqliu @tangmengqiu
*.css @0xEmberZz @hzb1115 @xqliu @tangmengqiu
*.scss @0xEmberZz @hzb1115 @xqliu @tangmengqiu
# Configuration Files | 配置文件
*.json @0xEmberZz @hzb1115 @xqliu @tangmengqiu
*.yaml @0xEmberZz @hzb1115 @xqliu @tangmengqiu
*.yml @0xEmberZz @hzb1115 @xqliu @tangmengqiu
*.toml @0xEmberZz @hzb1115 @xqliu @tangmengqiu
*.ini @0xEmberZz @hzb1115 @xqliu @tangmengqiu
# Documentation | 文档
# Markdown and documentation files
*.md @hzb1115 @tangmengqiu
/docs/ @hzb1115 @tangmengqiu
README.md @hzb1115 @tangmengqiu
# GitHub Workflows & Actions | GitHub 工作流和 Actions
# CI/CD configuration and automation
/.github/ @hzb1115
/.github/workflows/ @hzb1115
/.github/workflows/*.yml @hzb1115
# Docker | Docker 配置
Dockerfile @tangmengqiu
docker-compose.yml @tangmengqiu
.dockerignore @tangmengqiu
# Database | 数据库
# Database migrations and schemas
/migrations/ @SkywalkerJi @hzb1115 @Icyoung @tangmengqiu
/db/ @SkywalkerJi @hzb1115 @Icyoung @tangmengqiu
*.sql @SkywalkerJi @hzb1115 @Icyoung @tangmengqiu
# Scripts | 脚本
/scripts/ @hzb1115 @xqliu @tangmengqiu
*.sh @hzb1115 @xqliu @tangmengqiu
*.bash @hzb1115 @tangmengqiu
*.py @hzb1115 @tangmengqiu (if Python scripts exist)
# Tests | 测试
# Test files require review from component owners
*_test.go @xqliu @SkywalkerJi @heronsbillC
/tests/ @xqliu @SkywalkerJi @Icyoung @heronsbillC
/web/tests/ @Icyoung @hzb1115 @heronsbillC
# Security & Dependencies | 安全和依赖
# Security-sensitive files require extra attention
.env.example @hzb1115 @tangmengqiu
.gitignore @hzb1115 @tangmengqiu
go.sum @xqliu @hzb1115 @tangmengqiu
package-lock.json @Icyoung @hzb1115 @tangmengqiu
yarn.lock @Icyoung @hzb1115 @tangmengqiu
# Build Configuration | 构建配置
Makefile @hzb1115 @xqliu @tangmengqiu
/build/ @hzb1115 @xqliu @tangmengqiu
/dist/ @hzb1115 @tangmengqiu
# License & Legal | 许可证和法律文件
LICENSE @hzb1115
COPYING @hzb1115
# =============================================================================
# Notes | 注意事项
# =============================================================================
#
# 1. All PRs will be assigned to the 5 global owners
# 所有 PR 都会分配给这 5 个全局 owners
#
# 2. Specific paths may add additional reviewers
# 特定路径可能会添加额外的 reviewers
#
# 3. PR author will NOT be requested for review (GitHub handles this)
# PR 作者不会被请求审查GitHub 自动处理)
#
# 4. You can adjust patterns and owners as needed
# 你可以根据需要调整模式和 owners
#
# 5. To require multiple approvals, configure branch protection rules
# 要求多个批准,请配置分支保护规则
#
# ⚠️ IMPORTANT - Permission Requirements | 重要 - 权限要求:
# - Users listed here will ONLY be auto-requested if they have Write+ permission
# 这里列出的用户只有在拥有 Write 或以上权限时才会被自动请求
# - GitHub will silently skip users without proper permissions
# GitHub 会静默跳过没有适当权限的用户
# - See CODEOWNERS_PERMISSIONS.md for details
# 详见 CODEOWNERS_PERMISSIONS.md
#
# =============================================================================

View File

@@ -82,7 +82,7 @@ By claiming this bounty, I acknowledge that:
- [ ] I have read the [Contributing Guide](../../CONTRIBUTING.md)
- [ ] I will follow the [Code of Conduct](../../CODE_OF_CONDUCT.md)
- [ ] I understand the acceptance criteria
- [ ] My contribution will be licensed under MIT License
- [ ] My contribution will be licensed under AGPL-3.0 License
- [ ] Payment is subject to successful PR merge
---

View File

@@ -1,33 +1,44 @@
# Pull Request | PR 提交
> **💡 提示 Tip:** 推荐 PR 标题格式 Recommended PR title format: `type(scope): description`
> 例如 Examples: `feat(trader): add new strategy` | `fix(api): resolve auth issue`
> 详情 Details: [PR Title Guide](./PR_TITLE_GUIDE.md)
> **📋 选择专用模板 | Choose Specialized Template**
>
> 我们现在提供了针对不同类型PR的专用模板帮助你更快速地填写PR信息
> We now offer specialized templates for different types of PRs to help you fill out the information faster:
>
> - 🔧 **[Backend PR Template](./PULL_REQUEST_TEMPLATE/backend.md)** | 后端PR模板 - For Go/API/Trading changes
> - 🎨 **[Frontend PR Template](./PULL_REQUEST_TEMPLATE/frontend.md)** | 前端PR模板 - For UI/UX changes
> - 📝 **[Documentation PR Template](./PULL_REQUEST_TEMPLATE/docs.md)** | 文档PR模板 - For documentation updates
> - 📦 **[General PR Template](./PULL_REQUEST_TEMPLATE/general.md)** | 通用PR模板 - For mixed or other changes
>
> **如何使用?| How to use?**
> - 创建PR时在URL中添加 `?template=backend.md` 或其他模板名称
> - When creating a PR, add `?template=backend.md` or other template name to the URL
> - 或者直接复制粘贴对应模板的内容
> - Or simply copy and paste the content from the corresponding template
---
> **💡 提示 Tip:** 推荐 PR 标题格式 `type(scope): description`
> 例如: `feat(trader): add new strategy` | `fix(api): resolve auth issue`
---
## 📝 Description | 描述
<!-- Provide a brief summary of your changes -->
<!-- 简要描述你的变更 -->
**English:** **中文:**
**English:**
**中文:**
---
## 🎯 Type of Change | 变更类型
<!-- Mark the relevant option with an "x" -->
<!-- 在相关选项上打"x" -->
- [ ] 🐛 Bug fix | 修复 Bug不影响现有功能的修复
- [ ] ✨ New feature | 新功能(不影响现有功能的新增)
- [ ] 💥 Breaking change | 破坏性变更(会导致现有功能无法正常工作的修复或功能)
- [ ] 🐛 Bug fix | 修复 Bug
- [ ] ✨ New feature | 新功能
- [ ] 💥 Breaking change | 破坏性变更
- [ ] 📝 Documentation update | 文档更新
- [ ] 🎨 Code style update | 代码样式更新(格式化、重命名等)
- [ ] ♻️ Refactoring | 重构(无功能变更)
- [ ] 🎨 Code style update | 代码样式更新
- [ ] ♻️ Refactoring | 重构
- [ ] ⚡ Performance improvement | 性能优化
- [ ] ✅ Test update | 测试更新
- [ ] 🔧 Build/config change | 构建/配置变更
@@ -37,9 +48,6 @@
## 🔗 Related Issues | 相关 Issue
<!-- Link related issues below. Use "Closes #123" to auto-close issues when PR is merged -->
<!-- 在下方关联相关 issue。使用 "Closes #123" 可以在 PR 合并时自动关闭 issue -->
- Closes # | 关闭 #
- Related to # | 相关 #
@@ -47,242 +55,50 @@
## 📋 Changes Made | 具体变更
<!-- List the specific changes you made -->
<!-- 列出你做的具体变更 -->
**English:**
- Change 1
- Change 2
- Change 3
**中文:**
- 变更 1
- 变更 2
- 变更 3
**English:** **中文:**
-
-
---
## 🧪 Testing | 测试
### Manual Testing | 手动测试
<!-- Describe how you tested your changes -->
<!-- 描述你如何测试你的变更 -->
- [ ] Tested locally | 本地测试通过
- [ ] Tested on testnet | 测试网测试通过(交易所集成相关)
- [ ] Tested with different configurations | 测试了不同配置
- [ ] Tests pass | 测试通过
- [ ] Verified no existing functionality broke | 确认没有破坏现有功能
### Test Environment | 测试环境
- **OS | 操作系统:** [e.g. macOS, Ubuntu, Windows]
- **Go Version | Go 版本:** [e.g. 1.21.5]
- **Node Version | Node 版本:** [e.g. 18.x] (if applicable | 如适用)
- **Exchange | 交易所:** [if applicable | 如适用]
### Test Results | 测试结果
<!-- Paste relevant test output or describe results -->
<!-- 粘贴相关测试输出或描述结果 -->
```
Test output here | 测试输出
```
---
## 📸 Screenshots / Demo | 截图/演示
<!-- If applicable, add screenshots or video demo -->
<!-- 如适用,添加截图或视频演示 -->
<!-- For UI changes, include before/after screenshots -->
<!-- 对于 UI 变更,包含变更前后的截图 -->
**Before | 变更前:**
**After | 变更后:**
---
## ✅ Checklist | 检查清单
<!-- Mark completed items with an "x" -->
<!-- 在已完成的项目上打"x" -->
### Code Quality | 代码质量
- [ ] My code follows the project's code style | 我的代码遵循项目代码风格 ([Contributing Guide](../CONTRIBUTING.md))
- [ ] I have performed a self-review of my code | 我已进行代码自查
- [ ] I have commented my code, particularly in hard-to-understand areas | 我已添加代码注释,特别是难以理解的部分
- [ ] My changes generate no new warnings or errors | 我的变更没有产生新的警告或错误
- [ ] Code compiles successfully | 代码编译成功 (`go build` / `npm run build`)
- [ ] I have run `go fmt` (for Go code) | 我已运行 `go fmt`Go 代码)
- [ ] I have run `npm run lint` (for frontend code) | 我已运行 `npm run lint`(前端代码)
### Testing | 测试
- [ ] I have added tests that prove my fix is effective or that my feature works | 我已添加证明修复有效或功能正常的测试
- [ ] New and existing unit tests pass locally | 新旧单元测试在本地通过
- [ ] I have tested on testnet (for trading/exchange changes) | 我已在测试网测试(交易/交易所变更)
- [ ] Integration tests pass | 集成测试通过
- [ ] Code follows project style | 代码遵循项目风格
- [ ] Self-review completed | 已完成代码自查
- [ ] Comments added for complex logic | 已添加必要注释
### Documentation | 文档
- [ ] I have updated the documentation accordingly | 我已相应更新文档
- [ ] I have updated the README if needed | 我已更新 README如需要
- [ ] I have added inline code comments where necessary | 我已在必要处添加代码注释
- [ ] I have updated type definitions (for TypeScript changes) | 我已更新类型定义TypeScript 变更)
- [ ] I have updated API documentation (if applicable) | 我已更新 API 文档(如适用)
- [ ] Updated relevant documentation | 已更新相关文档
### Git
- [ ] My commits follow the conventional commits format | 我的提交遵循 Conventional Commits 格式 (`feat:`, `fix:`, etc.)
- [ ] I have rebased my branch on the latest `dev` branch | 我已将分支 rebase 到最新的 `dev` 分支
- [ ] There are no merge conflicts | 没有合并冲突
- [ ] Commit messages are clear and descriptive | 提交信息清晰明确
---
## 🔒 Security Considerations | 安全考虑
<!-- Answer these questions for security-sensitive changes -->
<!-- 对于安全敏感的变更,请回答以下问题 -->
- [ ] No API keys or secrets are hardcoded | 没有硬编码 API 密钥或密钥
- [ ] User inputs are properly validated | 用户输入已正确验证
- [ ] No SQL injection vulnerabilities introduced | 未引入 SQL 注入漏洞
- [ ] No XSS vulnerabilities introduced | 未引入 XSS 漏洞
- [ ] Authentication/authorization properly handled | 认证/授权已正确处理
- [ ] Sensitive data is encrypted | 敏感数据已加密
- [ ] N/A (not security-related) | 不适用(非安全相关)
---
## ⚡ Performance Impact | 性能影响
<!-- Describe any performance implications -->
<!-- 描述任何性能影响 -->
- [ ] No significant performance impact | 无显著性能影响
- [ ] Performance improved | 性能提升
- [ ] Performance may be impacted (explain below) | 性能可能受影响(请在下方说明)
<!-- If performance impacted, explain: -->
<!-- 如果性能受影响,请说明: -->
**English:**
**中文:**
---
## 🌐 Internationalization | 国际化
<!-- For UI/documentation changes -->
<!-- 对于 UI/文档变更 -->
- [ ] All user-facing text supports i18n | 所有面向用户的文本支持国际化
- [ ] Both English and Chinese versions provided | 提供了中英文版本
- [ ] N/A | 不适用
- [ ] Commits follow conventional format | 提交遵循 Conventional Commits 格式
- [ ] Rebased on latest `dev` branch | 已 rebase 到最新 `dev` 分支
- [ ] No merge conflicts | 无合并冲突
---
## 📚 Additional Notes | 补充说明
<!-- Any additional information for reviewers -->
<!-- 给审查者的任何补充信息 -->
**English:** **中文:**
**English:**
**中文:**
---
## 💰 For Bounty Claims | 赏金申请
**By submitting this PR, I confirm | 提交此 PR我确认**
<!-- Fill this section only if claiming a bounty -->
<!-- 仅在申请赏金时填写此部分 -->
- [ ] This PR is for bounty issue # | 此 PR 用于赏金 issue #
- [ ] All acceptance criteria from the bounty issue are met | 满足赏金 issue 的所有验收标准
- [ ] I have included a demo video/screenshots | 我已包含演示视频/截图
- [ ] I am ready for payment upon merge | 我准备好在合并后接收付款
**Payment Details | 付款详情:** <!-- Discuss privately with maintainers | 与维护者私下讨论 -->
- [ ] I have read the [Contributing Guidelines](../CONTRIBUTING.md) | 已阅读贡献指南
- [ ] I agree to the [Code of Conduct](../CODE_OF_CONDUCT.md) | 同意行为准则
- [ ] My contribution is licensed under AGPL-3.0 | 贡献遵循 AGPL-3.0 许可证
---
## 🙏 Reviewer Notes | 审查者注意事项
<!-- Optional: anything specific you want reviewers to focus on? -->
<!-- 可选:你希望审查者关注的特定内容? -->
**English:**
**中文:**
---
## 📋 PR Size Estimate | PR 大小估计
<!-- This helps reviewers plan their time -->
<!-- 这有助于审查者安排时间 -->
- [ ] 🟢 Small (< 100 lines) | < 100
- [ ] 🟡 Medium (100-500 lines) | 100-500
- [ ] 🔴 Large (> 500 lines) | 大(> 500 行)
<!-- For large PRs, consider: -->
<!-- 对于大型 PR考虑 -->
<!-- - Breaking into smaller, focused PRs | 拆分为更小、更专注的 PR -->
<!-- - Providing a detailed explanation | 提供详细说明 -->
<!-- - Highlighting the most important changes | 突出最重要的变更 -->
---
## 🎯 Review Focus Areas | 审查重点
<!-- Help reviewers know where to focus their attention -->
<!-- 帮助审查者了解重点关注的地方 -->
Please pay special attention to:
请特别注意:
- [ ] Logic changes | 逻辑变更
- [ ] Security implications | 安全影响
- [ ] Performance optimization | 性能优化
- [ ] API changes | API 变更
- [ ] Database schema changes | 数据库架构变更
- [ ] UI/UX changes | UI/UX 变更
---
**By submitting this PR, I confirm that:**
**提交此 PR我确认**
- [ ] I have read the [Contributing Guidelines](../CONTRIBUTING.md) | 我已阅读[贡献指南](../CONTRIBUTING.md)
- [ ] I agree to the [Code of Conduct](../CODE_OF_CONDUCT.md) | 我同意[行为准则](../CODE_OF_CONDUCT.md)
- [ ] My contribution is licensed under the MIT License | 我的贡献遵循 MIT 许可证
- [ ] I understand this is a voluntary contribution | 我理解这是自愿贡献
- [ ] I have the right to submit this code | 我有权提交此代码
---
<!--
🌟 感谢你的贡献Thank you for your contribution!
贡献者来自世界各地,我们重视每一份贡献。
Contributors come from all around the world, and we value every contribution.
如果你是首次贡献,欢迎加入我们的社区!
If this is your first contribution, welcome to our community!
💬 需要帮助Feel free to ask questions in:
- GitHub Discussions
- Discord: [链接 Link]
- Telegram: [链接 Link]
-->
🌟 **Thank you for your contribution! | 感谢你的贡献!**

213
.github/PULL_REQUEST_TEMPLATE/README.md vendored Normal file
View File

@@ -0,0 +1,213 @@
# PR Templates | PR 模板
## 📋 模板概述 | Template Overview
我们提供了4种针对不同类型PR的专用模板帮助贡献者快速填写PR信息
We offer 4 specialized templates for different types of PRs to help contributors quickly fill out PR information:
### 1. 🔧 Backend Template | 后端模板
**文件:** `backend.md`
**适用于 | Use for:**
- Go代码变更 | Go code changes
- API端点开发 | API endpoint development
- 交易逻辑实现 | Trading logic implementation
- 后端性能优化 | Backend performance optimization
- 数据库相关改动 | Database-related changes
**包含 | Includes:**
- Go测试环境配置 | Go test environment
- 安全考虑检查 | Security considerations
- 性能影响评估 | Performance impact assessment
- `go fmt``go build` 检查 | `go fmt` and `go build` checks
### 2. 🎨 Frontend Template | 前端模板
**文件:** `frontend.md`
**适用于 | Use for:**
- UI/UX变更 | UI/UX changes
- React/Vue组件开发 | React/Vue component development
- 前端样式更新 | Frontend styling updates
- 浏览器兼容性修复 | Browser compatibility fixes
- 前端性能优化 | Frontend performance optimization
**包含 | Includes:**
- 截图/演示要求 | Screenshots/demo requirements
- 浏览器测试清单 | Browser testing checklist
- 国际化检查 | Internationalization checks
- 响应式设计验证 | Responsive design verification
- `npm run lint``npm run build` 检查 | Linting and build checks
### 3. 📝 Documentation Template | 文档模板
**文件:** `docs.md`
**适用于 | Use for:**
- README更新 | README updates
- API文档编写 | API documentation
- 教程和指南 | Tutorials and guides
- 代码注释改进 | Code comment improvements
- 翻译工作 | Translation work
**包含 | Includes:**
- 文档类型分类 | Documentation type classification
- 内容质量检查 | Content quality checks
- 双语要求(中英文)| Bilingual requirements (EN/CN)
- 链接有效性验证 | Link validity verification
### 4. 📦 General Template | 通用模板
**文件:** `general.md`
**适用于 | Use for:**
- 混合类型变更 | Mixed-type changes
- 跨多个领域的PR | Cross-domain PRs
- 构建配置变更 | Build configuration changes
- 依赖更新 | Dependency updates
- 不确定使用哪个模板时 | When unsure which template to use
## 🤖 自动模板建议 | Automatic Template Suggestion
我们的GitHub Action会自动分析你的PR并建议最合适的模板
Our GitHub Action automatically analyzes your PR and suggests the most suitable template:
### 工作原理 | How it works:
1. **文件分析 | File Analysis**
- 检测PR中所有变更的文件类型
- Detects all changed file types in the PR
2. **智能判断 | Smart Detection**
- 如果 >50% 是 `.go` 文件 → 建议**后端模板**
- If >50% are `.go` files → Suggests **Backend template**
- 如果 >50% 是 `.js/.ts/.tsx/.vue` 文件 → 建议**前端模板**
- If >50% are `.js/.ts/.tsx/.vue` files → Suggests **Frontend template**
- 如果 >70% 是 `.md` 文件 → 建议**文档模板**
- If >70% are `.md` files → Suggests **Documentation template**
3. **自动评论 | Auto-comment**
- 如果检测到你使用了默认模板,但应该用专用模板
- If it detects you're using the default template but should use a specialized one
- 会自动添加友好的评论建议
- It will automatically add a friendly comment suggestion
4. **自动标签 | Auto-labeling**
- 自动添加对应的标签:`backend``frontend``documentation`
- Automatically adds corresponding labels: `backend`, `frontend`, `documentation`
## 📖 使用方法 | How to Use
### 方法1: URL参数推荐 | Method 1: URL Parameter (Recommended)
创建PR时在URL末尾添加模板参数
When creating a PR, add the template parameter to the URL:
```
https://github.com/YOUR_ORG/nofx/compare/dev...YOUR_BRANCH?template=backend.md
```
替换 `backend.md` 为:
Replace `backend.md` with:
- `backend.md` - 后端模板 | Backend template
- `frontend.md` - 前端模板 | Frontend template
- `docs.md` - 文档模板 | Documentation template
- `general.md` - 通用模板 | General template
### 方法2: 手动选择 | Method 2: Manual Selection
1. 创建PR时默认模板会显示
When creating a PR, the default template will be shown
2. 根据顶部的指引链接,点击查看对应的模板
Follow the guidance links at the top to view the corresponding template
3. 复制模板内容到PR描述中
Copy the template content into the PR description
### 方法3: 跟随自动建议 | Method 3: Follow Auto-suggestion
1. 使用任何模板创建PR
Create a PR with any template
2. GitHub Action会自动分析并评论建议
GitHub Action will automatically analyze and comment with a suggestion
3. 根据建议更新PR描述
Update the PR description based on the suggestion
## 🎯 最佳实践 | Best Practices
1. **提前选择 | Choose in Advance**
- 在创建PR前确定变更类型
- Determine the change type before creating the PR
2. **完整填写 | Complete Filling**
- 不要跳过必填项(标记为 required
- Don't skip required items
3. **保持简洁 | Keep it Concise**
- 描述清晰但简洁
- Keep descriptions clear but concise
4. **添加截图 | Add Screenshots**
- 对于UI变更务必添加截图
- For UI changes, always add screenshots
5. **测试证明 | Test Evidence**
- 提供测试通过的证据
- Provide evidence that tests pass
## 🔧 自定义 | Customization
如果需要修改模板或自动检测逻辑:
If you need to modify templates or auto-detection logic:
1. **修改模板** | **Modify Templates**
- 编辑 `.github/PULL_REQUEST_TEMPLATE/*.md` 文件
- Edit `.github/PULL_REQUEST_TEMPLATE/*.md` files
2. **调整检测阈值** | **Adjust Detection Threshold**
- 编辑 `.github/workflows/pr-template-suggester.yml`
- Edit `.github/workflows/pr-template-suggester.yml`
- 修改文件类型占比阈值当前50%后端50%前端70%文档)
- Modify file type percentage thresholds (current: 50% backend, 50% frontend, 70% docs)
3. **添加新模板** | **Add New Template**
-`PULL_REQUEST_TEMPLATE/` 目录创建新的 `.md` 文件
- Create a new `.md` file in the `PULL_REQUEST_TEMPLATE/` directory
- 更新工作流以支持新的文件类型检测
- Update the workflow to support new file type detection
## ❓ FAQ
**Q: 我的PR既有前端又有后端代码用哪个模板**
**Q: My PR has both frontend and backend code, which template should I use?**
A: 使用**通用模板**`general.md`),或选择主要变更类型的模板。
A: Use the **General template** (`general.md`), or choose the template for the primary change type.
---
**Q: 自动建议的模板不合适怎么办?**
**Q: What if the automatically suggested template is not suitable?**
A: 你可以忽略建议,继续使用当前模板。自动建议仅供参考。
A: You can ignore the suggestion and continue using the current template. Auto-suggestions are for reference only.
---
**Q: 可以不使用任何模板吗?**
**Q: Can I not use any template?**
A: 不推荐。模板帮助确保PR包含必要信息加快审查速度。
A: Not recommended. Templates help ensure PRs contain necessary information and speed up reviews.
---
**Q: 如何禁用自动模板建议?**
**Q: How to disable automatic template suggestions?**
A: 删除或禁用 `.github/workflows/pr-template-suggester.yml` 文件。
A: Delete or disable the `.github/workflows/pr-template-suggester.yml` file.
---
🌟 **感谢使用我们的PR模板系统| Thank you for using our PR template system!**

121
.github/PULL_REQUEST_TEMPLATE/backend.md vendored Normal file
View File

@@ -0,0 +1,121 @@
# Pull Request - Backend | 后端 PR
> **💡 提示 Tip:** 推荐 PR 标题格式 `type(scope): description`
> 例如: `feat(trader): add new strategy` | `fix(api): resolve auth issue`
---
## 📝 Description | 描述
**English:** **中文:**
---
## 🎯 Type of Change | 变更类型
- [ ] 🐛 Bug fix | 修复 Bug
- [ ] ✨ New feature | 新功能
- [ ] 💥 Breaking change | 破坏性变更
- [ ] ♻️ Refactoring | 重构
- [ ] ⚡ Performance improvement | 性能优化
- [ ] 🔒 Security fix | 安全修复
- [ ] 🔧 Build/config change | 构建/配置变更
---
## 🔗 Related Issues | 相关 Issue
- Closes # | 关闭 #
- Related to # | 相关 #
---
## 📋 Changes Made | 具体变更
**English:** **中文:**
-
-
---
## 🧪 Testing | 测试
### Test Environment | 测试环境
- **OS | 操作系统:**
- **Go Version | Go 版本:**
- **Exchange | 交易所:** [if applicable | 如适用]
### Manual Testing | 手动测试
- [ ] Tested locally | 本地测试通过
- [ ] Tested on testnet | 测试网测试通过(交易所集成相关)
- [ ] Unit tests pass | 单元测试通过
- [ ] Verified no existing functionality broke | 确认没有破坏现有功能
### Test Results | 测试结果
```
Test output here | 测试输出
```
---
## 🔒 Security Considerations | 安全考虑
- [ ] No API keys or secrets hardcoded | 没有硬编码 API 密钥
- [ ] User inputs properly validated | 用户输入已正确验证
- [ ] No SQL injection vulnerabilities | 无 SQL 注入漏洞
- [ ] Authentication/authorization properly handled | 认证/授权正确处理
- [ ] Sensitive data is encrypted | 敏感数据已加密
- [ ] N/A (not security-related) | 不适用
---
## ⚡ Performance Impact | 性能影响
- [ ] No significant performance impact | 无显著性能影响
- [ ] Performance improved | 性能提升
- [ ] Performance may be impacted (explain below) | 性能可能受影响
**If impacted, explain | 如果受影响,请说明:**
---
## ✅ Checklist | 检查清单
### Code Quality | 代码质量
- [ ] Code follows project style | 代码遵循项目风格
- [ ] Self-review completed | 已完成代码自查
- [ ] Comments added for complex logic | 已添加必要注释
- [ ] Code compiles successfully | 代码编译成功 (`go build`)
- [ ] Ran `go fmt` | 已运行 `go fmt`
### Documentation | 文档
- [ ] Updated relevant documentation | 已更新相关文档
- [ ] Added inline comments where necessary | 已添加必要的代码注释
- [ ] Updated API documentation (if applicable) | 已更新 API 文档
### Git
- [ ] Commits follow conventional format | 提交遵循 Conventional Commits 格式
- [ ] Rebased on latest `dev` branch | 已 rebase 到最新 `dev` 分支
- [ ] No merge conflicts | 无合并冲突
---
## 📚 Additional Notes | 补充说明
**English:** **中文:**
---
**By submitting this PR, I confirm | 提交此 PR我确认**
- [ ] I have read the [Contributing Guidelines](../../CONTRIBUTING.md) | 已阅读贡献指南
- [ ] I agree to the [Code of Conduct](../../CODE_OF_CONDUCT.md) | 同意行为准则
- [ ] My contribution is licensed under AGPL-3.0 | 贡献遵循 AGPL-3.0 许可证
---
🌟 **Thank you for your contribution! | 感谢你的贡献!**

97
.github/PULL_REQUEST_TEMPLATE/docs.md vendored Normal file
View File

@@ -0,0 +1,97 @@
# Pull Request - Documentation | 文档 PR
> **💡 提示 Tip:** 推荐 PR 标题格式 `docs(scope): description`
> 例如: `docs(api): update trading endpoints` | `docs(readme): add setup guide`
---
## 📝 Description | 描述
**English:** **中文:**
---
## 📚 Type of Documentation | 文档类型
- [ ] 📖 README update | README 更新
- [ ] 📋 API documentation | API 文档
- [ ] 🎓 Tutorial/Guide | 教程/指南
- [ ] 📝 Code comments | 代码注释
- [ ] 🔧 Configuration docs | 配置文档
- [ ] 🐛 Fix typo/error | 修复拼写/错误
- [ ] 🌍 Translation | 翻译
---
## 🔗 Related Issues | 相关 Issue
- Closes # | 关闭 #
- Related to # | 相关 #
---
## 📋 Changes Made | 具体变更
**English:** **中文:**
-
-
---
## 📸 Screenshots (if applicable) | 截图(如适用)
<!-- For documentation with images, diagrams, or UI examples -->
<!-- 用于包含图片、图表或 UI 示例的文档 -->
---
## 🌐 Internationalization | 国际化
- [ ] English version complete | 英文版本完整
- [ ] Chinese version complete | 中文版本完整
- [ ] Both versions are consistent | 两个版本内容一致
- [ ] N/A (only one language needed) | 不适用(只需要一种语言)
---
## ✅ Checklist | 检查清单
### Content Quality | 内容质量
- [ ] Information is accurate and up-to-date | 信息准确且最新
- [ ] Language is clear and concise | 语言清晰简洁
- [ ] No spelling or grammar errors | 无拼写或语法错误
- [ ] Links are valid and working | 链接有效且可用
- [ ] Code examples are tested and working | 代码示例已测试且可用
- [ ] Formatting is consistent | 格式一致
### Documentation Standards | 文档标准
- [ ] Follows project documentation style | 遵循项目文档风格
- [ ] Includes necessary examples | 包含必要的示例
- [ ] Technical terms are explained | 技术术语已解释
- [ ] Self-review completed | 已完成自查
### Git
- [ ] Commits follow conventional format | 提交遵循 Conventional Commits 格式
- [ ] Rebased on latest `dev` branch | 已 rebase 到最新 `dev` 分支
- [ ] No merge conflicts | 无合并冲突
---
## 📚 Additional Notes | 补充说明
**English:** **中文:**
---
**By submitting this PR, I confirm | 提交此 PR我确认**
- [ ] I have read the [Contributing Guidelines](../../CONTRIBUTING.md) | 已阅读贡献指南
- [ ] I agree to the [Code of Conduct](../../CODE_OF_CONDUCT.md) | 同意行为准则
- [ ] My contribution is licensed under AGPL-3.0 | 贡献遵循 AGPL-3.0 许可证
---
🌟 **Thank you for your contribution! | 感谢你的贡献!**

View File

@@ -0,0 +1,119 @@
# Pull Request - Frontend | 前端 PR
> **💡 提示 Tip:** 推荐 PR 标题格式 `type(scope): description`
> 例如: `feat(ui): add dark mode toggle` | `fix(form): resolve validation bug`
---
## 📝 Description | 描述
**English:** **中文:**
---
## 🎯 Type of Change | 变更类型
- [ ] 🐛 Bug fix | 修复 Bug
- [ ] ✨ New feature | 新功能
- [ ] 💥 Breaking change | 破坏性变更
- [ ] 🎨 Code style update | 代码样式更新
- [ ] ♻️ Refactoring | 重构
- [ ] ⚡ Performance improvement | 性能优化
---
## 🔗 Related Issues | 相关 Issue
- Closes # | 关闭 #
- Related to # | 相关 #
---
## 📋 Changes Made | 具体变更
**English:** **中文:**
-
-
---
## 📸 Screenshots / Demo | 截图/演示
<!-- For UI changes, include before/after screenshots or video demo -->
<!-- 对于 UI 变更,请包含变更前后的截图或视频演示 -->
**Before | 变更前:**
**After | 变更后:**
---
## 🧪 Testing | 测试
### Test Environment | 测试环境
- **OS | 操作系统:**
- **Node Version | Node 版本:**
- **Browser(s) | 浏览器:**
### Manual Testing | 手动测试
- [ ] Tested in development mode | 开发模式测试通过
- [ ] Tested production build | 生产构建测试通过
- [ ] Tested on multiple browsers | 多浏览器测试通过
- [ ] Tested responsive design | 响应式设计测试通过
- [ ] Verified no existing functionality broke | 确认没有破坏现有功能
---
## 🌐 Internationalization | 国际化
- [ ] All user-facing text supports i18n | 所有面向用户的文本支持国际化
- [ ] Both English and Chinese versions provided | 提供了中英文版本
- [ ] N/A | 不适用
---
## ✅ Checklist | 检查清单
### Code Quality | 代码质量
- [ ] Code follows project style | 代码遵循项目风格
- [ ] Self-review completed | 已完成代码自查
- [ ] Comments added for complex logic | 已添加必要注释
- [ ] Code builds successfully | 代码构建成功 (`npm run build`)
- [ ] Ran `npm run lint` | 已运行 `npm run lint`
- [ ] No console errors or warnings | 无控制台错误或警告
### Testing | 测试
- [ ] Component tests added/updated | 已添加/更新组件测试
- [ ] Tests pass locally | 测试在本地通过
### Documentation | 文档
- [ ] Updated relevant documentation | 已更新相关文档
- [ ] Updated type definitions (TypeScript) | 已更新类型定义
- [ ] Added JSDoc comments where necessary | 已添加 JSDoc 注释
### Git
- [ ] Commits follow conventional format | 提交遵循 Conventional Commits 格式
- [ ] Rebased on latest `dev` branch | 已 rebase 到最新 `dev` 分支
- [ ] No merge conflicts | 无合并冲突
---
## 📚 Additional Notes | 补充说明
**English:** **中文:**
---
**By submitting this PR, I confirm | 提交此 PR我确认**
- [ ] I have read the [Contributing Guidelines](../../CONTRIBUTING.md) | 已阅读贡献指南
- [ ] I agree to the [Code of Conduct](../../CODE_OF_CONDUCT.md) | 同意行为准则
- [ ] My contribution is licensed under AGPL-3.0 | 贡献遵循 AGPL-3.0 许可证
---
🌟 **Thank you for your contribution! | 感谢你的贡献!**

View File

@@ -0,0 +1,98 @@
# Pull Request - General | 通用 PR
> **💡 提示 Tip:** 推荐 PR 标题格式 `type(scope): description`
> 例如: `feat(trader): add new strategy` | `fix(api): resolve auth issue` | `docs(readme): update`
---
## 📝 Description | 描述
**English:** **中文:**
---
## 🎯 Type of Change | 变更类型
- [ ] 🐛 Bug fix | 修复 Bug
- [ ] ✨ New feature | 新功能
- [ ] 💥 Breaking change | 破坏性变更
- [ ] 📝 Documentation update | 文档更新
- [ ] 🎨 Code style update | 代码样式更新
- [ ] ♻️ Refactoring | 重构
- [ ] ⚡ Performance improvement | 性能优化
- [ ] ✅ Test update | 测试更新
- [ ] 🔧 Build/config change | 构建/配置变更
- [ ] 🔒 Security fix | 安全修复
---
## 🔗 Related Issues | 相关 Issue
- Closes # | 关闭 #
- Related to # | 相关 #
---
## 📋 Changes Made | 具体变更
**English:** **中文:**
-
-
---
## 🧪 Testing | 测试
- [ ] Tested locally | 本地测试通过
- [ ] Tests pass | 测试通过
- [ ] Verified no existing functionality broke | 确认没有破坏现有功能
**Test details | 测试详情:**
---
## ✅ Checklist | 检查清单
### Code Quality | 代码质量
- [ ] Code follows project style | 代码遵循项目风格
- [ ] Self-review completed | 已完成代码自查
- [ ] Comments added for complex logic | 已添加必要注释
- [ ] No new warnings or errors | 无新的警告或错误
### Documentation | 文档
- [ ] Updated relevant documentation | 已更新相关文档
- [ ] Added inline comments where necessary | 已添加必要的代码注释
### Git
- [ ] Commits follow conventional format | 提交遵循 Conventional Commits 格式
- [ ] Rebased on latest `dev` branch | 已 rebase 到最新 `dev` 分支
- [ ] No merge conflicts | 无合并冲突
---
## 🔒 Security (if applicable) | 安全(如适用)
- [ ] No API keys or secrets hardcoded | 没有硬编码 API 密钥
- [ ] User inputs properly validated | 用户输入已正确验证
- [ ] N/A | 不适用
---
## 📚 Additional Notes | 补充说明
**English:** **中文:**
---
**By submitting this PR, I confirm | 提交此 PR我确认**
- [ ] I have read the [Contributing Guidelines](../../CONTRIBUTING.md) | 已阅读贡献指南
- [ ] I agree to the [Code of Conduct](../../CODE_OF_CONDUCT.md) | 同意行为准则
- [ ] My contribution is licensed under AGPL-3.0 | 贡献遵循 AGPL-3.0 许可证
---
🌟 **Thank you for your contribution! | 感谢你的贡献!**

95
.github/workflows/docker-build.yml vendored Normal file
View File

@@ -0,0 +1,95 @@
name: Build and Push Docker Images
on:
push:
branches:
- main
- dev
tags:
- 'v*'
pull_request:
branches:
- main
- dev
workflow_dispatch:
env:
REGISTRY_GHCR: ghcr.io
IMAGE_NAME_BACKEND: ${{ github.repository }}/nofx-backend
IMAGE_NAME_FRONTEND: ${{ github.repository }}/nofx-frontend
jobs:
build-and-push:
runs-on: ubuntu-22.04
permissions:
contents: read
packages: write
strategy:
matrix:
include:
- name: backend
dockerfile: ./docker/Dockerfile.backend
image_suffix: backend
- name: frontend
dockerfile: ./docker/Dockerfile.frontend
image_suffix: frontend
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY_GHCR }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
continue-on-error: true
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.REGISTRY_GHCR }}/${{ github.repository }}/nofx-${{ matrix.image_suffix }}
${{ secrets.DOCKERHUB_USERNAME && format('{0}/nofx-{1}', secrets.DOCKERHUB_USERNAME, matrix.image_suffix) || '' }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha,prefix={{branch}}
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push ${{ matrix.name }} image
uses: docker/build-push-action@v5
with:
context: .
file: ${{ matrix.dockerfile }}
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
BUILD_DATE=${{ github.event.head_commit.timestamp }}
VCS_REF=${{ github.sha }}
VERSION=${{ github.ref_name }}
- name: Image digest
run: echo "Image digest for ${{ matrix.name }} - ${{ steps.meta.outputs.digest }}"

View File

@@ -104,6 +104,53 @@ jobs:
echo "⚠️ Frontend results artifact not found"
fi
- name: Get PR information
id: pr-info
if: steps.backend.outputs.pr_number != '0'
uses: actions/github-script@v7
with:
script: |
const prNumber = ${{ steps.backend.outputs.pr_number }};
// Get PR details
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber
});
// Check PR title format (Conventional Commits)
const prTitle = pr.title;
const conventionalCommitPattern = /^(feat|fix|docs|style|refactor|perf|test|chore|ci|security|build)(\(.+\))?: .+/;
const titleValid = conventionalCommitPattern.test(prTitle);
core.setOutput('pr_title', prTitle);
core.setOutput('title_valid', titleValid);
// Calculate PR size
const additions = pr.additions;
const deletions = pr.deletions;
const total = additions + deletions;
let size = '';
let sizeEmoji = '';
if (total < 300) {
size = 'Small';
sizeEmoji = '🟢';
} else if (total < 1000) {
size = 'Medium';
sizeEmoji = '🟡';
} else {
size = 'Large';
sizeEmoji = '🔴';
}
core.setOutput('pr_size', size);
core.setOutput('size_emoji', sizeEmoji);
core.setOutput('total_lines', total);
core.setOutput('additions', additions);
core.setOutput('deletions', deletions);
- name: Post advisory results comment
if: steps.backend.outputs.pr_number != '0'
uses: actions/github-script@v7
@@ -113,7 +160,40 @@ jobs:
let comment = '## 🤖 Advisory Check Results\n\n';
comment += 'These are **advisory** checks to help improve code quality. They won\'t block your PR from being merged.\n\n';
comment += '> **Note:** PR title and size checks are handled by the main workflow and may appear in a separate comment.\n\n';
// PR Information section
const prTitle = '${{ steps.pr-info.outputs.pr_title }}';
const titleValid = '${{ steps.pr-info.outputs.title_valid }}' === 'true';
const prSize = '${{ steps.pr-info.outputs.pr_size }}';
const sizeEmoji = '${{ steps.pr-info.outputs.size_emoji }}';
const totalLines = '${{ steps.pr-info.outputs.total_lines }}';
const additions = '${{ steps.pr-info.outputs.additions }}';
const deletions = '${{ steps.pr-info.outputs.deletions }}';
comment += '### 📋 PR Information\n\n';
// Title check
if (titleValid) {
comment += '**Title Format:** ✅ Good - Follows Conventional Commits\n';
} else {
comment += '**Title Format:** ⚠️ Suggestion - Consider using `type(scope): description`\n';
comment += '<details><summary>Recommended format</summary>\n\n';
comment += '**Valid types:** `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`, `ci`, `security`, `build`\n\n';
comment += '**Examples:**\n';
comment += '- `feat(trader): add new trading strategy`\n';
comment += '- `fix(api): resolve authentication issue`\n';
comment += '- `docs: update README`\n';
comment += '</details>\n\n';
}
// Size check
comment += `**PR Size:** ${sizeEmoji} ${prSize} (${totalLines} lines: +${additions} -${deletions})\n`;
if (prSize === 'Large') {
comment += '\n💡 **Suggestion:** This is a large PR. Consider breaking it into smaller, focused PRs for easier review.\n';
}
comment += '\n';
// Backend checks
const fmtStatus = '${{ steps.backend.outputs.fmt_status }}';
@@ -208,37 +288,71 @@ jobs:
return;
}
const prNumber = pulls.data[0].number;
const pr = pulls.data[0];
const prNumber = pr.number;
const comment = [
'## ⚠️ Advisory Checks - Results Unavailable',
'',
'The advisory checks workflow completed, but results could not be retrieved.',
'',
'### Possible reasons:',
'- Artifacts were not uploaded successfully',
'- Artifacts expired (retention: 1 day)',
'- Permission issues',
'',
'### What to do:',
'1. Check the [PR Checks - Run workflow](${{ github.event.workflow_run.html_url }}) logs',
'2. Ensure your code passes local checks:',
'```bash',
'# Backend',
'go fmt ./...',
'go vet ./...',
'go build',
'go test ./...',
'',
'# Frontend (if applicable)',
'cd web',
'npm run build',
'```',
'',
'---',
'',
'*This is an automated fallback message. The advisory checks ran but results are not available.*'
].join('\n');
// Get PR information for fallback comment
const prTitle = pr.title;
const conventionalCommitPattern = /^(feat|fix|docs|style|refactor|perf|test|chore|ci|security|build)(\(.+\))?: .+/;
const titleValid = conventionalCommitPattern.test(prTitle);
const additions = pr.additions || 0;
const deletions = pr.deletions || 0;
const total = additions + deletions;
let size = '';
let sizeEmoji = '';
if (total < 300) {
size = 'Small';
sizeEmoji = '🟢';
} else if (total < 1000) {
size = 'Medium';
sizeEmoji = '🟡';
} else {
size = 'Large';
sizeEmoji = '🔴';
}
let comment = '## ⚠️ Advisory Checks - Results Unavailable\n\n';
comment += 'The advisory checks workflow completed, but results could not be retrieved.\n\n';
// Add PR Information
comment += '### 📋 PR Information\n\n';
if (titleValid) {
comment += '**Title Format:** ✅ Good - Follows Conventional Commits\n';
} else {
comment += '**Title Format:** ⚠️ Suggestion - Consider using `type(scope): description`\n';
}
comment += `**PR Size:** ${sizeEmoji} ${size} (${total} lines: +${additions} -${deletions})\n\n`;
if (size === 'Large') {
comment += '💡 **Suggestion:** This is a large PR. Consider breaking it into smaller, focused PRs for easier review.\n\n';
}
comment += '---\n\n';
comment += '### ⚠️ Backend/Frontend Check Results\n\n';
comment += 'Results could not be retrieved.\n\n';
comment += '**Possible reasons:**\n';
comment += '- Artifacts were not uploaded successfully\n';
comment += '- Artifacts expired (retention: 1 day)\n';
comment += '- Permission issues\n\n';
comment += '**What to do:**\n';
comment += `1. Check the [PR Checks - Run workflow](${context.payload.workflow_run?.html_url || 'logs'}) logs\n`;
comment += '2. Ensure your code passes local checks:\n';
comment += '```bash\n';
comment += '# Backend\n';
comment += 'go fmt ./...\n';
comment += 'go vet ./...\n';
comment += 'go build\n';
comment += 'go test ./...\n\n';
comment += '# Frontend (if applicable)\n';
comment += 'cd web\n';
comment += 'npm run build\n';
comment += '```\n\n';
comment += '---\n\n';
comment += '*This is an automated fallback message. The advisory checks ran but results are not available.*';
await github.rest.issues.createComment({
issue_number: prNumber,

View File

@@ -0,0 +1,189 @@
name: PR Template Suggester
on:
pull_request:
types: [opened, edited, synchronize]
permissions:
pull-requests: write
contents: read
jobs:
suggest-template:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Analyze PR files and auto-apply template
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { data: files } = await github.rest.pulls.listFiles({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
});
let goFiles = 0, jsFiles = 0, tsFiles = 0, mdFiles = 0, otherFiles = 0;
for (const file of files) {
const filename = file.filename.toLowerCase();
if (filename.endsWith('.go')) goFiles++;
else if (filename.endsWith('.js') || filename.endsWith('.jsx')) jsFiles++;
else if (filename.endsWith('.ts') || filename.endsWith('.tsx') || filename.endsWith('.vue')) tsFiles++;
else if (filename.endsWith('.md')) mdFiles++;
else otherFiles++;
}
const totalFiles = goFiles + jsFiles + tsFiles + mdFiles + otherFiles;
if (totalFiles === 0) { console.log('No files changed'); return; }
let suggestedTemplate = null, templateEmoji = '', templateLabel = '';
if (goFiles / totalFiles > 0.5) {
suggestedTemplate = 'backend'; templateEmoji = '🔧'; templateLabel = 'backend';
} else if ((jsFiles + tsFiles) / totalFiles > 0.5) {
suggestedTemplate = 'frontend'; templateEmoji = '🎨'; templateLabel = 'frontend';
} else if (mdFiles / totalFiles > 0.7) {
suggestedTemplate = 'docs'; templateEmoji = '📝'; templateLabel = 'documentation';
}
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
});
const prBody = pr.body || '';
const usesBackendTemplate = prBody.includes('Pull Request - Backend');
const usesFrontendTemplate = prBody.includes('Pull Request - Frontend');
const usesDocsTemplate = prBody.includes('Pull Request - Documentation');
const usesGeneralTemplate = prBody.includes('Pull Request - General');
const usingDefaultTemplate = !usesBackendTemplate && !usesFrontendTemplate && !usesDocsTemplate && !usesGeneralTemplate;
if (templateLabel) {
try {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: [templateLabel]
});
console.log('Added label: ' + templateLabel);
} catch (error) {
console.log('Label might not exist, skipping...');
}
}
function isPRBodyEmpty(body) {
if (!body || body.trim().length < 100) return true;
const hasEmptyDescription = body.includes('**English:**') && body.match(/\*\*English:\*\*\s*\n\s*\n\s*\n/);
const hasEmptyChanges = body.includes('具体变更') && body.match(/\*\*中文:\*\*\s*\n\s*-\s*\n\s*-\s*\n/);
if (hasEmptyDescription || hasEmptyChanges) return true;
const descMatch = body.match(/\*\*English:\*\*[|]\s*\*\*中文:\*\*\s*\n\s*(.+)/);
if (!descMatch || descMatch[1].trim().length < 10) return true;
return false;
}
if (suggestedTemplate && usingDefaultTemplate) {
const shouldAutoApply = isPRBodyEmpty(prBody);
const templatePath = '.github/PULL_REQUEST_TEMPLATE/' + suggestedTemplate + '.md';
if (shouldAutoApply) {
try {
const { data: templateFile } = await github.rest.repos.getContent({
owner: context.repo.owner,
repo: context.repo.repo,
path: templatePath,
ref: context.payload.pull_request.head.ref
});
const templateContent = Buffer.from(templateFile.content, 'base64').toString('utf-8');
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
body: templateContent
});
console.log('Auto-applied ' + suggestedTemplate + ' template');
let fileStats = [];
if (goFiles > 0) fileStats.push('- 🔧 Go files: ' + goFiles);
if (jsFiles > 0) fileStats.push('- 🎨 JavaScript files: ' + jsFiles);
if (tsFiles > 0) fileStats.push('- 🎨 TypeScript files: ' + tsFiles);
if (mdFiles > 0) fileStats.push('- 📝 Markdown files: ' + mdFiles);
if (otherFiles > 0) fileStats.push('- 📦 Other files: ' + otherFiles);
const fileStatsText = fileStats.join('\n');
const notifyComment = '## ' + templateEmoji + ' 已自动应用专用模板 | Auto-Applied Template\n\n' +
'检测到您的PR主要包含 **' + suggestedTemplate + '** 相关的变更,系统已自动为您应用相应的模板。\n\n' +
'Detected that your PR primarily contains **' + suggestedTemplate + '** changes. The appropriate template has been automatically applied.\n\n' +
'**文件统计 | File Statistics**\n' + fileStatsText + '\n\n' +
'**已应用模板 | Applied Template**\n`' + templatePath + '`\n\n' +
'✨ 您现在可以直接在PR描述中填写相关信息了\n\n' +
'✨ You can now fill in the relevant information in the PR description!';
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: notifyComment
});
} catch (error) {
console.log('Failed to fetch or apply template: ' + error.message);
const templateUrl = 'https://raw.githubusercontent.com/' + context.repo.owner + '/' + context.repo.repo + '/dev/.github/PULL_REQUEST_TEMPLATE/' + suggestedTemplate + '.md';
const fallbackComment = '## ' + templateEmoji + ' 建议使用专用模板 | Suggested Template\n\n' +
'您的PR主要包含 **' + suggestedTemplate + '** 相关的变更。\n\n' +
'**推荐模板 | Recommended Template:** `.github/PULL_REQUEST_TEMPLATE/' + suggestedTemplate + '.md`\n\n' +
'**如何使用 | How to use:** [点击查看模板内容](' + templateUrl + ')';
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: fallbackComment
});
}
} else {
console.log('PR body has content, sending suggestion only');
let fileStats = [];
if (goFiles > 0) fileStats.push('- 🔧 Go files: ' + goFiles);
if (jsFiles > 0) fileStats.push('- 🎨 JavaScript files: ' + jsFiles);
if (tsFiles > 0) fileStats.push('- 🎨 TypeScript files: ' + tsFiles);
if (mdFiles > 0) fileStats.push('- 📝 Markdown files: ' + mdFiles);
if (otherFiles > 0) fileStats.push('- 📦 Other files: ' + otherFiles);
const fileStatsText = fileStats.join('\n');
const templateUrl = 'https://raw.githubusercontent.com/' + context.repo.owner + '/' + context.repo.repo + '/dev/.github/PULL_REQUEST_TEMPLATE/' + suggestedTemplate + '.md';
const comment = '## ' + templateEmoji + ' 建议使用专用模板 | Suggested Template\n\n' +
'您的PR主要包含 **' + suggestedTemplate + '** 相关的变更。我们建议使用更适合的模板以简化填写。\n\n' +
'Your PR primarily contains **' + suggestedTemplate + '** changes. We suggest using a more suitable template to simplify filling.\n\n' +
'**文件统计 | File Statistics**\n' + fileStatsText + '\n\n' +
'**推荐模板 | Recommended Template**\n```\n.github/PULL_REQUEST_TEMPLATE/' + suggestedTemplate + '.md\n```\n\n' +
'**如何使用 | How to use**\n' +
'1. 编辑PR描述 | Edit PR description\n' +
'2. 复制 [' + suggestedTemplate + ' 模板内容](' + templateUrl + ') | Copy [' + suggestedTemplate + ' template content](' + templateUrl + ')\n' +
'3. 或在创建PR时使用URL参数 | Or use URL parameter when creating PR\n' +
' `?template=' + suggestedTemplate + '.md`\n\n' +
'_这是一个自动建议您可以继续使用当前模板。_\n\n' +
'_This is an automated suggestion. You may continue using the current template._';
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: comment
});
}
} else if (suggestedTemplate && !usingDefaultTemplate) {
console.log('PR already uses a specific template');
} else {
console.log('No specific template suggestion needed - mixed changes');
}