mirror of
https://github.com/silenceper/wechat.git
synced 2026-02-06 21:52:27 +08:00
Compare commits
2 Commits
v2.1.11
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8157cad2cb | ||
|
|
be6e95e987 |
@@ -13,7 +13,6 @@ import (
|
|||||||
"github.com/silenceper/wechat/v2/miniprogram/express"
|
"github.com/silenceper/wechat/v2/miniprogram/express"
|
||||||
"github.com/silenceper/wechat/v2/miniprogram/message"
|
"github.com/silenceper/wechat/v2/miniprogram/message"
|
||||||
"github.com/silenceper/wechat/v2/miniprogram/minidrama"
|
"github.com/silenceper/wechat/v2/miniprogram/minidrama"
|
||||||
"github.com/silenceper/wechat/v2/miniprogram/ocr"
|
|
||||||
"github.com/silenceper/wechat/v2/miniprogram/operation"
|
"github.com/silenceper/wechat/v2/miniprogram/operation"
|
||||||
"github.com/silenceper/wechat/v2/miniprogram/order"
|
"github.com/silenceper/wechat/v2/miniprogram/order"
|
||||||
"github.com/silenceper/wechat/v2/miniprogram/privacy"
|
"github.com/silenceper/wechat/v2/miniprogram/privacy"
|
||||||
@@ -192,8 +191,3 @@ func (miniProgram *MiniProgram) GetOperation() *operation.Operation {
|
|||||||
func (miniProgram *MiniProgram) GetExpress() *express.Express {
|
func (miniProgram *MiniProgram) GetExpress() *express.Express {
|
||||||
return express.NewExpress(miniProgram.ctx)
|
return express.NewExpress(miniProgram.ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOCR OCR接口
|
|
||||||
func (miniProgram *MiniProgram) GetOCR() *ocr.OCR {
|
|
||||||
return ocr.NewOCR(miniProgram.ctx)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,248 +0,0 @@
|
|||||||
package ocr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/silenceper/wechat/v2/miniprogram/context"
|
|
||||||
"github.com/silenceper/wechat/v2/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
ocrIDCardURL = "https://api.weixin.qq.com/cv/ocr/idcard"
|
|
||||||
ocrBankCardURL = "https://api.weixin.qq.com/cv/ocr/bankcard"
|
|
||||||
ocrDrivingURL = "https://api.weixin.qq.com/cv/ocr/driving"
|
|
||||||
ocrDrivingLicenseURL = "https://api.weixin.qq.com/cv/ocr/drivinglicense"
|
|
||||||
ocrBizLicenseURL = "https://api.weixin.qq.com/cv/ocr/bizlicense"
|
|
||||||
ocrCommonURL = "https://api.weixin.qq.com/cv/ocr/comm"
|
|
||||||
)
|
|
||||||
|
|
||||||
// OCR struct
|
|
||||||
type OCR struct {
|
|
||||||
*context.Context
|
|
||||||
}
|
|
||||||
|
|
||||||
// coordinate 坐标
|
|
||||||
type coordinate struct {
|
|
||||||
X int64 `json:"x,omitempty"`
|
|
||||||
Y int64 `json:"y,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// position 位置
|
|
||||||
type position struct {
|
|
||||||
LeftTop coordinate `json:"left_top"`
|
|
||||||
RightTop coordinate `json:"right_top"`
|
|
||||||
RightBottom coordinate `json:"right_bottom"`
|
|
||||||
LeftBottom coordinate `json:"left_bottom"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// imageSize 图片尺寸
|
|
||||||
type imageSize struct {
|
|
||||||
Width int64 `json:"w,omitempty"`
|
|
||||||
Height int64 `json:"h,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResDriving 行驶证返回结果
|
|
||||||
type ResDriving struct {
|
|
||||||
util.CommonError
|
|
||||||
|
|
||||||
PlateNumber string `json:"plate_num,omitempty"`
|
|
||||||
VehicleType string `json:"vehicle_type,omitempty"`
|
|
||||||
Owner string `json:"owner,omitempty"`
|
|
||||||
Address string `json:"addr,omitempty"`
|
|
||||||
UseCharacter string `json:"use_character,omitempty"`
|
|
||||||
Model string `json:"model,omitempty"`
|
|
||||||
Vin string `json:"vin,omitempty"`
|
|
||||||
EngineNumber string `json:"engine_num,omitempty"`
|
|
||||||
RegisterDate string `json:"register_date,omitempty"`
|
|
||||||
IssueDate string `json:"issue_date,omitempty"`
|
|
||||||
PlateNumberB string `json:"plate_num_b,omitempty"`
|
|
||||||
Record string `json:"record,omitempty"`
|
|
||||||
PassengersNumber string `json:"passengers_num,omitempty"`
|
|
||||||
TotalQuality string `json:"total_quality,omitempty"`
|
|
||||||
PrepareQuality string `json:"prepare_quality,omitempty"`
|
|
||||||
OverallSize string `json:"overall_size,omitempty"`
|
|
||||||
CardPositionFront map[string]position `json:"card_position_front,omitempty"`
|
|
||||||
CardPositionBack map[string]position `json:"card_position_back,omitempty"`
|
|
||||||
ImageSize imageSize `json:"img_size,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResIDCard 身份证返回结果
|
|
||||||
type ResIDCard struct {
|
|
||||||
util.CommonError
|
|
||||||
|
|
||||||
Type string `json:"type,omitempty"`
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
ID string `json:"id,omitempty"`
|
|
||||||
Address string `json:"addr,omitempty"`
|
|
||||||
Gender string `json:"gender,omitempty"`
|
|
||||||
Nationality string `json:"nationality,omitempty"`
|
|
||||||
ValidDate string `json:"valid_date,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResBankCard 银行卡返回结果
|
|
||||||
type ResBankCard struct {
|
|
||||||
util.CommonError
|
|
||||||
|
|
||||||
Number string `json:"number,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResDrivingLicense 驾驶证返回结果
|
|
||||||
type ResDrivingLicense struct {
|
|
||||||
util.CommonError
|
|
||||||
|
|
||||||
IDNumber string `json:"id_num,omitempty"`
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
Sex string `json:"sex,omitempty"`
|
|
||||||
Nationality string `json:"nationality,omitempty"`
|
|
||||||
Address string `json:"address,omitempty"`
|
|
||||||
Birthday string `json:"birth_date,omitempty"`
|
|
||||||
IssueDate string `json:"issue_date,omitempty"`
|
|
||||||
CarClass string `json:"car_class,omitempty"`
|
|
||||||
ValidFrom string `json:"valid_from,omitempty"`
|
|
||||||
ValidTo string `json:"valid_to,omitempty"`
|
|
||||||
OfficialSeal string `json:"official_seal,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResBizLicense 营业执照返回结果
|
|
||||||
type ResBizLicense struct {
|
|
||||||
util.CommonError
|
|
||||||
|
|
||||||
RegisterNumber string `json:"reg_num,omitempty"`
|
|
||||||
Serial string `json:"serial,omitempty"`
|
|
||||||
LegalRepresentative string `json:"legal_representative,omitempty"`
|
|
||||||
EnterpriseName string `json:"enterprise_name,omitempty"`
|
|
||||||
TypeOfOrganization string `json:"type_of_organization,omitempty"`
|
|
||||||
Address string `json:"address,omitempty"`
|
|
||||||
TypeOfEnterprise string `json:"type_of_enterprise,omitempty"`
|
|
||||||
BusinessScope string `json:"business_scope,omitempty"`
|
|
||||||
RegisteredCapital string `json:"registered_capital,omitempty"`
|
|
||||||
PaidInCapital string `json:"paid_in_capital,omitempty"`
|
|
||||||
ValidPeriod string `json:"valid_period,omitempty"`
|
|
||||||
RegisterDate string `json:"registered_date,omitempty"`
|
|
||||||
CertPosition map[string]position `json:"cert_position,omitempty"`
|
|
||||||
ImageSize imageSize `json:"img_size,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResCommon 公共印刷品返回结果
|
|
||||||
type ResCommon struct {
|
|
||||||
util.CommonError
|
|
||||||
|
|
||||||
Items []commonItem `json:"items,omitempty"`
|
|
||||||
ImageSize imageSize `json:"img_size,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// commonItem 公共元素
|
|
||||||
type commonItem struct {
|
|
||||||
Position position `json:"pos"`
|
|
||||||
Text string `json:"text"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewOCR 实例
|
|
||||||
func NewOCR(c *context.Context) *OCR {
|
|
||||||
ocr := new(OCR)
|
|
||||||
ocr.Context = c
|
|
||||||
return ocr
|
|
||||||
}
|
|
||||||
|
|
||||||
// IDCard 身份证OCR识别接口
|
|
||||||
func (ocr *OCR) IDCard(path string) (resIDCard ResIDCard, err error) {
|
|
||||||
accessToken, err := ocr.GetAccessToken()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := util.HTTPPost(fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrIDCardURL, url.QueryEscape(path), accessToken), "")
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = util.DecodeWithError(response, &resIDCard, "OCRIDCard")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// BankCard 银行卡OCR识别接口
|
|
||||||
func (ocr *OCR) BankCard(path string) (resBankCard ResBankCard, err error) {
|
|
||||||
accessToken, err := ocr.GetAccessToken()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := util.HTTPPost(fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrBankCardURL, url.QueryEscape(path), accessToken), "")
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = util.DecodeWithError(response, &resBankCard, "OCRBankCard")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Driving 行驶证OCR识别接口
|
|
||||||
func (ocr *OCR) Driving(path string) (resDriving ResDriving, err error) {
|
|
||||||
accessToken, err := ocr.GetAccessToken()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := util.HTTPPost(fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrDrivingURL, url.QueryEscape(path), accessToken), "")
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = util.DecodeWithError(response, &resDriving, "OCRDriving")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DrivingLicense 驾驶证OCR识别接口
|
|
||||||
func (ocr *OCR) DrivingLicense(path string) (resDrivingLicense ResDrivingLicense, err error) {
|
|
||||||
accessToken, err := ocr.GetAccessToken()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := util.HTTPPost(fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrDrivingLicenseURL, url.QueryEscape(path), accessToken), "")
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = util.DecodeWithError(response, &resDrivingLicense, "OCRDrivingLicense")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// BizLicense 营业执照OCR识别接口
|
|
||||||
func (ocr *OCR) BizLicense(path string) (resBizLicense ResBizLicense, err error) {
|
|
||||||
accessToken, err := ocr.GetAccessToken()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := util.HTTPPost(fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrBizLicenseURL, url.QueryEscape(path), accessToken), "")
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = util.DecodeWithError(response, &resBizLicense, "OCRBizLicense")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Common 通用印刷体OCR识别接口
|
|
||||||
func (ocr *OCR) Common(path string) (resCommon ResCommon, err error) {
|
|
||||||
accessToken, err := ocr.GetAccessToken()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := util.HTTPPost(fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrCommonURL, url.QueryEscape(path), accessToken), "")
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = util.DecodeWithError(response, &resCommon, "OCRCommon")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
16
util/http.go
16
util/http.go
@@ -292,19 +292,13 @@ func httpWithTLS(rootCa, key string) (*http.Client, error) {
|
|||||||
Certificates: []tls.Certificate{cert},
|
Certificates: []tls.Certificate{cert},
|
||||||
}
|
}
|
||||||
|
|
||||||
// 安全地获取 *http.Transport
|
var baseTransport http.RoundTripper
|
||||||
var trans *http.Transport
|
|
||||||
// 尝试从 DefaultHTTPClient 获取 Transport,如果失败则使用默认值
|
|
||||||
if DefaultHTTPClient.Transport != nil {
|
if DefaultHTTPClient.Transport != nil {
|
||||||
if t, ok := DefaultHTTPClient.Transport.(*http.Transport); ok {
|
baseTransport = DefaultHTTPClient.Transport
|
||||||
trans = t.Clone()
|
} else {
|
||||||
}
|
baseTransport = http.DefaultTransport
|
||||||
}
|
}
|
||||||
// 如果无法获取有效的 Transport,使用默认值
|
trans := baseTransport.(*http.Transport).Clone()
|
||||||
if trans == nil {
|
|
||||||
trans = http.DefaultTransport.(*http.Transport).Clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
trans.TLSClientConfig = config
|
trans.TLSClientConfig = config
|
||||||
trans.DisableCompression = true
|
trans.DisableCompression = true
|
||||||
client = &http.Client{Transport: trans}
|
client = &http.Client{Transport: trans}
|
||||||
|
|||||||
@@ -1,81 +0,0 @@
|
|||||||
package util
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestHttpWithTLS_NilTransport tests the scenario where DefaultHTTPClient.Transport is nil
|
|
||||||
func TestHttpWithTLS_NilTransport(t *testing.T) {
|
|
||||||
// Save original transport
|
|
||||||
originalTransport := DefaultHTTPClient.Transport
|
|
||||||
defer func() {
|
|
||||||
DefaultHTTPClient.Transport = originalTransport
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Set Transport to nil to simulate the bug scenario
|
|
||||||
DefaultHTTPClient.Transport = nil
|
|
||||||
|
|
||||||
// This should not panic after the fix
|
|
||||||
// Note: This will fail due to invalid cert path, but shouldn't panic on type assertion
|
|
||||||
_, err := httpWithTLS("./testdata/invalid_cert.p12", "password")
|
|
||||||
|
|
||||||
// We expect an error (cert file not found), but NOT a panic
|
|
||||||
if err == nil {
|
|
||||||
t.Error("Expected error due to invalid cert path, but got nil")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestHttpWithTLS_CustomTransport tests the scenario where DefaultHTTPClient has a custom Transport
|
|
||||||
func TestHttpWithTLS_CustomTransport(t *testing.T) {
|
|
||||||
// Save original transport
|
|
||||||
originalTransport := DefaultHTTPClient.Transport
|
|
||||||
defer func() {
|
|
||||||
DefaultHTTPClient.Transport = originalTransport
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Set a custom http.Transport
|
|
||||||
customTransport := &http.Transport{
|
|
||||||
MaxIdleConns: 100,
|
|
||||||
}
|
|
||||||
DefaultHTTPClient.Transport = customTransport
|
|
||||||
|
|
||||||
// This should not panic
|
|
||||||
_, err := httpWithTLS("./testdata/invalid_cert.p12", "password")
|
|
||||||
|
|
||||||
// We expect an error (cert file not found), but NOT a panic
|
|
||||||
if err == nil {
|
|
||||||
t.Error("Expected error due to invalid cert path, but got nil")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CustomRoundTripper is a custom implementation of http.RoundTripper
|
|
||||||
type CustomRoundTripper struct{}
|
|
||||||
|
|
||||||
func (c *CustomRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
||||||
return http.DefaultTransport.RoundTrip(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestHttpWithTLS_CustomRoundTripper tests the edge case where DefaultHTTPClient has a custom RoundTripper
|
|
||||||
// that is NOT *http.Transport
|
|
||||||
func TestHttpWithTLS_CustomRoundTripper(t *testing.T) {
|
|
||||||
// Save original transport
|
|
||||||
originalTransport := DefaultHTTPClient.Transport
|
|
||||||
defer func() {
|
|
||||||
DefaultHTTPClient.Transport = originalTransport
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Set a custom RoundTripper that is NOT *http.Transport
|
|
||||||
customRoundTripper := &CustomRoundTripper{}
|
|
||||||
DefaultHTTPClient.Transport = customRoundTripper
|
|
||||||
|
|
||||||
// Create a recovery handler to catch potential panic
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
t.Errorf("httpWithTLS panicked with custom RoundTripper: %v", r)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// This might panic if the code doesn't handle non-*http.Transport RoundTripper properly
|
|
||||||
_, _ = httpWithTLS("./testdata/invalid_cert.p12", "password")
|
|
||||||
}
|
|
||||||
@@ -158,20 +158,18 @@ func (r *Client) UserCreate(req *UserCreateRequest) (*UserCreateResponse, error)
|
|||||||
|
|
||||||
// UserUpdateRequest 更新成员请求
|
// UserUpdateRequest 更新成员请求
|
||||||
type UserUpdateRequest struct {
|
type UserUpdateRequest struct {
|
||||||
UserID string `json:"userid"`
|
UserID string `json:"userid"`
|
||||||
NewUserID string `json:"new_userid"`
|
NewUserID string `json:"new_userid"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Alias string `json:"alias"`
|
Alias string `json:"alias"`
|
||||||
Mobile string `json:"mobile"`
|
Mobile string `json:"mobile"`
|
||||||
Department []int `json:"department"`
|
Department []int `json:"department"`
|
||||||
Order []int `json:"order"`
|
Order []int `json:"order"`
|
||||||
Position string `json:"position"`
|
Position string `json:"position"`
|
||||||
Gender int `json:"gender"`
|
Gender int `json:"gender"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
BizMail string `json:"biz_mail"`
|
BizMail string `json:"biz_mail"`
|
||||||
BizMailAlias struct {
|
BizMailAlias string `json:"biz_mail_alias"`
|
||||||
Item []string `json:"item"`
|
|
||||||
} `json:"biz_mail_alias"`
|
|
||||||
IsLeaderInDept []int `json:"is_leader_in_dept"`
|
IsLeaderInDept []int `json:"is_leader_in_dept"`
|
||||||
DirectLeader []string `json:"direct_leader"`
|
DirectLeader []string `json:"direct_leader"`
|
||||||
Enable int `json:"enable"`
|
Enable int `json:"enable"`
|
||||||
|
|||||||
Reference in New Issue
Block a user