274 lines
11 KiB
Go
274 lines
11 KiB
Go
package advice
|
||
|
||
import (
|
||
"ai_scheduler/internal/biz"
|
||
"ai_scheduler/internal/pkg"
|
||
"ai_scheduler/internal/pkg/wx"
|
||
"context"
|
||
"encoding/json"
|
||
"fmt"
|
||
"log"
|
||
|
||
"github.com/gofiber/fiber/v2"
|
||
)
|
||
|
||
// WxProxyService 微信 API 包装转发服务(非透传代理)
|
||
// 每个接口使用强类型请求/响应结构体,符合项目本身的输入输出规范
|
||
type WxProxyService struct {
|
||
projectBiz *biz.AdviceProjectBiz
|
||
}
|
||
|
||
// NewWxProxyService 创建微信代理服务
|
||
func NewWxProxyService(projectBiz *biz.AdviceProjectBiz) *WxProxyService {
|
||
return &WxProxyService{projectBiz: projectBiz}
|
||
}
|
||
|
||
// wxDispatcher 微信接口调度函数类型
|
||
type wxDispatcher func(ctx context.Context, token string, body []byte) (interface{}, error)
|
||
|
||
// callWx 泛型调度器:将 JSON body 解析为强类型请求,调用上游 wx API,返回强类型响应
|
||
func callWx[Req any, Res any](api wx.Api[Req, Res]) wxDispatcher {
|
||
return func(ctx context.Context, token string, body []byte) (interface{}, error) {
|
||
var req Req
|
||
if err := json.Unmarshal(body, &req); err != nil {
|
||
return nil, fmt.Errorf("请求参数解析失败: %w", err)
|
||
}
|
||
var res Res
|
||
if err := wx.Request(ctx, token, api, req, &res); err != nil {
|
||
return nil, err
|
||
}
|
||
return res, nil
|
||
}
|
||
}
|
||
|
||
// wxDispatchMap 接口路径 → 强类型调度函数映射表
|
||
var wxDispatchMap = map[string]wxDispatcher{
|
||
// ==================== 登录接口 ====================
|
||
"login/getLoginQrCode": callWx(wx.GetLoginQrCode),
|
||
"login/checkLogin": callWx(wx.CheckLogin),
|
||
"login/dialogLogin": callWx(wx.DialogLogin),
|
||
"login/setCallback": callWx(wx.SetCallback),
|
||
"login/checkOnline": callWx(wx.CheckOnline),
|
||
"login/logout": callWx(wx.Logout),
|
||
"login/reconnection": callWx(wx.Reconnection),
|
||
|
||
// ==================== 联系人接口 ====================
|
||
"contacts/fetchContactsList": callWx(wx.FetchContactsList),
|
||
"contacts/fetchContactsListCache": callWx(wx.FetchContactsListCache),
|
||
"contacts/getBriefInfo": callWx(wx.GetBriefInfo),
|
||
"contacts/getDetailInfo": callWx(wx.GetDetailInfo),
|
||
"contacts/search": callWx(wx.ContactsSearch),
|
||
"contacts/addContacts": callWx(wx.AddContacts),
|
||
"contacts/deleteFriend": callWx(wx.DeleteFriend),
|
||
"contacts/setFriendPermissions": callWx(wx.SetFriendPermissions),
|
||
"contacts/setFriendRemark": callWx(wx.SetFriendRemark),
|
||
"contacts/getPhoneAddressList": callWx(wx.GetPhoneAddressList),
|
||
"contacts/uploadPhoneAddressList": callWx(wx.UploadPhoneAddressList),
|
||
"contacts/imSearch": callWx(wx.ImSearch),
|
||
"contacts/imAdd": callWx(wx.ImAdd),
|
||
"contacts/imSync": callWx(wx.ImSync),
|
||
"contacts/imDetail": callWx(wx.ImDetail),
|
||
|
||
// ==================== 消息接口-下载 ====================
|
||
"message/downloadCdn": callWx(wx.DownloadCdn),
|
||
"message/downloadImage": callWx(wx.DownloadImage),
|
||
"message/downloadVoice": callWx(wx.DownloadVoice),
|
||
"message/downloadVideo": callWx(wx.DownloadVideo),
|
||
"message/downloadEmojiMd5": callWx(wx.DownloadEmojiMd5),
|
||
"message/downloadFile": callWx(wx.DownloadFile),
|
||
|
||
// ==================== 消息接口 ====================
|
||
"message/postText": callWx(wx.PostText),
|
||
"message/postFile": callWx(wx.PostFile),
|
||
"message/postImage": callWx(wx.PostImage),
|
||
"message/postVoice": callWx(wx.PostVoice),
|
||
"message/postVideo": callWx(wx.PostVideo),
|
||
"message/postLink": callWx(wx.PostLink),
|
||
"message/postNameCard": callWx(wx.PostNameCard),
|
||
"message/postEmoji": callWx(wx.PostEmoji),
|
||
"message/postAppMsg": callWx(wx.PostAppMsg),
|
||
"message/postMiniApp": callWx(wx.PostMiniApp),
|
||
"message/forwardFile": callWx(wx.ForwardFile),
|
||
"message/forwardImage": callWx(wx.ForwardImage),
|
||
"message/forwardVideo": callWx(wx.ForwardVideo),
|
||
"message/forwardUrl": callWx(wx.ForwardUrl),
|
||
"message/forwardMiniApp": callWx(wx.ForwardMiniApp),
|
||
"message/revokeMsg": callWx(wx.RevokeMsg),
|
||
|
||
// ==================== 个人接口 ====================
|
||
"personal/getProfile": callWx(wx.PersonalGetProfile),
|
||
"personal/getQrCode": callWx(wx.PersonalGetQrCode),
|
||
"personal/getSafetyInfo": callWx(wx.GetSafetyInfo),
|
||
"personal/privacySettings": callWx(wx.PrivacySettings),
|
||
"personal/updateProfile": callWx(wx.PersonalUpdateProfile),
|
||
"personal/updateHeadImg": callWx(wx.UpdateHeadImg),
|
||
|
||
// ==================== 标签接口 ====================
|
||
"label/add": callWx(wx.LabelAdd),
|
||
"label/delete": callWx(wx.LabelDelete),
|
||
"label/list": callWx(wx.LabelList),
|
||
"label/modifyMemberList": callWx(wx.ModifyMemberList),
|
||
|
||
// ==================== 群聊接口 ====================
|
||
"group/createChatroom": callWx(wx.CreateChatroom),
|
||
"group/modifyChatroomName": callWx(wx.ModifyChatroomName),
|
||
"group/modifyChatroomRemark": callWx(wx.ModifyChatroomRemark),
|
||
"group/modifyChatroomNickNameForSelf": callWx(wx.ModifyChatroomNickNameForSelf),
|
||
"group/inviteMember": callWx(wx.InviteMember),
|
||
"group/removeMember": callWx(wx.RemoveMember),
|
||
"group/quitChatroom": callWx(wx.QuitChatroom),
|
||
"group/disbandChatroom": callWx(wx.DisbandChatroom),
|
||
"group/getChatroomInfo": callWx(wx.GetChatroomInfo),
|
||
"group/getChatroomMemberList": callWx(wx.GetChatroomMemberList),
|
||
"group/getChatroomMemberDetail": callWx(wx.GetChatroomMemberDetail),
|
||
"group/getChatroomAnnouncement": callWx(wx.GetChatroomAnnouncement),
|
||
"group/setChatroomAnnouncement": callWx(wx.SetChatroomAnnouncement),
|
||
"group/agreeJoinRoom": callWx(wx.AgreeJoinRoom),
|
||
"group/addGroupMemberAsFriend": callWx(wx.AddGroupMemberAsFriend),
|
||
"group/getChatroomQrCode": callWx(wx.GetChatroomQrCode),
|
||
"group/saveContractList": callWx(wx.SaveContractList),
|
||
"group/adminOperate": callWx(wx.AdminOperate),
|
||
"group/pinChat": callWx(wx.PinChat),
|
||
"group/setMsgSilence": callWx(wx.SetMsgSilence),
|
||
"group/joinRoomUsingQRCode": callWx(wx.JoinRoomUsingQRCode),
|
||
"group/roomAccessApplyCheckApprove": callWx(wx.RoomAccessApplyCheckApprove),
|
||
|
||
// ==================== 朋友圈接口 ====================
|
||
"sns/likeSns": callWx(wx.LikeSns),
|
||
"sns/delSns": callWx(wx.DelSns),
|
||
"sns/snsVisibleScope": callWx(wx.SnsVisibleScope),
|
||
"sns/strangerVisibilityEnabled": callWx(wx.StrangerVisibilityEnabled),
|
||
"sns/snsSetPrivacy": callWx(wx.SnsSetPrivacy),
|
||
"sns/downloadSnsVideo": callWx(wx.DownloadSnsVideo),
|
||
"sns/sendTextSns": callWx(wx.SendTextSns),
|
||
"sns/sendImgSns": callWx(wx.SendImgSns),
|
||
"sns/sendVideoSns": callWx(wx.SendVideoSns),
|
||
"sns/sendUrlSns": callWx(wx.SendUrlSns),
|
||
"sns/uploadSnsImage": callWx(wx.UploadSnsImage),
|
||
"sns/uploadSnsVideo": callWx(wx.UploadSnsVideo),
|
||
"sns/forwardSns": callWx(wx.ForwardSns),
|
||
"sns/snsList": callWx(wx.SnsList),
|
||
"sns/contactsSnsList": callWx(wx.ContactsSnsList),
|
||
"sns/snsDetails": callWx(wx.SnsDetails),
|
||
"sns/commentSns": callWx(wx.CommentSns),
|
||
}
|
||
|
||
// Proxy 处理微信 API 请求:
|
||
// 1. 从请求体中提取 projectId,查询项目级 wx_token
|
||
// 2. 根据接口路径分发到对应的强类型处理函数
|
||
// 3. 以标准 {code, message, data} 格式返回
|
||
func (s *WxProxyService) Proxy(c *fiber.Ctx) error {
|
||
sub := c.Params("*")
|
||
|
||
// 查找对应的调度函数
|
||
dispatcher, ok := wxDispatchMap[sub]
|
||
if !ok {
|
||
return pkg.HandleResponse(c, nil, fiber.NewError(fiber.StatusBadRequest, "不支持的微信接口: "+sub))
|
||
}
|
||
|
||
// 解析请求体
|
||
var reqBody map[string]interface{}
|
||
if len(c.Body()) > 0 {
|
||
if err := json.Unmarshal(c.Body(), &reqBody); err != nil {
|
||
return pkg.HandleResponse(c, nil, fiber.NewError(fiber.StatusBadRequest, "请求体解析失败: "+err.Error()))
|
||
}
|
||
} else {
|
||
reqBody = map[string]interface{}{}
|
||
}
|
||
|
||
// 从请求体提取 projectId
|
||
var projectId int32
|
||
if v, ok := reqBody["projectId"]; ok {
|
||
switch pv := v.(type) {
|
||
case float64:
|
||
projectId = int32(pv)
|
||
case int:
|
||
projectId = int32(pv)
|
||
}
|
||
}
|
||
|
||
if projectId == 0 {
|
||
return pkg.HandleResponse(c, nil, fiber.NewError(fiber.StatusBadRequest, "缺少 projectId 参数"))
|
||
}
|
||
|
||
// 查询项目的 wx_token
|
||
project, err := s.projectBiz.BaseInfo(projectId)
|
||
if err != nil {
|
||
return pkg.HandleResponse(c, nil, err)
|
||
}
|
||
if project.WxToken == "" {
|
||
return pkg.HandleResponse(c, nil, fiber.NewError(fiber.StatusBadRequest, "项目未配置微信接入 Token,请先在项目设置中配置"))
|
||
}
|
||
|
||
// 调用对应的强类型处理函数
|
||
data, err := dispatcher(c.UserContext(), project.WxToken, c.Body())
|
||
if err != nil {
|
||
return pkg.HandleResponse(c, nil, fiber.NewError(fiber.StatusBadGateway, "微信接口调用失败: "+err.Error()))
|
||
}
|
||
|
||
// TODO: 临时调试日志,排查完成后删除
|
||
if sub == "contacts/getDetailInfo" {
|
||
if raw, jerr := json.Marshal(data); jerr == nil {
|
||
log.Printf("[DEBUG] contacts/getDetailInfo raw response: %s", string(raw))
|
||
}
|
||
}
|
||
|
||
// 标记跳过 registerCommon 的二次包装,避免响应被双层嵌套
|
||
// Proxy 已自行返回标准 {code, data, message} 格式,无需中间件再包一层
|
||
c.Locals("skip_response_wrap", true)
|
||
return c.JSON(fiber.Map{
|
||
"code": 0,
|
||
"data": data,
|
||
"message": "ok",
|
||
})
|
||
}
|
||
|
||
// BatchChatroomInfoReq 批量获取群信息请求
|
||
type BatchChatroomInfoReq struct {
|
||
ProjectId int32 `json:"projectId"`
|
||
AppId string `json:"appId"`
|
||
ChatroomIds []string `json:"chatroomIds"`
|
||
}
|
||
|
||
// BatchChatroomInfo 批量获取多个群的信息(内部循环调用 wx API,一次返回)
|
||
func (s *WxProxyService) BatchChatroomInfo(c *fiber.Ctx) error {
|
||
var req BatchChatroomInfoReq
|
||
if err := c.BodyParser(&req); err != nil {
|
||
return pkg.HandleResponse(c, nil, err)
|
||
}
|
||
if len(req.ChatroomIds) == 0 {
|
||
return pkg.HandleResponse(c, nil, nil)
|
||
}
|
||
|
||
// 查询项目的 wx_token
|
||
project, err := s.projectBiz.BaseInfo(req.ProjectId)
|
||
if err != nil {
|
||
return pkg.HandleResponse(c, nil, err)
|
||
}
|
||
if project.WxToken == "" {
|
||
return pkg.HandleResponse(c, nil, fiber.NewError(fiber.StatusBadRequest, "项目未配置微信接入 Token"))
|
||
}
|
||
|
||
// 循环调用 wx API,收集所有结果
|
||
results := make([]wx.GetChatroomInfoResData, 0, len(req.ChatroomIds))
|
||
for _, chatroomId := range req.ChatroomIds {
|
||
wxReq := wx.GetChatroomInfoReq{
|
||
AppId: req.AppId,
|
||
ChatroomId: chatroomId,
|
||
}
|
||
var wxRes wx.GetChatroomInfoResData
|
||
if err := wx.Request(c.UserContext(), project.WxToken, wx.GetChatroomInfo, wxReq, &wxRes); err != nil {
|
||
log.Printf("[WARN] batch_chatroom_info: getChatroomInfo(%s) failed: %v", chatroomId, err)
|
||
continue // 单个失败不影响其他
|
||
}
|
||
results = append(results, wxRes)
|
||
}
|
||
|
||
c.Locals("skip_response_wrap", true)
|
||
return c.JSON(fiber.Map{
|
||
"code": 0,
|
||
"data": results,
|
||
"message": "ok",
|
||
})
|
||
}
|