Compare commits

..

42 Commits

Author SHA1 Message Date
wangfuduo e3097efa24 feat: add wx mp encrypt url 2026-08-29 15:49:44 +08:00
jiangyong e126b97031 qiniu 2026-08-28 12:46:04 +08:00
jiangyong 2ff4235310 payclone 2026-08-26 20:35:15 +08:00
jiangyong 5d481e4ca8 delete 2026-08-22 23:12:53 +08:00
jiangyong a52f8618da report 2026-08-02 09:54:26 +08:00
jiangyong f32444afe2 test 2026-08-02 00:07:52 +08:00
jiangyong 6568cc0690 httppostform 2026-08-02 00:04:59 +08:00
jiangyong ec91501a44 update 2026-07-05 18:15:02 +08:00
jiangyong 95d164da1d message 2026-07-05 14:19:42 +08:00
jiangyong 5f84186a64 change password 2026-07-05 14:13:57 +08:00
jiangyong 8bb537ff6a fixbug 2026-07-05 12:44:05 +08:00
jiangyong c726d792bd eapi 2026-07-05 12:10:27 +08:00
jiangyong e50a565e80 staffscore 2026-07-01 23:54:43 +08:00
jiangyong 6fa0b3b8af bizno 2026-07-01 23:49:38 +08:00
jiangyong dd97565ce0 datetime 2026-06-27 15:08:35 +08:00
jiangyong c9341af4aa revert: remove duplicate GetVacationQuota from app_checkin 2026-06-27 11:59:21 +08:00
jiangyong 67e974de0e feat: add GetVacationQuota to AppCheckin 2026-06-27 11:58:36 +08:00
jiangyong 68cc3c14e6 setvacation 2026-06-27 11:46:22 +08:00
jiangyong 4d48f8fce3 any 2026-06-26 13:20:20 +08:00
jiangyong 616b3bc99e ,omitempty 2026-06-26 13:20:10 +08:00
jiangyong 42d65797f3 messge clone 2026-06-26 00:42:58 +08:00
jiangyong b55ac43bb9 login 2026-06-19 00:31:38 +08:00
jiangyong 593bf8e46a ext 2026-06-18 23:48:53 +08:00
jiangyong 6ae64b443b sendmedia 2026-06-11 22:19:08 +08:00
jiangyong a1dc360fac sendvoice 2026-06-11 21:30:15 +08:00
jiangyong 3a9efa6ff7 gosdk 2026-05-14 00:03:08 +08:00
jiangyong 9228429d63 unify 2026-05-10 11:17:39 +08:00
jiangyong 4f189f6e2f ext 2026-05-06 16:24:13 +08:00
jiangyong c9bfe84484 ext 2026-05-06 14:29:53 +08:00
jiangyong 62187b9f96 goutil 2026-05-02 09:27:06 +08:00
jiangyong 17110618a8 table 2026-05-02 09:19:43 +08:00
jiangyong 2b8eaeedc1 profit 2026-05-01 01:47:21 +08:00
jiangyong 1e666ae8e5 getuisms 2026-04-30 11:43:19 +08:00
jiangyong 672496d34d pay extra 2026-04-28 11:16:53 +08:00
jiangyong 9b8dfe1646 level 2026-04-26 13:48:30 +08:00
jiangyong 2342ce66b1 count 2026-04-26 12:54:36 +08:00
jiangyong e02ab6405d Merge branch 'master' of git.u8t.cn:open/gosdk 2026-04-22 10:57:17 +08:00
jiangyong 7e231be804 fingerprint 2026-04-22 10:57:09 +08:00
jiangyong f70c80c40e sort 2026-04-19 18:34:00 +08:00
jiangyong 48e8dbb4d6 package 2026-04-19 18:32:12 +08:00
jiangyong 10a63309ee enterprise 2026-04-19 18:29:42 +08:00
jiangyong 9e09eece6f Merge pull request 'refactor: reuse httpClient' (#5) from wfd into master
Reviewed-on: #5
2026-04-16 16:13:45 +08:00
22 changed files with 760 additions and 121 deletions

1
.gitignore vendored
View File

@ -11,3 +11,4 @@ go.sum
pkg pkg
test.go test.go
.claude .claude
opencode.json

View File

@ -1,59 +0,0 @@
package config
import (
"encoding/json"
"strings"
)
const (
FormTypeInput = "input" //文本输入框
FormTypeText = "text" //多行文本
FormTypeRadio = "radio" //单选框
FormTypeCheckbox = "checkbox" //多选框
FormTypeSwitch = "switch" //开关
FormTypeJson = "json" //JSON输入框
)
type FormOption struct {
Name string `json:"name"` //选项名称
Value string `json:"value"` //选项值
}
type Form struct {
Type string `json:"type"` //表单类型
Name string `json:"name"` //表单名称
Key string `json:"key"` //表单KEY
Value interface{} `json:"value"` //表单值
Default interface{} `json:"default"` //默认值
Disable bool `json:"disable,omitempty"` //是否禁用
Tips string `json:"tips"` //表单提示
Option []*FormOption `json:"option"` //表单选项,radio和checkbox需要
}
func NewFroms(tplConfig, saveConfig string) ([]*Form, error) {
var forms []*Form
err := json.Unmarshal([]byte(tplConfig), &forms)
if err != nil {
return nil, err
}
var cfg map[string]interface{}
if strings.TrimSpace(saveConfig) != "" {
err = json.Unmarshal([]byte(saveConfig), &cfg)
if err != nil {
return nil, err
}
}
if cfg == nil {
cfg = make(map[string]interface{})
}
for _, form := range forms {
if _, ok := cfg[form.Key]; ok {
form.Value = cfg[form.Key]
} else {
form.Value = form.Default
}
}
return forms, err
}

View File

@ -0,0 +1,53 @@
package enterprise
import (
"errors"
"git.u8t.cn/open/goutil"
"github.com/tidwall/gjson"
)
const (
ApprovalProfitAmountTypeCost = "消耗"
ApprovalProfitAmountTypeProfit = "利润"
ApprovalProfitAmountTypeAward = "奖励"
)
type ApprovalProfitCreate struct {
SpNo string `json:"sp_no"` //申请单号,相同的单号数据将会被替换
Month string `json:"month"` //业绩对应月份
Username string `json:"username"` //业绩对应责任人
AmountType string `json:"amount_type"` //业绩类型
Amount string `json:"amount"` //业绩金额 单位元
Description string `json:"description"` //业绩描述
AppId string `json:"app_id"` //业绩对应的业务ID
}
type ApprovalProfit struct {
Enterprise
}
func NewApprovalProfit(baseUrl, token string) *ApprovalProfit {
return &ApprovalProfit{
Enterprise: Enterprise{
baseUrl: baseUrl,
token: token,
},
}
}
func (e *ApprovalProfit) Create(req *ApprovalProfitCreate) error {
var reqBody string
reqBody = goutil.EncodeJSON(req)
reqUrl := e.GetBaseUrl() + "/ext/approval/profit"
body, err := goutil.HttpPost(reqUrl, e.GetHeader(), []byte(reqBody))
if err != nil {
return err
}
g := gjson.ParseBytes(body)
if g.Get("code").Int() != 0 {
return errors.New(string(body))
}
return nil
}

19
enterprise/enterprise.go Normal file
View File

@ -0,0 +1,19 @@
package enterprise
type Enterprise struct {
token string
baseUrl string
}
func (e *Enterprise) GetHeader() map[string]string {
return map[string]string{
"x-token": e.token,
}
}
func (e *Enterprise) GetBaseUrl() string {
if e.baseUrl != "" {
return e.baseUrl
}
return "http://e.batiao8.com"
}

View File

@ -0,0 +1,42 @@
package enterprise
import (
"errors"
"git.u8t.cn/open/goutil"
"github.com/tidwall/gjson"
)
type StaffTarget struct {
Enterprise
}
type StaffTargetCreate struct {
Username string `json:"username"`
Month string `json:"month"`
Day string `json:"day"`
Score string `json:"score"`
Comment string `json:"comment"`
}
func NewStaffTarget(baseUrl, token string) *StaffTarget {
return &StaffTarget{
Enterprise: Enterprise{
baseUrl: baseUrl,
token: token,
},
}
}
func (e *StaffTarget) Create(req *StaffTargetCreate) error {
reqUrl := e.GetBaseUrl() + "/ext/staff/target"
body, err := goutil.HttpPost(reqUrl, e.GetHeader(), []byte(goutil.EncodeJSON(req)))
if err != nil {
return err
}
g := gjson.ParseBytes(body)
if g.Get("code").Int() != 0 {
return errors.New(string(body))
}
return nil
}

75
enterprise/staff_user.go Normal file
View File

@ -0,0 +1,75 @@
package enterprise
import (
"errors"
"fmt"
"git.u8t.cn/open/goutil"
"github.com/tidwall/gjson"
)
var (
corpId = 1000
)
type VerifyCode struct {
Username string `json:"username"`
Timestamp string `json:"timestamp"`
}
type StaffInfo struct {
Username string `json:"username"`
Realname string `json:"realname"`
Phone string `json:"phone"`
}
type StaffUser struct {
Enterprise
}
func NewStaffUser(baseUrl, token string) *StaffUser {
return &StaffUser{
Enterprise: Enterprise{
baseUrl: baseUrl,
token: token,
},
}
}
func (e *StaffUser) SendVerifyCode(username, scene string) (*VerifyCode, error) {
var reqBody string
reqBody = fmt.Sprintf(`{"username":"%s","scene":"%s"}`, username, scene)
reqUrl := e.GetBaseUrl() + "/ext/staff/verify/code"
body, err := goutil.HttpPost(reqUrl, e.GetHeader(), []byte(reqBody))
if err != nil {
return nil, err
}
g := gjson.ParseBytes(body)
if g.Get("code").Int() != 0 {
return nil, errors.New(string(body))
}
r := new(VerifyCode)
r.Timestamp = g.Get("data.timestamp").String()
r.Username = g.Get("data.username").String()
return r, nil
}
func (e *StaffUser) Login(username, timestamp, code string) (*StaffInfo, error) {
reqBody := fmt.Sprintf(`{"username":"%s","timestamp":"%s","code":"%s"}`, username, timestamp, code)
reqUrl := e.GetBaseUrl() + "/ext/staff/login"
body, err := goutil.HttpPost(reqUrl, e.GetHeader(), []byte(reqBody))
if err != nil {
return nil, err
}
g := gjson.ParseBytes(body)
if g.Get("code").Int() != 0 {
return nil, errors.New(string(body))
}
r := new(StaffInfo)
r.Realname = g.Get("data.realname").String()
r.Username = g.Get("data.username").String()
r.Phone = g.Get("data.phone").String()
return r, nil
}

53
erp/approval_profit.go Normal file
View File

@ -0,0 +1,53 @@
package erp
import (
"errors"
"git.u8t.cn/open/goutil"
"github.com/tidwall/gjson"
)
const (
ApprovalProfitAmountTypeCost = "消耗"
ApprovalProfitAmountTypeProfit = "利润"
ApprovalProfitAmountTypeAward = "奖励"
)
type ApprovalProfitCreate struct {
BizNo string `json:"biz_no"` //申请单号,相同的单号数据将会被替换
Month string `json:"month"` //业绩对应月份
Username string `json:"username"` //业绩对应责任人
AmountType string `json:"amount_type"` //业绩类型
Amount string `json:"amount"` //业绩金额 单位元
Description string `json:"description"` //业绩描述
AppId string `json:"app_id"` //业绩对应的业务ID
}
type ApprovalProfit struct {
Erp
}
func NewApprovalProfit(baseUrl, token string) *ApprovalProfit {
return &ApprovalProfit{
Erp: Erp{
baseUrl: baseUrl,
token: token,
},
}
}
func (e *ApprovalProfit) Create(req *ApprovalProfitCreate) error {
var reqBody string
reqBody = goutil.EncodeJSON(req)
reqUrl := e.GetBaseUrl() + "/ext/staff/profit"
body, err := goutil.HttpPost(reqUrl, e.GetHeader(), []byte(reqBody))
if err != nil {
return err
}
g := gjson.ParseBytes(body)
if g.Get("code").Int() != 0 {
return errors.New(string(body))
}
return nil
}

19
erp/erp.go Normal file
View File

@ -0,0 +1,19 @@
package erp
type Erp struct {
token string
baseUrl string
}
func (e *Erp) GetHeader() map[string]string {
return map[string]string{
"x-token": e.token,
}
}
func (e *Erp) GetBaseUrl() string {
if e.baseUrl != "" {
return e.baseUrl
}
return "http://eapi.batiao8.com"
}

42
erp/staff_score.go Normal file
View File

@ -0,0 +1,42 @@
package erp
import (
"errors"
"git.u8t.cn/open/goutil"
"github.com/tidwall/gjson"
)
type StaffScore struct {
Erp
}
type StaffScoreCreate struct {
Username string `json:"username"`
Month string `json:"month"`
Day string `json:"day"`
Score string `json:"score"`
Comment string `json:"comment"`
}
func NewStaffScore(baseUrl, token string) *StaffScore {
return &StaffScore{
Erp: Erp{
baseUrl: baseUrl,
token: token,
},
}
}
func (e *StaffScore) Create(req *StaffScoreCreate) error {
reqUrl := e.GetBaseUrl() + "/ext/staff/score"
body, err := goutil.HttpPost(reqUrl, e.GetHeader(), []byte(goutil.EncodeJSON(req)))
if err != nil {
return err
}
g := gjson.ParseBytes(body)
if g.Get("code").Int() != 0 {
return errors.New(string(body))
}
return nil
}

111
erp/staff_user.go Normal file
View File

@ -0,0 +1,111 @@
package erp
import (
"errors"
"fmt"
"git.u8t.cn/open/goutil"
"github.com/tidwall/gjson"
)
const (
LoginTypePassword = "password"
LoginTypeVerify = "verify"
)
type StaffCode struct {
Timestamp string `json:"timestamp"`
}
type LoginReq struct {
Type string `json:"type"`
Username string `json:"username"`
Password string `json:"password"`
Timestamp string `json:"timestamp"`
}
type LoginRsp struct {
Username string `json:"username"`
Realname string `json:"realname"`
Phone string `json:"phone"`
}
type UpdateReq struct {
Username string `json:"username"`
Password string `json:"password"`
}
type StaffUser struct {
Erp
}
func NewStaffUser(baseUrl, token string) *StaffUser {
return &StaffUser{
Erp: Erp{
baseUrl: baseUrl,
token: token,
},
}
}
func (e *StaffUser) SendCode(username, scene string) (*StaffCode, error) {
var reqBody string
reqBody = fmt.Sprintf(`{"username":"%s","scene":"%s"}`, username, scene)
reqUrl := e.GetBaseUrl() + "/ext/staff/user/code"
body, err := goutil.HttpPost(reqUrl, e.GetHeader(), []byte(reqBody))
if err != nil {
return nil, err
}
g := gjson.ParseBytes(body)
if g.Get("code").Int() != 0 {
return nil, errors.New(string(body))
}
r := new(StaffCode)
r.Timestamp = g.Get("data.timestamp").String()
return r, nil
}
func (e *StaffUser) Login(req *LoginReq) (*LoginRsp, error) {
var reqBody string
reqBody = fmt.Sprintf(`{"username":"%s","password":"%s", "timestamp":"%s","type":"%s"}`,
req.Username, req.Password, req.Timestamp, req.Type)
reqUrl := e.GetBaseUrl() + "/ext/staff/user/login"
body, err := goutil.HttpPost(reqUrl, e.GetHeader(), []byte(reqBody))
if err != nil {
return nil, err
}
g := gjson.ParseBytes(body)
if g.Get("code").Int() != 0 {
return nil, errors.New(string(body))
}
r := new(LoginRsp)
r.Realname = g.Get("data.realname").String()
r.Username = g.Get("data.username").String()
r.Phone = g.Get("data.phone").String()
return r, nil
}
func (e *StaffUser) Update(req *UpdateReq) error {
var reqBody string
reqBody = goutil.EncodeJSON(req)
reqUrl := e.GetBaseUrl() + "/ext/staff/user"
body, err := goutil.HttpPut(reqUrl, e.GetHeader(), []byte(reqBody))
if err != nil {
return err
}
g := gjson.ParseBytes(body)
if g.Get("code").Int() != 0 {
return errors.New(g.Get("message").String())
}
return nil
}

34
go.mod
View File

@ -1,9 +1,9 @@
module git.u8t.cn/open/gosdk module git.u8t.cn/open/gosdk
go 1.25 go 1.25.0
require ( require (
git.u8t.cn/open/goutil v0.0.0-20251006031723-35e9c33cf3b5 git.u8t.cn/open/goutil v0.0.0-20260701172715-c0058361ddcc
github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf
github.com/eclipse/paho.mqtt.golang v1.5.1 github.com/eclipse/paho.mqtt.golang v1.5.1
github.com/gin-gonic/gin v1.11.0 github.com/gin-gonic/gin v1.11.0
@ -12,13 +12,13 @@ require (
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c
github.com/minio/minio-go v6.0.14+incompatible github.com/minio/minio-go v6.0.14+incompatible
github.com/qiniu/go-sdk/v7 v7.25.4 github.com/qiniu/go-sdk/v7 v7.21.0
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.4
github.com/spf13/cast v1.10.0 github.com/spf13/cast v1.10.0
github.com/tidwall/gjson v1.18.0 github.com/tidwall/gjson v1.18.0
github.com/wechatpay-apiv3/wechatpay-go v0.2.21 github.com/wechatpay-apiv3/wechatpay-go v0.2.21
golang.org/x/crypto v0.47.0 golang.org/x/crypto v0.51.0
gorm.io/gorm v1.31.0 gorm.io/gorm v1.31.2
) )
require ( require (
@ -29,7 +29,6 @@ require (
github.com/cloudwego/base64x v0.1.6 // indirect github.com/cloudwego/base64x v0.1.6 // indirect
github.com/fogleman/gg v1.3.0 // indirect github.com/fogleman/gg v1.3.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/gammazero/toposort v0.1.1 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect github.com/gin-contrib/sse v1.1.0 // indirect
github.com/go-ini/ini v1.67.0 // indirect github.com/go-ini/ini v1.67.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
@ -45,29 +44,30 @@ require (
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/matishsiao/goInfo v0.0.0-20210923090445-da2e3fa8d45f // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect github.com/nxadm/tail v1.4.8 // indirect
github.com/onsi/gomega v1.39.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.54.0 // indirect github.com/quic-go/quic-go v0.54.0 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/speps/go-hashids v2.0.0+incompatible // indirect github.com/speps/go-hashids v2.0.0+incompatible // indirect
github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect github.com/tidwall/pretty v1.2.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.0 // indirect github.com/ugorji/go/codec v1.3.0 // indirect
go.uber.org/mock v0.5.0 // indirect go.uber.org/mock v0.5.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/arch v0.20.0 // indirect golang.org/x/arch v0.20.0 // indirect
golang.org/x/image v0.31.0 // indirect golang.org/x/image v0.43.0 // indirect
golang.org/x/mod v0.32.0 // indirect golang.org/x/mod v0.36.0 // indirect
golang.org/x/net v0.49.0 // indirect golang.org/x/net v0.54.0 // indirect
golang.org/x/sync v0.19.0 // indirect golang.org/x/sync v0.21.0 // indirect
golang.org/x/sys v0.40.0 // indirect golang.org/x/sys v0.46.0 // indirect
golang.org/x/text v0.33.0 // indirect golang.org/x/text v0.38.0 // indirect
golang.org/x/tools v0.41.0 // indirect golang.org/x/tools v0.45.0 // indirect
google.golang.org/protobuf v1.36.9 // indirect google.golang.org/protobuf v1.36.9 // indirect
modernc.org/fileutil v1.0.0 // indirect
) )

View File

@ -5,8 +5,8 @@ import (
"errors" "errors"
"fmt" "fmt"
"git.u8t.cn/open/gosdk/util" "git.u8t.cn/open/gosdk/util"
log "github.com/sirupsen/logrus"
"git.u8t.cn/open/goutil" "git.u8t.cn/open/goutil"
log "github.com/sirupsen/logrus"
"github.com/spf13/cast" "github.com/spf13/cast"
"time" "time"
) )
@ -78,7 +78,7 @@ func (g *GetuiSms) Token() string {
return g.token return g.token
} }
func (g *GetuiSms) Send(phone string, data map[string]string) (interface{}, error) { func (g *GetuiSms) Send(phone string, data string) (interface{}, error) {
reqUrl := "https://openapi-smsp.getui.com/v1/sps/push_sms_list" reqUrl := "https://openapi-smsp.getui.com/v1/sps/push_sms_list"
params := make(map[string]interface{}) params := make(map[string]interface{})
@ -93,7 +93,7 @@ func (g *GetuiSms) Send(phone string, data map[string]string) (interface{}, erro
log.Errorf("http post [%s] error :%s", string(reqBody), err.Error()) log.Errorf("http post [%s] error :%s", string(reqBody), err.Error())
return nil, err return nil, err
} }
fmt.Println(string(reqBody), string(rspBody))
_, err = g.getResult(rspBody) _, err = g.getResult(rspBody)
if err != nil { if err != nil {
log.Errorf("get Result error :%s", err.Error()) log.Errorf("get Result error :%s", err.Error())

View File

@ -51,6 +51,10 @@ type MessageRequest struct {
MediaID string `json:"media_id"` MediaID string `json:"media_id"`
} `json:"image"` } `json:"image"`
Voice struct {
MediaID string `json:"media_id"`
} `json:"voice"`
Video struct { Video struct {
MediaID string `json:"media_id"` MediaID string `json:"media_id"`
Title string `json:"title"` Title string `json:"title"`
@ -235,6 +239,33 @@ func (a *App) SendImage(receiver []string, meidiaId string) error {
return nil return nil
} }
func (a *App) SendVoice(receiver []string, meidiaId string) error {
url := fmt.Sprintf("%s?access_token=%s", urlQiyeSend, a.GetToken())
req := new(MessageRequest)
req.MstType = "voice"
req.AgentId = a.config.Agent
req.ToUser = strings.Join(receiver, "|")
req.Voice.MediaID = meidiaId
data, _ := json.Marshal(req)
result, err := util.HttpPostJson(url, nil, data)
if err != nil {
return err
}
var rsp BaseResponse
err = json.Unmarshal([]byte(result), &rsp)
if err != nil {
return err
}
if rsp.ErrCode != 0 {
return fmt.Errorf("%d:%s", rsp.ErrCode, rsp.ErrMsg)
}
return nil
}
func (a *App) SendVideo(receiver []string, meidiaId, title, desc string) error { func (a *App) SendVideo(receiver []string, meidiaId, title, desc string) error {
url := fmt.Sprintf("%s?access_token=%s", urlQiyeSend, a.GetToken()) url := fmt.Sprintf("%s?access_token=%s", urlQiyeSend, a.GetToken())

View File

@ -86,6 +86,21 @@ type ApproveDetailRsp struct {
Info *ApproveDetail `json:"info"` Info *ApproveDetail `json:"info"`
} }
type VacationQuota struct {
Id uint32 `json:"id"`
AssignDuration uint32 `json:"assignduration"`
UsedDuration uint32 `json:"usedduration"`
LeftDuration uint32 `json:"leftduration"`
VacationName string `json:"vacationname"`
RealAssignDuration uint32 `json:"real_assignduration"`
}
type VacationQuotaRsp struct {
Errcode int `json:"errcode"`
Errmsg string `json:"errmsg"`
Lists []*VacationQuota `json:"lists"`
}
type AppApprove struct { type AppApprove struct {
App App
} }
@ -165,6 +180,46 @@ func NewAppApprove(cfg *AppConfig) *AppApprove {
} }
} }
func (q *AppApprove) GetVacationQuota(userid string) ([]*VacationQuota, error) {
reqUrl := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/oa/vacation/getuservacationquota?access_token=%s", q.GetToken())
reqParam := map[string]interface{}{
"userid": userid,
}
rspBody, err := util.HttpPostJson(reqUrl, nil, []byte(goutil.EncodeJSON(reqParam)))
if err != nil {
return nil, err
}
if _, err := q.GetResult(rspBody); err != nil {
return nil, err
}
var rsp VacationQuotaRsp
if err := json.Unmarshal(rspBody, &rsp); err != nil {
log.Errorf("get body[%s] json error :%s", string(rspBody), err.Error())
return nil, err
}
return rsp.Lists, nil
}
func (q *AppApprove) SetVacationQuota(userid string, vacationId, leftDuration, timeAttr uint32, remarks string) error {
reqUrl := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/oa/vacation/setoneuserquota?access_token=%s", q.GetToken())
reqParam := map[string]interface{}{
"userid": userid,
"vacation_id": vacationId,
"leftduration": leftDuration,
"time_attr": timeAttr,
}
if remarks != "" {
reqParam["remarks"] = remarks
}
rspBody, err := util.HttpPostJson(reqUrl, nil, []byte(goutil.EncodeJSON(reqParam)))
if err != nil {
return err
}
_, err = q.GetResult(rspBody)
return err
}
func (q *AppApprove) GetDetail(spNo string) (*ApproveDetail, error) { func (q *AppApprove) GetDetail(spNo string) (*ApproveDetail, error) {
reqUrl := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/oa/getapprovaldetail?access_token=%s", q.GetToken()) reqUrl := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/oa/getapprovaldetail?access_token=%s", q.GetToken())
reqParam := fmt.Sprintf(`{"sp_no":"%s"}`, spNo) reqParam := fmt.Sprintf(`{"sp_no":"%s"}`, spNo)

View File

@ -22,6 +22,7 @@ type QiniuConfig struct {
type Qiniu struct { type Qiniu struct {
config *QiniuConfig config *QiniuConfig
domains []string
} }
func NewQiniu(cfg *QiniuConfig) Storage { func NewQiniu(cfg *QiniuConfig) Storage {
@ -137,41 +138,25 @@ func (s *Qiniu) List(objectPrefix string) ([]string, error) {
} }
func (s *Qiniu) Get(objectName, fileName string) error { func (s *Qiniu) Get(objectName, fileName string) error {
mac := qbox.NewMac(s.config.AK, s.config.SK) s.loadDomains()
cfg := storage.Config{ if len(s.domains) <= 0 {
UseHTTPS: false,
}
bucketManager := storage.NewBucketManager(mac, &cfg)
domains, err := bucketManager.ListBucketDomains(s.config.Bucket)
if err != nil {
return err
}
if len(domains) <= 0 {
return errors.New("bucket no domain") return errors.New("bucket no domain")
} }
url := fmt.Sprintf("http://%s/%s", domains[0].Domain, objectName) url := fmt.Sprintf("http://%s/%s", s.domains[0], objectName)
return Download(url, fileName) return Download(url, fileName)
} }
func (s *Qiniu) Url(objectName string, expire time.Duration, https ...bool) string { func (s *Qiniu) Url(objectName string, expire time.Duration, https ...bool) string {
mac := qbox.NewMac(s.config.AK, s.config.SK) mac := qbox.NewMac(s.config.AK, s.config.SK)
cfg := storage.Config{
UseHTTPS: false, s.loadDomains()
if len(s.domains) <= 0 {
return ""
} }
bucketManager := storage.NewBucketManager(mac, &cfg) domain := "http://" + s.domains[0]
domains, err := bucketManager.ListBucketDomains(s.config.Bucket)
if err != nil {
return ""
}
if len(domains) <= 0 {
return ""
}
domain := "http://" + domains[0].Domain
if len(https) > 0 && https[0] { if len(https) > 0 && https[0] {
domain = "https://" + domains[0].Domain domain = "https://" + s.domains[0]
} }
if expire == 0 { if expire == 0 {
@ -218,6 +203,27 @@ func isQiniuNotFound(err error) bool {
return false return false
} }
func (s *Qiniu) loadDomains() {
if len(s.domains) != 0 {
return
}
mac := qbox.NewMac(s.config.AK, s.config.SK)
cfg := storage.Config{
UseHTTPS: false,
}
bucketManager := storage.NewBucketManager(mac, &cfg)
domains, err := bucketManager.ListBucketDomains(s.config.Bucket)
if err != nil {
return
}
s.domains = make([]string, 0)
for _, domain := range domains {
s.domains = append(s.domains, domain.Domain)
}
}
func (s *Qiniu) Fetch(url, objectName string, local ...bool) error { func (s *Qiniu) Fetch(url, objectName string, local ...bool) error {
if len(local) > 0 && local[0] == true { if len(local) > 0 && local[0] == true {
return s.fetchLocal(url, objectName) return s.fetchLocal(url, objectName)

View File

@ -3,8 +3,10 @@ package unify
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"git.u8t.cn/open/gosdk/util"
"net/url" "net/url"
"git.u8t.cn/open/gosdk/util"
"git.u8t.cn/open/goutil"
) )
type Message struct { type Message struct {
@ -24,8 +26,20 @@ func NewMessage(addreess string, token string, sender string) *Message {
} }
} }
func (m *Message) Send(receiver, content string) error { func (m *Message) Clone(sender string) *Message {
reqUrl := fmt.Sprintf("%s/admin/message/send?sender=%s&receiver=%s&content=%s", m.address, m.sender, receiver, url.QueryEscape(content)) return &Message{
address: m.address,
token: m.token,
sender: sender,
}
}
func (m *Message) Send(args ...string) error {
return m.SendText(args...)
}
func (m *Message) SendMedia(receiver, mediaType, mediaId string) error {
reqUrl := fmt.Sprintf("%s/admin/message/send?sender=%s&receiver=%s&type=%s&content=%s", m.address, m.sender, receiver, mediaType, url.QueryEscape(mediaId))
body, err := util.HttpGet(reqUrl, map[string]string{ body, err := util.HttpGet(reqUrl, map[string]string{
"x-token": m.token, "x-token": m.token,
}) })
@ -41,3 +55,93 @@ func (m *Message) Send(receiver, content string) error {
} }
return nil return nil
} }
func (m *Message) SendText(args ...string) error {
var receiver, content, fingerprint, level, count string
if len(args) == 2 {
receiver, content = args[0], args[1]
} else if len(args) == 3 {
receiver, content, fingerprint = args[0], args[1], args[2]
} else if len(args) == 4 {
receiver, content, fingerprint, level = args[0], args[1], args[2], args[3]
} else if len(args) > 4 {
receiver, content, fingerprint, level, count = args[0], args[1], args[2], args[3], args[4]
}
// Build form data
formData := url.Values{}
formData.Set("sender", m.sender)
formData.Set("receiver", receiver)
formData.Set("type", "text")
formData.Set("fingerprint", fingerprint)
formData.Set("count", count)
formData.Set("level", level)
formData.Set("content", content)
reqUrl := fmt.Sprintf("%s/admin/message/send", m.address)
body, err := util.HttpPostForm(reqUrl, map[string]string{
"x-token": m.token,
}, formData)
if err != nil {
return err
}
var rsp util.Response
if err := json.Unmarshal(body, &rsp); err != nil {
return err
}
if rsp.Code != 0 {
return fmt.Errorf("%d:%s", rsp.Code, rsp.Message)
}
return nil
}
func (m *Message) SendMail(receiver, title, content string) error {
// Build form data
formData := url.Values{}
formData.Set("sender", m.sender)
formData.Set("receiver", receiver)
formData.Set("type", "text")
formData.Set("title", title)
formData.Set("content", content)
reqUrl := fmt.Sprintf("%s/admin/message/send", m.address)
body, err := util.HttpPostForm(reqUrl, map[string]string{
"x-token": m.token,
}, formData)
if err != nil {
return err
}
var rsp util.Response
if err := json.Unmarshal(body, &rsp); err != nil {
return err
}
if rsp.Code != 0 {
return fmt.Errorf("%d:%s", rsp.Code, rsp.Message)
}
return nil
}
func (m *Message) SendTable(receiver string, table *goutil.TableData) error {
content := goutil.EncodeJSON(table)
reqUrl := fmt.Sprintf("%s/admin/message/send", m.address)
reqData := make(map[string]interface{})
reqData["type"] = "table"
reqData["content"] = content
reqData["receiver"] = receiver
reqData["sender"] = m.sender
body, err := util.HttpPostJson(reqUrl, map[string]string{"x-token": m.token},
[]byte(goutil.EncodeJSON(reqData)))
if err != nil {
return err
}
var rsp util.Response
if err := json.Unmarshal(body, &rsp); err != nil {
return err
}
if rsp.Code != 0 {
return fmt.Errorf("%d:%s", rsp.Code, rsp.Message)
}
return nil
}

View File

@ -8,12 +8,10 @@ import (
func TestMessage_Send(t *testing.T) { func TestMessage_Send(t *testing.T) {
s := NewMessage("http://u.batiao8.com", "", "alarm") s := NewMessage("http://u.batiao8.com", "c39e1c6e-38f7-422c-a7d1-9e05bf65596f", "batiao-report-mail")
mess := make([]string, 0) mess := make([]string, 0)
mess = append(mess, "加发到") mess = append(mess, `abcc`)
mess = append(mess, "asdfasdfadfa") err := s.SendMail("jiangyong@batiao8.com", "测试", strings.Join(mess, "\n"))
mess = append(mess, "@3q452345~!@@#$$%^&*())")
err := s.Send("jiangyong", strings.Join(mess, "\n"))
fmt.Println(err) fmt.Println(err)
} }

View File

@ -30,6 +30,13 @@ func NewPay(address string, token string) *Pay {
} }
} }
func (p *Pay) Clone(token string) *Pay {
return &Pay{
address: p.address,
token: token,
}
}
func (p *Pay) CreateOrder(order *CreateOrderReq) (map[string]interface{}, error) { func (p *Pay) CreateOrder(order *CreateOrderReq) (map[string]interface{}, error) {
if order.PayType == "" { if order.PayType == "" {
errors.New("payType is nil") errors.New("payType is nil")

View File

@ -61,6 +61,7 @@ type TransferReq struct {
PayChannel string `json:"payChannel"` PayChannel string `json:"payChannel"`
UserId string `json:"userId"` UserId string `json:"userId"`
OutTradeNo string `json:"outTradeNo"` OutTradeNo string `json:"outTradeNo"`
Extra map[string]interface{} `json:"extra"`
} }
type CommonResponse struct { type CommonResponse struct {

View File

@ -6,6 +6,8 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"net/url"
"strings"
"time" "time"
) )
@ -72,6 +74,27 @@ func HttpPutJson(link string, header map[string]string, json []byte) ([]byte, er
return io.ReadAll(resp.Body) return io.ReadAll(resp.Body)
} }
func HttpPostForm(url string, headers map[string]string, data url.Values) ([]byte, error) {
req, err := http.NewRequest("POST", url, strings.NewReader(data.Encode()))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
for k, v := range headers {
req.Header.Set(k, v)
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
// Get 请求 link:请求url // Get 请求 link:请求url
func HttpGet(link string, header map[string]string) ([]byte, error) { func HttpGet(link string, header map[string]string) ([]byte, error) {
req, err := http.NewRequest("GET", link, nil) req, err := http.NewRequest("GET", link, nil)

View File

@ -20,6 +20,7 @@ const (
userPhoneNumberUrl string = "https://api.weixin.qq.com/wxa/business/getuserphonenumber" userPhoneNumberUrl string = "https://api.weixin.qq.com/wxa/business/getuserphonenumber"
getWxACodeUnLimitUrl string = "https://api.weixin.qq.com/wxa/getwxacodeunlimit" getWxACodeUnLimitUrl string = "https://api.weixin.qq.com/wxa/getwxacodeunlimit"
getWxScheme string = "https://api.weixin.qq.com/wxa/generatescheme" getWxScheme string = "https://api.weixin.qq.com/wxa/generatescheme"
getWxUrlLink string = "https://api.weixin.qq.com/wxa/generate_urllink"
code2UserinfoUrl string = "https://api.weixin.qq.com/sns/userinfo" code2UserinfoUrl string = "https://api.weixin.qq.com/sns/userinfo"
oaQrCodeCreateUrl string = "https://api.weixin.qq.com/cgi-bin/qrcode/create" oaQrCodeCreateUrl string = "https://api.weixin.qq.com/cgi-bin/qrcode/create"
userInfoByOpenid string = "https://api.weixin.qq.com/cgi-bin/user/info" userInfoByOpenid string = "https://api.weixin.qq.com/cgi-bin/user/info"

View File

@ -2,13 +2,15 @@ package weixin
import ( import (
"bytes" "bytes"
"cmp"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/spf13/cast"
"github.com/tidwall/gjson"
"io" "io"
"net/http" "net/http"
"github.com/spf13/cast"
"github.com/tidwall/gjson"
) )
type MpSdk struct { type MpSdk struct {
@ -151,3 +153,58 @@ func (o *MpSdk) GetScheme(params map[string]interface{}) (string, error) {
} }
return g.Get("openlink").String(), nil return g.Get("openlink").String(), nil
} }
func (o *MpSdk) GetEncryptUrl(params map[string]any) (string, error) {
newParams := map[string]any{
"env_version": cmp.Or(cast.ToString(params["env_version"]), "release"),
}
if path := cast.ToString(params["path"]); path != "" {
newParams["path"] = path
}
if query := cast.ToString(params["query"]); query != "" {
newParams["query"] = query
}
_, hasExpireType := params["expire_type"]
_, hasExpireTime := params["expire_time"]
_, hasExpireInterval := params["expire_interval"]
if hasExpireType {
newParams["expire_type"] = cast.ToInt(params["expire_type"])
} else if hasExpireInterval {
newParams["expire_type"] = 1
} else if hasExpireTime {
newParams["expire_type"] = 0
}
if hasExpireTime {
newParams["expire_time"] = cast.ToInt64(params["expire_time"])
}
if hasExpireInterval {
newParams["expire_interval"] = cast.ToInt(params["expire_interval"])
}
marshal, err := json.Marshal(newParams)
if err != nil {
return "", err
}
accessToken, err := o.getAccessToken()
if err != nil {
return "", err
}
url := fmt.Sprintf("%s?access_token=%s", getWxUrlLink, accessToken)
res, err := http.Post(url, "application/json", bytes.NewBuffer(marshal))
if err != nil {
return "", err
}
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
return "", err
}
g := gjson.ParseBytes(body)
errcode := g.Get("errcode").Int()
if errcode != 0 {
return "", fmt.Errorf("%d:%s", errcode, g.Get("errmsg"))
}
return g.Get("url_link").String(), nil
}