From 1121e2af88460612918271b57c6d413f6b5fe2f9 Mon Sep 17 00:00:00 2001 From: Hankin Date: Thu, 30 Jul 2026 18:11:06 +0800 Subject: [PATCH] first commit --- go.mod | 5 + go.sum | 2 + main.go | 63 ++++++++++ service/access_token.go | 108 ++++++++++++++++ service/alive.go | 213 +++++++++++++++++++++++++++++++ service/camp.go | 54 ++++++++ service/class.go | 54 ++++++++ service/client.go | 98 +++++++++++++++ service/common.go | 32 +++++ service/course.go | 179 ++++++++++++++++++++++++++ service/goods.go | 257 +++++++++++++++++++++++++++++++++++++ service/purchase.go | 219 ++++++++++++++++++++++++++++++++ service/user.go | 272 ++++++++++++++++++++++++++++++++++++++++ 13 files changed, 1556 insertions(+) create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 service/access_token.go create mode 100644 service/alive.go create mode 100644 service/camp.go create mode 100644 service/class.go create mode 100644 service/client.go create mode 100644 service/common.go create mode 100644 service/course.go create mode 100644 service/goods.go create mode 100644 service/purchase.go create mode 100644 service/user.go diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..dfc6971 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module xiaoetech + +go 1.24.4 + +require github.com/faabiosr/cachego v0.26.0 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..5e17caf --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/faabiosr/cachego v0.26.0 h1:EDDv2y9T0XJ4Cx3tUhbKSUayGWxCGkkZUivNLceHRWY= +github.com/faabiosr/cachego v0.26.0/go.mod h1:p54WXVzeB1CctH1ix/rjqv1EotNzD0Xoxk2IsR1PQX8= diff --git a/main.go b/main.go new file mode 100644 index 0000000..6b3505f --- /dev/null +++ b/main.go @@ -0,0 +1,63 @@ +package main + +import ( + "encoding/json" + "fmt" + "net/http" + "os" + "strings" + "xiaoetech/service" + + "github.com/faabiosr/cachego/file" +) + +const ( + XiaoEAppId = "appru2yutaz8506" // 店铺app_id + XiaoEClientId = "xopKH8sWbrt9308" // 店铺client_id + XiaoEAppSecret = "APFvqtqs0qh2dphCPbDq9VgpMXhLiBc2" // 店铺client_sercet + XiaoEGrantType = "client_credential" // 固定不变 + + GetUserListOpenApi = "https://api.xiaoe-tech.com/xe.user.batch.get/2.0.0" +) + +func main() { + xiaoE := &service.DefaultAccessTokenManager{ + Id: XiaoEAppId, + Name: "access_token", + GetRefreshRequestFunc: func() *http.Request { + params := make(map[string]string) + params["app_id"] = XiaoEAppId + params["client_id"] = XiaoEClientId + params["secret_key"] = XiaoEAppSecret + params["grant_type"] = XiaoEGrantType + + str, err := json.Marshal(params) + if err != nil { + fmt.Println(err) + } + payload := strings.NewReader(string(str)) + req, err := http.NewRequest(http.MethodGet, service.AccessTokenUrl, payload) + if err != nil { + return nil + } + + return req + }, + // os.TempDir() - 设置缓存路径,默认为系统默认缓存路径,为了方便查找建议修改路径 + Cache: file.New(os.TempDir()), + } + + // 小鹅云 客户端 + xiaoEClient := service.NewClient(xiaoE) + + // 调用示例 不需要传入access_token + UserListParams := make(map[string]interface{}) + UserListParams["page"] = 1 + UserListParams["page_size"] = 2 + + resp, err := xiaoEClient.CurlDo(http.MethodPost, GetUserListOpenApi, UserListParams) + if err != nil { + fmt.Println("err: ", err) + } + fmt.Println(string(resp)) +} diff --git a/service/access_token.go b/service/access_token.go new file mode 100644 index 0000000..f51578f --- /dev/null +++ b/service/access_token.go @@ -0,0 +1,108 @@ +package service + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "sync" + "time" + + "github.com/faabiosr/cachego" +) + +type AccessTokenManager interface { + GetName() (name string) + GetAccessToken() (accessToken string, err error) +} + +type getRefreshRequestFunc func() *http.Request + +type DefaultAccessTokenManager struct { + Id string + Name string + GetRefreshRequestFunc getRefreshRequestFunc + Cache cachego.Cache +} + +// 防止多个 goroutine 并发刷新冲突 +var getAccessTokenLock sync.Mutex + +// GetAccessToken 获取access_token +func (m *DefaultAccessTokenManager) GetAccessToken() (accessToken string, err error) { + cacheKey := m.getCacheKey() + accessToken, err = m.Cache.Fetch(cacheKey) + if accessToken != "" { + return + } + + getAccessTokenLock.Lock() + defer getAccessTokenLock.Unlock() + + accessToken, err = m.Cache.Fetch(cacheKey) + if accessToken != "" { + return + } + + req := m.GetRefreshRequestFunc() + // 添加 serverUrl + if !strings.HasPrefix(req.URL.String(), "http") { + parse, _ := url.Parse(AccessTokenUrl) + req.URL.Host = parse.Host + req.URL.Scheme = parse.Scheme + } + + req.Header.Set("Content-Type", contentTypeApplicationJson) + + response, err := http.DefaultClient.Do(req) + if err != nil { + return + } + + resp, err := io.ReadAll(response.Body) + if err != nil { + return + } + defer response.Body.Close() + + var result = struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data struct { + AccessToken string `json:"access_token"` + ExpiresIn float64 `json:"expires_in"` + } `json:"data"` + }{} + + err = json.Unmarshal(resp, &result) + if err != nil { + err = fmt.Errorf("unmarshal error %s", string(resp)) + return + } + + if result.Data.AccessToken == "" { + err = fmt.Errorf("%s", string(resp)) + return + } + + accessToken = result.Data.AccessToken + + err = m.Cache.Save(cacheKey, accessToken, time.Duration(result.Data.ExpiresIn)*time.Second) + if err != nil { + return + } + + return +} + +// getCacheKey +func (m *DefaultAccessTokenManager) getCacheKey() (key string) { + return "access_token:" + m.Id +} + +// GetName 获取 access_token 参数名称 +func (m *DefaultAccessTokenManager) GetName() (name string) { + return m.Name +} diff --git a/service/alive.go b/service/alive.go new file mode 100644 index 0000000..b469020 --- /dev/null +++ b/service/alive.go @@ -0,0 +1,213 @@ +package service + +import ( + "encoding/json" + "net/http" +) + +type GetAliveListRequest struct { + AlivePlayState *int `json:"alive_play_state,omitempty"` // 直播状态:-1全部,0未开始,1直播中,2已结束;默认0 + SearchAliveType *int `json:"search_alive_type,omitempty"` // 直播模式:-1全部,10横屏,11竖屏,12语音,13录播;默认(文档未明确,建议传-1或0) + SearchContent *string `json:"search_content,omitempty"` // 直播名称关键字 + CreateMode *int `json:"create_mode,omitempty"` // 创建类型 -1全部 0店铺课程 1转播;默认0 + State *int `json:"state,omitempty"` // 上架状态: -1全部,0已上架,1已下架,2待上架;默认0 + ZbStartAtMin *string `json:"zb_start_at_min,omitempty"` // 直播开始时间-最小值 + ZbStartAtMax *string `json:"zb_start_at_max,omitempty"` // 直播开始时间-最大值 + TagIds []int `json:"tag_ids,omitempty"` // 商品分组ID数组 + Page *int `json:"page,omitempty"` // 页码,默认1 + PageSize *int `json:"page_size,omitempty"` // 每页条数,最大50,默认10 + IsGetThumbs *int `json:"is_get_thumbs,omitempty"` // 是否需要获取点赞数 0:否 1:是 +} + +type Alive struct { + AppId string `json:"app_id"` // 店铺ID + Id string `json:"id"` // 直播ID + RoomId string `json:"room_id"` // 房间ID + Title string `json:"title"` // 直播标题 + ImgUrl string `json:"img_url"` // 直播封面图 + PageUrl string `json:"page_url"` // 页面url + ImgUrlCompressed string `json:"img_url_compressed"` // 封面压缩后的路径 + CommentCount int `json:"comment_count"` // 评论数量 + IsTakegoods int `json:"is_takegoods"` // 带货开关:1开,0关 + Takegoods string `json:"takegoods"` // 带货商品分组ID + PaymentType int `json:"payment_type"` // 付费类型:1-免费、2-单笔、3-付费产品包 + IsPublic int `json:"is_public"` // 是否公开售卖,1公开,0不公开 + IsStopSell int `json:"is_stop_sell"` // 是否停售,0-否、1-是 + IsTranscode int `json:"is_transcode"` // 视频是否转码,0未转码,1已转码,2转码失败 + PiecePrice int `json:"piece_price"` // 单笔价格或专栏价格(分) + LinePrice int `json:"line_price"` // 划线价(分) + HavePassword int `json:"have_password"` // 该资源是否需要密码 + AliveType int `json:"alive_type"` // 直播类型:0-语音,1-视频,2-推流,3-ppt + PurchaseCount int `json:"purchase_count"` // 订阅量 + RewardSum int `json:"reward_sum"` // 打赏金额 + IsBan int `json:"is_ban"` // 强制封禁:0-否 1-是 + OnShelf int `json:"on_shelf"` // 强制下架:0-否 1-是 + RecycleBinState int `json:"recycle_bin_state"` // 上下架状态:0-上架,1-下架 + PushState int `json:"push_state"` // 推流状态:0断流,1推流中,2推流未开始 + State int `json:"state"` // 直播状态:0-可见,1-关闭,2-删除 + StartAt string `json:"start_at"` // 上架时间 + ZbStartAt string `json:"zb_start_at"` // 直播开始时间 + ManualStopAt string `json:"manual_stop_at"` // 手动结束直播时间 + SourceShopName string `json:"source_shop_name"` // 转播店铺名称 + MaterialState int `json:"material_state"` // 素材状态 + VideoLength int `json:"video_length"` // 视频时长(秒) + AliveState int `json:"alive_state"` // 直播状态 + AliveMode int `json:"alive_mode"` // 直播模式:0传统横屏,1竖屏直播(沉浸全屏) + CreateMode int `json:"create_mode"` // 创建类型:0-自创建,1-转播创建 + IsRoundTableOn int `json:"is_round_table_on"` // 圆桌会议功能是否开启 + QueryPackageList string `json:"query_package_list"` // 关联商品(可能为JSON字符串) + CourseExpire CourseExpire `json:"course_expire"` // 售卖有效期 + LikeNum int `json:"like_num"` // 直播点赞数 +} + +// CourseExpire 售卖有效期 +type CourseExpire struct { + PeriodType int `json:"period_type"` // 有效期类型:0永久,1固定,2自定义 + PeriodValue string `json:"period_value"` // 自定义有效时长 + IsAllowRepeatPurchase int `json:"is_allow_repeat_purchase"` // 是否允许重复购买:1是,0否 +} + +type GetAliveListResponseData struct { + TotalCount int `json:"total_count"` + LiveList []Alive `json:"live_list"` +} + +type GetAliveListResponse struct { + BaseResponse + + Data GetAliveListResponseData `json:"data"` +} + +type GetAliveDetailRequest struct { + Id string `json:"id"` +} + +// ResourceInfo 资源信息 +type ResourceInfo struct { + AppId string `json:"app_id"` // 店铺id + Id string `json:"id"` // 直播ID + Title string `json:"title"` // 直播标题 + Summary string `json:"summary"` // 直播简介 + Descrb string `json:"descrb"` // 直播详情(纯文本) + AliveType int `json:"alive_type"` // 直播类型:0-语音,1-录播直播,2-推流直播 + AliveState int `json:"alive_state"` // 直播状态:0-未开始,1-直播中,2-已结束 + State int `json:"state"` // 删除状态:0-可见,1-关闭,2-删除 + PushState int `json:"push_state"` // 推流状态:0-断流,1-推流中,2-推流未开始 + IsTranscode int `json:"is_transcode"` // 转码状态:0-转码中,1-转码完成,2-转码失败 + ManualStopAt string `json:"manual_stop_at"` // 手动结束时间 + ZbStartAt string `json:"zb_start_at"` // 预设直播开始时间 + ZbStopAt int `json:"zb_stop_at"` // 预设直播时长(秒) + ImgMaterialId string `json:"img_material_id"` // 详情封面图素材ID + AliveImgMaterialId string `json:"alive_img_material_id"` // 宣传封面图素材ID + AliveroomImgMaterialId string `json:"aliveroom_img_material_id"` // 暖场封面图素材ID + WarmUpVideoCoverMaterialId string `json:"warm_up_video_cover_material_id"` // 暖场视频封面图素材ID + WarmUpVideoMaterialId string `json:"warm_up_video_material_id"` // 暖场视频素材ID + AliveVideoMaterialId string `json:"alive_video_material_id"` // 录播视频素材ID + IosAppletDesc IosAppletDesc `json:"ios_applet_desc"` // 苹果端小程序信息调整 +} + +// IosAppletDesc 苹果端小程序信息 +type IosAppletDesc struct { + Title string `json:"title"` // 直播标题 + Summary string `json:"summary"` // 直播简介 + Descrb string `json:"descrb"` // 直播详情 + State int `json:"state"` // 显示设置:0-显示,1-不显示 + ImgMaterialId string `json:"img_material_id"` // 直播封面链接素材ID + AliveImgMaterialId string `json:"alive_img_material_id"` // 宣传封面图素材ID +} + +// ModuleInfo 配置信息 +type ModuleInfo struct { + IsLookback int `json:"is_lookback"` // 是否开启回放:0-开启,1-关闭 + PlayFastStateSwitch int `json:"play_fast_state_switch"` // 回放是否允许倍速/快进:0-允许,1-禁止 + AliveMode int `json:"alive_mode"` // 直播模式:0-横屏,1-竖屏 + ExpireType int `json:"expire_type"` // 回放有效期设置:1-永久,2-限时 + Expire string `json:"expire"` // 回放过期时间 + WarmUp int `json:"warm_up"` // 暖场设置:1-暖场图,2-暖场视频 + IsOpenCompleteTime int `json:"is_open_complete_time"` // 是否开启完成条件:0-关闭,1-开启 + CompleteTime int `json:"complete_time"` // 最短学习时间(分钟) + IsContactOn int `json:"is_contact_on"` // 是否开启联系学员:0-关闭,1-开启 +} + +// GoodsInfo 商品信息 +type GoodsInfo struct { + SaleType int `json:"sale_type"` // 售卖类型:1-单独售卖,2-关联售卖 + PaymentType int `json:"payment_type"` // 支付类型:1-免费,2-收费,3-加密,4-指定学员,5-仅关联上级 + PiecePrice int `json:"piece_price"` // 价格(分) + LinePrice int `json:"line_price"` // 划线价(分) + ResourcePassword string `json:"resource_password"` // 密码 + RecycleBinState int `json:"recycle_bin_state"` // 上下架:1-下架,0-上架 + StartAt string `json:"start_at"` // 定时上架时间 + IsStopSell int `json:"is_stop_sell"` // 是否停售:0-否,1-是 + GoodsSn string `json:"goods_sn"` // 商品编码 + State int `json:"state"` // 商品状态:0-可见,1-隐藏,2-删除 +} + +// RelationInfo 关联信息 +type RelationInfo struct { + Package []string `json:"package"` // 资源id数组 + AttachGoods []string `json:"attach_goods"` // 预留字段 + Tags []string `json:"tags"` // 预留字段 +} + +// RoleInfoItem 讲师信息 +type RoleInfoItem struct { + RoleName string `json:"role_name"` // 自定义身份标签 + UserId string `json:"user_id"` // 用户id + Nickname string `json:"nickname"` // 用户昵称 + Avator string `json:"avator"` // 用户头像 + IsCanExceptional int `json:"is_can_exceptional"` // 是否接受打赏:1-接受,0-不接受 +} +type GetAliveDetailResponseData struct { + ResourceInfo ResourceInfo `json:"resource_info"` // 资源信息 + ModuleInfo ModuleInfo `json:"module_info"` // 配置信息 + GoodsInfo GoodsInfo `json:"goods_info"` // 商品信息 + RelationInfo RelationInfo `json:"relation_info"` // 关联信息 + RoleInfo []RoleInfoItem `json:"role_info"` // 讲师信息列表 +} +type GetAliveDetailResponse struct { + BaseResponse + + Data GetAliveDetailResponseData `json:"data"` +} + +// MARK 获取直播列表 +func (client *Client) GetAliveList(req *GetAliveListRequest) (*GetAliveListResponse, error) { + paramsMap, err := ToMap(req) + if err != nil { + return nil, err + } + + respByte, err := client.CurlDo(http.MethodPost, GetAliveListUrl, paramsMap) + if err != nil { + return nil, err + } + + resp := GetAliveListResponse{} + err = json.Unmarshal(respByte, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +// MARK 获取直播详情 +func (client *Client) GetAliveDetail(req *GetAliveDetailRequest) (*GetAliveDetailResponse, error) { + paramsMap, err := ToMap(req) + if err != nil { + return nil, err + } + + respByte, err := client.CurlDo(http.MethodPost, GetAliveDetailUrl, paramsMap) + if err != nil { + return nil, err + } + + resp := GetAliveDetailResponse{} + err = json.Unmarshal(respByte, &resp) + if err != nil { + return nil, err + } + + return &resp, nil +} diff --git a/service/camp.go b/service/camp.go new file mode 100644 index 0000000..6f33d78 --- /dev/null +++ b/service/camp.go @@ -0,0 +1,54 @@ +package service + +import ( + "encoding/json" + "net/http" +) + +type GetCampTaskListRequest struct { + TermId string `json:"term_id"` // 营期 ID +} + +type GetCampTaskListResponseData struct { + AppId string `json:"app_id"` // 店铺 ID + CampId string `json:"camp_id"` // 训练营 ID + TermId string `json:"term_id"` // 营期 ID + Id string `json:"id"` // 节点 ID + Type int `json:"type"` // 节点类型 0-章节 1-学习任务 + Pid string `json:"pid"` // 学习任务对应的章节ID,0-代表该节点为章节 + Title string `json:"title"` // 节点名称 + ResourceId string `json:"resource_id"` // 学习任务资源 ID,为空代表为章节 + ResourceType int `json:"resource_type"` // 学习任务资源类型,为-1代表为章节 + IsTry int `json:"is_try"` // 学习任务资源是否试看,0-不试看 1-试看 + OrderWeight int `json:"order_weight"` // 排序,排在第几位 + NeedPush int `json:"need_push"` // 是否推送:0-不推送,1-推送 + UnlockTime string `json:"unlock_time"` // 节点的解锁时间(营期为日期解锁时间),也是节点解锁的通知时间 + PushState int `json:"push_state"` // 推送状态:0-等待推送 1-推送中 2-推送成功 3-推送失败 + CreatedAt string `json:"created_at"` // 创建时间 + UpdatedAt string `json:"updated_at"` // 更新时间 +} + +type GetCampTaskListResponse struct { + BaseResponse + Data GetCampTaskListResponseData `json:"data"` +} + +// MARK 获取训练营营期任务 +func (client *Client) GetCampTaskList(req *GetCampTaskListRequest) (*GetCampTaskListResponse, error) { + paramsMap, err := ToMap(req) + if err != nil { + return nil, err + } + + respByte, err := client.CurlDo(http.MethodPost, GetCampTaskListURL, paramsMap) + if err != nil { + return nil, err + } + resp := GetCampTaskListResponse{} + err = json.Unmarshal(respByte, &resp) + if err != nil { + return nil, err + } + return &resp, nil + +} diff --git a/service/class.go b/service/class.go new file mode 100644 index 0000000..4830c38 --- /dev/null +++ b/service/class.go @@ -0,0 +1,54 @@ +package service + +import ( + "encoding/json" + "net/http" +) + +type GetClassListRequest struct { + PageIndex *int `json:"page_index,omitempty"` // 第几页,默认值1 + PageSize *int `json:"page_size,omitempty"` // 每页条数,最大50条,默认值10 + SearchContent *string `json:"search_content,omitempty"` // 班课名称关键字 + ClassType *int `json:"class_type,omitempty"` // 班课类型:1-系列课 2-单节课,其他值表示全部 +} + +type Class struct { + Id string `json:"id"` + AppId string `json:"app_id"` + Title string `json:"title"` + ClassType int `json:"class_type"` + DisplayState int `json:"display_state"` + JoinCount int `json:"join_count"` + Address string `json:"address"` + CoverUrl string `json:"cover_url"` +} + +type GetClassListResponseData struct { + PageInfoResponse + List []Class `json:"list"` +} + +type GetClassListResponse struct { + BaseResponse + + Data GetClassListResponseData `json:"data"` +} + +// MARK 获取班课列表 +func (client *Client) GetClassList(req *GetClassListRequest) (*GetClassListResponse, error) { + paramsMap, err := ToMap(req) + if err != nil { + return nil, err + } + + respByte, err := client.CurlDo(http.MethodPost, GetClassListUrl, paramsMap) + if err != nil { + return nil, err + } + resp := GetClassListResponse{} + err = json.Unmarshal(respByte, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} diff --git a/service/client.go b/service/client.go new file mode 100644 index 0000000..13d091c --- /dev/null +++ b/service/client.go @@ -0,0 +1,98 @@ +package service + +import ( + "encoding/json" + "io" + "net/http" + "net/url" + "strings" +) + +const ( + contentTypeApplicationJson = "application/json" +) + +var ( + AccessTokenUrl = "https://api.xiaoe-tech.com/token" // 获取access_token + + RegisterUserUrl = "https://api.xiaoe-tech.com/xe.user.register/1.0.0" // 注册用户 + UpdateUserUrl = "https://api.xiaoe-tech.com/xe.user.info.update/1.0.0" // 修改用户信息 + GetUserInfoUrl = "https://api.xiaoe-tech.com/xe.user.info.get/1.0.0" // 获取用户信息 + GetUserListUrl = "https://api.xiaoe-tech.com/xe.user.batch.get/2.0.0" // 获取用户列表 + + GetAliveListUrl = "https://api.xiaoe-tech.com/xe.alive.list.get/2.0.0" // 获取直播列表 + GetAliveDetailUrl = "https://api.xiaoe-tech.com/xe.alive.detail.get/2.0.0" // 获取直播详情 + + GetClassListUrl = "https://api.xiaoe-tech.com/xe.big_class.list.get/1.0.0" // 获取班课列表 + + GetGoodsListUrl = "https://api.xiaoe-tech.com/xe.goods.list.get/4.0.0" // 获取商品列表 + GetGoodsInfoUrl = "https://api.xiaoe-tech.com/xe.goods.detail.get/4.0.0" // 获取商品详情 + + GetCampTaskListURL = "https://api.xiaoe-tech.com/xe.camp.task.list/1.0.0" // 获取训练营营期任务 + + GetCourseListUrl = "https://api.xiaoe-tech.com/xe.course.course.list/1.0.0" // 获取课程列表 + GetSubCourseListUrl = "https://api.xiaoe-tech.com/xe.course.sub.course.list/1.0.0" // 获取子课程列表 + GetCourseChapterListUrl = "https://api.xiaoe-tech.com/xe.course.course.chapter.get/1.0.0" // 获取课程章节列表 + + OrderDeliveryUrl = "https://api.xiaoe-tech.com/xe.order.delivery/2.0.0" // 用户开通产品包权益 + OrderDeliveryUrlV1 = "https://api.xiaoe-tech.com/xe.order.delivery/1.0.0" // 用户开通产品包权益1.0 + PermissionCheckUrl = "https://api.xiaoe-tech.com/xe.user.permission.check/1.0.0" // 查询用户资源权益 + PurchaseDeleteUrl = "https://api.xiaoe-tech.com/xe.purchase.delete/1.0.0" // 取消订购 +) + +// NewClient +func NewClient(AccessTokenManager AccessTokenManager) (client *Client) { + return &Client{ + AccessTokenManager: AccessTokenManager, + HttpClient: http.DefaultClient, + } +} + +type Client struct { + AccessTokenManager AccessTokenManager + HttpClient *http.Client +} + +// CurlDo 执行 请求 +func (client *Client) CurlDo(method string, methodUrl string, paramsMap map[string]interface{}) (resp []byte, err error) { + // 添加 access_token + accessToken, err := client.AccessTokenManager.GetAccessToken() + if err != nil { + return + } + params := make(map[string]interface{}) + params["access_token"] = accessToken + + for k, v := range paramsMap { + params[k] = v + } + + str, _ := json.Marshal(params) + payload := strings.NewReader(string(str)) + + req, _ := http.NewRequest(method, methodUrl, payload) + + // 检测URL + if !strings.HasPrefix(req.URL.String(), "http") { + parse, _ := url.Parse(AccessTokenUrl) + req.URL.Host = parse.Host + req.URL.Scheme = parse.Scheme + } + + // 默认 Header Content-Type + if req.Method == http.MethodPost && req.Header.Get("Content-Type") == "" { + req.Header.Set("Content-Type", contentTypeApplicationJson) + } + + response, err := client.HttpClient.Do(req) + if err != nil { + return + } + defer response.Body.Close() + + resp, err = io.ReadAll(response.Body) + if err != nil { + return + } + return +} diff --git a/service/common.go b/service/common.go new file mode 100644 index 0000000..e3f9ae9 --- /dev/null +++ b/service/common.go @@ -0,0 +1,32 @@ +package service + +import "encoding/json" + +type BaseResponse struct { + Code int `json:"code"` + Msg string `json:"msg"` +} + +type PageInfoResponse struct { + Total int `json:"total"` // 查询结果记录数 + PageIndex int `json:"page_index"` // 当前页码 + PageSize int `json:"page_size"` // 每页显示数量 + PageCount int `json:"page_count"` // 查询结果页数 +} + +// 转换函数 +func ToMap(obj interface{}) (map[string]interface{}, error) { + // 序列化为 JSON 字节 + bytes, err := json.Marshal(obj) + if err != nil { + return nil, err + } + + // 反序列化为 map + var result map[string]interface{} + err = json.Unmarshal(bytes, &result) + if err != nil { + return nil, err + } + return result, nil +} diff --git a/service/course.go b/service/course.go new file mode 100644 index 0000000..1943208 --- /dev/null +++ b/service/course.go @@ -0,0 +1,179 @@ +package service + +import ( + "encoding/json" + "net/http" +) + +type GetCourseListRequest struct { + SearchContent *string `json:"search_content,omitempty"` // 课程名字模糊搜索 + OrderBy *string `json:"order_by,omitempty"` // 是否需要根据创建时间排序(是:modify,否:'') + OrderType *int `json:"order_type,omitempty"` // 排序类型(1:降序,2:升序) + PageIndex *int `json:"page_index,omitempty"` // 当前页 + PageSize *int `json:"page_size,omitempty"` // 每页条数(1~50条) + SaleStatus *int `json:"sale_status,omitempty"` // 上架状态(-1:全部 0 :下架 1:上架 2:待上架) + CreatedSource *int `json:"created_source,omitempty"` // 课程创建来源(0:全部,1:课程,2:圈子) + Tags []string `json:"tags,omitempty"` // 商品分组id数组 +} + +type Course struct { + ResourceId string `json:"resource_id"` // 课程id + ResourceType int `json:"resource_type"` // 资源种类(此接口返回必定是50,表示鹅课程) + Title string `json:"title"` // 课程名称 + ImgUrl string `json:"img_url"` // 课程封面图 + ImgUrlCompressed string `json:"img_url_compressed"` // 课程封面压缩图 + UserCount int `json:"user_count"` // 用户数 + ResourceCnt int `json:"resource_cnt"` // 内容数 + InteractiveCnt int `json:"interactive_cnt"` // 互动数 + LastUpdatedAt string `json:"last_updated_at"` // 最新修改时间 + CurriculumTime string `json:"curriculum_time"` // 开课时间 + CurriculumEndTime string `json:"curriculum_end_time"` // 开课结束时间 + CreatedSource int `json:"created_source"` // 创建来源 1-课程 2-圈子 + Price int `json:"price"` // 商品价格 + LinePrice int `json:"line_price"` // 划线价格 + IsFree int `json:"is_free"` // 是否免费(0-收费 1-免费) + IsPublic int `json:"is_public"` // 是否公开售卖(0-不公开 1-公开) + IsPassword int `json:"is_password"` // 是否加密(0-不加密 1--加密) + IsStopSell int `json:"is_stop_sell"` // 是否停售:0否 1是 + IsDisplay int `json:"is_display"` // 是否显示:0否(隐藏状态) 1是(显示状态) + SellType int `json:"sell_type"` // 售卖方式:1 = 独立售卖,2 = 关联售卖(商品放入专栏/会员/训练营中售卖) + SaleAt string `json:"sale_at"` // 上架时间 + SaleStatus int `json:"sale_status"` // 上架状态: 0下架 1上架 2(定时上架还未上架阶段)待上架 + CanSoldStart string `json:"can_sold_start"` // 可售开始时间 + CanSoldEnd string `json:"can_sold_end"` // 可售结束时间 + GoodsType int `json:"goods_type"` // 商品种类(1免费2付费3加密4指派5非单卖) + IsBan int `json:"is_ban"` // 商品是否被封禁:0 = 否,1 = 是 + Position int `json:"position"` // 在列表中所在的位置 + IsJoinMarketAct bool `json:"is_join_market_act"` // 是否参与营销活动 + CreatedByResourceInfo CreatedByResourceInfo `json:"created_by_resource_info"` // 创建来源的信息 + Period Period `json:"period"` // 有效期 +} + +type CreatedByResourceInfo struct { + ResourceId string `json:"resource_id"` // 创建来源资源id + ResourceType int `json:"resource_type"` // 创建来源资源种类 + Title string `json:"title"` // 创建来源资源名称 +} + +type Period struct { + PeriodType int `json:"period_type"` // 有效期的种类(-1永久有效,大于等于0是真实有效期) + PeriodValue string `json:"period_value"` // 有效期的值(单位是s) +} + +type GetCourseListResponseData struct { + List []Course `json:"list"` + Total int `json:"total"` +} + +type GetCourseListResponse struct { + BaseResponse + Data GetCourseListResponseData `json:"data"` +} + +type GetSubCourseListRequest struct { + CourseId string `json:"course_id"` // 必填,父课程id + Page *int `json:"page,omitempty"` // 可选,当前页,不传或0则不分页 + PageSize *int `json:"page_size,omitempty"` // 可选,每页条数(1~50),不传或0则不分页 +} + +type GetSubCourseListResponseData struct { + SubCourseId string `json:"sub_course_id"` // 子课程id + ChapterTitle string `json:"chapter_title"` // 子课程名称 + SubCourseImg string `json:"sub_course_img"` // 子课程封面图 + SortValue int `json:"sort_value"` // 排序值(升序排序) + TaskCount int `json:"task_count"` // 小节数 + SectionCount int `json:"section_count"` // 章节数 +} + +type GetSubCourseListResponse struct { + BaseResponse + + Data []GetSubCourseListResponseData `json:"data"` +} + +type GetCourseChapterListRequest struct { + CourseId string `json:"course_id"` // 课程id +} + +type CourseChapter struct { + ChapterId string `json:"chapter_id"` // 章节id + ChapterTitle string `json:"chapter_title"` // 章节名称 + ChapterType int `json:"chapter_type"` // 章节类型 0-无 1-章 2-节 + ResourceType int `json:"resource_type"` // 关联资源种类 + Children []CourseChapter `json:"children"` // 子章节集合 + Id int `json:"id"` // 主键id + IsElective int `json:"is_elective"` // 是否选修:0-非选修;1-选修 + IsTry int `json:"is_try"` // 是否在目录中设置试看 0-否 1-是 + PId string `json:"p_id"` // 父id(所属章节id) + SectionNum int `json:"section_num"` // 子小节数 + SortValue int `json:"sort_value"` // 排序值(升序排序) + SubCourseId string `json:"sub_course_id"` // 所属子课程id + SubCourseSortValue int `json:"sub_course_sort_value"` // 所属子课程排序值(升序排序) +} +type GetCourseChapterListResponseData struct { + List []CourseChapter `json:"list"` + Total int `json:"total"` +} + +type GetCourseChapterListResponse struct { + BaseResponse + + Data GetCourseChapterListResponseData `json:"data"` +} + +// MARK 获取课程列表 +func (client *Client) GetCourseList(req *GetCourseListRequest) (*GetCourseListResponse, error) { + paramsMap, err := ToMap(req) + if err != nil { + return nil, err + } + + respByte, err := client.CurlDo(http.MethodPost, GetCourseListUrl, paramsMap) + if err != nil { + return nil, err + } + resp := GetCourseListResponse{} + err = json.Unmarshal(respByte, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +// MARK 获取子课程列表 +func (client *Client) GetSubCourseList(req *GetSubCourseListRequest) (*GetSubCourseListResponse, error) { + paramsMap, err := ToMap(req) + if err != nil { + return nil, err + } + + respByte, err := client.CurlDo(http.MethodPost, GetSubCourseListUrl, paramsMap) + if err != nil { + return nil, err + } + resp := GetSubCourseListResponse{} + err = json.Unmarshal(respByte, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +// MARK 获取课程章节列表 +func (client *Client) GetCourseChapterList(req *GetCourseChapterListRequest) (*GetCourseChapterListResponse, error) { + paramsMap, err := ToMap(req) + if err != nil { + return nil, err + } + + respByte, err := client.CurlDo(http.MethodPost, GetCourseChapterListUrl, paramsMap) + if err != nil { + return nil, err + } + resp := GetCourseChapterListResponse{} + err = json.Unmarshal(respByte, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} diff --git a/service/goods.go b/service/goods.go new file mode 100644 index 0000000..988dd7e --- /dev/null +++ b/service/goods.go @@ -0,0 +1,257 @@ +package service + +import ( + "encoding/json" + "net/http" +) + +type GetGoodsListRequest struct { + Page int `json:"page,omitempty"` // 分页,默认第一页 + PageSize int `json:"page_size,omitempty"` // 页数 默认10条,最大100 + GoodsName *string `json:"goods_name,omitempty"` // 商品名称(支持模糊) + ResourceType *int `json:"resource_type,omitempty"` // 资源类型 + HasDistribute *int `json:"has_distribute,omitempty"` // 是否参与推广 + SaleStatus *int `json:"sale_status,omitempty"` // 上架状态 + IsReturnDeleted *int `json:"is_return_deleted,omitempty"` // 是否删除 + IsReturnForbid *int `json:"is_return_forbid,omitempty"` // 是否被封禁 + IsReturnStopSell *int `json:"is_return_stop_sell,omitempty"` // 是否停售 + IsReturnSellType *int `json:"is_return_sell_type,omitempty"` // 售卖方式 + IsReturnDisplay *int `json:"is_return_display,omitempty"` // 是否显示 + IsReturnSellMode []int `json:"is_return_sell_mode,omitempty"` // 售卖类型数组 + IsReturnZeroPrice *int `json:"is_return_zero_price,omitempty"` // 是否返回0元商品 + IsReturnPassword *int `json:"is_return_password,omitempty"` // 是否加密 + IsReturnPublic *int `json:"is_return_public,omitempty"` // 是否公开售卖 + StartTime *string `json:"start_time,omitempty"` // 创建开始时间 + EndTime *string `json:"end_time,omitempty"` // 创建结束时间 +} + +type Goods struct { + AppId string `json:"app_id"` // 店铺ID + Id int `json:"id"` // id + ResourceId string `json:"resource_id"` // 资源id(唯一值) + SpuId string `json:"spu_id"` // 统一商品id + SpuType string `json:"spu_type"` // 统一商品类型 + GoodsCategoryId string `json:"goods_category_id"` // 商品分类id + GoodsName string `json:"goods_name"` // 商品名称 + GoodsImg []string `json:"goods_img"` // 商品封面图(默认封面图) + CustomCover string `json:"custom_cover"` // 主图视频自定义封面 + SellType int `json:"sell_type"` // 付费类型:1独立售卖,2关联售卖 + PriceLow int `json:"price_low"` // 商品最低价(单位:分) + PriceHigh int `json:"price_high"` // 商品高价(单位:分) + PriceLine int `json:"price_line"` // 划线价(单位:分) + VisitNum int `json:"visit_num"` // 访问量 + GoodsTag string `json:"goods_tag"` // 商品标签 + GoodsTagIsShow int `json:"goods_tag_is_show"` // 商品标签是否展示 + SaleStatus int `json:"sale_status"` // 上架状态:0下架 1上架 2待上架 + IsTimingSale int `json:"is_timing_sale"` // 是否定时上架:1是 0否 + TimingSale string `json:"timing_sale"` // 定时上架时间 + SaleAt string `json:"sale_at"` // 上架的时间 + HasDistribute int `json:"has_distribute"` // 是否参与推广分销:0否 1是 + VideoImgUrl string `json:"video_img_url"` // 主图视频封面url + IsGoodsPackage int `json:"is_goods_package"` // 是否带货:0否 1是 + IsDisplay int `json:"is_display"` // 是否显示:0否(隐藏) 1是(显示) + IsStopSell int `json:"is_stop_sell"` // 是否停售:0否 1是 + IsForbid int `json:"is_forbid"` // 商品是否被封禁:0否 1是 + IsIgnore int `json:"is_ignore"` // 商品是否被忽略:0否 1是 + LimitPurchase int `json:"limit_purchase"` // 限购数量 + StockDeductMode int `json:"stock_deduct_mode"` // 扣库存方式:0付款减库存 1拍下减库存 + AppraiseNum int `json:"appraise_num"` // 评价数 + ShowStock int `json:"show_stock"` // 是否展示库存:0不展示 1展示 + IsBest int `json:"is_best"` // 是否精品:0否 1是 + IsHot int `json:"is_hot"` // 是否热销产品:0否 1是 + IsNew int `json:"is_new"` // 是否新品:0否 1是 + IsRecom int `json:"is_recom"` // 是否推荐:0否 1是 + DistributionPattern int `json:"distribution_pattern"` // 配送方式 + Freight int `json:"freight"` // 运费(单位:分) + AuditReason string `json:"audit_reason"` // 审核原因 + AuditTime string `json:"audit_time"` // 审核时间 + AuditUserId string `json:"audit_user_id"` // 审核人user_id + SpuExtend string `json:"spu_extend"` // 业务侧扩展字段 + ParentSpuId string `json:"parent_spu_id"` // 父级的spu_id + ParentAppId string `json:"parent_app_id"` // 父级的app_id + IsUniformFreight int `json:"is_uniform_freight"` // 是否统一运费:1统一运费 2运费模板 + FreightTemplateId int `json:"freight_template_id"` // 运费模板ID + ImgUrlCompressed string `json:"img_url_compressed"` // 压缩后的列表配图url + IsDeleted int `json:"is_deleted"` // 是否删除:0正常 1已删除 + CreatedAt string `json:"created_at"` // 创建时间 + UpdatedAt string `json:"updated_at"` // 更新时间 + Attr []interface{} `json:"attr"` // 属性信息(可根据实际结构定义) + Extend []interface{} `json:"extend"` // 扩展信息 +} + +type GetGoodsListResponseData struct { + CurrentPage int `json:"current_page"` + List []Goods `json:"list"` + Total int `json:"total"` // 总数 +} + +type GetGoodsListResponse struct { + BaseResponse + Data GetGoodsListResponseData `json:"data"` +} + +type GetGoodsInfoResource struct { + Ids []string `json:"ids"` // 资源ID集合,可使用查询商品列表2.0获取resource_id的值 + Type int `json:"type"` // 资源类型,可使用查询商品列表2.0获取resource_type的值 +} + +type GetGoodsInfoRequest struct { + Resources []GetGoodsInfoResource `json:"resources"` + IsReturnDeleted *int `json:"is_return_deleted,omitempty"` // 是否删除 0正常 1已删除 不传默认返回所有数据 + IsReturnForbid *int `json:"is_return_forbid,omitempty"` // 商品是否被封禁:0 = 否,1 = 是 不传默认返回所有数据 + IsCache *bool `json:"is_cache,omitempty"` // 是否查询缓存:false = 否,true = 是 默认是 + Body *string `json:"body,omitempty"` // 传“stock”查询库存信息,传“sku”查商品规格相关信息,传“attr”查属性相关信息,不传则不查附属信息 +} + +type GetGoodsInfoResponseData struct { + ID int `json:"id"` // 自增主键 + AppId string `json:"app_id"` // 店铺ID + SkuCostPrice int `json:"sku_cost_price"` // sku成本价,单位:分 + SkuVolume string `json:"sku_volume"` // sku体积 + SkuSpecCode string `json:"sku_spec_code"` // sku规格编码 + SkuWeight string `json:"sku_weight"` // sku重量 + ResourceId string `json:"resource_id"` // 资源id + ResourceType int `json:"resource_type"` // 资源类型 + SpuId string `json:"spu_id"` // 统一商品id + SpuType string `json:"spu_type"` // 统一商品类型 + SpuTypeName string `json:"spu_type_name"` // 统一商品类型名称 + GoodsSn string `json:"goods_sn"` // 商品编号(商家录入) + GoodsCategoryId string `json:"goods_category_id"` // 商品分类id + WxGoodsCategoryId string `json:"wx_goods_category_id"` // 微信商品分类id + GoodsName string `json:"goods_name"` // 商品名称 + GoodsImg []string `json:"goods_img"` // 商品封面图(默认封面图) + CustomCover string `json:"custom_cover"` // 主图视频自定义封面 + DetailCosUrl string `json:"detail_cos_url"` // 富文本的cos链接 + GoodsBriefText string `json:"goods_brief_text"` // 商品简介 + GoodsDetailText string `json:"goods_detail_text"` // 商品详情/买点 + SellType int `json:"sell_type"` // 付费类型:1独立售卖,2关联售卖 + PriceLow int `json:"price_low"` // 商品最低价(单位:分) + PriceHigh int `json:"price_high"` // 商品高价(单位:分) + PriceLine int `json:"price_line"` // 划线价(单位:分) + GoodsTag string `json:"goods_tag"` // 商品标签 + GoodsTagIsShow int `json:"goods_tag_is_show"` // 商品标签是否展示 0不展示 1展示 + SaleStatus int `json:"sale_status"` // 上架状态 0下架 1上架 2待上架 + IsTimingSale int `json:"is_timing_sale"` // 是否定时上架 1是 0否 + IsTimingOff int `json:"is_timing_off"` // 是否定时下架 1是 0否 + TimingSale string `json:"timing_sale"` // 定时上架的时间 + TimingOfftime string `json:"timing_offtime"` // 实际下架的时间 + TimingOff string `json:"timing_off"` // 定时下架的时间 + SaleAt string `json:"sale_at"` // 上架的时间 + HasDistribute int `json:"has_distribute"` // 是否参与推广分销 0否 1是 + IsGoodsPackage int `json:"is_goods_package"` // 是否带货 0否 1是 + IsDisplay int `json:"is_display"` // 是否显示 0否(隐藏) 1是(显示) + IsStopSell int `json:"is_stop_sell"` // 是否停售 0否 1是 + IsForbid int `json:"is_forbid"` // 商品是否被封禁 0否 1是 + IsIgnore int `json:"is_ignore"` // 商品是否被忽略 0否 1是 + IsDeleted int `json:"is_deleted"` // 是否删除 0正常 1已删除 + CreatedAt string `json:"created_at"` // 创建时间 + UpdatedAt string `json:"updated_at"` // 更新时间 + VideoImgUrl string `json:"video_img_url"` // 实物商品主图视频封面url + LimitPurchase int `json:"limit_purchase"` // 限购数量 + StockDeductMode int `json:"stock_deduct_mode"` // 扣库存方式 0付款减库存 1拍下减库存 + AppraiseNum int `json:"appraise_num"` // 评价数 + ShowStock int `json:"show_stock"` // 是否展示库存 0不展示 1展示 + IsBest int `json:"is_best"` // 是否精品 0否 1是 + IsHot int `json:"is_hot"` // 是否热销产品 0否 1是 + IsNew int `json:"is_new"` // 是否新品 0否 1是 + IsRecom int `json:"is_recom"` // 是否推荐 0否 1是 + Freight int `json:"freight"` // 运费(单位:分) + ImgUrlCompressed string `json:"img_url_compressed"` // 压缩后的列表配图url + FreightTemplateId int `json:"freight_template_id"` // 运费模板ID + IsUniformFreight int `json:"is_uniform_freight"` // 是否统一运费 1统一运费 2运费模板 + IsPublic int `json:"is_public"` // 是否公开售卖 0不公开 1公开 + IsPassword int `json:"is_password"` // 是否加密 0不加密 1加密 + IsFree int `json:"is_free"` // 是否免费 0收费 1免费 + CanSoldStart string `json:"can_sold_start"` // 可售开始时间 + CanSoldEnd string `json:"can_sold_end"` // 可售结束时间 + IsSingle int `json:"is_single"` // 是否为单品 0否 1是 + Period int `json:"period"` // 有效期 -1永久,>=0真实有效期 + DistributionPattern int `json:"distribution_pattern"` // 配送方式 + SellMode int `json:"sell_mode"` // 售卖类型 1-自营 2-内容市场 + PeriodType int `json:"period_type"` // 有效期类型 0长期 1具体时间前 2有效期范围(秒) + PeriodValue string `json:"period_value"` // 有效期值 + Stock []StockItem `json:"stock"` // 库存信息 + Sku []SkuItem `json:"sku"` // sku信息 + Attr []interface{} `json:"attr"` // 属性信息 + Extend []interface{} `json:"extend"` // 扩展 + Pv int `json:"pv"` // 学员学习该资源的总次数 + Uv int `json:"uv"` // 学习该资源的学员数 + AttachCount int `json:"attach_count"` // 带货的数量 +} + +// StockItem 库存信息 +type StockItem struct { + SkuId string `json:"sku_id"` // 商品 SKU_ID + StockType int `json:"stock_type"` // 库存类型 0-数量购买,2-买断/免费 + SettingStock int `json:"setting_stock"` // 设置库存 + SellNum int `json:"sell_num"` // 售卖数量 + LeftNum int `json:"left_num"` // 剩余数量 + IsDeleted int `json:"is_deleted"` // 是否删除 0否 1是 + Remark string `json:"remark"` // 属性备注 +} + +// SkuItem sku信息 +type SkuItem struct { + SkuId string `json:"sku_id"` // 商品 SKU_ID + SkuBusinessId string `json:"sku_business_id"` // 资源侧的 SKU_ID + AttrData []interface{} `json:"attr_data"` // 属性信息 + SkuImg int `json:"sku_img"` // sku默认图片 + SkuName int `json:"sku_name"` // sku名字(文档写int,保持) + SkuDesc int `json:"sku_desc"` // 规格描述(文档写int) + SkuPrice int `json:"sku_price"` // 价格(单位:分) + SkuLinePrice string `json:"sku_line_price"` // 划线价 + LimitPurchase int `json:"limit_purchase"` // 限购数量 + LimitPurchaseType int `json:"limit_purchase_type"` // 限购类型 + SkuMinPurchase int `json:"sku_min_purchase"` // 起购数量 + PeriodType int `json:"period_type"` // 有效期类型 + PeriodValue string `json:"period_value"` // 有效期值 + ValueCount int `json:"value_count"` // 规格 1-单规格 2-多规格 + OrderSkuLimit int `json:"order_sku_limit"` // 下单是否受库存限制 0否 1是 + State int `json:"state"` // 上下架状态 1上架 0下架 + IsDefault int `json:"is_default"` // 是否默认 0否 1是 + IsDeleted int `json:"is_deleted"` // 是否删除 0否 1是 + IsFree int `json:"is_free"` // 是否免费 0否 1是 +} + +type GetGoodsInfoResponse struct { + BaseResponse + Data []GetGoodsInfoResponseData `json:"data"` +} + +// MKARK 获取商品列表 +func (client *Client) GetGoodsList(req *GetGoodsListRequest) (*GetGoodsListResponse, error) { + paramsMap, err := ToMap(req) + if err != nil { + return nil, err + } + + respByte, err := client.CurlDo(http.MethodPost, GetGoodsListUrl, paramsMap) + if err != nil { + return nil, err + } + resp := GetGoodsListResponse{} + err = json.Unmarshal(respByte, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +// MKARK 获取商品详情 +func (client *Client) GetGoodsInfo(req *GetGoodsInfoRequest) (*GetGoodsInfoResponse, error) { + paramsMap, err := ToMap(req) + if err != nil { + return nil, err + } + + respByte, err := client.CurlDo(http.MethodPost, GetGoodsInfoUrl, paramsMap) + if err != nil { + return nil, err + } + resp := GetGoodsInfoResponse{} + err = json.Unmarshal(respByte, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} diff --git a/service/purchase.go b/service/purchase.go new file mode 100644 index 0000000..b63b3b5 --- /dev/null +++ b/service/purchase.go @@ -0,0 +1,219 @@ +package service + +import ( + "encoding/json" + "net/http" +) + +type OrderDeliveryRequestData struct { + UserId string `json:"user_id"` // 必填 用户ID + WithPackage *int `json:"with_package,omitempty"` // 可选 是否发放商品打包售卖权益,默认0, 0-不发放 1-发放 + PayWay *string `json:"pay_way,omitempty"` // 可选 支付渠道,默认是0 0-线上微信,1-线上支付宝 + OutOrderId *string `json:"out_order_id,omitempty"` // 可选 商家侧关联外部订单号 + TransactionId *string `json:"transaction_id,omitempty"` // 可选 商家侧交易单号 + ProductInfos []ProductInfo `json:"product_infos"` // 必填 商品列表 最多20个 + Express *ExpressInfo `json:"express,omitempty"` // 可选快递配送信息(实物必填) +} + +// ProductInfo 商品信息 +type ProductInfo struct { + SpuId string `json:"spu_id"` // 必填 统一商品ID + SkuId *string `json:"sku_id,omitempty"` // 必填 统一商品规格ID + BuyNum *int `json:"buy_num,omitempty"` // 可选 购买份数 默认1 + DiscountPrice *int `json:"discount_price,omitempty"` // 可选 api优惠金额 单位分 + Period *int `json:"period,omitempty"` // 可选 有效期 单位秒 + PeriodTime *string `json:"period_time,omitempty"` // 可选 权益开始生效时间 +} + +// ExpressInfo 快递配送信息(当 express 存在时,内部字段均必填) +type ExpressInfo struct { + Receiver string `json:"receiver"` // 必填 收货人 + Phone string `json:"phone"` // 必填 收货人联系方式 + Province string `json:"province"` // 必填 收货人地址省份 + City string `json:"city"` // 必填 收货人地址城市 + County string `json:"county"` // 必填 收货人地址所在区 + Detail string `json:"detail"` // 必填 收货人地址详细地址 +} + +type OrderDeliveryRequest struct { + Data OrderDeliveryRequestData `json:"data"` +} + +type OrderDeliveryOrder struct { + Price int `json:"price"` + OrderId string `json:"order_id"` + PurchaseName string `json:"PurchaseName"` + CreatedAt string `json:"created_at"` +} + +type OrderDeliveryResponseData struct { + Orders []OrderDeliveryOrder `json:"orders"` +} + +type OrderDeliveryResponse struct { + BaseResponse + Data OrderDeliveryResponseData `json:"data"` +} + +type ResourcePurchaseCheckRequest struct { + UserId string `json:"user_id"` // 用户id + ResourceId string `json:"resource_id"` // 资源id +} + +type ResourcePurchaseCheckResponseData struct { + AuthState int `json:"auth_state"` // 是否拥有权益 0-否 1-是 +} + +type ResourcePurchaseCheckResponse struct { + BaseResponse + + Data ResourcePurchaseCheckResponseData `json:"data"` +} + +type PurchaseDeleteRequestData struct { + PaymentType string `json:"payment_type"` // 必填,购买方式:2-单笔、3-付费产品包、15-超级会员 + ResourceType *string `json:"resource_type,omitempty"` // 可选(单笔购买时必填),资源类型 + ResourceId *string `json:"resource_id,omitempty"` // 可选(单笔购买时必填),资源id + ProductId *string `json:"product_id,omitempty"` // 可选(产品包/超级会员时必填),产品包id + UserId string `json:"user_id"` // 必填,用户id +} + +type PurchaseDeleteRequest struct { + UserId string `json:"user_id"` // 用户id + Data PurchaseDeleteRequestData `json:"data"` +} + +type PurchaseDeleteResponseData struct { + Delete int `json:"delete"` // 1-订阅取消成功 +} + +type PurchaseDeleteResponse struct { + BaseResponse + + Data PurchaseDeleteResponseData `json:"data"` +} + +type OrderDeliveryRequestV1 struct { + UserId string `json:"user_id"` // 用户ID(顶层) + Data OrderDeliveryDataV1 `json:"data"` // 订单数据 +} + +// OrderDeliveryDataV1 订单数据 +type OrderDeliveryDataV1 struct { + PaymentType int `json:"payment_type"` // 必填,付费类型:2-单品,3-产品包,15-超级会员 + ResourceType int `json:"resource_type"` // 必填,资源类型 + ResourceId *string `json:"resource_id,omitempty"` // 单品ID(payment_type=2时必填) + ProductId *string `json:"product_id,omitempty"` // 产品包ID(payment_type=3时必填) + UserId string `json:"user_id"` // 必填,用户ID + OutOrderId *string `json:"out_order_id,omitempty"` // 可选,外部订单号 + PayWay *int `json:"pay_way,omitempty"` // 可选,支付渠道:0-线上微信,2-线上支付宝,1-未指定 + ChannelId *string `json:"channel_id,omitempty"` // 可选,渠道ID + ChannelInfo *string `json:"channel_info,omitempty"` // 可选,渠道来源 + Period *string `json:"period,omitempty"` // 可选,有效期(秒),超级会员必传 + PeriodTime *string `json:"period_time,omitempty"` // 可选,会员开始时间,超级会员必传 + Agent *string `json:"agent,omitempty"` // 可选,用户设备信息 + DiscountPrice *int `json:"discount_price,omitempty"` // 可选,API优惠金额(单位:分) + Source *int `json:"source,omitempty"` // 可选,开通知识带货商品必传10 +} + +// OrderDeliveryResponseV1 订单发货响应(V1版本) +type OrderDeliveryResponseV1 struct { + Code int `json:"code"` // 请求结果码,0表示成功 + Msg string `json:"msg"` // 描述信息 + Data OrderDeliveryResponseDataV1 `json:"data"` // 数据 +} + +// OrderDeliveryResponseDataV1 响应数据 +type OrderDeliveryResponseDataV1 struct { + Price int `json:"price"` // 商品价格(单位:分) + OrderId string `json:"order_id"` // 订单编号 + PurchaseName string `json:"purchase_name"` // 商品名称 + CreatedAt CreatedAt `json:"created_at"` // 创建时间 +} + +type CreatedAt struct { + Date string `json:"date"` + Timezone string `json:"timezone"` + TimezoneType int `json:"timezone_type"` +} + +// MARK 用户开通产品包权益2.0 +func (client *Client) OrderDelivery(req *OrderDeliveryRequest) (*OrderDeliveryResponse, error) { + paramsMap, err := ToMap(req) + if err != nil { + return nil, err + } + + respByte, err := client.CurlDo(http.MethodPost, OrderDeliveryUrl, paramsMap) + if err != nil { + return nil, err + } + + resp := OrderDeliveryResponse{} + err = json.Unmarshal(respByte, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +// MARK 用户开通产品包权益1.0 +func (client *Client) OrderDeliveryV1(req *OrderDeliveryRequestV1) (*OrderDeliveryResponseV1, error) { + paramsMap, err := ToMap(req) + if err != nil { + return nil, err + } + + respByte, err := client.CurlDo(http.MethodPost, OrderDeliveryUrlV1, paramsMap) + if err != nil { + return nil, err + } + + resp := OrderDeliveryResponseV1{} + err = json.Unmarshal(respByte, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +// MARK 查询用户资源权益 +func (client *Client) ResourcePurchaseCheck(req *ResourcePurchaseCheckRequest) (resp *ResourcePurchaseCheckResponse, err error) { + paramsMap, err := ToMap(req) + if err != nil { + return + } + + respByte, err := client.CurlDo(http.MethodPost, PermissionCheckUrl, paramsMap) + if err != nil { + return + } + resp2 := ResourcePurchaseCheckResponse{} + err = json.Unmarshal(respByte, &resp2) + if err != nil { + return + } + resp = &resp2 + return +} + +// MARK 取消订购 +func (client *Client) PurchaseDelete(req *PurchaseDeleteRequest) (resp *PurchaseDeleteResponse, err error) { + paramsMap, err := ToMap(req) + if err != nil { + return + } + + respByte, err := client.CurlDo(http.MethodPost, PurchaseDeleteUrl, paramsMap) + if err != nil { + return + } + resp2 := PurchaseDeleteResponse{} + + err = json.Unmarshal(respByte, &resp2) + if err != nil { + return + } + resp = &resp2 + return +} diff --git a/service/user.go b/service/user.go new file mode 100644 index 0000000..3d38ebe --- /dev/null +++ b/service/user.go @@ -0,0 +1,272 @@ +package service + +import ( + "encoding/json" + "net/http" +) + +type BaseUserInfo struct { + UserId *string `json:"userId,omitempty"` // 头像 + Avatar *string `json:"avatar,omitempty"` // 头像 + NickName *string `json:"nickname,omitempty"` // 昵称 + Country *string `json:"country,omitempty"` // 国家 + Province *string `json:"province,omitempty"` // 省份 + City *string `json:"city,omitempty"` // 城市 + Gender *int `json:"gender,omitempty"` // 性别 0-无 1-男 2-女 + WxName *string `json:"wx_name,omitempty"` // 真实姓名 + Name *string `json:"name,omitempty"` // 真实姓名 + Company *string `json:"company,omitempty"` // 公司 + Industry *string `json:"industry,omitempty"` // 行业 + Job *string `json:"job,omitempty"` // 工作 + WxEmail *string `json:"wx_email,omitempty"` // 邮箱 + Birth *string `json:"birth,omitempty"` // 生日 + Address *string `json:"address,omitempty"` // 地址 +} + +type RegisterUserRequestData struct { + WxUnionId *string `json:"wx_union_id,omitempty"` + Phone *string `json:"phone,omitempty"` + SdkUserId *string `json:"sdk_user_id,omitempty"` + SdkAppId *string `json:"sdk_app_id,omitempty"` + BaseUserInfo +} + +type RegisterUserRequest struct { + Data RegisterUserRequestData `json:"data"` +} + +type RegisterUserResponseData struct { + UserId string `json:"user_id"` + UserExists int `json:"user_exists"` +} + +type RegisterUserResponse struct { + BaseResponse + Data RegisterUserResponseData `json:"data"` +} + +type UpdateData struct { + BaseUserInfo +} + +type UpdateUserRequestData struct { + UpdateData UpdateData `json:"update_data"` +} + +type UpdateUserRequest struct { + UserId string `json:"user_id"` + Data UpdateUserRequestData `json:"data"` +} + +type UpdateUserResponse struct { + BaseResponse + Data interface{} `json:"data"` +} + +type FieldList struct { + WxUnionId *string `json:"wx_union_id,omitempty"` // 商家自有服务号绑定开放平台union_id + WxOpenId *string `json:"wx_open_id,omitempty"` // 商家自有服务号open_id + WxAppOpenId *string `json:"wx_app_open_id,omitempty"` // 商家授权小程序 open_id + WxEmail *string `json:"wx_email,omitempty"` // 微信邮箱 + Nickname *string `json:"nickname,omitempty"` // 昵称 + Name *string `json:"name,omitempty"` // 真实姓名 + Avatar *string `json:"avatar,omitempty"` // 压缩后的头像 + Gender *int `json:"gender,omitempty"` // 性别 0-无 1-男 2-女 + City *string `json:"city,omitempty"` // 城市 + Province *string `json:"province,omitempty"` // 省份 + Country *string `json:"country,omitempty"` // 国家 + Phone *string `json:"phone,omitempty"` // 手机号码 + Birth *string `json:"birth,omitempty"` // 生日 1996-09-09 + Address *string `json:"address,omitempty"` // 地址 + Company *string `json:"company,omitempty"` // 公司 + IsSeal *string `json:"is_seal,omitempty"` // 用户状态:-1-已注销,0-正常,1-已封号,-2-待注销,3-待激活 + Job *string `json:"job,omitempty"` // 职位 + WxAccount *string `json:"wx_account,omitempty"` // 微信号 + PhoneCollect *string `json:"phone_collect,omitempty"` // 信息采集手机号 + SdkUserId *string `json:"sdk_user_id,omitempty"` // sdk用户id + CreatedAt *string `json:"created_at,omitempty"` // 创建时间 +} + +type UserInfoQueryData struct { + WxUnionId *string `json:"wx_union_id,omitempty"` // 微信 union_id + Phone *string `json:"phone,omitempty"` // 手机号码 + WxOpenId *string `json:"wx_open_id,omitempty"` // 微信 open_id(顶层) + FieldList []string `json:"field_list"` // 必选,查询字段集合 +} + +type GetUserInfoRequest struct { + UserId *string `json:"user_id,omitempty"` // 用户id + Data UserInfoQueryData `json:"data"` +} + +type GetUserInfoResponseData struct { + AppId string `json:"app_id"` // 店铺id + SdkUserId string `json:"sdk_user_id"` // sdk用户id + CreatedAt string `json:"created_at"` // 用户创建时间 + UserId string `json:"user_id"` // 用户id + WxUnionId string `json:"wx_union_id"` // 微信 union_id + WxEmail string `json:"wx_email"` // 微信邮箱 + Name string `json:"name"` // 真实姓名 + Nickname string `json:"nickname"` // 昵称 + Avatar string `json:"avatar"` // 压缩后的头像 + WxAvatar string `json:"wx_avatar"` // 微信原始头像 + Gender int `json:"gender"` // 性别 0-无 1-男 2-女 + City string `json:"city"` // 城市 + Province string `json:"province"` // 省份 + Country string `json:"country"` // 国家 + Age int `json:"age"` // 年龄 + Birth string `json:"birth"` // 生日 + Phone string `json:"phone"` // 电话(若为空且phone_collect不为空,则返回phone_collect) + Address string `json:"address"` // 地址 + Job string `json:"job"` // 工作 + Company string `json:"company"` // 公司 + Industry string `json:"industry"` // 行业 + Tags string `json:"tags"` // 兴趣标签 + WxAccount string `json:"wx_account"` // 用户微信号 + PhoneCollect string `json:"phone_collect"` // 信息采集手机号 + PhoneResource string `json:"phone_resource"` // phone来源标识 phone-来源手机号 collect-来源信息采集 +} + +type GetUserInfoResponse struct { + BaseResponse + + Data GetUserInfoResponseData `json:"data"` +} + +// 用户注册 +func (client *Client) RegisterUser(req *RegisterUserRequest) (*RegisterUserResponse, error) { + paramsMap, err := ToMap(req) + if err != nil { + return nil, err + } + + respByte, err := client.CurlDo(http.MethodPost, RegisterUserUrl, paramsMap) + if err != nil { + return nil, err + } + + var resp = RegisterUserResponse{} + + err = json.Unmarshal(respByte, &resp) + if err != nil { + return nil, err + } + + return &resp, nil +} + +// 修改用户信息 +func (client *Client) UpdateUserInfo(req *UpdateUserRequest) (*UpdateUserResponse, error) { + paramsMap, err := ToMap(req) + if err != nil { + return nil, err + } + + respByte, err := client.CurlDo(http.MethodPost, UpdateUserUrl, paramsMap) + if err != nil { + return nil, err + } + var resp = UpdateUserResponse{} + + err = json.Unmarshal(respByte, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +// 获取单个用户信息 +func (client *Client) GetUserInfo(req *GetUserInfoRequest) (*GetUserInfoResponse, error) { + paramsMap, err := ToMap(req) + if err != nil { + return nil, err + } + + respByte, err := client.CurlDo(http.MethodPost, GetUserInfoUrl, paramsMap) + if err != nil { + return nil, err + } + var resp = GetUserInfoResponse{} + + err = json.Unmarshal(respByte, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +type GetUserListRequest struct { + PageSize int `json:"page_size,omitempty"` // 必填(实际有默认值),每页条数,最大50 + EsSkip *string `json:"es_skip,omitempty"` // 可选,上一页最后一条数据的es_skip字段,用于翻页 + Phone *string `json:"phone,omitempty"` // 可选,手机号(优先级高) + Nickname *string `json:"nickname,omitempty"` // 可选,昵称(优先级中) + Name *string `json:"name,omitempty"` // 可选,姓名(优先级低) + TagId *string `json:"tag_id,omitempty"` // 可选,用户标签id,多个用逗号隔开 + From *string `json:"from,omitempty"` // 可选,用户来源:-1全部、0-微信... + UserType *string `json:"user_type,omitempty"` // 可选,用户身份:0-全部、1-黑名单、2-超级会员 + LastPaytimeStart *string `json:"last_paytime_start,omitempty"` // 可选,支付起始时间 yyyy-MM-dd + LastPaytimeEnd *string `json:"last_paytime_end,omitempty"` // 可选,支付截止时间 yyyy-MM-dd + MinPaySum *string `json:"min_pay_sum,omitempty"` // 可选,支付最小金额 + MaxPaySum *string `json:"max_pay_sum,omitempty"` // 可选,支付最大金额 + MinPunchCount *string `json:"min_punch_count,omitempty"` // 可选,支付最小次数 + MaxPunchCount *string `json:"max_punch_count,omitempty"` // 可选,支付最大次数 + UserCreatedStart *string `json:"user_created_start,omitempty"` // 可选,用户创建起始时间 yyyy-mm-dd + UserCreatedEnd *string `json:"user_created_end,omitempty"` // 可选,用户创建截止时间 yyyy-mm-dd + LatestVisitedStart *string `json:"latest_visited_start,omitempty"` // 可选,最近访问起始时间 yyyy-mm-dd + LatestVisitedEnd *string `json:"latest_visited_end,omitempty"` // 可选,最近访问结束时间 yyyy-mm-dd + NeedColumn []string `json:"need_column,omitempty"` // 可选,需要返回的字段列表 +} + +// GetUserListData 用户列表数据 +type GetUserListData struct { + List []UserItem `json:"list"` // 用户列表 + Total int `json:"total"` // 查询结果记录数 + EsSkip *EsSkip `json:"es_skip,omitempty"` // 翻页字段,用于下一页请求 +} + +// UserItem 用户信息 +type UserItem struct { + UserId string `json:"user_id"` // 用户id + UserNickname string `json:"user_nickname"` // 用户昵称 + BindPhone string `json:"bind_phone"` // 绑定手机号 + CollectPhone string `json:"collect_phone"` // 采集手机号 + Avatar string `json:"avatar"` // 头像 + From string `json:"from"` // 用户来源:0-微信,1-sdk... + LatestVisitedAt int64 `json:"latest_visited_at"` // 最后一次访问时间(毫秒时间戳),0表示未访问 + PaySum float64 `json:"pay_sum"` // 消费总额(单位:分) + PunchCount int `json:"punch_count"` // 购买次数 + WxUnionId string `json:"wx_union_id"` // 微信 union_id + WxAppOpenId string `json:"wx_app_open_id"` // 小程序 open_id + WxOpenId string `json:"wx_open_id"` // 微信 open_id + UserCreatedAt string `json:"user_created_at"` // 用户创建时间 +} + +// EsSkip 翻页字段 +type EsSkip struct { + Id string `json:"id"` // 用户id + UserCreatedAt string `json:"user_created_at"` // 用户创建时间戳 +} +type GetUserListResponse struct { + BaseResponse + Data GetUserListData `json:"data"` +} + +// 获取用户列表 +func (client *Client) GetUserList(req *GetUserListRequest) (*GetUserListResponse, error) { + paramsMap, err := ToMap(req) + if err != nil { + return nil, err + } + + respByte, err := client.CurlDo(http.MethodPost, GetUserListUrl, paramsMap) + if err != nil { + return nil, err + } + var resp = GetUserListResponse{} + + err = json.Unmarshal(respByte, &resp) + if err != nil { + return nil, err + } + return &resp, nil +}