Files
2026-05-21 19:52:45 +08:00

247 lines
8.3 KiB
Go

package share
import (
"context"
"net/http"
"github.com/labstack/echo/v4"
wechat_v2 "github.com/silenceper/wechat/v2"
"github.com/silenceper/wechat/v2/cache"
offConfig "github.com/silenceper/wechat/v2/officialaccount/config"
"github.com/silenceper/wechat/v2/officialaccount/message"
"github.com/chaitin/panda-wiki/consts"
"github.com/chaitin/panda-wiki/domain"
"github.com/chaitin/panda-wiki/handler"
"github.com/chaitin/panda-wiki/log"
"github.com/chaitin/panda-wiki/usecase"
)
type ShareAppHandler struct {
*handler.BaseHandler
logger *log.Logger
usecase *usecase.AppUsecase
}
func NewShareAppHandler(
e *echo.Echo,
baseHandler *handler.BaseHandler,
logger *log.Logger,
usecase *usecase.AppUsecase,
) *ShareAppHandler {
h := &ShareAppHandler{
BaseHandler: baseHandler,
logger: logger.WithModule("handler.share.app"),
usecase: usecase,
}
share := e.Group("share/v1/app",
func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
c.Response().Header().Set("Access-Control-Allow-Origin", "*")
c.Response().Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
c.Response().Header().Set("Access-Control-Allow-Headers", "Content-Type, Origin, Accept")
if c.Request().Method == "OPTIONS" {
return c.NoContent(http.StatusOK)
}
return next(c)
}
})
share.GET("/web/info", h.GetWebAppInfo)
share.GET("/widget/info", h.GetWidgetAppInfo)
share.GET("/wechat/info", h.WechatAppInfo)
// wechat official account
share.GET("/wechat/official_account", h.VerifyUrlWechatOfficialAccount)
share.POST("/wechat/official_account", h.WechatHandlerOfficialAccount)
return h
}
// GetWebAppInfo
//
// @Summary GetAppInfo
// @Description GetAppInfo
// @Tags share_app
// @Accept json
// @Produce json
// @Param X-KB-ID header string true "kb id"
// @Success 200 {object} domain.Response{data=domain.AppInfoResp}
// @Router /share/v1/app/web/info [get]
func (h *ShareAppHandler) GetWebAppInfo(c echo.Context) error {
kbID := c.Request().Header.Get("X-KB-ID")
if kbID == "" {
return h.NewResponseWithError(c, "kb_id is required", nil)
}
ctx := context.WithValue(c.Request().Context(), consts.ContextKeyEdition, consts.GetLicenseEdition(c))
appInfo, err := h.usecase.ShareGetWebAppInfo(ctx, kbID, domain.GetAuthID(c))
if err != nil {
return h.NewResponseWithError(c, err.Error(), err)
}
return h.NewResponseWithData(c, appInfo)
}
// GetWidgetAppInfo
//
// @Summary GetWidgetAppInfo
// @Description GetWidgetAppInfo
// @Tags share_app
// @Accept json
// @Produce json
// @Param X-KB-ID header string true "kb id"
// @Success 200 {object} domain.Response
// @Router /share/v1/app/widget/info [get]
func (h *ShareAppHandler) GetWidgetAppInfo(c echo.Context) error {
kbID := c.Request().Header.Get("X-KB-ID")
if kbID == "" {
return h.NewResponseWithError(c, "kb_id is required", nil)
}
appInfo, err := h.usecase.GetWidgetAppInfo(c.Request().Context(), kbID)
if err != nil {
return h.NewResponseWithError(c, err.Error(), err)
}
return h.NewResponseWithData(c, appInfo)
}
// WechatAppInfo
//
// @Summary WechatAppInfo
// @Description WechatAppInfo
// @Tags share_chat
// @Accept json
// @Produce json
// @Param X-KB-ID header string true "kb id"
// @Success 200 {object} domain.Response{data=v1.WechatAppInfoResp}
// @Router /share/v1/app/wechat/info [get]
func (h *ShareAppHandler) WechatAppInfo(c echo.Context) error {
kbID := c.Request().Header.Get("X-KB-ID")
if kbID == "" {
return h.NewResponseWithError(c, "kb_id is required", nil)
}
appInfo, err := h.usecase.GetWechatAppInfo(c.Request().Context(), kbID)
if err != nil {
return h.NewResponseWithError(c, err.Error(), err)
}
return h.NewResponseWithData(c, appInfo)
}
func (h *ShareAppHandler) VerifyUrlWechatOfficialAccount(c echo.Context) error {
kbID := c.Request().Header.Get("X-KB-ID")
if kbID == "" {
return h.NewResponseWithError(c, "kb_id is required", nil)
}
ctx := c.Request().Context()
// get wechat official account info
appInfo, err := h.usecase.GetAppDetailByKBIDAndAppType(ctx, kbID, domain.AppTypeWechatOfficialAccount)
if err != nil {
h.logger.Error("get app detail failed")
return h.NewResponseWithError(c, "GetAppDetailByKBIDAndAppType failed", err)
}
if appInfo.Settings.WechatOfficialAccountIsEnabled != nil && !*appInfo.Settings.WechatOfficialAccountIsEnabled {
return h.NewResponseWithError(c, "wechat official account is not enabled", err)
}
wc := wechat_v2.NewWechat()
memory := cache.NewMemory()
cfg := &offConfig.Config{
AppID: appInfo.Settings.WechatOfficialAccountAppID,
AppSecret: appInfo.Settings.WechatOfficialAccountAppSecret,
Token: appInfo.Settings.WechatOfficialAccountToken,
EncodingAESKey: appInfo.Settings.WechatOfficialAccountEncodingAESKey,
Cache: memory,
}
officialAccount := wc.GetOfficialAccount(cfg)
server := officialAccount.GetServer(c.Request(), c.Response().Writer)
// success
err = server.Serve()
if err != nil {
return h.NewResponseWithError(c, "serve message failed", err)
}
return nil
}
func (h *ShareAppHandler) WechatHandlerOfficialAccount(c echo.Context) error {
kbID := c.Request().Header.Get("X-KB-ID")
if kbID == "" {
return h.NewResponseWithError(c, "kb_id is required", nil)
}
ctx := c.Request().Context()
// get wechat official account info
appInfo, err := h.usecase.GetAppDetailByKBIDAndAppType(ctx, kbID, domain.AppTypeWechatOfficialAccount)
if err != nil {
h.logger.Error("get app detail failed")
return h.NewResponseWithError(c, "GetAppDetailByKBIDAndAppType failed", err)
}
if appInfo.Settings.WechatOfficialAccountIsEnabled != nil && !*appInfo.Settings.WechatOfficialAccountIsEnabled {
return h.NewResponseWithError(c, "wechat official account is not enabled", err)
}
wc := wechat_v2.NewWechat()
memory := cache.NewMemory()
cfg := &offConfig.Config{
AppID: appInfo.Settings.WechatOfficialAccountAppID,
AppSecret: appInfo.Settings.WechatOfficialAccountAppSecret,
Token: appInfo.Settings.WechatOfficialAccountToken,
EncodingAESKey: appInfo.Settings.WechatOfficialAccountEncodingAESKey,
Cache: memory,
}
officialAccount := wc.GetOfficialAccount(cfg)
server := officialAccount.GetServer(c.Request(), c.Response().Writer)
// message handler
server.SetMessageHandler(func(msg *message.MixMessage) *message.Reply {
h.logger.Info("received message:", log.Any("msgtype", msg.MsgType), log.Any("fromUserName", msg.FromUserName), log.String("content", msg.Content), log.Any("event type", msg.Event))
switch msg.MsgType {
case message.MsgTypeText:
// text消息
userOpenID := msg.FromUserName
userContent := msg.Content
h.logger.Info("user_open_id user_content", log.Any("user_open_id", userOpenID), log.Any("user content", userContent))
// 异步发送
go func(openID, content string) {
ctx := context.Background()
// send content to ai
result, err := h.usecase.GetWechatOfficialAccountResponse(ctx, officialAccount, kbID, openID, content)
if err != nil {
h.logger.Error("get wechat official account response failed", log.Error(err))
return
}
// send response to user --> 需要开启客服消息权限
err = h.usecase.SendCustomerServiceMessage(officialAccount, string(userOpenID), result)
if err != nil {
h.logger.Error("send to customer service failed", log.Error(err))
}
}(string(userOpenID), userContent)
return &message.Reply{MsgType: message.MsgTypeText, MsgData: message.NewText("您的问题已经收到,正在努力思考中,请稍候...")}
case message.MsgTypeEvent:
if msg.Event == message.EventSubscribe {
return &message.Reply{MsgType: message.MsgTypeText, MsgData: message.NewText("感谢关注,欢迎提问!")} // 立即回复简单信息
}
return nil
default:
h.logger.Info("unknown message type", log.Any("message type", msg.MsgType))
return &message.Reply{MsgType: message.MsgTypeText, MsgData: message.NewText("未知消息类型,请发送正确的类型...")}
}
})
// success
err = server.Serve()
if err != nil {
h.logger.Error("serve message failed", log.Error(err))
return h.NewResponseWithError(c, "serve message failed", err)
}
// send message to user
err = server.Send()
if err != nil {
h.logger.Error("send message failed", log.Error(err))
return h.NewResponseWithError(c, "send message failed", err)
}
return nil
}