diff --git a/XinxinAkuma/AI/AI.go b/XinxinAkuma/AI/AI.go new file mode 100644 index 0000000..aef8d43 --- /dev/null +++ b/XinxinAkuma/AI/AI.go @@ -0,0 +1,210 @@ +package AI + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "encoding/json" + "fmt" + "github.com/gorilla/websocket" + "github.com/joho/godotenv" + "io" + "log" + "net/http" + "net/url" + "os" + "strings" + "time" +) + +const ( + xunfeiAIAPIUrl = "wss://spark-api.xf-yun.com/v3.5/chat" +) + +// GenerateSum 通过WebSocket与AI模型交互以生成答案 +func GenerateSum(question string, answers []string) (string, error) { + err := godotenv.Load("D:/ermian/Akuma/secret.env") + if err != nil { + log.Fatal("Error loading .env file") + } + + apiKey := os.Getenv("API_KEY") + apiSecret := os.Getenv("API_SECRET") + appId := os.Getenv("APP_ID") + + d := websocket.Dialer{ + HandshakeTimeout: 5 * time.Second, + } + // 握手并建立websocket连接 + conn, resp, err := d.Dial(assembleAuthUrl1(xunfeiAIAPIUrl, apiKey, apiSecret), nil) + if err != nil { + return "", fmt.Errorf("连接失败: %s, %v", readResp(resp), err) + } + defer func(conn *websocket.Conn) { + err := conn.Close() + if err != nil { + + } + }(conn) // 确保在函数结束时关闭连接 + + // 将所有的答案用 | 符号连接起来 + joinedAnswers := strings.Join(answers, "| ") + + // 构造最终的提示词 + prompt := fmt.Sprintf("我会给你一个问题和一组用 | 符号分隔的答案,帮我总结一个完整的回答,不要带有自己的评论和分析。 问题: %s\n答案: %s", question, joinedAnswers) + data := genParams1(appId, prompt) + + // 发送数据 + if err := conn.WriteJSON(data); err != nil { + return "", fmt.Errorf("发送数据失败: %v", err) + } + + var answer string + + // 获取返回的数据 + for { + _, msg, err := conn.ReadMessage() + if err != nil { + return "", fmt.Errorf("读取消息失败: %v", err) + } + + var data map[string]interface{} + if err := json.Unmarshal(msg, &data); err != nil { + return "", fmt.Errorf("解析JSON失败: %v", err) + } + // 解析数据 + payload, ok := data["payload"].(map[string]interface{}) + if !ok { + return "", fmt.Errorf("无效的payload格式") + } + choices, ok := payload["choices"].(map[string]interface{}) + if !ok { + return "", fmt.Errorf("无效的choices格式") + } + header, ok := data["header"].(map[string]interface{}) + if !ok { + return "", fmt.Errorf("无效的header格式") + } + code, ok := header["code"].(float64) + if !ok || code != 0 { + return "", fmt.Errorf("错误的响应代码: %v", data["payload"]) + } + + status, ok := choices["status"].(float64) + if !ok { + return "", fmt.Errorf("无效的status格式") + } + text, ok := choices["text"].([]interface{}) + if !ok { + return "", fmt.Errorf("无效的text格式") + } + content, ok := text[0].(map[string]interface{})["content"].(string) + if !ok { + return "", fmt.Errorf("无效的content格式") + } + + if status != 2 { + answer += content + } else { + answer += content + usage, ok := payload["usage"].(map[string]interface{}) + if ok { + temp, ok := usage["text"].(map[string]interface{}) + if ok { + totalTokens, ok := temp["total_tokens"].(float64) + if ok { + fmt.Println("total_tokens:", totalTokens) + } + } + } + break + } + } + + // 输出返回结果 + return answer, nil +} + +// 生成参数 +func genParams1(appid, question string) map[string]interface{} { // 根据实际情况修改返回的数据结构和字段名 + + messages := []Message{ + {Role: "user", Content: question}, + } + + data := map[string]interface{}{ // 根据实际情况修改返回的数据结构和字段名 + "header": map[string]interface{}{ // 根据实际情况修改返回的数据结构和字段名 + "app_id": appid, // 根据实际情况修改返回的数据结构和字段名 + }, + "parameter": map[string]interface{}{ // 根据实际情况修改返回的数据结构和字段名 + "chat": map[string]interface{}{ // 根据实际情况修改返回的数据结构和字段名 + "domain": "general", // 根据实际情况修改返回的数据结构和字段名 + "temperature": float64(0.8), // 根据实际情况修改返回的数据结构和字段名 + "top_k": int64(6), // 根据实际情况修改返回的数据结构和字段名 + "max_tokens": int64(2048), // 根据实际情况修改返回的数据结构和字段名 + "auditing": "default", // 根据实际情况修改返回的数据结构和字段名 + }, + }, + "payload": map[string]interface{}{ // 根据实际情况修改返回的数据结构和字段名 + "message": map[string]interface{}{ // 根据实际情况修改返回的数据结构和字段名 + "text": messages, // 根据实际情况修改返回的数据结构和字段名 + }, + }, + } + return data // 根据实际情况修改返回的数据结构和字段名 +} + +// 创建鉴权url apikey 即 hmac username +func assembleAuthUrl1(hosturl string, apiKey, apiSecret string) string { + ul, err := url.Parse(hosturl) + if err != nil { + fmt.Println(err) + } + //签名时间 + date := time.Now().UTC().Format(time.RFC1123) + //date = "Tue, 28 May 2019 09:10:42 MST" + //参与签名的字段 host ,date, request-line + signString := []string{"host: " + ul.Host, "date: " + date, "GET " + ul.Path + " HTTP/1.1"} + //拼接签名字符串 + sgin := strings.Join(signString, "\n") + // fmt.Println(sgin) + //签名结果 + sha := HmacWithShaTobase64("hmac-sha256", sgin, apiSecret) + // fmt.Println(sha) + //构建请求参数 此时不需要urlencoding + authUrl := fmt.Sprintf("hmac username=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", apiKey, + "hmac-sha256", "host date request-line", sha) + //将请求参数使用base64编码 + authorization := base64.StdEncoding.EncodeToString([]byte(authUrl)) + + v := url.Values{} + v.Add("host", ul.Host) + v.Add("date", date) + v.Add("authorization", authorization) + //将编码后的字符串url encode后添加到url后面 + callurl := hosturl + "?" + v.Encode() + return callurl +} + +func HmacWithShaTobase64(algorithm, data, key string) string { + mac := hmac.New(sha256.New, []byte(key)) + mac.Write([]byte(data)) + encodeData := mac.Sum(nil) + return base64.StdEncoding.EncodeToString(encodeData) +} + +func readResp(resp *http.Response) string { + if resp == nil { + return "" + } + b, err := io.ReadAll(resp.Body) + if err != nil { + panic(err) + } + return fmt.Sprintf("code=%d,body=%s", resp.StatusCode, string(b)) +} + +type Message struct { + Role string `json:"role"` + Content string `json:"content"` +} diff --git a/XinxinAkuma/auth/jwt.go b/XinxinAkuma/auth/jwt.go new file mode 100644 index 0000000..46a57a0 --- /dev/null +++ b/XinxinAkuma/auth/jwt.go @@ -0,0 +1,93 @@ +package auth + +import ( + "errors" + "github.com/gin-gonic/gin" + "github.com/golang-jwt/jwt/v4" + "net/http" + "os" + "strings" + "time" +) + +// 秘钥 +var jwtSecret = []byte(os.Getenv("JWT_KEY")) + +// 定义 JWT 的声明结构 +type Claims struct { + UserID uint `json:"user_id"` + UserName string `json:"name"` + jwt.RegisteredClaims +} + +// 生成JWT Token +func GenerateToken(userID uint, userName string) (string, error) { + // 定义 Token 的声明,包含用户信息和到期时间 + claims := &Claims{ + UserID: userID, + UserName: userName, + RegisteredClaims: jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 72)), // Token 有效期 72 小时 + }, + } + + // 创建带有声明的 Token + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + + // 签署 Token 并返回 + return token.SignedString(jwtSecret) +} + +// 验证JWT的中间件 +func AuthMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + // 获取请求头中的 Authorization 字段 + tokenString := strings.TrimSpace(c.GetHeader("Authorization")) + if tokenString == "" || !strings.HasPrefix(tokenString, "Bearer ") { + c.JSON(http.StatusUnauthorized, gin.H{"error": "未授权,请登录"}) + c.Abort() + return + } + + // 移除 Bearer 前缀 + tokenString = strings.TrimSpace(strings.TrimPrefix(tokenString, "Bearer ")) + + // 解析 Token + token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, errors.New("无效的签名方法") + } + return jwtSecret, nil + }) + + // 检查 Token 解析是否出错或者无效 + if err != nil { + if ve, ok := err.(*jwt.ValidationError); ok { + if ve.Errors&jwt.ValidationErrorMalformed != 0 { + c.JSON(http.StatusUnauthorized, gin.H{"error": "Token格式错误"}) + } else if ve.Errors&jwt.ValidationErrorExpired != 0 { + c.JSON(http.StatusUnauthorized, gin.H{"error": "Token已过期"}) + } else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 { + c.JSON(http.StatusUnauthorized, gin.H{"error": "Token尚未生效"}) + } else { + c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的Token"}) + } + c.Abort() + return + } + } + + // 检查 Token 是否有效 + if claims, ok := token.Claims.(*Claims); ok && token.Valid { + // 将用户信息保存在上下文中 + c.Set("user_id", claims.UserID) + c.Set("user_name", claims.UserName) + } else { + c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的Token"}) + c.Abort() + return + } + + c.Next() // 继续执行下一个处理器 + } +} diff --git a/XinxinAkuma/database1/userdatabase.go b/XinxinAkuma/database1/userdatabase.go new file mode 100644 index 0000000..268072e --- /dev/null +++ b/XinxinAkuma/database1/userdatabase.go @@ -0,0 +1,52 @@ +package database1 + +import ( + "fmt" + "github.com/joho/godotenv" + "gorm.io/driver/mysql" + "gorm.io/gorm" + "log" + "os" +) + +var DB *gorm.DB + +// InitDB 初始化数据库连接 +func InitDB() { + er := godotenv.Load("D:/ermian/Akuma/secret.env") + if er != nil { + log.Fatal("Error loading .env file") + } + + dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", + os.Getenv("DB_USER"), + os.Getenv("DB_PASSWORD"), + os.Getenv("DB_HOST"), + os.Getenv("DB_PORT"), + os.Getenv("DB_1")) + var err error + DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) + if err != nil { + log.Fatal("无法连接到数据库:", err) + } + log.Println("数据库连接成功") + + // 测试数据库连接 + sqlDB, err := DB.DB() + if err != nil { + log.Fatal("获取数据库实例失败:", err) + } + + // Ping 数据库 + if err := sqlDB.Ping(); err != nil { + log.Fatal("数据库连接失败:", err) + } + log.Println("数据库连接测试成功") +} + +func AutoMigrate(models ...interface{}) { + err := DB.AutoMigrate(models...) + if err != nil { + log.Fatal("自动迁移失败:", err) + } +} diff --git a/XinxinAkuma/database2/prodatabse.go b/XinxinAkuma/database2/prodatabse.go new file mode 100644 index 0000000..084b5f6 --- /dev/null +++ b/XinxinAkuma/database2/prodatabse.go @@ -0,0 +1,52 @@ +package database2 + +import ( + "fmt" + "github.com/joho/godotenv" + "gorm.io/driver/mysql" + "gorm.io/gorm" + "log" + "os" +) + +var DB *gorm.DB + +// InitDB 初始化数据库连接 +func InitDB() { + er := godotenv.Load("D:/ermian/Akuma/secret.env") + if er != nil { + log.Fatal("Error loading .env file") + } + + dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", + os.Getenv("DB_USER"), + os.Getenv("DB_PASSWORD"), + os.Getenv("DB_HOST"), + os.Getenv("DB_PORT"), + os.Getenv("DB_2")) + var err error + DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) + if err != nil { + log.Fatal("无法连接到数据库:", err) + } + log.Println("数据库连接成功") + + // 测试数据库连接 + sqlDB, err := DB.DB() + if err != nil { + log.Fatal("获取数据库实例失败:", err) + } + + // Ping 数据库 + if err := sqlDB.Ping(); err != nil { + log.Fatal("数据库连接失败:", err) + } + log.Println("数据库连接测试成功") +} + +func AutoMigrate(models ...interface{}) { + err := DB.AutoMigrate(models...) + if err != nil { + log.Fatal("自动迁移失败:", err) + } +} diff --git a/XinxinAkuma/go.mod b/XinxinAkuma/go.mod new file mode 100644 index 0000000..f14d799 --- /dev/null +++ b/XinxinAkuma/go.mod @@ -0,0 +1,66 @@ +module Akuma + +go 1.23.1 + +require ( + filippo.io/edwards25519 v1.1.0 // indirect + github.com/bytedance/sonic v1.12.3 // indirect + github.com/bytedance/sonic/loader v0.2.0 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect + github.com/gabriel-vasile/mimetype v1.4.5 // indirect + github.com/gin-contrib/sessions v1.0.1 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/gin-gonic/gin v1.10.0 // indirect + github.com/go-playground/assert/v2 v2.2.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.22.1 // indirect + github.com/go-sql-driver/mysql v1.8.1 // indirect + github.com/goccy/go-json v0.10.3 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang/protobuf v1.5.0 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/gorilla/context v1.1.2 // indirect + github.com/gorilla/securecookie v1.1.2 // indirect + github.com/gorilla/sessions v1.2.2 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/joho/godotenv v1.5.1 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.8 // indirect + github.com/knz/go-libedit v1.10.1 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/testify v1.9.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + github.com/yuin/goldmark v1.4.13 // indirect + golang.org/x/arch v0.10.0 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.29.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2 // indirect + golang.org/x/term v0.24.0 // indirect + golang.org/x/text v0.18.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + gorm.io/driver/mysql v1.5.7 // indirect + gorm.io/gorm v1.25.12 // indirect + nullprogram.com/x/optparse v1.0.0 // indirect + rsc.io/pdf v0.1.1 // indirect +) diff --git a/XinxinAkuma/go.sum b/XinxinAkuma/go.sum new file mode 100644 index 0000000..365e13e --- /dev/null +++ b/XinxinAkuma/go.sum @@ -0,0 +1,121 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU= +github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= +github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4= +github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4= +github.com/gin-contrib/sessions v1.0.1 h1:3hsJyNs7v7N8OtelFmYXFrulAf6zSR7nW/putcPEHxI= +github.com/gin-contrib/sessions v1.0.1/go.mod h1:ouxSFM24/OgIud5MJYQJLpy6AwxQ5EYO9yLhbtObGkM= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= +github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA= +github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= +github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o= +github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM= +github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= +github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= +github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= +github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= +github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/arch v0.10.0 h1:S3huipmSclq3PJMNe76NGwkBR504WFkQ5dhzWzP8ZW8= +golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= +golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= +gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= +gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= +gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/XinxinAkuma/login/login.go b/XinxinAkuma/login/login.go new file mode 100644 index 0000000..a1294b6 --- /dev/null +++ b/XinxinAkuma/login/login.go @@ -0,0 +1,66 @@ +package login + +import ( + "Akuma/auth" + "Akuma/database1" + "github.com/gin-gonic/gin" + "golang.org/x/crypto/bcrypt" + "net/http" +) + +type LoginRequest struct { + Name string `json:"name" binding:"required"` + Password string `json:"password" binding:"required"` +} + +type User struct { + ID uint `gorm:"primaryKey"` + Name string `json:"name" binding:"required"` + Password string `json:"password" binding:"required"` + Role string `json:"role"` // 用户角色字段 +} + +func CheckPasswordHash(password, hash string) bool { + err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) + return err == nil +} + +func Login(c *gin.Context) { + var input LoginRequest + // 绑定 JSON 输入 + if err := c.ShouldBindJSON(&input); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "输入无效"}) + return + } + + var user User + // 查找用户 + if err := database1.DB.Where("name = ?", input.Name).First(&user).Error; err != nil { + c.JSON(http.StatusUnauthorized, gin.H{"error": "用户名或密码错误"}) + return + } + + // 检查密码 + if !CheckPasswordHash(input.Password, user.Password) { + c.JSON(http.StatusUnauthorized, gin.H{"error": "用户名或密码错误"}) + return + } + + // 登录成功,返回用户信息 + token, err := auth.GenerateToken(user.ID, user.Name) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "无法生成token", + }) + return + } + + c.SetCookie("token", token, 3600*12, "/", "127.0.0.1", false, true) + + c.JSON(http.StatusOK, gin.H{ + "message": "登录成功", + "token": token, + "your_id": user.ID, + "role": user.Role, // 返回用户角色 + }) +} diff --git a/XinxinAkuma/main.go b/XinxinAkuma/main.go new file mode 100644 index 0000000..d2c598b --- /dev/null +++ b/XinxinAkuma/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "Akuma/database1" + "Akuma/database2" + "Akuma/problem" + "Akuma/register" + "Akuma/router" + "log" + "os" +) + +func main() { + database1.InitDB() + database1.AutoMigrate(®ister.User{}) + database2.InitDB() + database2.AutoMigrate(&problem.Problem{}, &problem.Submission{}) + + r := router.InitRouter() + port := os.Getenv("PORT") + if port == "" { + port = "8080" + } + log.Printf("Starting server on port %s", port) + err := r.Run(":" + port) + if err != nil { + log.Fatalf("Failed to start server: %v", err) + } +} diff --git a/XinxinAkuma/problem/create.go b/XinxinAkuma/problem/create.go new file mode 100644 index 0000000..9efadde --- /dev/null +++ b/XinxinAkuma/problem/create.go @@ -0,0 +1,87 @@ +package problem + +import ( + "Akuma/database2" + "errors" + "github.com/gin-gonic/gin" + "gorm.io/gorm" + "net/http" + "time" +) + +type Problem struct { + ID uint `gorm:"primaryKey" json:"id"` + Question string `json:"question" binding:"required"` + CreatedAt time.Time `json:"created_at"` + UserID uint `json:"user_id"` +} + +func Create(c *gin.Context) { + + userid, exist := c.Get("user_id") + if !exist { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "无法获取用户身份", + }) + return + } + + var create Problem + if err := c.ShouldBindJSON(&create); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "输入无效,请检查您的数据。", + }) + return + } + + var existingPro Problem + result := database2.DB.Where("question = ?", create.Question).First(&existingPro) + + if result.Error != nil && !errors.Is(result.Error, gorm.ErrRecordNotFound) { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "数据库查询错误。", + }) + return + } + + if result.Error == nil { + c.JSON(http.StatusConflict, gin.H{ + "error": "问题已存在。", + }) + return + } + + userIDUint, ok := userid.(uint) + if !ok { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "用户ID类型错误。", + }) + return + } + create.UserID = userIDUint + create.CreatedAt = time.Now() + + if err := database2.DB.Create(&create).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "问题创建失败。", + }) + return + } + + c.JSON(http.StatusOK, gin.H{ + "message": "问题创建成功。", + "problem": create, + }) +} +func GetProblem(c *gin.Context) { + + var pro []Problem + if err := database2.DB.Find(&pro).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "获取问题失败", + }) + } + c.JSON(http.StatusOK, gin.H{ + "problem": pro, + }) +} diff --git a/XinxinAkuma/problem/delete.go b/XinxinAkuma/problem/delete.go new file mode 100644 index 0000000..cdf7d15 --- /dev/null +++ b/XinxinAkuma/problem/delete.go @@ -0,0 +1,95 @@ +package problem + +import ( + "Akuma/database1" + "Akuma/database2" + "errors" + "github.com/gin-gonic/gin" + "gorm.io/gorm" + "net/http" +) + +type questionId struct { + QuestionId int `json:"questionId" binding:"required"` +} + +func DeleteProblem(c *gin.Context) { + username, exist := c.Get("user_name") + if !exist { + c.JSON(http.StatusUnauthorized, gin.H{ + "error": "无法获取用户身份", + }) + } + + var questionId questionId + + if err := c.ShouldBindJSON(&questionId); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "输入无效,请检查您的数据。", + }) + return + } + + var user User + database1.DB.Where("name=?", username).First(&user) + if user.Role == "admin" { + + var problem Problem + if err := database2.DB.Where("id=?", questionId.QuestionId).First(&problem).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + c.JSON(http.StatusNotFound, gin.H{ + "error": "问题未找到。", + }) + return + } + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "查询错误。", + }) + return + } + + if err := database2.DB.Delete(&problem).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "删除问题失败。", + }) + return + } + + c.JSON(http.StatusOK, gin.H{ + "message": "问题已成功删除。", + }) + } else { + var problem Problem + if err := database2.DB.Where("id=?", questionId.QuestionId).First(&problem).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + c.JSON(http.StatusNotFound, gin.H{ + "error": "问题未找到。", + }) + return + } + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "查询错误。", + }) + return + } + + if user.ID != problem.UserID { + c.JSON(http.StatusUnauthorized, gin.H{ + "error": "你没有权限删除别人的问题。", + }) + return + } + + if err := database2.DB.Delete(&problem).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "删除问题失败。", + }) + return + } + + c.JSON(http.StatusOK, gin.H{ + "message": "问题已成功删除。", + }) + } + +} diff --git a/XinxinAkuma/problem/search.go b/XinxinAkuma/problem/search.go new file mode 100644 index 0000000..7ed1612 --- /dev/null +++ b/XinxinAkuma/problem/search.go @@ -0,0 +1,56 @@ +package problem + +import ( + "Akuma/database2" + "errors" + "github.com/gin-gonic/gin" + "gorm.io/gorm" + "net/http" +) + +type Search struct { + Search string `json:"search" binding:"required"` +} + +func SearchProblem(c *gin.Context) { + var search Search + + if err := c.ShouldBindJSON(&search); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "输入无效。", + }) + return + } + + var question Problem + + if search.Search == "" { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "请输入问题", + }) + return + } + + result := database2.DB.Where("question LIKE ?", "%"+search.Search+"%").First(&question) + + if result.Error != nil { + if errors.Is(result.Error, gorm.ErrRecordNotFound) { + c.JSON(http.StatusNotFound, gin.H{ + "message": "未找到匹配的问题。", + }) + return + } + + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "查询错误。" + result.Error.Error(), + }) + return + } + + if result.Error == nil { + c.JSON(http.StatusOK, gin.H{ + "message": "已找到问题.", + "question_id": question.ID, + }) + } +} diff --git a/XinxinAkuma/problem/submission.go b/XinxinAkuma/problem/submission.go new file mode 100644 index 0000000..8942041 --- /dev/null +++ b/XinxinAkuma/problem/submission.go @@ -0,0 +1,169 @@ +package problem + +import ( + "Akuma/AI" + "Akuma/database2" + "errors" + "github.com/gin-gonic/gin" + "gorm.io/gorm" + "net/http" + "time" +) + +type User struct { + ID uint `gorm:"primaryKey"` + Name string `json:"name" binding:"required"` + Password string `json:"password" binding:"required"` + Role string `json:"role"` // 用户角色字段 +} + +type Submission struct { + ID uint `gorm:"primaryKey" json:"id"` + QuestionId uint `json:"question_id" binding:"required"` + UserID uint `json:"user_id"` + Submit string `json:"submit" binding:"required"` + CreatedAt time.Time `json:"created_at"` +} + +func Submit(c *gin.Context) { + var submit Submission + + if err := c.ShouldBindJSON(&submit); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "输入无效,请检查您的数据。", + }) + return + } + userid, exist := c.Get("user_id") + if !exist { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "无法获取用户身份", + }) + return + } + + submit.UserID = userid.(uint) + + var problem Problem + if err := database2.DB.First(&problem, submit.QuestionId).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "提交的问题不存在。", + }) + } else { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "数据库查询错误。", + }) + } + return + } + + var existingSub Submission + result := database2.DB.Where("question_id = ? AND submit = ?", submit.QuestionId, submit.Submit).First(&existingSub) + + if result.Error != nil && !errors.Is(result.Error, gorm.ErrRecordNotFound) { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "数据库查询错误。", + }) + return + } + + if result.Error == nil { + c.JSON(http.StatusConflict, gin.H{ + "error": "相同的回答已存在。", + }) + return + } + + submit.CreatedAt = time.Now() + if err := database2.DB.Create(&submit).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "提交失败。", + }) + return + } + + c.JSON(http.StatusOK, gin.H{ + "message": "提交成功。", + "submission": submit, + }) +} + +func GetSubmission(c *gin.Context) { + var questionId questionId + + if err := c.ShouldBindJSON(&questionId); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "输入无效,请检查您的数据。", + }) + return + } + + _, exist := c.Get("user_id") + if !exist { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "无法获取用户身份", + }) + return + } + + var sub []Submission + database2.DB.Where("question_id = ?", questionId.QuestionId).Find(&sub) + + c.JSON(http.StatusOK, gin.H{ + "submission": sub, + }) +} + +func GenerateAnswer(c *gin.Context) { + var questionId questionId + + if err := c.ShouldBindJSON(&questionId); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "输入无效,请检查您的数据。", + }) + return + } + + var question Problem + + if err := database2.DB.Where("id=?", questionId.QuestionId).First(&question).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "不存在此问题。", + }) + return + } + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "查询错误。" + err.Error(), + }) + return + } + + //获取所有提交的答案 + var submissions []Submission + if err := database2.DB.Where("question_id = ?", questionId.QuestionId).Find(&submissions).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "无法获取提交的回答。", + }) + return + } + + var answers []string + for _, submission := range submissions { + answers = append(answers, submission.Submit) + } + + //调用AI模型生成答案 + summary, err := AI.GenerateSum(question.Question, answers) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "无法生成." + err.Error(), + }) + return + } + + c.JSON(http.StatusOK, gin.H{ + "summary": summary, + }) +} diff --git a/XinxinAkuma/problem/update.go b/XinxinAkuma/problem/update.go new file mode 100644 index 0000000..8b81f13 --- /dev/null +++ b/XinxinAkuma/problem/update.go @@ -0,0 +1,77 @@ +package problem + +import ( + "Akuma/database2" + "github.com/gin-gonic/gin" + "net/http" + + "time" +) + +type updateProblem struct { + QuestionId int `json:"question_id" binding:"required"` + Question string `json:"question" binding:"required"` +} + +// Update 更新问题 +func Update(c *gin.Context) { + + var updatedProblem updateProblem + + if err := c.ShouldBindJSON(&updatedProblem); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "输入错误。", + }) + return + } + userid, exist := c.Get("user_id") + if !exist { + c.JSON(http.StatusUnauthorized, gin.H{ + "error": "无法获取用户身份", + }) + return + } + + // 检查是否存在该问题 + var existingProblem Problem + if err := database2.DB.First(&existingProblem, updatedProblem.QuestionId).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{ + "error": "问题未找到。", + }) + return + } + + // 检查是否存在与更新的问题内容相同的其他问题 + var duplicateProblem Problem + result := database2.DB.Where("question = ? AND id != ?", updatedProblem.Question, updatedProblem.QuestionId).First(&duplicateProblem) + if result.Error == nil { + // 如果找到与更新内容相同但 ID 不同的问题,返回错误 + c.JSON(http.StatusConflict, gin.H{ + "error": "相同问题已存在。", + }) + return + } + + if userid != existingProblem.UserID { + c.JSON(http.StatusUnauthorized, gin.H{ + "error": "你没有权限更新其他人问题。", + }) + return + } + + existingProblem.Question = updatedProblem.Question + existingProblem.CreatedAt = time.Now() + + if err := database2.DB.Save(&existingProblem).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "问题更新失败。", + }) + return + } + + // 返回成功响应 + c.JSON(http.StatusOK, gin.H{ + "message": "问题更新成功。", + "problem": existingProblem, + }) +} diff --git a/XinxinAkuma/register/register.go b/XinxinAkuma/register/register.go new file mode 100644 index 0000000..c5adce5 --- /dev/null +++ b/XinxinAkuma/register/register.go @@ -0,0 +1,83 @@ +package register + +import ( + "Akuma/database1" + "github.com/gin-gonic/gin" + "golang.org/x/crypto/bcrypt" // 用于密码哈希 + "net/http" +) + +type User struct { + ID uint `gorm:"primaryKey"` + Name string `json:"name" binding:"required" gorm:"unique"` + Password string `json:"password" binding:"required"` + Tpassword string `json:"tpassword" binding:"required" gorm:"-"` + Role string `json:"role" gorm:"default:'user'"` // 默认角色为普通用户 + InviteCode string `json:"invite_code" binding:"-"` // 邀请码字段 +} + +func Register(c *gin.Context) { + var register User + // 绑定 JSON 输入 + if err := c.ShouldBindJSON(®ister); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "输入无效,请检查您的数据。", + }) + return + } + + var existingUser User + result := database1.DB.Where("name = ?", register.Name).First(&existingUser) + + if result.Error == nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "用户已存在。", + }) + return + } + + if register.Password != register.Tpassword { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "两次输入的密码不相同。", + }) + return + } + + if register.InviteCode == "114514" { + register.Role = "admin" // 输入了有效的邀请码,则设置角色为管理员 + } else if register.InviteCode != "" { + // 如果输入了其他邀请码,返回错误 + c.JSON(http.StatusBadRequest, gin.H{ + "error": "邀请码无效。", + }) + return + } else { + register.Role = "user" // 没有输入邀请码,则设置角色为普通用户 + } + + // 哈希密码 + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(register.Password), bcrypt.DefaultCost) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "密码加密失败。", + }) + return + } + + register.Password = string(hashedPassword) // 替换为哈希后的密码 + + if err := database1.DB.Create(®ister).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "无法保存用户信息", + }) + return + } + + c.JSON(http.StatusOK, gin.H{ + "message": "你已注册成功", + "user": gin.H{ + "name": register.Name, + "role": register.Role, + }, + }) +} diff --git a/XinxinAkuma/router/router.go b/XinxinAkuma/router/router.go new file mode 100644 index 0000000..3ef5f3b --- /dev/null +++ b/XinxinAkuma/router/router.go @@ -0,0 +1,25 @@ +package router + +import ( + "Akuma/auth" + "Akuma/login" + "Akuma/problem" + "Akuma/register" + "github.com/gin-gonic/gin" +) + +func InitRouter() *gin.Engine { + r := gin.Default() + r.POST("/register", register.Register) // 用户注册路由 + r.POST("/login", login.Login) // 用户登录路由 + + r.POST("/create", auth.AuthMiddleware(), problem.Create) // 用户创建问题路由 + r.GET("/create", auth.AuthMiddleware(), problem.GetProblem) //查询问题路由 + r.POST("/submit", auth.AuthMiddleware(), problem.Submit) //用户回答问题路由 + r.GET("/submit", auth.AuthMiddleware(), problem.GetSubmission) //所有回答路由 + r.PUT("/update", auth.AuthMiddleware(), problem.Update) //更新问题 + r.POST("/search", auth.AuthMiddleware(), problem.SearchProblem) //查询问题 + r.GET("/generateAianswer", auth.AuthMiddleware(), problem.GenerateAnswer) //查询ai答案 + r.DELETE("/delete", auth.AuthMiddleware(), problem.DeleteProblem) //删除问题 + return r +} diff --git a/XinxinAkuma/secret.env b/XinxinAkuma/secret.env new file mode 100644 index 0000000..168d888 --- /dev/null +++ b/XinxinAkuma/secret.env @@ -0,0 +1,11 @@ +API_KEY="ad54d6374685da80a5f420297ab6af00" +API_SECRET="OTM2NGMxOWJjY2FkOGYwZTEyOTVjZGY2" +APP_ID="cee63188" +JWT_KEY="Xinxin" +DB_USER="root" +DB_PASSWORD="Wu12345678" +DB_HOST="127.0.0.1" +DB_PORT="3306" +DB_1="user_db" +DB_2="problem_db" +PORT="8080" \ No newline at end of file