Преглед на файлове

feat(miniprogram): 添加学生身份核验和通用API调用功能 (#893)

* feat: 添加 微信物流服务 /微信物流服务(商家查看) /消息组件

* fix lint issues

* fix: 查询运单详情信息返回结果结构

* 追加官方文档地址

* feat: 追加接口【微信物流服务 /微信物流服务(商家查看) /查询组件】

* fix: comments

* fix: indents

* feat(miniprogram): 添加学生身份核验和通用API调用功能

- 新增学生身份核验模块,支持快速获取学生身份接口
- 添加通用API调用功能,用于访问SDK尚未封装的小程序接口
- 实现GET/POST请求及响应解码的通用方法
- 在文档中补充学生身份相关API说明
ccfish преди 3 седмици
родител
ревизия
ad48392a5b
променени са 4 файла, в които са добавени 221 реда и са изтрити 0 реда
  1. 9 0
      doc/api/miniprogram.md
  2. 127 0
      miniprogram/generic/generic.go
  3. 12 0
      miniprogram/miniprogram.go
  4. 73 0
      miniprogram/student/student.go

+ 9 - 0
doc/api/miniprogram.md

@@ -48,3 +48,12 @@ TODO
 | :----------------: | -------- | :------------------- | ---------- | ------------------------------------------ |
 | 获取用户的安全等级 | POST     | /wxa/getuserriskrank | YES        | (riskControl *RiskControl) GetUserRiskRank |
 
+
+## 学生身份
+
+[官方文档](https://developers.weixin.qq.com/miniprogram/dev/server/API/student/api_quickcheckstudentidentity.html)
+
+|       名称       | 请求方式 | URL                             | 是否已实现 | 使用方法                                        |
+| :--------------: | -------- | :------------------------------ | ---------- | ----------------------------------------------- |
+| 快速获取学生身份 | POST     | /intp/quickcheckstudentidentity | YES        | (student *Student) QuickCheckStudentIdentity    |
+

+ 127 - 0
miniprogram/generic/generic.go

@@ -0,0 +1,127 @@
+package generic
+
+import (
+	context2 "context"
+	"net/url"
+	"strings"
+
+	"github.com/silenceper/wechat/v2/miniprogram/context"
+	"github.com/silenceper/wechat/v2/util"
+)
+
+// defaultBaseURL 微信 API 默认基础地址
+const defaultBaseURL = "https://api.weixin.qq.com"
+
+// Generic 通用 API 调用,用于访问 SDK 尚未封装的小程序接口。
+// 支持自定义 path、query 参数、request body 以及 response 结构体。
+type Generic struct {
+	*context.Context
+}
+
+// NewGeneric init
+func NewGeneric(ctx *context.Context) *Generic {
+	return &Generic{ctx}
+}
+
+// BuildURL 构建完整请求 URL。
+// path 支持相对路径(如 "/intp/xxx")或完整 URL(如 "https://api.weixin.qq.com/intp/xxx")。
+// query 为额外的 URL 查询参数,可为 nil;access_token 会自动追加(若 query 中已包含则不重复追加)。
+func (g *Generic) BuildURL(path string, query url.Values) (string, error) {
+	return g.BuildURLContext(context2.Background(), path, query)
+}
+
+// BuildURLContext 构建完整请求 URL(带 context)
+func (g *Generic) BuildURLContext(ctx context2.Context, path string, query url.Values) (string, error) {
+	var baseURL string
+	switch {
+	case strings.HasPrefix(path, "http://"), strings.HasPrefix(path, "https://"):
+		baseURL = path
+	case strings.HasPrefix(path, "/"):
+		baseURL = defaultBaseURL + path
+	default:
+		baseURL = defaultBaseURL + "/" + path
+	}
+
+	// 克隆 query,避免修改调用方原始值
+	q := make(url.Values, len(query)+1)
+	for k, v := range query {
+		q[k] = v
+	}
+
+	// 自动追加 access_token
+	if q.Get("access_token") == "" {
+		accessToken, err := g.GetAccessTokenContext(ctx)
+		if err != nil {
+			return "", err
+		}
+		q.Set("access_token", accessToken)
+	}
+
+	sep := "?"
+	if strings.Contains(baseURL, "?") {
+		sep = "&"
+	}
+	return baseURL + sep + q.Encode(), nil
+}
+
+// Get 发送 GET 请求,返回原始响应字节。
+// 使用 util.DecodeWithError 可将响应解码到自定义结构体。
+func (g *Generic) Get(path string, query url.Values) (response []byte, err error) {
+	return g.GetContext(context2.Background(), path, query)
+}
+
+// GetContext 发送 GET 请求(带 context),返回原始响应字节
+func (g *Generic) GetContext(ctx context2.Context, path string, query url.Values) (response []byte, err error) {
+	var uri string
+	if uri, err = g.BuildURLContext(ctx, path, query); err != nil {
+		return
+	}
+	return util.HTTPGetContext(ctx, uri)
+}
+
+// PostJSON 发送 POST 请求(JSON body),返回原始响应字节。
+// body 可为 nil(无请求体);使用 util.DecodeWithError 可将响应解码到自定义结构体。
+func (g *Generic) PostJSON(path string, query url.Values, body interface{}) (response []byte, err error) {
+	return g.PostJSONContext(context2.Background(), path, query, body)
+}
+
+// PostJSONContext 发送 POST 请求(JSON body,带 context),返回原始响应字节
+func (g *Generic) PostJSONContext(ctx context2.Context, path string, query url.Values, body interface{}) (response []byte, err error) {
+	var uri string
+	if uri, err = g.BuildURLContext(ctx, path, query); err != nil {
+		return
+	}
+	return util.PostJSONContext(ctx, uri, body)
+}
+
+// GetDecode 发送 GET 请求并将响应解码到 resp。
+// resp 应为指向结构体的指针,结构体中可嵌入 util.CommonError 以自动校验错误码。
+// apiName 用于错误信息标识。
+func (g *Generic) GetDecode(path string, query url.Values, resp interface{}, apiName string) (err error) {
+	return g.GetDecodeContext(context2.Background(), path, query, resp, apiName)
+}
+
+// GetDecodeContext 发送 GET 请求并将响应解码到 resp(带 context)
+func (g *Generic) GetDecodeContext(ctx context2.Context, path string, query url.Values, resp interface{}, apiName string) (err error) {
+	var response []byte
+	if response, err = g.GetContext(ctx, path, query); err != nil {
+		return
+	}
+	return util.DecodeWithError(response, resp, apiName)
+}
+
+// PostJSONDecode 发送 POST 请求(JSON body)并将响应解码到 resp。
+// resp 应为指向结构体的指针,结构体中可嵌入 util.CommonError 以自动校验错误码。
+// apiName 用于错误信息标识。
+func (g *Generic) PostJSONDecode(path string, query url.Values, body, resp interface{}, apiName string) (err error) {
+	return g.PostJSONDecodeContext(context2.Background(), path, query, body, resp, apiName)
+}
+
+// PostJSONDecodeContext 发送 POST 请求(JSON body)并将响应解码到 resp(带 context)
+func (g *Generic) PostJSONDecodeContext(ctx context2.Context, path string, query url.Values, body, resp interface{}, apiName string) (err error) {
+	var response []byte
+	if response, err = g.PostJSONContext(ctx, path, query, body); err != nil {
+		return
+	}
+	return util.DecodeWithError(response, resp, apiName)
+}

+ 12 - 0
miniprogram/miniprogram.go

@@ -11,6 +11,7 @@ import (
 	"github.com/silenceper/wechat/v2/miniprogram/context"
 	"github.com/silenceper/wechat/v2/miniprogram/encryptor"
 	"github.com/silenceper/wechat/v2/miniprogram/express"
+	"github.com/silenceper/wechat/v2/miniprogram/generic"
 	"github.com/silenceper/wechat/v2/miniprogram/message"
 	"github.com/silenceper/wechat/v2/miniprogram/minidrama"
 	"github.com/silenceper/wechat/v2/miniprogram/ocr"
@@ -22,6 +23,7 @@ import (
 	"github.com/silenceper/wechat/v2/miniprogram/riskcontrol"
 	"github.com/silenceper/wechat/v2/miniprogram/security"
 	"github.com/silenceper/wechat/v2/miniprogram/shortlink"
+	"github.com/silenceper/wechat/v2/miniprogram/student"
 	"github.com/silenceper/wechat/v2/miniprogram/subscribe"
 	"github.com/silenceper/wechat/v2/miniprogram/tcb"
 	"github.com/silenceper/wechat/v2/miniprogram/urllink"
@@ -202,3 +204,13 @@ func (miniProgram *MiniProgram) GetOCR() *ocr.OCR {
 func (miniProgram *MiniProgram) GetIntracity() *express.Express {
 	return express.NewExpress(miniProgram.ctx)
 }
+
+// GetStudent 学生身份核验接口
+func (miniProgram *MiniProgram) GetStudent() *student.Student {
+	return student.NewStudent(miniProgram.ctx)
+}
+
+// GetGeneric 通用 API 调用,用于访问 SDK 尚未封装的接口
+func (miniProgram *MiniProgram) GetGeneric() *generic.Generic {
+	return generic.NewGeneric(miniProgram.ctx)
+}

+ 73 - 0
miniprogram/student/student.go

@@ -0,0 +1,73 @@
+package student
+
+import (
+	context2 "context"
+	"fmt"
+
+	"github.com/silenceper/wechat/v2/miniprogram/context"
+	"github.com/silenceper/wechat/v2/util"
+)
+
+const (
+	// quickCheckStudentIdentityURL 快速获取学生身份
+	quickCheckStudentIdentityURL = "https://api.weixin.qq.com/intp/quickcheckstudentidentity?access_token=%s"
+)
+
+// Student 学生身份核验
+type Student struct {
+	*context.Context
+}
+
+// NewStudent init
+func NewStudent(ctx *context.Context) *Student {
+	return &Student{ctx}
+}
+
+// BindStatus 学生身份绑定状态
+type BindStatus int
+
+const (
+	// BindStatusUnbound 未绑定
+	BindStatusUnbound BindStatus = 1
+	// BindStatusAuditing 审核中
+	BindStatusAuditing BindStatus = 2
+	// BindStatusBound 已绑定
+	BindStatusBound BindStatus = 3
+)
+
+// QuickCheckStudentIdentityRequest 快速获取学生身份请求参数
+type QuickCheckStudentIdentityRequest struct {
+	OpenID             string `json:"openid"`               // 用户在业务方下的openid,需为已开通接口权限小程序对应的用户openid
+	WxStudentCheckCode string `json:"wx_studentcheck_code"` // 用户授权查询code,由授权插件返回,code有效期两小时
+}
+
+// QuickCheckStudentIdentityResponse 快速获取学生身份返回参数
+type QuickCheckStudentIdentityResponse struct {
+	util.CommonError
+	BindStatus BindStatus `json:"bind_status"` // 绑定状态:1-未绑定,2-审核中,3-已绑定
+	IsStudent  bool       `json:"is_student"`  // 用户学生身份绑定状态说明,true-是学生,false-不是学生
+}
+
+// QuickCheckStudentIdentity 快速获取学生身份
+// see https://developers.weixin.qq.com/miniprogram/dev/server/API/student/api_quickcheckstudentidentity.html
+func (student *Student) QuickCheckStudentIdentity(in *QuickCheckStudentIdentityRequest) (out QuickCheckStudentIdentityResponse, err error) {
+	return student.QuickCheckStudentIdentityContext(context2.Background(), in)
+}
+
+// QuickCheckStudentIdentityContext 快速获取学生身份
+func (student *Student) QuickCheckStudentIdentityContext(ctx context2.Context, in *QuickCheckStudentIdentityRequest) (out QuickCheckStudentIdentityResponse, err error) {
+	var accessToken string
+	if accessToken, err = student.GetAccessTokenContext(ctx); err != nil {
+		return
+	}
+
+	uri := fmt.Sprintf(quickCheckStudentIdentityURL, accessToken)
+	var response []byte
+	if response, err = util.PostJSONContext(ctx, uri, in); err != nil {
+		return
+	}
+
+	// 使用通用方法返回错误
+	err = util.DecodeWithError(response, &out, "student.QuickCheckStudentIdentity")
+	return
+}