README.md

# LibWechat

一个用于微信 API 的 Elixir 库,提供小程序、公众号等微信平台的功能接口。

## 特性

- 🔐 **认证管理**: 自动获取和管理 access_token
- 📱 **小程序支持**: 小程序码、URL Link、Scheme 码生成
- 📨 **消息推送**: 订阅消息发送
- 📞 **手机号获取**: 小程序用户手机号获取
- 🔒 **安全检查**: 文本内容安全检测
- 🚀 **高性能**: 基于 Finch HTTP 客户端,支持连接池
- 🛡️ **类型安全**: 完整的类型规范和错误处理

## 安装

### 1. 添加依赖

在 `mix.exs` 文件中添加 `lib_wechat` 到依赖列表:

```elixir
def deps do
  [
    {:lib_wechat, "~> 0.4.0"}
  ]
end
```

### 2. 安装依赖

```bash
mix deps.get
```

### 3. 配置

在你的应用配置文件中(如 `config/config.exs`)添加微信应用的配置:

```elixir
config :my_app, MyApp.Wechat,
  appid: "your_app_id",
  secret: "your_app_secret",
```

### 4. 启动应用
在你的模块中使用
```elixir
defmodule MyApp.Wechat do
  use LibWechat, otp_app: :my_app
end
```
在你的应用监督树中添加Wechat:

```elixir
# 在你的 application.ex 中
def start(_type, _args) do
  children = [
    # 其他子进程...
    MyApp.Wechat
  ]

  opts = [strategy: :one_for_one, name: MyApp.Supervisor]
  Supervisor.start_link(children, opts)
end
```


### 获取 Access Token

```elixir
# 获取 access_token
case MyApp.Wechat.get_access_token() do
  {:ok, %{"access_token" => token, "expires_in" => expires}} ->
    IO.puts("Token: #{token}, Expires in: #{expires}")
  {:error, reason} ->
    IO.puts("获取失败: #{inspect(reason)}")
end
```

### 小程序登录

```elixir
# 使用 code 获取 session
case MyApp.Wechat.jscode_to_session("user_js_code") do
  {:ok, %{"openid" => openid, "session_key" => session_key}} ->
    IO.puts("OpenID: #{openid}")
  {:error, reason} ->
    IO.puts("登录失败: #{inspect(reason)}")
end
```

### 生成小程序码

```elixir
# 获取不限量小程序码
payload = %{
  "scene" => "foo=bar",
  "page" => "pages/index/index",
  "width" => 430,
  "auto_color" => false,
  "line_color" => %{"r" => 0, "g" => 0, "b" => 0},
  "is_hyaline" => false
}

case MyApp.Wechat.get_unlimited_wxacode(token, payload) do
  {:ok, binary_data} ->
    # binary_data 是图片的二进制数据
    File.write!("qrcode.png", binary_data)
  {:error, reason} ->
    IO.puts("生成小程序码失败: #{inspect(reason)}")
end
```

### 生成 URL Link

```elixir
# 获取小程序 URL Link
payload = %{
  "path" => "pages/index/index",
  "query" => "foo=bar",
  "is_expire" => false,
  "expire_type" => 0,
  "expire_time" => 0
}

case MyApp.Wechat.get_urllink(token, payload) do
  {:ok, %{"url_link" => url_link}} ->
    IO.puts("URL Link: #{url_link}")
  {:error, reason} ->
    IO.puts("生成 URL Link 失败: #{inspect(reason)}")
end
```

### 生成 Scheme 码

```elixir
# 获取小程序 scheme 码
payload = %{
  "jump_wxa" => %{
    "path" => "pages/index/index",
    "query" => "foo=bar"
  },
  "is_expire" => false,
  "expire_type" => 0,
  "expire_time" => 0
}

case MyApp.Wechat.generate_scheme(token, payload) do
  {:ok, %{"openlink" => openlink}} ->
    IO.puts("Scheme: #{openlink}")
  {:error, reason} ->
    IO.puts("生成 Scheme 失败: #{inspect(reason)}")
end
```

### 发送订阅消息

