feat: 添加云端函数HTTP请求、SMTP邮件、数据库操作能力

- 添加HTTP客户端工具支持GET/POST请求
- 添加SMTP邮件发送功能
- 添加数据库操作(db.getRecords, db.deleteRecord, db.deleteRecords)
- 添加订单管理云端函数(读取、接收、筛选、成功、失败)
- 添加测试推送和邮件通知云端函数
- 修复云端函数编辑页面预览代码换行显示
- 云端变量数据模型重构(合并数据类型)
This commit is contained in:
2026-04-30 20:54:02 +08:00
parent a4df9e91a0
commit 10044b474f
46 changed files with 15249 additions and 250 deletions
+6 -6
View File
@@ -730,8 +730,8 @@ func handleAppCreateVariableRecord(c *gin.Context) {
return
}
if variable.DataType != "stream" {
response.Error(c, 400, "该变量不是流水类型")
if variable.VarType != "stream" {
response.Error(c, 400, "该变量不是记录类型")
return
}
@@ -817,8 +817,8 @@ func handleAppGetVariableRecords(c *gin.Context) {
return
}
if variable.DataType != "stream" {
response.Error(c, 400, "该变量不是流水类型")
if variable.VarType != "stream" {
response.Error(c, 400, "该变量不是记录类型")
return
}
@@ -894,8 +894,8 @@ func handleAppDeleteVariableRecord(c *gin.Context) {
return
}
if variable.DataType != "stream" {
response.Error(c, 400, "该变量不是流水类型")
if variable.VarType != "stream" {
response.Error(c, 400, "该变量不是记录类型")
return
}
+281
View File
@@ -1,6 +1,8 @@
package app
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
@@ -8,6 +10,8 @@ import (
"verification-platform-backend/internal/database"
"verification-platform-backend/internal/model"
"verification-platform-backend/internal/service"
"verification-platform-backend/internal/utils/httputil"
"verification-platform-backend/internal/utils/smtputil"
"verification-platform-backend/pkg/jwt"
"verification-platform-backend/pkg/response"
@@ -244,6 +248,68 @@ func handleExecuteDynamicCode(c *gin.Context) {
return
}
httpClient := httputil.NewHTTPClient(10 * time.Second)
httpObj := map[string]interface{}{
"get": func(url string, headers map[string]interface{}) *httputil.HTTPResponse {
convertedHeaders := make(map[string]string)
for k, v := range headers {
convertedHeaders[k] = fmt.Sprintf("%v", v)
}
return httpClient.Get(url, convertedHeaders)
},
"post": func(url string, headers map[string]interface{}, body interface{}) *httputil.HTTPResponse {
convertedHeaders := make(map[string]string)
for k, v := range headers {
convertedHeaders[k] = fmt.Sprintf("%v", v)
}
return httpClient.Post(url, convertedHeaders, body)
},
}
if err := vm.Set("http", httpObj); err != nil {
response.Error(c, 500, "HTTP对象设置失败")
return
}
dbObj := map[string]interface{}{
"getRecords": func(variableKey string, page, pageSize int) map[string]interface{} {
return getRecords(app.ID, req.UserID, variableKey, page, pageSize)
},
"deleteRecord": func(variableKey string, recordID uint) map[string]interface{} {
return deleteRecord(app.ID, req.UserID, variableKey, recordID)
},
"deleteRecords": func(variableKey string, recordIDs []uint) map[string]interface{} {
return deleteRecords(app.ID, req.UserID, variableKey, recordIDs)
},
}
if err := vm.Set("db", dbObj); err != nil {
response.Error(c, 500, "数据库对象设置失败")
return
}
smtpObj := map[string]interface{}{
"send": func(config map[string]interface{}, to []string, subject, body string, html bool) *smtputil.SMTPResult {
smtpConfig := smtputil.SMTPConfig{
Host: getString(config, "host"),
Port: getInt(config, "port", 587),
Username: getString(config, "username"),
Password: getString(config, "password"),
From: getString(config, "from"),
UseTLS: getBool(config, "use_tls", true),
}
client := smtputil.NewSMTPClient(smtpConfig, 30*time.Second)
return client.Send(smtputil.EmailMessage{
To: to,
Subject: subject,
Body: body,
HTML: html,
})
},
}
if err := vm.Set("smtp", smtpObj); err != nil {
response.Error(c, 500, "SMTP对象设置失败")
return
}
value, err := vm.RunString("(function() { " + dynamicCode.Code + " })()")
if err != nil {
response.Error(c, 400, "代码执行错误: "+err.Error())
@@ -278,6 +344,11 @@ func handleExecuteDynamicCode(c *gin.Context) {
response.Error(c, 500, "更新应用变量失败: "+err.Error())
return
}
case "add_record":
if err := handleAddRecord(app.ID, req.UserID, actionMap); err != nil {
response.Error(c, 500, "添加记录失败: "+err.Error())
return
}
}
}
}
@@ -425,3 +496,213 @@ func toString(v interface{}) string {
return ""
}
}
func handleAddRecord(appID uint, userID *uint, actionMap map[string]interface{}) error {
variableKey, ok := actionMap["variable_key"].(string)
if !ok {
return fmt.Errorf("缺少variable_key参数")
}
recordData, ok := actionMap["record_data"]
if !ok {
return fmt.Errorf("缺少record_data参数")
}
var variable model.CloudVariable
if err := database.DB.Where("app_id = ? AND key = ?", appID, variableKey).First(&variable).Error; err != nil {
return fmt.Errorf("云端变量不存在: %s", variableKey)
}
if variable.VarType != "stream" {
return fmt.Errorf("该变量不是记录类型")
}
dataBytes, err := json.Marshal(recordData)
if err != nil {
return fmt.Errorf("序列化记录数据失败: %v", err)
}
record := model.CloudVariableRecord{
CloudVariableID: variable.ID,
Data: string(dataBytes),
}
if variable.Scope == "user" && userID != nil {
appUserID := *userID
record.AppUserID = &appUserID
}
if err := database.DB.Create(&record).Error; err != nil {
return fmt.Errorf("创建记录失败: %v", err)
}
if variable.MaxRecords > 0 {
var total int64
database.DB.Model(&model.CloudVariableRecord{}).Where("cloud_variable_id = ?", variable.ID).Count(&total)
if int(total) > variable.MaxRecords {
deleteCount := int(total) - variable.MaxRecords
var oldRecords []model.CloudVariableRecord
database.DB.Where("cloud_variable_id = ?", variable.ID).
Order("created_at ASC").
Limit(deleteCount).
Find(&oldRecords)
for _, oldRecord := range oldRecords {
database.DB.Delete(&oldRecord)
}
}
}
return nil
}
func getRecords(appID uint, userID *uint, variableKey string, page, pageSize int) map[string]interface{} {
result := map[string]interface{}{
"success": false,
"records": []interface{}{},
"total": 0,
"page": page,
"pageSize": pageSize,
}
var variable model.CloudVariable
if err := database.DB.Where("app_id = ? AND key = ?", appID, variableKey).First(&variable).Error; err != nil {
result["error"] = "云端变量不存在"
return result
}
if variable.VarType != "stream" {
result["error"] = "该变量不是记录类型"
return result
}
if page < 1 {
page = 1
}
if pageSize < 1 || pageSize > 100 {
pageSize = 20
}
var total int64
query := database.DB.Model(&model.CloudVariableRecord{}).Where("cloud_variable_id = ?", variable.ID)
if variable.Scope == "user" && userID != nil {
query = query.Where("app_user_id = ?", *userID)
}
query.Count(&total)
var records []model.CloudVariableRecord
offset := (page - 1) * pageSize
if err := query.Order("created_at DESC").Offset(offset).Limit(pageSize).Find(&records).Error; err != nil {
result["error"] = "查询记录失败"
return result
}
var recordList []interface{}
for _, record := range records {
var data interface{}
if err := json.Unmarshal([]byte(record.Data), &data); err == nil {
recordList = append(recordList, map[string]interface{}{
"id": record.ID,
"data": data,
"created_at": record.CreatedAt,
})
}
}
result["success"] = true
result["records"] = recordList
result["total"] = total
return result
}
func deleteRecord(appID uint, userID *uint, variableKey string, recordID uint) map[string]interface{} {
result := map[string]interface{}{
"success": false,
}
var variable model.CloudVariable
if err := database.DB.Where("app_id = ? AND key = ?", appID, variableKey).First(&variable).Error; err != nil {
result["error"] = "云端变量不存在"
return result
}
if variable.VarType != "stream" {
result["error"] = "该变量不是记录类型"
return result
}
query := database.DB.Where("id = ? AND cloud_variable_id = ?", recordID, variable.ID)
if variable.Scope == "user" && userID != nil {
query = query.Where("app_user_id = ?", *userID)
}
if err := query.Delete(&model.CloudVariableRecord{}).Error; err != nil {
result["error"] = "删除记录失败"
return result
}
result["success"] = true
return result
}
func deleteRecords(appID uint, userID *uint, variableKey string, recordIDs []uint) map[string]interface{} {
result := map[string]interface{}{
"success": false,
"deleted": 0,
}
var variable model.CloudVariable
if err := database.DB.Where("app_id = ? AND key = ?", appID, variableKey).First(&variable).Error; err != nil {
result["error"] = "云端变量不存在"
return result
}
if variable.VarType != "stream" {
result["error"] = "该变量不是记录类型"
return result
}
query := database.DB.Where("id IN ? AND cloud_variable_id = ?", recordIDs, variable.ID)
if variable.Scope == "user" && userID != nil {
query = query.Where("app_user_id = ?", *userID)
}
deleteResult := query.Delete(&model.CloudVariableRecord{})
if deleteResult.Error != nil {
result["error"] = "删除记录失败"
return result
}
result["success"] = true
result["deleted"] = deleteResult.RowsAffected
return result
}
func getString(m map[string]interface{}, key string) string {
if v, ok := m[key]; ok {
if s, ok := v.(string); ok {
return s
}
}
return ""
}
func getInt(m map[string]interface{}, key string, defaultValue int) int {
if v, ok := m[key]; ok {
switch val := v.(type) {
case int:
return val
case float64:
return int(val)
}
}
return defaultValue
}
func getBool(m map[string]interface{}, key string, defaultValue bool) bool {
if v, ok := m[key]; ok {
if b, ok := v.(bool); ok {
return b
}
}
return defaultValue
}