feat(server-go): 微信订阅消息骨架(#11,绑定/订阅/巡检触发,配置占位)
This commit is contained in:
@@ -2,6 +2,7 @@ package handler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -25,8 +26,8 @@ func isUUID(s string) bool {
|
||||
}
|
||||
|
||||
// RegisterInspectionRoutes 注册 AI 巡检路由
|
||||
func RegisterInspectionRoutes(rg *gin.RouterGroup, db *gorm.DB, s3 *service.S3Service, ai *service.AIClient, imageBucket string) {
|
||||
rg.POST("/inspections", middleware.RequirePermission(db, "inspection:create"), createInspection(db, s3, ai, imageBucket))
|
||||
func RegisterInspectionRoutes(rg *gin.RouterGroup, db *gorm.DB, s3 *service.S3Service, ai *service.AIClient, imageBucket string, wechat *service.WechatService, inspectionTemplateID string) {
|
||||
rg.POST("/inspections", middleware.RequirePermission(db, "inspection:create"), createInspection(db, s3, ai, imageBucket, wechat, inspectionTemplateID))
|
||||
rg.GET("/inspections", middleware.RequirePermission(db, "inspection:read"), listInspections(db))
|
||||
}
|
||||
|
||||
@@ -44,7 +45,7 @@ func currentUserID(c *gin.Context) *string {
|
||||
|
||||
// createInspection 拍照巡检:图片存 S3 → 调 AI /detect → 写记录。
|
||||
// 幂等:客户端传 Idempotency-Key 头时,重复请求返回已有记录。
|
||||
func createInspection(db *gorm.DB, s3 *service.S3Service, ai *service.AIClient, bucket string) gin.HandlerFunc {
|
||||
func createInspection(db *gorm.DB, s3 *service.S3Service, ai *service.AIClient, bucket string, wechat *service.WechatService, inspectionTemplateID string) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
idemKey := strings.TrimSpace(c.GetHeader("Idempotency-Key"))
|
||||
roomID := strings.TrimSpace(c.PostForm("roomId"))
|
||||
@@ -129,6 +130,33 @@ func createInspection(db *gorm.DB, s3 *service.S3Service, ai *service.AIClient,
|
||||
rec.RiskScore = &score
|
||||
level := service.RiskLevel(score)
|
||||
rec.RiskLevel = &level
|
||||
|
||||
// 微信订阅消息(#11 骨架):风险非绿且用户已授权时异步推送
|
||||
if key := service.WechatTemplateKey(level); key != "" {
|
||||
go func(uid *string, lv string, sc float64) {
|
||||
if uid == nil || !wechat.Configured() || inspectionTemplateID == "" {
|
||||
return
|
||||
}
|
||||
var binding model.WechatBinding
|
||||
if db.Where("user_id = ?", *uid).First(&binding).Error != nil {
|
||||
return
|
||||
}
|
||||
var authorized []string
|
||||
if len(binding.AuthorizedTemplates) > 0 {
|
||||
_ = json.Unmarshal(binding.AuthorizedTemplates, &authorized)
|
||||
}
|
||||
if !service.IsAuthorized(authorized, key) {
|
||||
return
|
||||
}
|
||||
_ = wechat.SendSubscribe(
|
||||
context.Background(),
|
||||
binding.OpenID,
|
||||
inspectionTemplateID,
|
||||
service.BuildSubscribeData(lv, sc),
|
||||
"pages/inspection/index",
|
||||
)
|
||||
}(rec.UserID, level, score)
|
||||
}
|
||||
}
|
||||
|
||||
if err := db.Create(&rec).Error; err != nil {
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"silk-server-go/internal/middleware"
|
||||
"silk-server-go/internal/model"
|
||||
"silk-server-go/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// RegisterWechatRoutes 注册微信订阅消息路由(骨架,配置占位)
|
||||
func RegisterWechatRoutes(rg *gin.RouterGroup, db *gorm.DB, wechat *service.WechatService) {
|
||||
read := middleware.RequirePermission(db, "notification:read")
|
||||
write := middleware.RequirePermission(db, "notification:write")
|
||||
rg.GET("/wechat/binding", read, getWechatBinding(db))
|
||||
rg.POST("/wechat/bind", write, bindWechat(db, wechat))
|
||||
rg.POST("/wechat/subscribe", write, updateWechatSubscribe(db))
|
||||
}
|
||||
|
||||
// getWechatBinding 当前用户的微信绑定与授权模板
|
||||
func getWechatBinding(db *gorm.DB) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
uid := currentUserID(c)
|
||||
if uid == nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "未登录"})
|
||||
return
|
||||
}
|
||||
var b model.WechatBinding
|
||||
if db.Where("user_id = ?", *uid).First(&b).Error != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"bound": false})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"bound": true,
|
||||
"openId": b.OpenID,
|
||||
"authorized": b.AuthorizedTemplates,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// bindWechat 用 wx.login 的 code 换 openid 并绑定当前用户
|
||||
func bindWechat(db *gorm.DB, wechat *service.WechatService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
uid := currentUserID(c)
|
||||
if uid == nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "未登录"})
|
||||
return
|
||||
}
|
||||
var body struct {
|
||||
Code string `json:"code"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil || body.Code == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "缺少 code"})
|
||||
return
|
||||
}
|
||||
openid, err := wechat.Code2Session(c.Request.Context(), body.Code)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
var b model.WechatBinding
|
||||
if db.Where("user_id = ?", *uid).First(&b).Error != nil {
|
||||
b = model.WechatBinding{UserID: *uid, OpenID: openid}
|
||||
if err := db.Create(&b).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "绑定失败"})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
db.Model(&model.WechatBinding{}).Where("id = ?", b.ID).Update("open_id", openid)
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"bound": true, "openId": openid})
|
||||
}
|
||||
}
|
||||
|
||||
// updateWechatSubscribe 更新用户对某场景的订阅授权(alarm/inspection)
|
||||
func updateWechatSubscribe(db *gorm.DB) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
uid := currentUserID(c)
|
||||
if uid == nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "未登录"})
|
||||
return
|
||||
}
|
||||
var body struct {
|
||||
Template string `json:"template"`
|
||||
Authorized bool `json:"authorized"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil || body.Template == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "缺少 template"})
|
||||
return
|
||||
}
|
||||
var b model.WechatBinding
|
||||
if db.Where("user_id = ?", *uid).First(&b).Error != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "请先绑定微信"})
|
||||
return
|
||||
}
|
||||
var templates []string
|
||||
if len(b.AuthorizedTemplates) > 0 {
|
||||
_ = json.Unmarshal(b.AuthorizedTemplates, &templates)
|
||||
}
|
||||
templates = toggleTemplate(templates, body.Template, body.Authorized)
|
||||
raw, _ := json.Marshal(templates)
|
||||
db.Model(&model.WechatBinding{}).Where("id = ?", b.ID).Update("authorized_templates", raw)
|
||||
c.JSON(http.StatusOK, gin.H{"authorized": templates})
|
||||
}
|
||||
}
|
||||
|
||||
// toggleTemplate 增删授权模板键
|
||||
func toggleTemplate(list []string, key string, add bool) []string {
|
||||
out := make([]string, 0, len(list))
|
||||
exists := false
|
||||
for _, k := range list {
|
||||
if k == key {
|
||||
exists = true
|
||||
if add {
|
||||
out = append(out, k)
|
||||
}
|
||||
continue
|
||||
}
|
||||
out = append(out, k)
|
||||
}
|
||||
if add && !exists {
|
||||
out = append(out, key)
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user