```elixir
# 发送订阅消息
payload = %{
  "touser" => "USER_OPENID",
  "template_id" => "TEMPLATE_ID",
  "page" => "index",
  "miniprogram_state" => "developer",
  "lang" => "zh_CN",
  "data" => %{
    "number01" => %{"value" => "339208499"},
    "date01" => %{"value" => "2015年01月05日"},
    "site01" => %{"value" => "TIT创意园"},
    "site02" => %{"value" => "广州市新港中路397号"}
  }
}

case MyApp.Wechat.subscribe_send(token, payload) do
  {:ok, %{"msgid" => msgid}} ->
    IO.puts("消息发送成功,ID: #{msgid}")
  {:error, reason} ->
    IO.puts("消息发送失败: #{inspect(reason)}")
end
```

### 获取用户手机号

```elixir
# 获取用户手机号
case MyApp.Wechat.get_phone_number(token, "phone_code") do
  {:ok, %{"phone_info" => phone_info}} ->
    IO.puts("手机号: #{phone_info["phoneNumber"]}")
  {:error, reason} ->
    IO.puts("获取手机号失败: #{inspect(reason)}")
end
```

### 内容安全检查

```elixir
# 文本内容安全检查
payload = %{
  "openid" => "USER_OPENID",
  "scene" => 1,
  "version" => 2,
  "content" => "要检查的文本内容"
}

case MyApp.Wechat.msg_sec_check(token, payload) do
  {:ok, result} ->
    case result["result"]["suggest"] do
      "pass" -> IO.puts("内容安全")
      "risky" -> IO.puts("内容存在风险")
      _ -> IO.puts("需要人工审核")
    end
  {:error, reason} ->
    IO.puts("安全检查失败: #{inspect(reason)}")
end
```

## 配置选项

| 选项 | 类型 | 必需 | 默认值 | 说明 |
|------|------|------|--------|------|
| `appid` | `string` | 是 | - | 微信应用 ID |
| `secret` | `string` | 是 | - | 微信应用密钥 |
| `service_host` | `string` | 否 | `"api.weixin.qq.com"` | 微信 API 服务器地址 |

## 错误处理

所有 API 调用都返回 `{:ok, result}` 或 `{:error, error}` 格式的结果:

```elixir
case MyApp.Wechat.some_api_call(params) do
  {:ok, result} ->
    # 处理成功结果
    IO.puts("成功: #{inspect(result)}")
  {:error, %LibWechat.Error{reason: reason, message: message}} ->
    # 处理错误
    IO.puts("错误: #{message} (#{reason})")
end
```

## 开发

### 环境设置

```bash
# 克隆项目
git clone https://github.com/tt67wq/lib-wechat.git
cd lib-wechat

# 安装依赖
mix setup

# 运行测试
mix test

# 代码格式化
mix fmt

# 静态检查
mix lint
```

### 测试

测试需要配置微信应用的测试账号:

```bash
# 复制环境变量模板
cp .env.example .env

# 编辑 .env 文件,填入测试账号信息
export TEST_APP_ID="your_test_app_id"
export TEST_APP_SECRET="your_test_app_secret"
```

然后运行测试:

```bash
mix test
```

### 贡献

1. Fork 项目
2. 创建功能分支 (`git checkout -b feature/amazing-feature`)
3. 提交更改 (`git commit -m 'Add amazing feature'`)
4. 推送到分支 (`git push origin feature/amazing-feature`)
5. 创建 Pull Request

## 许可证

本项目采用 MIT 许可证 - 详见 [LICENSE](LICENSE) 文件。

## 相关链接

- [微信官方文档](https://developers.weixin.qq.com/miniprogram/dev/api-backend/)
- [GitHub 仓库](https://github.com/tt67wq/lib-wechat)
- [Hex 包](https://hex.pm/packages/lib_wechat)

## 更新日志

### v0.4.0
- 重构为模块化架构
- 添加完整的类型规范
- 改进错误处理机制
- 支持 Finch HTTP 客户端

### v0.3.0
- 添加小程序码生成功能
- 添加 URL Link 和 Scheme 码生成
- 添加订阅消息发送功能

### v0.2.0
- 添加用户手机号获取功能
- 添加内容安全检查功能
- 改进配置管理

### v0.1.0
- 初始版本
- 基础认证功能
- Access Token 管理