Merge branch 'develop' of gitee.com:fusenpack/fusenapi into develop
This commit is contained in:
commit
a405a89567
10
server/feishu-sync/etc/feishu-sync.yaml
Normal file
10
server/feishu-sync/etc/feishu-sync.yaml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
Name: feishu-sync
|
||||||
|
Host: 0.0.0.0
|
||||||
|
Port: 9925
|
||||||
|
Timeout: 15000 #服务超时时间(毫秒)
|
||||||
|
SourceMysql: fsreaderwriter:XErSYmLELKMnf3Dh@tcp(fusen.cdmigcvz3rle.us-east-2.rds.amazonaws.com:3306)/fusen
|
||||||
|
SourceRabbitMq: ""
|
||||||
|
Auth:
|
||||||
|
AccessSecret: fusen2023
|
||||||
|
AccessExpire: 2592000
|
||||||
|
RefreshAfter: 1592000
|
37
server/feishu-sync/feishu-sync.go
Normal file
37
server/feishu-sync/feishu-sync.go
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"fusenapi/utils/auth"
|
||||||
|
"fusenapi/utils/fsconfig"
|
||||||
|
|
||||||
|
"fusenapi/server/feishu-sync/internal/config"
|
||||||
|
"fusenapi/server/feishu-sync/internal/handler"
|
||||||
|
"fusenapi/server/feishu-sync/internal/svc"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
var configFile = flag.String("f", "etc/feishu-sync.yaml", "the config file")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
var c config.Config
|
||||||
|
fsconfig.StartNacosConfig(*configFile, &c, nil)
|
||||||
|
|
||||||
|
c.Timeout = int64(time.Second * 15)
|
||||||
|
server := rest.MustNewServer(c.RestConf, rest.WithCustomCors(auth.FsCors, func(w http.ResponseWriter) {
|
||||||
|
}))
|
||||||
|
defer server.Stop()
|
||||||
|
|
||||||
|
ctx := svc.NewServiceContext(c)
|
||||||
|
handler.RegisterHandlers(server, ctx)
|
||||||
|
|
||||||
|
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
|
||||||
|
server.Start()
|
||||||
|
}
|
18
server/feishu-sync/internal/config/config.go
Normal file
18
server/feishu-sync/internal/config/config.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fusenapi/server/feishu-sync/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
rest.RestConf
|
||||||
|
SourceMysql string
|
||||||
|
Auth types.Auth
|
||||||
|
SourceRabbitMq string
|
||||||
|
FeiShu struct {
|
||||||
|
ApiHost string
|
||||||
|
EncryptKey string
|
||||||
|
VerificationToken string
|
||||||
|
}
|
||||||
|
}
|
22
server/feishu-sync/internal/handler/routes.go
Normal file
22
server/feishu-sync/internal/handler/routes.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"fusenapi/server/feishu-sync/internal/svc"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||||
|
server.AddRoutes(
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/api/feishu/webhook",
|
||||||
|
Handler: WebhookHandler(serverCtx),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
15
server/feishu-sync/internal/handler/webhookhandler.go
Normal file
15
server/feishu-sync/internal/handler/webhookhandler.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fusenapi/server/feishu-sync/internal/logic"
|
||||||
|
"fusenapi/server/feishu-sync/internal/svc"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func WebhookHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// 创建一个业务逻辑层实例
|
||||||
|
l := logic.NewWebhookLogic(r.Context(), svcCtx)
|
||||||
|
l.Webhook(w, r)
|
||||||
|
}
|
||||||
|
}
|
138
server/feishu-sync/internal/logic/user_webhook.go
Normal file
138
server/feishu-sync/internal/logic/user_webhook.go
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
package logic
|
||||||
|
|
||||||
|
type UserWebhookMsg struct {
|
||||||
|
Schema string `json:"schema"`
|
||||||
|
Header struct {
|
||||||
|
EventId string `json:"event_id"`
|
||||||
|
EventType string `json:"event_type"`
|
||||||
|
CreateTime string `json:"create_time"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
AppId string `json:"app_id"`
|
||||||
|
TenantKey string `json:"tenant_key"`
|
||||||
|
} `json:"header"`
|
||||||
|
Event struct {
|
||||||
|
Object struct {
|
||||||
|
OpenId string `json:"open_id"`
|
||||||
|
UnionId string `json:"union_id"`
|
||||||
|
UserId string `json:"user_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
EnName string `json:"en_name"`
|
||||||
|
Nickname string `json:"nickname"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
EnterpriseEmail string `json:"enterprise_email"`
|
||||||
|
JobTitle string `json:"job_title"`
|
||||||
|
Mobile string `json:"mobile"`
|
||||||
|
Gender int64 `json:"gender"`
|
||||||
|
Avatar struct {
|
||||||
|
Avatar72 string `json:"avatar_72"`
|
||||||
|
Avatar240 string `json:"avatar_240"`
|
||||||
|
Avatar640 string `json:"avatar_640"`
|
||||||
|
AvatarOrigin string `json:"avatar_origin"`
|
||||||
|
} `json:"avatar"`
|
||||||
|
Status struct {
|
||||||
|
IsFrozen bool `json:"is_frozen"`
|
||||||
|
IsResigned bool `json:"is_resigned"`
|
||||||
|
IsActivated bool `json:"is_activated"`
|
||||||
|
IsExited bool `json:"is_exited"`
|
||||||
|
IsUnjoin bool `json:"is_unjoin"`
|
||||||
|
} `json:"status"`
|
||||||
|
DepartmentIds []string `json:"department_ids"`
|
||||||
|
LeaderUserId string `json:"leader_user_id"`
|
||||||
|
City string `json:"city"`
|
||||||
|
Country string `json:"country"`
|
||||||
|
WorkStation string `json:"work_station"`
|
||||||
|
Joint64ime int64 `json:"join_time"`
|
||||||
|
EmployeeNo string `json:"employee_no"`
|
||||||
|
EmployeeType int64 `json:"employee_type"`
|
||||||
|
Orders []struct {
|
||||||
|
DepartmentId string `json:"department_id"`
|
||||||
|
UserOrder int64 `json:"user_order"`
|
||||||
|
DepartmentOrder int64 `json:"department_order"`
|
||||||
|
IsPrimaryDept bool `json:"is_primary_dept"`
|
||||||
|
} `json:"orders"`
|
||||||
|
CustomAttrs []struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Id string `json:"id"`
|
||||||
|
Value struct {
|
||||||
|
Text string `json:"text"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
PcUrl string `json:"pc_url"`
|
||||||
|
OptionId string `json:"option_id"`
|
||||||
|
OptionValue string `json:"option_value"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
PictureUrl string `json:"picture_url"`
|
||||||
|
GenericUser struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Type int64 `json:"type"`
|
||||||
|
} `json:"generic_user"`
|
||||||
|
} `json:"value"`
|
||||||
|
} `json:"custom_attrs"`
|
||||||
|
JobLevelId string `json:"job_level_id"`
|
||||||
|
JobFamilyId string `json:"job_family_id"`
|
||||||
|
DottedLineLeaderUserIds []string `json:"dotted_line_leader_user_ids"`
|
||||||
|
} `json:"object"`
|
||||||
|
} `json:"event"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 员工增删改信息
|
||||||
|
func (l *WebhookLogic) OnUserChange(data []byte) error {
|
||||||
|
return nil
|
||||||
|
/*var msg UserWebhookMsg
|
||||||
|
if err := json.Unmarshal(data, &msg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
avatar, _ := json.Marshal(msg.Event.Object.Avatar)
|
||||||
|
isFrozen := int64(0)
|
||||||
|
if msg.Event.Object.Status.IsFrozen {
|
||||||
|
isFrozen = 1
|
||||||
|
}
|
||||||
|
isResigned := int64(0)
|
||||||
|
if msg.Event.Object.Status.IsResigned {
|
||||||
|
isResigned = 1
|
||||||
|
}
|
||||||
|
isActivated := int64(0)
|
||||||
|
if msg.Event.Object.Status.IsActivated {
|
||||||
|
isActivated = 1
|
||||||
|
}
|
||||||
|
isExited := int64(0)
|
||||||
|
if msg.Event.Object.Status.IsExited {
|
||||||
|
isExited = 1
|
||||||
|
}
|
||||||
|
isUnjoin := int64(0)
|
||||||
|
if msg.Event.Object.Status.IsUnjoin {
|
||||||
|
isUnjoin = 1
|
||||||
|
}
|
||||||
|
departmentIds, _ := json.Marshal(msg.Event.Object.DepartmentIds)
|
||||||
|
orders, _ := json.Marshal(msg.Event.Object.Orders)
|
||||||
|
feiShuMsgCreateTimeInt64, err := strconv.ParseInt(msg.Header.CreateTime, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
feiShuMsgCreateTime := time.UnixMilli(feiShuMsgCreateTimeInt64)
|
||||||
|
return l.svcCtx.AllModels.FsFeishuUser.CreateOrUpdate(l.ctx, msg.Header.AppId, msg.Event.Object.OpenId, &gmodel.FsFeishuUser{
|
||||||
|
AppId: &msg.Header.AppId,
|
||||||
|
OpenId: &msg.Event.Object.OpenId,
|
||||||
|
UnionId: &msg.Event.Object.UnionId,
|
||||||
|
Name: &msg.Event.Object.Name,
|
||||||
|
EnName: &msg.Event.Object.EnName,
|
||||||
|
Nickname: &msg.Event.Object.Nickname,
|
||||||
|
Email: &msg.Event.Object.Email,
|
||||||
|
EnterpriseEmail: &msg.Event.Object.EnterpriseEmail,
|
||||||
|
JobTitle: &msg.Event.Object.JobTitle,
|
||||||
|
Mobile: &msg.Event.Object.Mobile,
|
||||||
|
Gender: &msg.Event.Object.Gender,
|
||||||
|
Avatar: &avatar,
|
||||||
|
IsFrozen: &isFrozen,
|
||||||
|
IsResigned: &isResigned,
|
||||||
|
IsActivated: &isActivated,
|
||||||
|
IsExited: &isExited,
|
||||||
|
IsUnjoin: &isUnjoin,
|
||||||
|
DepartmentIds: &departmentIds,
|
||||||
|
WorkStation: &msg.Event.Object.WorkStation,
|
||||||
|
EmployeeNo: &msg.Event.Object.EmployeeNo,
|
||||||
|
EmployeeType: &msg.Event.Object.EmployeeType,
|
||||||
|
Orders: &orders,
|
||||||
|
Ctime: &feiShuMsgCreateTime,
|
||||||
|
Utime: &feiShuMsgCreateTime,
|
||||||
|
})*/
|
||||||
|
}
|
150
server/feishu-sync/internal/logic/webhooklogic.go
Normal file
150
server/feishu-sync/internal/logic/webhooklogic.go
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
package logic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fusenapi/model/gmodel"
|
||||||
|
"fusenapi/server/feishu-sync/internal/svc"
|
||||||
|
"fusenapi/utils/feishu"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WebhookLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWebhookLogic(ctx context.Context, svcCtx *svc.ServiceContext) *WebhookLogic {
|
||||||
|
return &WebhookLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type EncryptWebhookMsg struct {
|
||||||
|
Encrypt string `json:"encrypt"` //加密的消息
|
||||||
|
}
|
||||||
|
type WebhookMsg struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Challenge string `json:"challenge"`
|
||||||
|
Header map[string]interface{} `json:"header"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// webhook消息事件header(body参数)基础信息
|
||||||
|
type BaseWebhookMsgHeaderType struct {
|
||||||
|
EventId string `json:"event_id"` //事件id(可作为消息唯一性确认)
|
||||||
|
EventType string `json:"event_type"` //事件类型
|
||||||
|
CreateTime string `json:"create_time"` //创建时间
|
||||||
|
Token string `json:"token"` //事件token
|
||||||
|
AppId string `json:"app_id"` //app id
|
||||||
|
TenantKey string `json:"tenant_key"` //租户key
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *WebhookLogic) Webhook(w http.ResponseWriter, r *http.Request) {
|
||||||
|
bodyBytes, err := io.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
logx.Error("读取请求body失败", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer r.Body.Close()
|
||||||
|
//计算签名
|
||||||
|
timestamp := r.Header.Get("X-Lark-Request-Timestamp")
|
||||||
|
nonce := r.Header.Get("X-Lark-Request-Nonce")
|
||||||
|
signature := r.Header.Get("X-Lark-Signature")
|
||||||
|
sign := feishu.CalculateFeiShuWebhookSignature(timestamp, nonce, l.svcCtx.Config.FeiShu.EncryptKey, bodyBytes)
|
||||||
|
if signature != sign {
|
||||||
|
logx.Error("非法的消息,签名验证不通过", sign, "====", signature)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var encryptMsg EncryptWebhookMsg
|
||||||
|
if err = json.Unmarshal(bodyBytes, &encryptMsg); err != nil {
|
||||||
|
logx.Error("反序列化body失败", err, "body数据:", string(bodyBytes))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if encryptMsg.Encrypt == "" {
|
||||||
|
logx.Error("消息加密信息是空的")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//解密
|
||||||
|
realMsgBytes, err := feishu.DecryptFeiShuWebhookMsg(encryptMsg.Encrypt, l.svcCtx.Config.FeiShu.EncryptKey)
|
||||||
|
if err != nil {
|
||||||
|
logx.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//如果只是验证http连接的消息
|
||||||
|
var webhookMsg WebhookMsg
|
||||||
|
if err = json.Unmarshal(realMsgBytes, &webhookMsg); err != nil {
|
||||||
|
logx.Error("反序列化请求body失败", err, "解密数据:", string(realMsgBytes))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//验证连接(直接返回)
|
||||||
|
if webhookMsg.Type == "url_verification" {
|
||||||
|
challengeRsp := map[string]string{
|
||||||
|
"challenge": webhookMsg.Challenge,
|
||||||
|
}
|
||||||
|
b, _ := json.Marshal(challengeRsp)
|
||||||
|
w.Write(b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bodyHeaderByte, err := json.Marshal(webhookMsg.Header)
|
||||||
|
if err != nil {
|
||||||
|
logx.Error("序列化请求体header失败:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var msgHeader BaseWebhookMsgHeaderType
|
||||||
|
if err = json.Unmarshal(bodyHeaderByte, &msgHeader); err != nil {
|
||||||
|
logx.Error("反序列化请求体中的header失败", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
httpHeaderBytes, _ := json.Marshal(r.Header)
|
||||||
|
httpHeaderStr := string(httpHeaderBytes)
|
||||||
|
//解密后的数据
|
||||||
|
decryptMsgStr := string(realMsgBytes)
|
||||||
|
feiShuMsgCreateTimeInt64, err := strconv.ParseInt(msgHeader.CreateTime, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
logx.Error("解析消息时间错误:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
feiShuMsgCreateTime := time.UnixMilli(feiShuMsgCreateTimeInt64)
|
||||||
|
now := time.Now().UTC()
|
||||||
|
//把事件加入日志
|
||||||
|
err = l.svcCtx.AllModels.FsFeishuWebhookLog.Create(l.ctx, &gmodel.FsFeishuWebhookLog{
|
||||||
|
AppId: &msgHeader.AppId,
|
||||||
|
EventId: &msgHeader.EventId,
|
||||||
|
EventType: &msgHeader.EventType,
|
||||||
|
HttpHeader: &httpHeaderStr,
|
||||||
|
Data: &encryptMsg.Encrypt,
|
||||||
|
DecryptData: &decryptMsgStr,
|
||||||
|
MsgCtime: &feiShuMsgCreateTime,
|
||||||
|
Ctime: &now,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logx.Error("保存webhook消息日志失败:", err)
|
||||||
|
}
|
||||||
|
switch msgHeader.EventType {
|
||||||
|
case "contact.department.created_v3": //部门新建
|
||||||
|
case "contact.department.deleted_v3": //部门删除
|
||||||
|
case "contact.department.updated_v3": //部门信息变化
|
||||||
|
case "contact.employee_type_enum.actived_v3": //启动人员类型事件
|
||||||
|
case "contact.employee_type_enum.created_v3": //新建人员类型事件
|
||||||
|
case "contact.employee_type_enum.deactivated_v3": //停用人员类型事件
|
||||||
|
case "contact.employee_type_enum.deleted_v3": //删除人员类型事件
|
||||||
|
case "contact.employee_type_enum.updated_v3": //修改人员类型名称事件
|
||||||
|
case "contact.user.created_v3": //员工入职
|
||||||
|
err = l.OnUserChange(realMsgBytes)
|
||||||
|
case "contact.user.deleted_v3": //员工离职
|
||||||
|
err = l.OnUserChange(realMsgBytes)
|
||||||
|
case "contact.user.updated_v3": //员工信息变化
|
||||||
|
err = l.OnUserChange(realMsgBytes)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
logx.Error("处理事件错误:", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
27
server/feishu-sync/internal/svc/servicecontext.go
Normal file
27
server/feishu-sync/internal/svc/servicecontext.go
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package svc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fusenapi/initalize"
|
||||||
|
"fusenapi/model/gmodel"
|
||||||
|
"fusenapi/server/feishu-sync/internal/config"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServiceContext struct {
|
||||||
|
Config config.Config
|
||||||
|
|
||||||
|
MysqlConn *gorm.DB
|
||||||
|
AllModels *gmodel.AllModelsGen
|
||||||
|
RabbitMq *initalize.RabbitMqHandle
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServiceContext(c config.Config) *ServiceContext {
|
||||||
|
conn := initalize.InitMysql(c.SourceMysql)
|
||||||
|
|
||||||
|
return &ServiceContext{
|
||||||
|
Config: c,
|
||||||
|
MysqlConn: conn,
|
||||||
|
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
|
||||||
|
RabbitMq: initalize.InitRabbitMq(c.SourceRabbitMq, nil),
|
||||||
|
}
|
||||||
|
}
|
75
server/feishu-sync/internal/types/types.go
Normal file
75
server/feishu-sync/internal/types/types.go
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fusenapi/utils/basic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Request struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"msg"`
|
||||||
|
Data interface{} `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Auth struct {
|
||||||
|
AccessSecret string `json:"accessSecret"`
|
||||||
|
AccessExpire int64 `json:"accessExpire"`
|
||||||
|
RefreshAfter int64 `json:"refreshAfter"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type File struct {
|
||||||
|
Filename string `fsfile:"filename"`
|
||||||
|
Header map[string][]string `fsfile:"header"`
|
||||||
|
Size int64 `fsfile:"size"`
|
||||||
|
Data []byte `fsfile:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Meta struct {
|
||||||
|
TotalCount int64 `json:"total_count"`
|
||||||
|
PageCount int64 `json:"page_count"`
|
||||||
|
CurrentPage int `json:"current_page"`
|
||||||
|
PerPage int `json:"per_page"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set 设置Response的Code和Message值
|
||||||
|
func (resp *Response) Set(Code int, Message string) *Response {
|
||||||
|
return &Response{
|
||||||
|
Code: Code,
|
||||||
|
Message: Message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set 设置整个Response
|
||||||
|
func (resp *Response) SetWithData(Code int, Message string, Data interface{}) *Response {
|
||||||
|
return &Response{
|
||||||
|
Code: Code,
|
||||||
|
Message: Message,
|
||||||
|
Data: Data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetStatus 设置默认StatusResponse(内部自定义) 默认msg, 可以带data, data只使用一个参数
|
||||||
|
func (resp *Response) SetStatus(sr *basic.StatusResponse, data ...interface{}) *Response {
|
||||||
|
newResp := &Response{
|
||||||
|
Code: sr.Code,
|
||||||
|
}
|
||||||
|
if len(data) == 1 {
|
||||||
|
newResp.Data = data[0]
|
||||||
|
}
|
||||||
|
return newResp
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetStatusWithMessage 设置默认StatusResponse(内部自定义) 非默认msg, 可以带data, data只使用一个参数
|
||||||
|
func (resp *Response) SetStatusWithMessage(sr *basic.StatusResponse, msg string, data ...interface{}) *Response {
|
||||||
|
newResp := &Response{
|
||||||
|
Code: sr.Code,
|
||||||
|
Message: msg,
|
||||||
|
}
|
||||||
|
if len(data) == 1 {
|
||||||
|
newResp.Data = data[0]
|
||||||
|
}
|
||||||
|
return newResp
|
||||||
|
}
|
16
server_api/feishu-sync.api
Normal file
16
server_api/feishu-sync.api
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
syntax = "v1"
|
||||||
|
|
||||||
|
info (
|
||||||
|
title: "飞书同步服务"// TODO: add title
|
||||||
|
desc: // TODO: add description
|
||||||
|
author: ""
|
||||||
|
email: ""
|
||||||
|
)
|
||||||
|
|
||||||
|
import "basic.api"
|
||||||
|
|
||||||
|
service feishu-sync {
|
||||||
|
//飞书ticket webhook事件接口
|
||||||
|
@handler WebhookHandler
|
||||||
|
post /api/feishu/webhook(request) returns (response);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user