# CONTRIBUTING

在你参与贡献本项目之前，请先阅读如下指南：

-   [如何提问？](#question)

-   [如何报告缺陷？](#bug-report)

-   [如何提出功能建议？](#feature-request)

-   [如何发起拉取请求（Pull Request）？](#pull-request)

---

## <a name="question"></a> 如何提问？

如果你对如何使用本库有疑问，请先阅读本项目提供的开发文档，并在 Issue 列表中尝试搜索。

如果你的疑问仍不能得到解决，请开启一个新的 Issue。

社区交流 QQ 群：875580418

---

## <a name="bug-report"></a> 如何报告缺陷？

如果你发现源代码中的漏洞、运行发生的异常、或文档里的错误，你可以通过提交 Issue 来指出。当然，如果你可以提交修复后的 PR 就再好不过了。

提交 Issue 时，请包含以下内容：

1.  关于问题的简单描述。

2.  发生问题的运行环境（如：操作系统版本，.NET Runtime 版本，引用本库的版本，等等）。

3.  与问题相关的代码，或可复现问题的最小化项目（可上传至代码托管仓库或使用 GitHub Gist）。

4.  抛出错误时的异常消息和堆栈跟踪。

请谨记，你所提供的信息越丰富，对于我们的帮助就越大，修复的可能性也就越高。

---

## <a name="feature-request"></a> 如何提出功能建议？

如果你需要某些新功能，或对现有实现提出更好的建议，你可以通过提交 Issue 来说明。

提交 Issue 前，请注意以下几点：

1.  本库只专注于 API 的封装，更偏向于 SDK 而非一个完整的 Web 框架，请不要提出本应该在框架层实现的功能。

2.  本库提供了很多可扩展的接口，请评估是否可以在不修改本项目的前提下实现你想要的功能。

3.  稳定性至关重要，请谨慎提出需要大量破坏性变化的功能。

---

## <a name="pull-request"></a> 如何发起拉取请求？

在向本项目发起 PR 时，请确保已执行了以下操作：

1. 检查新的代码是否遵循了本项目的现有代码格式和命名标准：

    - **禁止**：源代码中不得包含连续的空白行，字段、属性、方法之间应保留一个空白行；

    - **必须**：应确保目录结构与现有目录结构一致；

    - **必须**：应使用大驼峰命名方式命名类（即 Class）、属性（即 Property）和方法（即 Method），具体可参考已有模型；

    - **必须**：应遵循开发文档中关于 API 模型命名的方式来提供新的 API，具体可参考已有模型；

    - **必须**：应为可公开访问的类、属性和方法提供 XML 文档注释，具体可参考已有注释；

    - **必须**：API 模型中的数组形式的字段，在请求模型中应声明为 `IList` 泛型类型，在响应模型中应声明为数组类型，具体可参考已有模型；

    - **必须**：API 模型中的子类型，应统一包含在一个名为 _Types_ 的公开的嵌套静态类中，具体可参考已有模型；

    - **必须**：API 模型中的 JsonConverter，应统一嵌套在一个名为 _Converters_ 的内部的嵌套静态类中，具体可参考已有模型；

    - **必须**：API 模型中的引用类型的非空属性，在请求模型中应赋值为该类型的默认构造实例（`string` 类型则为 `string.Empty`），在响应模型中应赋值为 `default!`，具体可参考已有模型；

    - **必须**：API 模型中的引用类型的可空属性，应在声明时标记为 `class?`（_class_ 为具体类名），具体可参考已有模型；

    - **必须**：API 模型中的属性，如果序列化后的命名为缩写，应恢复其完整形式（如 _img_ → _Image_、_thumb_ → _Thumbnail_、_avg_ → _Average_、_cid_ → _CategoryId_ 等等），但某些特定的专有名词则无需照此处理（如 _Id_、_Http_、_Url_、_Cgi_ 等等）；

    - **建议**：API 模型中的属性，如果序列化后的取值只有 0 / 1、且日后也不可能会增加其他取值，可声明为 `bool` 类型；

    - **建议**：API 模型中的属性，如果序列化后是日期时间类型的字符串，可声明为 `DateTimeOffset` 类型；

    - **建议**：API 模型中的属性，如果序列化后是 JSON 格式的字符串，可声明为强类型并配合相应的 JsonConverter、而非直接声明为 `string` 类型；

    - **建议**：API 模型中的 `bool` 类型的属性，命名可以 _Is_、_Has_ 等开头；

    - **建议**：API 模型中的表示时间戳的属性，命名可以 _Timestamp_ 等结尾；

    - 其他注意事项请参阅微软官方提供的[《框架设计准则》](https://docs.microsoft.com/zh-CN/dotnet/standard/design-guidelines)。

2. 编写新的单元测试、并运行已有的单元测试来验证你的更改，所有功能和修复的错误都必须进行以验证它们是否有效：

    - **禁止**：请注意单元测试项目的 `csproj` 文件，如果在其下添加新文件时，可能导致它的内容发生变化，请不要将此类修改操作提交到 git 中；

    - **禁止**：请注意单元测试项目下的 `appsettings.json` 文件，请在克隆仓库后的第一时间执行 `git update-index --assume-unchanged` 命令，避免修改此文件后将敏感信息提交到 git 中；

    - **必须**：Code Style 测试应完全通过，且提供新的 API 模型示例；

    - **必须**：如果是非 API 接口的调整（如工具类等），则应提供测试用例；

    - **必须**：单元测试项目下的 API 模型示例的目录结构与文件命名，应与源项目保持一致，具体可参考已有示例；

    - **建议**：请尽可能地与微信官方文档给出的示例保持一致。

3. 规范 Commit Log：

    - **必须**：提交记录的格式应固定为 `<type>(<scope>): <subject>`。

    - **必须**：提交记录中的 `<type>`，可取值为：

        - _feat_：新增或变更已有功能；
        - _fix_：修复缺陷或拼写错误；
        - _docs_：文档等内容的变更；
        - _style_：格式调整（即不涉及任何代码内容上的变化）；
        - _refactor_：重构（即代码内容发生变化，但不影响现有功能，也未新增任何功能）；
        - _test_：测试相关；
        - _chore_：其他杂项。

    - **必须**：提交记录中的 `<scope>`，可取值为：

        - _wxapi_：关于 `SKIT.FlurlHttpClient.Wechat.Api` 项目的变化；
        - _tenpayv2_：关于 `SKIT.FlurlHttpClient.Wechat.TenpayV2` 项目的变化；
        - _tenpayv3_：关于 `SKIT.FlurlHttpClient.Wechat.TenpayV3` 项目的变化；
        - _tenpaybusiness_：关于 `SKIT.FlurlHttpClient.Wechat.TenpayBusiness` 项目的变化；
        - _work_：关于 `SKIT.FlurlHttpClient.Wechat.Work` 项目的变化；
        - _wxads_：关于 `SKIT.FlurlHttpClient.Wechat.Ads` 项目的变化；
        - _openai_：关于 `SKIT.FlurlHttpClient.Wechat.OpenAI` 项目的变化；
        - 留空：不属于上述任何情形。

    - **建议**：提交记录应遵循最小化原则，避免修改或新增了几十处、却混杂在一起提交，以免为日后搜索查询造成困扰。

请注意，对软件来说，适当的规范和标准绝不是消灭代码内容的创造性、优雅性，而是限制过度个性化，以一种普遍认可的统一方式一起做事，提升协作效率，降低沟通成本。代码的字里行间流淌的是软件系统的血液，质量的提升是尽可能少踩坑，杜绝踩重复的坑，切实提升软件开发质量和团队协作效率。

因此，本项目会严格约束 PR 规范，并有权对 PR 的发起者提出需求意见和建议，只到发起者做出符合要求修改后才会被批准合并。（注：已产生的提交记录可通过 `git rebase` 命令修改）

另外需要说明的是，Gitee 同步仓库后可能会丢失 PR 中的提交记录，如果发起者介意这一点，请务必只在 GitHub 发起 PR。
