@@ -2,12 +2,14 @@ package youlianghui | |||
import ( | |||
"applet/app/cfg" | |||
"applet/app/lib/youlianghui/md" | |||
) | |||
type ApiService struct { | |||
MemberId string `json:"member_id"` | |||
Secret string `json:"secret"` | |||
Host string `json:"host"` | |||
MemberId string `json:"member_id"` | |||
Secret string `json:"secret"` | |||
Host string `json:"host"` | |||
Header map[string]string `json:"header"` | |||
} | |||
func NewApiService(memberId, secret string) (apiService ApiService, err error) { // set方法 | |||
@@ -17,34 +19,44 @@ func NewApiService(memberId, secret string) (apiService ApiService, err error) { | |||
if cfg.Prd { | |||
apiService.Host = "http://api.adnet.qq.com/open/v1.1" | |||
} | |||
apiService.Header = make(map[string]string) | |||
apiService.Header["Content-Type"] = "multipart/form-data" | |||
apiService.Header["token"] = GetToken(apiService.MemberId, apiService.Secret) | |||
return | |||
} | |||
// MediumAdd 创建媒体 | |||
// func (apiService *ApiService) MediumAdd() (appId string, err error) { // set方法 | |||
// token := GetToken(apiService.MemberId, apiService.Secret) | |||
// url := apiService.Host + "/medium/add" | |||
// params := map[string]interface{}{ | |||
// "name": name, | |||
// "type": string(adunitType), | |||
// } | |||
// if adunitType == enum.AdunitTypeForVideoFeeds { | |||
// params["video_duration_min"] = 6 | |||
// params["video_duration_max"] = 60 | |||
// } | |||
// postBody, err := utils.CurlPost(url, utils.SerializeStr(params), nil) | |||
// if err != nil { | |||
// return | |||
// } | |||
// var resp md.AgencyCreateAdunit | |||
// err = json.Unmarshal(postBody, &resp) | |||
// if err != nil { | |||
// return | |||
// } | |||
// if resp.Ret != 0 { | |||
// err = errors.New(resp.ErrMsg) | |||
// } | |||
// adUnitId = resp.AdUnitId | |||
// return | |||
// } | |||
func (apiService *ApiService) MediumAdd(req md.MediumAdd) (appId string, err error) { // set方法 | |||
url := apiService.Host + "/medium/add" | |||
if req.Affiliation == "" { | |||
req.Affiliation = "Agency" // Own:应用开发者、Agency:应用发行/代理方 | |||
} | |||
params := map[string]string{ | |||
"member_id": apiService.MemberId, | |||
"medium_name": req.MediumName, | |||
"industry_id_v2": req.IndustryIdV2, | |||
"os": req.Os, | |||
"affiliation": req.Affiliation, | |||
"package_name": req.PackageName, | |||
"full_package_name": req.FullPackageName, | |||
"wechat_app_id": req.WechatAppId, | |||
"package_name_wx_appid_rel": req.PackageNameWxAppidRel, | |||
"wechat_universal_link": req.WechatUniversalLink, | |||
} | |||
postBody, err := MultipartFormDataRequest(url, params, nil, apiService.Header) | |||
if err != nil { | |||
return "", err | |||
} | |||
var resp md.AgencyCreateAdunit | |||
err = json.Unmarshal(postBody, &resp) | |||
if err != nil { | |||
return | |||
} | |||
if resp.Ret != 0 { | |||
err = errors.New(resp.ErrMsg) | |||
} | |||
adUnitId = resp.AdUnitId | |||
return | |||
} |
@@ -0,0 +1,84 @@ | |||
package youlianghui | |||
import ( | |||
"bytes" | |||
"fmt" | |||
"io" | |||
"io/ioutil" | |||
"mime/multipart" | |||
"net/http" | |||
"os" | |||
) | |||
// MultipartFormDataRequest 封装 multipart/form-data 请求 | |||
func MultipartFormDataRequest(url string, fields map[string]string, files map[string]string, headers map[string]string) ([]byte, error) { | |||
// 创建一个缓冲区来存储表单数据 | |||
var b bytes.Buffer | |||
w := multipart.NewWriter(&b) | |||
// 添加文本字段 | |||
for key, value := range fields { | |||
if err := addFormValue(w, key, value); err != nil { | |||
return nil, err | |||
} | |||
} | |||
// 添加文件字段 | |||
for fieldName, filePath := range files { | |||
file, err := os.Open(filePath) | |||
if err != nil { | |||
return nil, fmt.Errorf("error opening file %s: %v", filePath, err) | |||
} | |||
defer file.Close() | |||
part, err := w.CreateFormFile(fieldName, filePath) | |||
if err != nil { | |||
return nil, fmt.Errorf("error creating form file %s: %v", fieldName, err) | |||
} | |||
if _, err := io.Copy(part, file); err != nil { | |||
return nil, fmt.Errorf("error copying file %s to part: %v", filePath, err) | |||
} | |||
} | |||
// 完成多部分表单 | |||
if err := w.Close(); err != nil { | |||
return nil, fmt.Errorf("error closing multipart writer: %v", err) | |||
} | |||
// 创建请求 | |||
req, err := http.NewRequest("POST", url, &b) | |||
if err != nil { | |||
return nil, fmt.Errorf("error creating request: %v", err) | |||
} | |||
// 设置头部 | |||
req.Header.Set("Content-Type", w.FormDataContentType()) | |||
for key, value := range headers { | |||
req.Header.Set(key, value) | |||
} | |||
// 发送请求 | |||
client := &http.Client{} | |||
resp, err := client.Do(req) | |||
if err != nil { | |||
return nil, fmt.Errorf("error sending request: %v", err) | |||
} | |||
defer resp.Body.Close() | |||
body, err := ioutil.ReadAll(resp.Body) // 读取响应 | |||
if err != nil { | |||
return nil, err | |||
} | |||
return body, nil | |||
} | |||
// addFormValue 辅助函数:添加表单值 | |||
func addFormValue(w *multipart.Writer, key, value string) error { | |||
fw, err := w.CreateFormField(key) | |||
if err != nil { | |||
return fmt.Errorf("error creating form field %s: %v", key, err) | |||
} | |||
_, err = fw.Write([]byte(value)) | |||
return err | |||
} |
@@ -2,8 +2,8 @@ package md | |||
type MediumAdd struct { | |||
MediumName string `json:"medium_name" example:"媒体名字"` | |||
IndustryIdV2 int `json:"industry_id_v2" example:"媒体所属新3级行业id"` | |||
Os int `json:"os" example:"操作系统,数字含义1-Android, 2-iOS"` | |||
IndustryIdV2 string `json:"industry_id_v2" example:"媒体所属新3级行业id"` | |||
Os string `json:"os" example:"操作系统,数字含义1-Android, 2-iOS"` | |||
DetailUrl string `json:"detail_url" example:"详情页url(支持的各个商店域名约束)"` | |||
Affiliation string `json:"affiliation" example:"媒体隶属关系"` | |||
PackageName string `json:"package_name" example:"主程序包名"` | |||
@@ -0,0 +1,38 @@ | |||
package mw | |||
import "github.com/gin-gonic/gin" | |||
//func SwagAuth(c *gin.Context) { | |||
// // 这里的 "user" 和 "password" 是示例,需要替换为实际的用户名和密码 | |||
// username := c.Query("user") | |||
// password := c.Query("password") | |||
// | |||
// // 验证用户名和密码 | |||
// if username != "micro_group" || password != "123456" { | |||
// //e.OutErr(c, e.ERR_UNAUTHORIZED, "Unauthorized!") | |||
// //return | |||
// c.AbortWithStatusJSON(401, gin.H{"error": "Unauthorized"}) | |||
// return | |||
// } | |||
// | |||
// // 如果密码正确,则继续执行后续的处理函数 | |||
// c.Next() | |||
//} | |||
// @summary 用于密码验证的中间件 | |||
func SwagAuth() gin.HandlerFunc { | |||
return func(c *gin.Context) { | |||
// 这里的 "user" 和 "password" 是示例,需要替换为实际的用户名和密码 | |||
username := c.Query("user") | |||
password := c.Query("password") | |||
// 验证用户名和密码 | |||
if username != "admin" || password != "secret" { | |||
c.AbortWithStatusJSON(401, gin.H{"error": "Unauthorized"}) | |||
return | |||
} | |||
// 如果密码正确,则继续执行后续的处理函数 | |||
c.Next() | |||
} | |||
} |
@@ -25,6 +25,19 @@ func Init() *gin.Engine { | |||
ginSwagger.DisablingWrapHandler(swaggerFiles.Handler, "SWAGGER")(c) | |||
}) | |||
// TODO::指定open的文档的位置 | |||
// 设置静态文件服务,提供 group2.json 文件 | |||
r.Static("/api-docs", "./docs") | |||
r.GET("/open/swagger/*any", gin.BasicAuth(gin.Accounts{ | |||
"zhiYin": "123456", | |||
}), func(c *gin.Context) { | |||
requestTls := "http://" | |||
if c.Request.TLS != nil { | |||
requestTls = "https://" | |||
} | |||
r.Use(mw.SwagAuth()) | |||
ginSwagger.WrapHandler(swaggerFiles.Handler, ginSwagger.URL(requestTls+c.Request.Host+"/api-docs/open.json"))(c) | |||
}) | |||
r.Use(mw.ChangeHeader) | |||
// 是否打印访问日志, 在非正式环境都打印 | |||
@@ -30,6 +30,20 @@ func GetUser(c *gin.Context) *model.Admin { | |||
func CheckUser(c *gin.Context) (*model.Admin, string, error) { | |||
token := c.GetHeader("Authorization") | |||
if token == "" { | |||
//TODO::兼容open | |||
appSecret := c.GetHeader("AppSecret") | |||
if appSecret != "" { | |||
//TODO::暂时给激活鸟写死 | |||
if appSecret == "2F125D59EE826535D7E84E407A13C107" { | |||
// 获取admin | |||
adminDb := implement.NewAdminDb(db.DBs[GetMasterId(c)]) | |||
admin, err := adminDb.GetAdmin(1001) | |||
if err != nil { | |||
return nil, "", err | |||
} | |||
return admin, "", nil | |||
} | |||
} | |||
return nil, "", errors.New("token not exist") | |||
} | |||
// 按空格分割 | |||
@@ -0,0 +1,127 @@ | |||
#!/bin/bash | |||
# 生成基础Swagger JSON文件 | |||
swag init --parseDependency --parseInternal --output ./docs | |||
# 提取带有 "open" 标签的路径 | |||
jq '.paths | with_entries( | |||
select( | |||
(.value.post?.tags? | type == "array" and any(. == "数据中心------嘉俊")) or | |||
(.value.get?.tags? | type == "array" and any(. == "数据中心------嘉俊")) or | |||
(.value.put?.tags? | type == "array" and any(. == "数据中心------嘉俊")) or | |||
(.value.delete?.tags? | type == "array" and any(. == "数据中心------嘉俊")) | |||
) | |||
) // {} as $filtered_paths | . * { paths: $filtered_paths }' docs/swagger.json > open_paths.json | |||
# 提取 definitions 和 securityDefinitions | |||
jq '.definitions' docs/swagger.json > definitions.json | |||
jq '.securityDefinitions' docs/swagger.json > securityDefinitions.json | |||
# 替换 description 中的内容 | |||
jq 'with_entries( | |||
.value |= ( | |||
if .post then | |||
.post.parameters |= map( | |||
if .description == "验证参数Bearer和token空格拼接" then | |||
.description = "秘钥内容" | |||
else | |||
. | |||
end | |||
) | |||
else | |||
. | |||
end, | |||
if .get then | |||
.get.parameters |= map( | |||
if .description == "验证参数Bearer和token空格拼接" then | |||
.description = "秘钥内容" | |||
else | |||
. | |||
end | |||
) | |||
else | |||
. | |||
end, | |||
if .put then | |||
.put.parameters |= map( | |||
if .description == "验证参数Bearer和token空格拼接" then | |||
.description = "秘钥内容" | |||
else | |||
. | |||
end | |||
) | |||
else | |||
. | |||
end, | |||
if .delete then | |||
.delete.parameters |= map( | |||
if .description == "验证参数Bearer和token空格拼接" then | |||
.description = "秘钥内容" | |||
else | |||
. | |||
end | |||
) | |||
else | |||
. | |||
end | |||
) | |||
)' open_paths.json > updated_tmp_open_paths.json | |||
# 将 "name": "Authorization" 替换成 "name": "AppSecret" | |||
jq 'walk(if type == "object" and has("name") and .name == "Authorization" then .name = "AppSecret" else . end)' updated_tmp_open_paths.json > updated_open_paths.json | |||
# 创建新的 Swagger 配置文件 | |||
cat <<EOF > temp.json | |||
{ | |||
"swagger": "2.0", | |||
"info": { | |||
"description": "广告联盟接口", | |||
"title": "广告联盟", | |||
"termsOfService": "http://swagger.io/terms/", | |||
"contact": { | |||
"name": "zhiying", | |||
"url": "http://www.swagger.io/support", | |||
"email": "zhiyongos@163.com" | |||
}, | |||
"license": { | |||
"name": "Apache 2.0", | |||
"url": "http://www.apache.org/licenses/LICENSE-2.0.html" | |||
}, | |||
"version": "1.0" | |||
}, | |||
"host": "xxxxx.adcms.zhiyingos.cn", | |||
"paths": { | |||
}, | |||
"definitions": { | |||
}, | |||
"securityDefinitions": { | |||
} | |||
} | |||
EOF | |||
# 合并路径信息到open.json | |||
jq -n ' | |||
input as $paths | | |||
input as $config | | |||
$config | .paths = $paths | |||
' updated_open_paths.json temp.json > temp_1.json | |||
jq -n ' | |||
input as $paths | | |||
input as $config | | |||
$config | .definitions = $paths | |||
' definitions.json temp_1.json > temp_2.json | |||
jq -n ' | |||
input as $paths | | |||
input as $config | | |||
$config | .securityDefinitions = $paths | |||
' securityDefinitions.json temp_2.json > open.json | |||
# 删除中间生成的文件 | |||
rm -f open_paths.json temp_1.json temp_2.json updated_open_paths.json temp.json definitions.json securityDefinitions.json updated_tmp_open_paths.json | |||
# 将最终生成的文件移动到docs目录下 | |||
mv open.json docs/open.json | |||
echo "Generated open.json successfully." |