├── .github
└── workflows
│ └── go.yml
├── .gitignore
├── LICENSE
├── README.md
├── cmd
├── gen
│ └── main.go
└── server
│ └── main.go
├── common
├── aiClient
│ ├── getter.go
│ ├── init.go
│ └── types.go
├── auth
│ ├── auth.go
│ ├── password
│ │ ├── password.go
│ │ └── password_test.go
│ └── u_info.go
├── bizError
│ └── biz_error.go
├── config
│ ├── config.go
│ ├── global.go
│ └── init.go
├── constant
│ └── constant.go
├── db
│ ├── db.go
│ └── gen.go
├── email
│ ├── email.go
│ └── template.go
├── env
│ └── env.go
├── goUtil
│ └── recover.go
├── inviteCodeGen
│ ├── invite_code.go
│ ├── invite_code2.go
│ └── invite_code_test.go
├── logs
│ └── log.go
├── random
│ └── rand_number.go
├── redis
│ ├── contents.go
│ └── v6.go
├── regexp
│ └── phone_email_regexp.go
└── types
│ ├── converter.go
│ ├── dataTrance.go
│ ├── id.go
│ ├── slice.go
│ └── time.go
├── config
└── prod.yml
├── dao
├── action.gen.go
├── aikey.gen.go
├── amount_details.gen.go
├── carmi.gen.go
├── cashback.gen.go
├── config.gen.go
├── dialog.gen.go
├── draw_record.gen.go
├── gen.go
├── installed_plugin.gen.go
├── invite_record.gen.go
├── message.gen.go
├── mytables.gen.go
├── notification.gen.go
├── order.gen.go
├── payment.gen.go
├── persona.gen.go
├── plugin.gen.go
├── product.gen.go
├── reward.gen.go
├── signin.gen.go
├── turnover.gen.go
├── upload_record.gen.go
├── user.gen.go
└── withdrawal_record.gen.go
├── docs
├── ChatGpt-Web-Api.md
└── openai.svg
├── go.mod
├── model
├── action.gen.go
├── aikey.gen.go
├── amount_details.gen.go
├── carmi.gen.go
├── cashback.gen.go
├── config.gen.go
├── dialog.gen.go
├── draw_record.gen.go
├── installed_plugin.gen.go
├── invite_record.gen.go
├── message.gen.go
├── mytables.gen.go
├── notification.gen.go
├── order.gen.go
├── payment.gen.go
├── persona.gen.go
├── plugin.gen.go
├── product.gen.go
├── reward.gen.go
├── signin.gen.go
├── sql
│ └── chatgpt.sql
├── turnover.gen.go
├── upload_record.gen.go
├── user.gen.go
├── user.go
└── withdrawal_record.gen.go
├── router
├── admin.go
├── admin
│ ├── amountHandlers
│ │ └── handler.go
│ ├── carmiHandlers
│ │ └── handler.go
│ ├── cashbackHandlers
│ │ └── handler.go
│ ├── configHandlers
│ │ └── handler.go
│ ├── dialogHandlers
│ │ └── handler.go
│ ├── drawHandlers
│ │ └── handler.go
│ ├── inviteHandlers
│ │ └── handler.go
│ ├── messageHandlers
│ │ └── handler.go
│ ├── notificationHandlers
│ │ └── handler.go
│ ├── orderHandlers
│ │ └── handler.go
│ ├── payHandlers
│ │ └── handler.go
│ ├── personaHandlers
│ │ └── handler.go
│ ├── pluginHandlers
│ │ └── handler.go
│ ├── productHandlers
│ │ └── handler.go
│ ├── signinHandlers
│ │ └── handler.go
│ ├── tokenHandlers
│ │ └── handler.go
│ ├── turnoverHandlers
│ │ └── handler.go
│ ├── userHandlers
│ │ └── handler.go
│ └── withdrawalHandlers
│ │ └── handler.go
├── base
│ ├── base.go
│ ├── request.go
│ └── response.go
├── front
│ ├── authHandlers
│ │ ├── handlers.go
│ │ ├── request.go
│ │ └── vo.go
│ ├── carmiHandlers
│ │ ├── handler.go
│ │ └── request.go
│ ├── chatHandlers
│ │ └── handler.go
│ ├── configHandlers
│ │ └── handler.go
│ ├── imagesHandlers
│ │ ├── handler.go
│ │ └── request.go
│ ├── messageHandlers
│ │ └── handler.go
│ ├── payHandlers
│ │ └── handler.go
│ ├── personaHandlers
│ │ └── handler.go
│ ├── pluginHandlers
│ │ └── handler.go
│ ├── productHandlers
│ │ ├── handler.go
│ │ └── response.go
│ ├── signInHandlers
│ │ └── signin.go
│ ├── turnoverHandlers
│ │ ├── handler.go
│ │ └── response.go
│ └── userHandlers
│ │ ├── handler.go
│ │ └── response.go
├── middlewares
│ ├── cors.go
│ ├── jwt.go
│ └── u_info.go
└── root.go
└── service
├── amount
└── amount.go
├── auth
├── login.go
└── password.go
├── carmi
├── admin.go
├── carmi.go
├── carmi_type.go
└── request.go
├── cashback
└── cashback.go
├── config
├── admin.go
├── config.go
└── models.go
├── dialog
└── dialog.go
├── draw
├── admin.go
├── draw_model.go
├── draw_openai.go
├── draw_sd.go
├── draw_strategy.go
├── proccess.go
├── request.go
└── service.go
├── gpt
├── gpt_test.go
├── process.go
└── requst.go
├── invite
└── invite.go
├── message
├── dto.go
├── message_service.go
└── response.go
├── notification
└── notification.go
├── order
└── order.go
├── pay
├── admin.go
└── pay.go
├── persona
├── admin.go
└── persona.go
├── plugin
├── admin.go
└── plugin.go
├── product
└── product.go
├── signin
├── admin.go
└── sign.go
├── sns
└── sns_codee.go
├── token
└── admin.go
├── turnover
├── admin.go
└── service3.go
├── user
├── admin.go
└── user.go
└── withdrawal
└── withdrawal.go
/.github/workflows/go.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a golang project
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
3 |
4 | name: Go
5 |
6 | on:
7 | push:
8 | branches: [ "master" ]
9 | pull_request:
10 | branches: [ "master" ]
11 |
12 | jobs:
13 |
14 | build:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - uses: actions/checkout@v3
18 |
19 | - name: Set up Go
20 | uses: actions/setup-go@v4
21 | with:
22 | go-version: '1.20'
23 |
24 | - name: Build
25 | run: |
26 | go mod tidy
27 | go build -v ./...
28 |
29 | # - name: Test
30 | # run: go test -v ./...
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 | .idea/
8 | .vscode/
9 | wechatbot
10 | storage.json
11 |
12 | # Test binary, built with `go test -c`
13 | *.test
14 |
15 | # Output of the go coverage tool, specifically when used with LiteIDE
16 | *.out
17 |
18 | # Dependency directories (remove the comment below to include it)
19 | # vendor/
20 | /config.json
21 | /config/dev.yml
22 |
23 | go.sum
24 |
25 | # log
26 | log/
27 | *.log
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 79E
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |

3 |
4 |
ChatGPT Web Go
5 |
6 | A commercially-viable ChatGpt web application built with Go.
7 |
8 | 可部署商业化的 ChatGpt 网页应用。
9 |
10 | 💡 本项目是后端服务,前端对应的项目是:[79E/ChatGPT-Web](https://github.com/79E/ChatGPT-Web/)
11 |
12 |
13 | [提交问题 Issues](https://github.com/heimeropen/chatgpt-web-go/issues)
14 |
15 |
16 |
17 |
18 | ## 交流群
19 |
20 |
21 |
22 |
23 |
24 |
25 | ## 主要功能
26 | #### 包括但不限于:
27 | - 后台管理系统,可对用户,Token,商品,卡密等进行管理
28 | - 精心设计的 UI,响应式设计
29 | - 极快的首屏加载速度(~100kb)
30 | - 支持Midjourney绘画和DALL·E模型绘画,GPT4等应用
31 | - 海量的内置 prompt 列表,来自[中文](https://github.com/PlexPt/awesome-chatgpt-prompts-zh)和[英文](https://github.com/f/awesome-chatgpt-prompts)
32 | - 一键导出聊天记录,完整的 Markdown 支持
33 | - 支持自定义API地址(如:[openAI](https://api.openai.com) / [API2D](https://api2d.com/r/192767))
34 |
35 |
36 | #### TODO:
37 | - [x] API Key 功能实现
38 | - [x] API Proxy 代理
39 | - [ ] 绘画功能
40 | - [ ] 思维导图功能
41 | - [ ] 支付功能完善
42 | - [ ] server端渲染模式支持
43 | - [ ] Docker 支持
44 |
45 |
46 | ## 本地启动
47 | **0.环境要求准备**
48 | - golang1.18
49 | - mysql 5.7+
50 | - redis
51 | - goland
52 |
53 | **1.先 `Fork` 本项目,然后克隆到本地。**
54 | ```
55 | 建议目录 ~/go/src/github.com/heimeropen/
56 | git clone https://github.com/heimeropen/chatgpt-web-go.git
57 | ```
58 |
59 | **2.导入sql**
60 | ```
61 | # sql文件
62 | ./model/sql/chatgpt.sql
63 | ```
64 |
65 | **3.配置文件**
66 | 在 ./config 目录下新建文件 dev.yml 内容如下:
67 | (配置内容需要更具自己环境更改)
68 | ```
69 | port: 8899
70 |
71 | db:
72 | type: mysql
73 | host: 127.0.0.1:3306
74 | user: root
75 | password: 123456
76 | name: chatgpt_web_go
77 |
78 | redis:
79 | addr: 127.0.0.1:6379
80 |
81 | gpt:
82 | proxy: # 代理支持 socks5h://x.x.x.x 或者 http://x.x.x.x
83 |
84 | emailServer:
85 | host:
86 | port:
87 | senderName:
88 | user:
89 | password:
90 | ```
91 |
92 | **4.运行**
93 | ```
94 | 用goland打开项目
95 | 启动main函数:
96 | ./cmd/server/main.go
97 | ```
98 |
99 | **前端服务**
100 | ```
101 | 前端服务安装参考:
102 | https://github.com/79E/ChatGpt-Web/blob/master/README.md
103 |
104 | 前端项目需要修改配置文件 .env.development, 指向本地服务端:
105 | VITE_APP_REQUEST_HOST=http://127.0.0.1:8899
106 | ```
107 |
108 |
109 |
110 | ### 页面截图
111 |
112 | 
113 | 
114 | 
115 | 
116 |
117 |
118 | ## 📋 开源协议
119 |
120 | [](https://github.com/79E/ChatGpt-Web/blob/master/license)
121 |
--------------------------------------------------------------------------------
/cmd/gen/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "chatgpt-web-new-go/common/config"
5 | "chatgpt-web-new-go/common/db"
6 | "chatgpt-web-new-go/common/logs"
7 | )
8 |
9 | func main() {
10 | // config init
11 | config.InitConfig()
12 |
13 | // log init
14 | logs.LogInit()
15 |
16 | // db init
17 | db.Init()
18 |
19 | // gen
20 | db.InitGen()
21 | }
22 |
--------------------------------------------------------------------------------
/cmd/server/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "chatgpt-web-new-go/common/aiClient"
5 | "chatgpt-web-new-go/common/config"
6 | "chatgpt-web-new-go/common/db"
7 | "chatgpt-web-new-go/common/email"
8 | "chatgpt-web-new-go/common/logs"
9 | "chatgpt-web-new-go/common/redis"
10 | "chatgpt-web-new-go/router"
11 | "fmt"
12 | "github.com/gin-gonic/gin"
13 | )
14 |
15 | func main() {
16 | // config init
17 | config.InitConfig()
18 |
19 | // log init
20 | logs.LogInit()
21 |
22 | // db init
23 | db.Init()
24 |
25 | // redis init
26 | redis.Init()
27 |
28 | // email service
29 | email.InitEmailDialer()
30 |
31 | // aiClient init
32 | aiClient.Init()
33 |
34 | // gin init
35 | engine := gin.Default()
36 |
37 | // route init
38 | router.Init(engine)
39 |
40 | // listen init
41 | port := fmt.Sprintf("%v", config.Config.Port)
42 | err := engine.Run("127.0.0.1:" + port)
43 | if err != nil {
44 | logs.Error("run webserver error %v", err)
45 | panic(err)
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/common/aiClient/getter.go:
--------------------------------------------------------------------------------
1 | package aiClient
2 |
3 | import (
4 | "math/rand"
5 | )
6 |
7 | func GetGptClient() *GptClient {
8 | if len(GptClients) < 1 {
9 | return nil
10 | }
11 |
12 | keyCount := len(GptClients)
13 |
14 | index := rand.Intn(keyCount)
15 |
16 | return GptClients[index]
17 | }
18 |
--------------------------------------------------------------------------------
/common/aiClient/init.go:
--------------------------------------------------------------------------------
1 | package aiClient
2 |
3 | import (
4 | "chatgpt-web-new-go/common/config"
5 | "chatgpt-web-new-go/common/logs"
6 | "chatgpt-web-new-go/dao"
7 | "context"
8 | "github.com/robfig/cron/v3"
9 | gogpt "github.com/sashabaranov/go-openai"
10 | "golang.org/x/net/proxy"
11 | "net"
12 | "net/http"
13 | "net/url"
14 | "strings"
15 | "time"
16 | )
17 |
18 | func Init() {
19 |
20 | // init first
21 | DoInitClient()
22 |
23 | // cron job
24 | c := cron.New()
25 | _, err := c.AddFunc("30 * * * *", DoInitClient)
26 | if err != nil {
27 | logs.Error("cron add func error: %v", err)
28 | panic(err)
29 | }
30 | c.Start()
31 | }
32 |
33 | func DoInitClient() {
34 | logs.Debug("doInitClient start len: %v", len(GptClients))
35 |
36 | dk := dao.Q.Aikey
37 | aiKeys, err := dk.Where(dk.IsDelete.Eq(0), dk.Status.Eq(1)).Find()
38 | if err != nil {
39 | logs.Error("ai keys get error: %v", err)
40 | panic(err)
41 | }
42 |
43 | var clientList []*GptClient
44 | for _, ak := range aiKeys {
45 | ak := ak
46 |
47 | gptConfig := gogpt.DefaultConfig(ak.Key)
48 |
49 | // proxy
50 | cnf := config.Config.Gpt
51 | if cnf != nil {
52 | addProxy(cnf.Proxy, gptConfig)
53 | }
54 |
55 | newClient := gogpt.NewClientWithConfig(gptConfig)
56 |
57 | gptClient := &GptClient{
58 | OpenAIClient: newClient,
59 | Model: ak,
60 | }
61 | clientList = append(clientList, gptClient)
62 | }
63 |
64 | GptClients = clientList
65 |
66 | logs.Debug("doInitClient start len: %v", len(GptClients))
67 | }
68 |
69 | func addProxy(proxy string, gptConfig gogpt.ClientConfig) {
70 | if proxy == "" {
71 | return
72 | }
73 |
74 | transport := &http.Transport{}
75 |
76 | if strings.HasPrefix(proxy, "socks5h://") {
77 | // 创建一个 DialContext 对象,并设置代理服务器
78 | dialContext, err := newDialContext(proxy[10:])
79 | if err != nil {
80 | panic(err)
81 | }
82 | transport.DialContext = dialContext
83 | } else {
84 | // 创建一个 HTTP Transport 对象,并设置代理服务器
85 | proxyUrl, err := url.Parse(proxy)
86 | if err != nil {
87 | panic(err)
88 | }
89 | transport.Proxy = http.ProxyURL(proxyUrl)
90 | }
91 | // 创建一个 HTTP 客户端,并将 Transport 对象设置为其 Transport 字段
92 | gptConfig.HTTPClient = &http.Client{
93 | Transport: transport,
94 | }
95 | }
96 |
97 | type dialContextFunc func(ctx context.Context, network, address string) (net.Conn, error)
98 |
99 | func newDialContext(socks5 string) (dialContextFunc, error) {
100 | baseDialer := &net.Dialer{
101 | Timeout: 60 * time.Second,
102 | KeepAlive: 60 * time.Second,
103 | }
104 |
105 | if socks5 != "" {
106 | // split socks5 proxy string [username:password@]host:port
107 | var auth *proxy.Auth = nil
108 |
109 | if strings.Contains(socks5, "@") {
110 | proxyInfo := strings.SplitN(socks5, "@", 2)
111 | proxyUser := strings.Split(proxyInfo[0], ":")
112 | if len(proxyUser) == 2 {
113 | auth = &proxy.Auth{
114 | User: proxyUser[0],
115 | Password: proxyUser[1],
116 | }
117 | }
118 | socks5 = proxyInfo[1]
119 | }
120 |
121 | dialSocksProxy, err := proxy.SOCKS5("tcp", socks5, auth, baseDialer)
122 | if err != nil {
123 | return nil, err
124 | }
125 |
126 | contextDialer, ok := dialSocksProxy.(proxy.ContextDialer)
127 | if !ok {
128 | return nil, err
129 | }
130 |
131 | return contextDialer.DialContext, nil
132 | } else {
133 | return baseDialer.DialContext, nil
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/common/aiClient/types.go:
--------------------------------------------------------------------------------
1 | package aiClient
2 |
3 | import (
4 | "chatgpt-web-new-go/model"
5 | sd "github.com/SpenserCai/sd-webui-go"
6 | "github.com/sashabaranov/go-openai"
7 | )
8 |
9 | var (
10 | GptClients []*GptClient
11 | DallE2Clients []*DallE2Client
12 | SDClients []*SDClient
13 | )
14 |
15 | type GptClient struct {
16 | OpenAIClient *openai.Client
17 | Model *model.Aikey
18 | }
19 |
20 | type DallE2Client struct {
21 | OpenAIClient *openai.Client
22 | Model *model.Aikey
23 | }
24 |
25 | type SDClient struct {
26 | SdClient sd.StableDiffInterface
27 | Model *model.Aikey
28 | }
29 |
--------------------------------------------------------------------------------
/common/auth/auth.go:
--------------------------------------------------------------------------------
1 | package auth
2 |
3 | import (
4 | "chatgpt-web-new-go/common/logs"
5 | "chatgpt-web-new-go/model"
6 | "errors"
7 | "github.com/gin-gonic/gin"
8 | "github.com/golang-jwt/jwt"
9 | "strings"
10 | "time"
11 | )
12 |
13 | var (
14 | GinCtxKey = "authUser"
15 | key = []byte("jansdfjizxuqhawiwehjioas7812738_asdf+787")
16 | )
17 |
18 | type CustomClaims struct {
19 | User *model.User
20 | jwt.StandardClaims
21 | }
22 |
23 | // Decode a token string into a token object
24 | func Decode(tokenString string) (*CustomClaims, error) {
25 | // Parse the token
26 | token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
27 | return key, nil
28 | })
29 |
30 | if err != nil {
31 | logs.Error("Decode jwt.ParseWithClaims errorL %v", err)
32 | return nil, err
33 | }
34 |
35 | // Validate the token and return the custom claims
36 | if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
37 | return claims, nil
38 | } else {
39 | logs.Error("Decode token.Claims errorL %v", err)
40 | return nil, err
41 | }
42 | }
43 |
44 | // Encode a claim into a JWT
45 | func Encode(user *model.User) (string, error) {
46 | expireToken := time.Now().Add(time.Hour * 7200).Unix()
47 |
48 | // Create the Claims
49 | claims := CustomClaims{
50 | user,
51 | jwt.StandardClaims{
52 | ExpiresAt: expireToken,
53 | Issuer: "chatgpt-web-new-go",
54 | },
55 | }
56 |
57 | // Create token
58 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
59 |
60 | // Sign token and return
61 | return token.SignedString(key)
62 | }
63 |
64 | // EncodeByCtx 从ctx中的token获取登录用户信息
65 | func EncodeByCtx(c *gin.Context) (*CustomClaims, error) {
66 | //1.获取token
67 | token := c.GetHeader("Token")
68 | if token != "" {
69 | tokenS := strings.Split(token, " ")
70 | token = tokenS[0]
71 | } else {
72 | token = c.Request.FormValue("token")
73 | }
74 | if token == "" || token == "undefined" {
75 | return nil, errors.New("not found token")
76 | }
77 |
78 | return Decode(token)
79 | }
80 |
--------------------------------------------------------------------------------
/common/auth/password/password.go:
--------------------------------------------------------------------------------
1 | package password
2 |
3 | import (
4 | "chatgpt-web-new-go/common/logs"
5 | "golang.org/x/crypto/bcrypt"
6 | )
7 |
8 | // Hash 进行加密
9 | func Hash(password string) string {
10 | bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
11 | if err != nil {
12 | logs.Error("hash password bizError: %v", err)
13 | }
14 |
15 | return string(bytes)
16 | }
17 |
18 | //CheckHash 检查密码和hash是否匹配
19 | func CheckHash(password string, hash string) bool {
20 | err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
21 | return err == nil
22 | }
23 |
24 | // IsHashed 检查密码和hash是否已经加密
25 | func IsHashed(str string) bool {
26 | return len(str) == 60
27 | }
28 |
--------------------------------------------------------------------------------
/common/auth/password/password_test.go:
--------------------------------------------------------------------------------
1 | package password
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestHash(t *testing.T) {
9 | pwd := "admin123"
10 | hash := Hash(pwd)
11 | fmt.Println(hash)
12 | }
13 |
--------------------------------------------------------------------------------
/common/auth/u_info.go:
--------------------------------------------------------------------------------
1 | package auth
2 |
3 | import "github.com/gin-gonic/gin"
4 |
5 | func GetClientIP(c *gin.Context) string {
6 | clientIP := c.ClientIP()
7 | if clientIP == "" {
8 | clientIP = c.RemoteIP()
9 | }
10 |
11 | return clientIP
12 | }
13 |
14 | func GetUA(c *gin.Context) string {
15 | return c.GetHeader("User-Agent")
16 | }
17 |
--------------------------------------------------------------------------------
/common/bizError/biz_error.go:
--------------------------------------------------------------------------------
1 | package bizError
2 |
3 | import "fmt"
4 |
5 | type BizError struct {
6 | Code int `json:"code"`
7 | Message string `json:"messageHandlers"`
8 | }
9 |
10 | func (e *BizError) Error() string {
11 | return fmt.Sprintf("[%v]%v", e.Code, e.Message)
12 | }
13 |
14 | func NewBizError(code int, message string) *BizError {
15 | return &BizError{
16 | Code: code,
17 | Message: message,
18 | }
19 | }
20 |
21 | var (
22 | UnknowError = NewBizError(-1, "未知错误!")
23 |
24 | CommonUpdateError = NewBizError(-100, "更新异常!")
25 | CommonDeleteError = NewBizError(-200, "删除异常!")
26 |
27 | LoginCodeNoneError = NewBizError(100000, "验证码不能为空!")
28 | LoginPassCodeNoneError = NewBizError(100001, "密码或验证码不能为空!")
29 | LoginCodeErrorError = NewBizError(100002, "验证码错误!")
30 | LoginPasswordError = NewBizError(100003, "密码错误!")
31 |
32 | SigninedAlreadyError = NewBizError(110000, "已经签到过啦!")
33 |
34 | IntegralNoneError = NewBizError(120000, "积分用光啦!")
35 |
36 | CarmiStatusError = NewBizError(130000, "卡密状态异常!")
37 | CarmiUseError = NewBizError(130001, "卡密使用异常!")
38 | CarmiDelError = NewBizError(130003, "卡密删除异常!")
39 |
40 | UserDelError = NewBizError(140000, "用户删除失败!")
41 | UserUpdateError = NewBizError(140001, "用户更新失败!")
42 |
43 | ProductUpdateError = NewBizError(150000, "商品更新异常!")
44 | ProductDeleteError = NewBizError(150001, "商品删除异常!")
45 |
46 | TurnoverDeleteError = NewBizError(160000, "消费记录删除异常!")
47 | TurnoverUpdateError = NewBizError(160001, "消费记录更新异常!")
48 |
49 | AiKeyTokenDeleteError = NewBizError(170000, "Token删除异常!")
50 | AiKeyTokenUpdateError = NewBizError(170001, "Token更新异常!")
51 | AiKeyNoneUsefullError = NewBizError(170002, "无可用Token!")
52 |
53 | PaymentDeleteError = NewBizError(180000, "支付配置删除异常!")
54 | PaymentUpdateError = NewBizError(180001, "支付配置更新异常!")
55 |
56 | NotificationDeleteError = NewBizError(190000, "同志更新异常!")
57 | NotificationUpdateError = NewBizError(190001, "同志更新异常!")
58 | )
59 |
--------------------------------------------------------------------------------
/common/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | type Configuration struct {
4 | Port int `json:"port"`
5 | Db *dbConfig `json:"db"`
6 | Redis *redisConfig `json:"redis"`
7 | Gpt *gptConfig `json:"aiClient"`
8 | EmailServer *emailServerConfig `json:"emailServer"`
9 | }
10 |
11 | type dbConfig struct {
12 | Type string `json:"type"`
13 | Name string `json:"name"`
14 | Host string `json:"host"` // and port
15 | HostR1 string `json:"host_r_1"`
16 | User string `json:"user"`
17 | Password string `json:"password"`
18 | }
19 |
20 | type redisConfig struct {
21 | Addr string `json:"addr"` // host and port
22 | Password string `json:"password"`
23 | DB int `json:"db"`
24 | }
25 |
26 | type gptConfig struct {
27 | ApiKey string `json:"api_key"`
28 | Proxy string `json:"proxy"`
29 | ApiURL string `json:"api_url"`
30 | BotDesc string `json:"bot_desc"`
31 | Model string `json:"model"`
32 | MaxTokens int `json:"max_tokens"`
33 | TopP float32 `json:"top_p"`
34 | FrequencyPenalty float32 `json:"frequency_penalty"`
35 | PresencePenalty float32 `json:"presence_penalty"`
36 | }
37 |
38 | type emailServerConfig struct {
39 | Host string `json:"host"`
40 | Port int `json:"port"`
41 | SenderName string `json:"sender_name"`
42 | User string `json:"user"`
43 | Password string `json:"password"`
44 | }
45 |
--------------------------------------------------------------------------------
/common/config/global.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "github.com/go-redis/redis"
5 | "github.com/robfig/cron/v3"
6 | "gopkg.in/gomail.v2"
7 | "gorm.io/gorm"
8 | )
9 |
10 | var (
11 | Config *Configuration
12 | DB *gorm.DB // DB instance
13 | Redis *redis.Client
14 | Gcron *cron.Cron // cron
15 | EmailDialer *gomail.Dialer
16 | )
17 |
--------------------------------------------------------------------------------
/common/config/init.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "chatgpt-web-new-go/common/env"
5 | "context"
6 | "flag"
7 | "github.com/fsnotify/fsnotify"
8 | "github.com/spf13/pflag"
9 | "github.com/spf13/viper"
10 | "log"
11 | )
12 |
13 | func InitConfig() {
14 | v := viper.New()
15 |
16 | // env initializer
17 | e := env.GetEnv()
18 |
19 | //设置配置文件的名字
20 | v.SetConfigName(e)
21 |
22 | //添加配置文件所在的路径,注意在Linux环境下%GOPATH要替换为$GOPATH
23 | v.AddConfigPath("%GOPATH/src/")
24 | v.AddConfigPath("./")
25 | v.AddConfigPath("./config")
26 | v.AddConfigPath("./../config")
27 | v.AddConfigPath("./../../config")
28 | v.AddConfigPath("./../../../config")
29 |
30 | //设置配置文件类型
31 | v.SetConfigType("yml")
32 |
33 | if err := v.ReadInConfig(); err != nil {
34 | panic(err)
35 | }
36 |
37 | // command line args
38 | commandLineConfig(v)
39 |
40 | Config = &Configuration{}
41 | err := v.Unmarshal(Config)
42 | if err != nil {
43 | panic(err)
44 | }
45 |
46 | log.Printf("global config: %v \n", Config)
47 | go watchConfigChange(v)
48 | }
49 |
50 | // 监听配置文件的修改和变动
51 | func watchConfigChange(v *viper.Viper) {
52 | defer func() {
53 | if err := recover(); err != nil {
54 | log.Printf("watchConfigChange panic recover: %v", err)
55 | }
56 | }()
57 |
58 | ctx, cancel := context.WithCancel(context.Background())
59 | v.WatchConfig()
60 | //监听回调函数
61 | watch := func(e fsnotify.Event) {
62 | log.Printf("Config file is changed: %s \n", e.String())
63 | cancel()
64 | }
65 | v.OnConfigChange(watch)
66 | <-ctx.Done()
67 | }
68 |
69 | func commandLineConfig(v *viper.Viper) {
70 | pflag.String("token", "", "please input the token")
71 | pflag.Int("adminUserId", 0, "please input the admin user id")
72 | pflag.Bool("debug", false, "please input the debug flag")
73 | //获取标准包的flag
74 | pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
75 | pflag.Parse()
76 |
77 | //BindFlag
78 | err := v.BindPFlags(pflag.CommandLine)
79 | if err != nil {
80 | panic(err)
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/common/constant/constant.go:
--------------------------------------------------------------------------------
1 | package constant
2 |
3 | var (
4 | WhiteListPhone = map[string]struct{}{
5 | "18888888888": {},
6 | "17777777777": {},
7 | }
8 | )
9 |
--------------------------------------------------------------------------------
/common/db/db.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "chatgpt-web-new-go/common/config"
5 | "chatgpt-web-new-go/dao"
6 | "database/sql"
7 | "fmt"
8 | "github.com/mattn/go-sqlite3"
9 | "gorm.io/driver/mysql"
10 | "gorm.io/driver/sqlite"
11 | "gorm.io/gorm"
12 | "gorm.io/gorm/logger"
13 | "log"
14 | "os"
15 | "time"
16 | )
17 |
18 | var dbTypeInitializer = map[string]func(){
19 | "mysql": initMysql,
20 | "sqlite": initSqlite,
21 | }
22 |
23 | func Init() {
24 | dbType := config.Config.Db.Type
25 | dbInitializer := dbTypeInitializer[dbType]
26 | dbInitializer()
27 |
28 | // gorm gen init
29 | dao.SetDefault(config.DB)
30 | }
31 |
32 | func initMysql() {
33 | dbConfig := config.Config.Db
34 | // refer https://github.com/go-sql-driver/mysql#dsn-data-source-name for details
35 | dsn := fmt.Sprintf("%v:%v@tcp(%v)/%v?charset=utf8mb4&parseTime=True&loc=Local",
36 | dbConfig.User, dbConfig.Password, dbConfig.Host, dbConfig.Name)
37 |
38 | var err error
39 | config.DB, err = gorm.Open(
40 | mysql.New(mysql.Config{
41 | DSN: dsn, // data source name
42 | DefaultStringSize: 256, // default size for string fields
43 | DisableDatetimePrecision: true, // disable datetime precision, which not supported before MySQL 5.6
44 | DontSupportRenameIndex: true, // drop & create when rename messageDao, rename messageDao not supported before MySQL 5.7, MariaDB
45 | DontSupportRenameColumn: true, // `change` when rename column, rename column not supported before MySQL 8, MariaDB
46 | SkipInitializeWithVersion: false, // autoconfigure based on currently MySQL version
47 | }),
48 | &gorm.Config{
49 | Logger: logger.New(
50 | log.New(os.Stdout, "\r\n", log.LstdFlags),
51 | logger.Config{
52 | SlowThreshold: time.Second,
53 | LogLevel: logger.Info,
54 | Colorful: true,
55 | },
56 | ),
57 | })
58 | if err != nil {
59 | panic(err)
60 | }
61 | sqlDB, err := config.DB.DB()
62 | sqlDB.SetMaxIdleConns(5)
63 | sqlDB.SetMaxOpenConns(100)
64 | sqlDB.SetConnMaxLifetime(time.Hour)
65 |
66 | // migrate
67 | //err = config.DB.AutoMigrate(&user.User{})
68 | //if err != nil {
69 | // panic(err)
70 | //}
71 | }
72 |
73 | // initSqlite 数据库初始化,包括新建数据库(如果还没有建立),基本数据的读写
74 | func initSqlite() {
75 | sql.Register("sqlite3_simple",
76 | &sqlite3.SQLiteDriver{
77 | Extensions: []string{
78 | "libsimple-osx-x64/libsimple",
79 | },
80 | },
81 | )
82 |
83 | var err error
84 | config.DB, err = gorm.Open(sqlite.Open("data.db"), &gorm.Config{})
85 | if err != nil {
86 | panic("failed to connect database")
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/common/db/gen.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "chatgpt-web-new-go/common/config"
5 | "gorm.io/gen"
6 | )
7 |
8 | // Querier Dynamic SQL
9 | type Querier interface {
10 | // FilterWithNameAndRole SELECT * FROM @@table WHERE name = @name{{if role !=""}} AND role = @role{{end}}
11 | FilterWithNameAndRole(name, role string) ([]gen.T, error)
12 | }
13 |
14 | func InitGen() {
15 | g := gen.NewGenerator(gen.Config{
16 | OutPath: "./dao",
17 | ModelPkgPath: "./model",
18 | Mode: gen.WithoutContext | gen.WithDefaultQuery | gen.WithQueryInterface, // generate mode
19 | WithUnitTest: false,
20 | })
21 |
22 | // db, _ := gorm.Open(mysql.Open("root:@(127.0.0.1:3306)/demo?charset=utf8mb4&parseTime=True&loc=Local"))
23 | g.UseDB(config.DB) // reuse your gorm db
24 |
25 | // Generate basic type-safe DAO API for struct `model.User` following conventions
26 | g.ApplyBasic(g.GenerateAllTable()...)
27 |
28 | // Generate Type Safe API with Dynamic SQL defined on Querier interface for `model.User` and `model.Company`
29 | g.ApplyInterface(func(Querier) {}, g.GenerateAllTable()...)
30 |
31 | // Generate the code
32 | g.Execute()
33 | }
34 |
--------------------------------------------------------------------------------
/common/email/email.go:
--------------------------------------------------------------------------------
1 | package email
2 |
3 | import (
4 | "chatgpt-web-new-go/common/config"
5 | "gopkg.in/gomail.v2"
6 | )
7 |
8 | func InitEmailDialer() {
9 | server := config.Config.EmailServer
10 |
11 | config.EmailDialer = gomail.NewDialer(server.Host, server.Port, server.User, server.Password)
12 | }
13 |
14 | func SendMail(subject, toAddress, content string) error {
15 | server := config.Config.EmailServer
16 |
17 | m := gomail.NewMessage()
18 | m.SetHeader("From", m.FormatAddress(server.User, server.SenderName))
19 | m.SetHeader("To", toAddress)
20 | m.SetHeader("Subject", subject)
21 | m.SetBody("text/html", content)
22 | err := config.EmailDialer.DialAndSend(m)
23 | return err
24 | }
25 |
--------------------------------------------------------------------------------
/common/email/template.go:
--------------------------------------------------------------------------------
1 | package email
2 |
3 | const (
4 | SendCodeTemplate = `
5 |
6 |
7 |
8 |
46 |
47 |
48 |
49 |
OurAI 验证码
50 |
以下是您本次请求的验证码,验证码有效期为10分钟,请勿透露给他人:
51 |
%s
52 |
55 |
56 |
57 |
58 | `
59 | )
60 |
--------------------------------------------------------------------------------
/common/env/env.go:
--------------------------------------------------------------------------------
1 | package env
2 |
3 | import "os"
4 |
5 | const (
6 | KEY = "env"
7 |
8 | KeyDev = "dev"
9 | KeyTest = "test"
10 | KeyProd = "prod"
11 | )
12 |
13 | func GetEnv() string {
14 | e := os.Getenv(KEY)
15 | if e == "" {
16 | return KeyDev
17 | }
18 | return e
19 | }
20 |
21 | func IsDevelop() bool {
22 | return GetEnv() == KeyDev || GetEnv() == ""
23 | }
24 |
25 | func IsTest() bool {
26 | return GetEnv() == KeyTest
27 | }
28 |
29 | func IsProduction() bool {
30 | return GetEnv() == KeyProd
31 | }
32 |
--------------------------------------------------------------------------------
/common/goUtil/recover.go:
--------------------------------------------------------------------------------
1 | package goUtil
2 |
3 | import (
4 | "chatgpt-web-new-go/common/logs"
5 | "runtime"
6 | )
7 |
8 | func Recover(source string) func() {
9 | return func() {
10 | if err := recover(); err != nil {
11 | logs.Error("%v panic: %v", source, err)
12 | var buf [4096]byte
13 | n := runtime.Stack(buf[:], false)
14 | logs.Error("%v panic %s\n", source, string(buf[:n]))
15 | }
16 | }
17 | }
18 |
19 | func New(f func()) {
20 | go func() {
21 | // recover
22 | defer func() {
23 | if err := recover(); err != nil {
24 | var buf [4096]byte
25 | n := runtime.Stack(buf[:], false)
26 | logs.Error("goUtil routiune panic %s\n", string(buf[:n]))
27 | }
28 | }()
29 |
30 | // do function
31 | f()
32 | }()
33 | }
34 |
--------------------------------------------------------------------------------
/common/inviteCodeGen/invite_code.go:
--------------------------------------------------------------------------------
1 | package inviteCodeGen
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "math/rand"
7 | "strings"
8 | "time"
9 | )
10 |
11 | var InviteCodeGen = code{
12 | base: "HVE8S2DZX9C7P5IK3MJUAR4WYLTN6BGQ",
13 | decimal: 32,
14 | pad: "F",
15 | len: 6,
16 | }
17 |
18 | func init() {
19 | // 初始化检查
20 | if res, err := InviteCodeGen.InitCheck(); !res {
21 | fmt.Println(err)
22 | panic(err)
23 | }
24 | }
25 |
26 | type code struct {
27 | base string // 进制的包含字符, string类型
28 | decimal int64 // 进制长度
29 | pad string // 补位字符,若生成的code小于最小长度,则补位+随机字符, 补位字符不能在进制字符中
30 | len int // code最小长度
31 | }
32 |
33 | // IdToCode id转code
34 | func (c *code) IdToCode(id int64) string {
35 | mod := int64(0)
36 | res := ""
37 | for id != 0 {
38 | mod = id % c.decimal
39 | id = id / c.decimal
40 | res += string(c.base[mod])
41 | }
42 | resLen := len(res)
43 | if resLen < c.len {
44 | res += c.pad
45 | for i := 0; i < c.len-resLen-1; i++ {
46 | rand.Seed(time.Now().UnixNano())
47 | res += string(c.base[rand.Intn(int(c.decimal))])
48 | }
49 | }
50 | return res
51 | }
52 |
53 | // CodeToId code转id
54 | func (c *code) CodeToId(code string) int64 {
55 | res := int64(0)
56 | lenCode := len(code)
57 |
58 | //var baseArr [] byte = []byte(c.base)
59 | baseArr := []byte(c.base) // 字符串进制转换为byte数组
60 | baseRev := make(map[byte]int) // 进制数据键值转换为map
61 | for k, v := range baseArr {
62 | baseRev[v] = k
63 | }
64 |
65 | // 查找补位字符的位置
66 | isPad := strings.Index(code, c.pad)
67 | if isPad != -1 {
68 | lenCode = isPad
69 | }
70 |
71 | r := 0
72 | for i := 0; i < lenCode; i++ {
73 | // 补充字符直接跳过
74 | if string(code[i]) == c.pad {
75 | continue
76 | }
77 | index := baseRev[code[i]]
78 | b := int64(1)
79 | for j := 0; j < r; j++ {
80 | b *= c.decimal
81 | }
82 | // pow 类型为 float64 , 类型转换太麻烦, 所以自己循环实现pow的功能
83 | //res += float64(index) * math.Pow(float64(32), float64(2))
84 | res += int64(index) * b
85 | r++
86 | }
87 | return res
88 | }
89 |
90 | // InitCheck 初始化检查
91 | func (c *code) InitCheck() (bool, error) {
92 | lenBase := len(c.base)
93 | // 检查进制字符
94 | if c.base == "" {
95 | return false, errors.New("base string is nil or empty")
96 | }
97 | // 检查长度是否符合
98 | if int64(lenBase) != c.decimal {
99 | return false, errors.New("base length and len not match")
100 | }
101 | return true, errors.New("")
102 | }
103 |
--------------------------------------------------------------------------------
/common/inviteCodeGen/invite_code2.go:
--------------------------------------------------------------------------------
1 | package inviteCodeGen
2 |
3 | import (
4 | "container/list"
5 | "errors"
6 | "fmt"
7 | )
8 |
9 | var (
10 | baseStr = "HVE8S2DZX9C7P5IK3MJUAR4WYLTN6BGQ"
11 | base = []byte(baseStr)
12 | )
13 |
14 | var baseMap map[byte]int
15 |
16 | func InitBaseMap() {
17 | baseMap = make(map[byte]int)
18 |
19 | for i, v := range base {
20 | baseMap[v] = i
21 | }
22 | }
23 |
24 | func Base34(n uint64) []byte {
25 | quotient := n
26 | mod := uint64(0)
27 | l := list.New()
28 |
29 | for quotient != 0 {
30 | mod = quotient % 32
31 | quotient = quotient / 32
32 | l.PushFront(base[int(mod)])
33 | }
34 |
35 | listLen := l.Len()
36 |
37 | if listLen >= 6 {
38 | res := make([]byte, 0, listLen)
39 | for i := l.Front(); i != nil; i = i.Next() {
40 | res = append(res, i.Value.(byte))
41 | }
42 | return res
43 | } else {
44 | res := make([]byte, 0, 6)
45 | for i := 0; i < 6; i++ {
46 | if i < 6-listLen {
47 | res = append(res, base[0])
48 | } else {
49 | res = append(res, l.Front().Value.(byte))
50 | l.Remove(l.Front())
51 | }
52 |
53 | }
54 | return res
55 | }
56 | }
57 |
58 | func Base34ToNum(str []byte) (uint64, error) {
59 | if baseMap == nil {
60 | return 0, errors.New("no init base map")
61 | }
62 |
63 | if str == nil || len(str) == 0 {
64 | return 0, errors.New("parameter is nil or empty")
65 | }
66 |
67 | var res uint64 = 0
68 | var r uint64 = 0
69 |
70 | for i := len(str) - 1; i >= 0; i-- {
71 | v, ok := baseMap[str[i]]
72 | if !ok {
73 | fmt.Printf("")
74 | return 0, errors.New("character is not base")
75 | }
76 |
77 | var b uint64 = 1
78 | for j := uint64(0); j < r; j++ {
79 | b *= 32
80 | }
81 |
82 | res += b * uint64(v)
83 | r++
84 | }
85 |
86 | return res, nil
87 | }
88 |
--------------------------------------------------------------------------------
/common/inviteCodeGen/invite_code_test.go:
--------------------------------------------------------------------------------
1 | package inviteCodeGen
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestInviteCode(t *testing.T) {
9 | inviteCode := code{
10 | base: "HVE8S2DZX9C7P5IK3MJUAR4WYLTN6BGQ",
11 | decimal: 32,
12 | pad: "F",
13 | len: 6,
14 | }
15 | // 初始化检查
16 | if res, err := inviteCode.InitCheck(); !res {
17 | fmt.Println(err)
18 | return
19 | }
20 | id := int64(1)
21 | code := inviteCode.IdToCode(1)
22 | fmt.Printf("id=%v, code=%v\n", id, code)
23 |
24 | code = "VFXXXX"
25 | id = inviteCode.CodeToId(code)
26 | fmt.Printf("code=%v, id=%v\n", code, id)
27 | }
28 |
29 | func TestInviteCode2(t *testing.T) {
30 | InitBaseMap()
31 |
32 | fmt.Printf("len(baseStr):%d, len(base):%d\n", len(baseStr), len(base))
33 |
34 | res := Base34(1)
35 |
36 | fmt.Printf("=base:1544804416->%s, %d\n", string(res), len(res))
37 |
38 | str := "VIVZ4EH"
39 |
40 | num, err := Base34ToNum([]byte(str))
41 |
42 | if err == nil {
43 | fmt.Printf("=base:%s->%d\n", str, num)
44 | } else {
45 | fmt.Printf("===============err:%s\n", err.Error())
46 | }
47 | }
48 |
49 | func TestByteString(t *testing.T) {
50 | s := "a"
51 | b := []byte(s)
52 |
53 | fmt.Println(b)
54 | }
55 |
--------------------------------------------------------------------------------
/common/logs/log.go:
--------------------------------------------------------------------------------
1 | package logs
2 |
3 | import (
4 | "chatgpt-web-new-go/common/env"
5 | rotateLogs "github.com/lestrrat-go/file-rotatelogs"
6 | "go.uber.org/zap"
7 | "go.uber.org/zap/zapcore"
8 | "io"
9 | "os"
10 | "time"
11 | )
12 |
13 | var (
14 | LoggerZap *zap.Logger // log
15 | Logger *zap.SugaredLogger // log
16 |
17 | Debug func(template string, args ...interface{})
18 | Info func(template string, args ...interface{})
19 | Warn func(template string, args ...interface{})
20 | Error func(template string, args ...interface{})
21 | )
22 |
23 | func LogSyncLast() {
24 | func() {
25 | _ = Logger.Sync()
26 | _ = LoggerZap.Sync()
27 | }()
28 | }
29 |
30 | func LogInit() {
31 | hook := getWriter("./log/aiClient.log")
32 | encoderConfig := zapcore.EncoderConfig{
33 | MessageKey: "msg",
34 | LevelKey: "level",
35 | TimeKey: "time",
36 | NameKey: "logger",
37 | CallerKey: "file",
38 | StacktraceKey: "stacktrace",
39 | LineEnding: zapcore.DefaultLineEnding,
40 | EncodeLevel: zapcore.CapitalColorLevelEncoder,
41 | EncodeTime: zapcore.ISO8601TimeEncoder,
42 | EncodeDuration: zapcore.SecondsDurationEncoder,
43 | EncodeCaller: zapcore.ShortCallerEncoder, // 短路径编码器
44 | EncodeName: zapcore.FullNameEncoder,
45 | }
46 | // 设置日志级别
47 | atomicLevel := zap.NewAtomicLevel()
48 | atomicLevel.SetLevel(zap.DebugLevel)
49 | var writes = []zapcore.WriteSyncer{zapcore.AddSync(hook)}
50 |
51 | // 如果是开发环境,同时在控制台上也输出
52 | if !env.IsProduction() {
53 | writes = append(writes, zapcore.AddSync(os.Stdout))
54 | }
55 | core := zapcore.NewCore(
56 | zapcore.NewConsoleEncoder(encoderConfig),
57 | zapcore.NewMultiWriteSyncer(writes...),
58 | atomicLevel,
59 | )
60 |
61 | // 开启开发模式,堆栈跟踪
62 | caller := zap.AddCaller()
63 | // 开启文件及行号
64 | development := zap.Development()
65 |
66 | // 设置初始化字段
67 | //field := zap.Fields(zap.String("env", IsProduction()))
68 |
69 | // 构造日志
70 | logger := zap.New(core, caller, development)
71 | LoggerZap = logger
72 | Logger = logger.Sugar()
73 |
74 | {
75 | Debug = Logger.Debugf
76 | Info = Logger.Infof
77 | Warn = Logger.Warnf
78 | Error = Logger.Errorf
79 | }
80 |
81 | Logger.Info("log initializer success")
82 | }
83 |
84 | func getWriter(filename string) io.Writer {
85 | // 生成rotateLogs的Logger 实际生成的文件名 demo.log.YYmmddHH
86 | // 保存90天内的日志,每1天(整点)分割一次日志
87 | hook, err := rotateLogs.New(
88 | filename+".%Y%m%d%H", // 没有使用go风格反人类的format格式
89 | rotateLogs.WithLinkName(filename),
90 | rotateLogs.WithMaxAge(time.Hour*24*365),
91 | rotateLogs.WithRotationTime(time.Hour*24),
92 | )
93 |
94 | if err != nil {
95 | panic(err)
96 | }
97 | return hook
98 | }
99 |
--------------------------------------------------------------------------------
/common/random/rand_number.go:
--------------------------------------------------------------------------------
1 | package random
2 |
3 | import (
4 | "fmt"
5 | "math/rand"
6 | "time"
7 | )
8 |
9 | const (
10 | randomRound = "0123456789"
11 |
12 | charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
13 | )
14 |
15 | var (
16 | seededRand *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano()))
17 | )
18 |
19 | func GenSmsCode() string {
20 | rand.Seed(time.Now().UnixNano())
21 | var slicePol []byte
22 | for i := 0; i < 4; i++ {
23 | n := rand.Intn(len(randomRound))
24 | slicePol = append(slicePol, randomRound[n])
25 | }
26 | return string(slicePol)
27 | }
28 |
29 | func GenCarmiKey() string {
30 | return fmt.Sprintf("CA%v%v", time.Now().Unix(), generateRandomString(6))
31 | }
32 |
33 | func generateRandomString(length int) string {
34 | b := make([]byte, length)
35 | for i := range b {
36 | b[i] = charset[seededRand.Intn(len(charset))]
37 | }
38 | return string(b)
39 | }
40 |
--------------------------------------------------------------------------------
/common/redis/contents.go:
--------------------------------------------------------------------------------
1 | package redis
2 |
3 | const (
4 | KeySnsCode = "sns_%v" // phone
5 | )
6 |
--------------------------------------------------------------------------------
/common/redis/v6.go:
--------------------------------------------------------------------------------
1 | package redis
2 |
3 | import (
4 | "chatgpt-web-new-go/common/config"
5 | "github.com/go-redis/redis"
6 | )
7 |
8 | func Init() {
9 | redisConfig := config.Config.Redis
10 | config.Redis = redis.NewClient(&redis.Options{
11 | Addr: redisConfig.Addr,
12 | Password: redisConfig.Password, // Redis 服务器没有设置密码
13 | DB: redisConfig.DB, // 使用默认数据库
14 | })
15 |
16 | _, err := config.Redis.Ping().Result()
17 | if err != nil {
18 | panic(err)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/common/regexp/phone_email_regexp.go:
--------------------------------------------------------------------------------
1 | package regexp
2 |
3 | import "regexp"
4 |
5 | const (
6 | // 正则表达式匹配中国合法手机号
7 | phoneRegex = "^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(17[6|7|8])|(18[0-9])|166|198|199)\\d{8}$"
8 |
9 | // 正则表达式匹配邮箱
10 | emailRegex = `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
11 | )
12 |
13 | func IsValidPhone(phone string) bool {
14 | isPhone, _ := regexp.MatchString(phoneRegex, phone)
15 | return isPhone
16 | }
17 |
18 | func isValidEmail(email string) bool {
19 | isEmail, _ := regexp.MatchString(emailRegex, email)
20 | return isEmail
21 | }
22 |
23 | func IsValidPhoneOrEmail(input string) bool {
24 | return IsValidPhone(input) || isValidEmail(input)
25 | }
26 |
--------------------------------------------------------------------------------
/common/types/converter.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "chatgpt-web-new-go/common/logs"
5 | "strconv"
6 | )
7 |
8 | func Int64ToString(num int64) string {
9 | return strconv.FormatInt(num, 10)
10 | }
11 |
12 | func UInt64ToString(num uint64) string {
13 | return strconv.FormatUint(num, 10)
14 | }
15 |
16 | func StringToInt(str string) int {
17 | num, err := strconv.Atoi(str)
18 | if err != nil {
19 | logs.Debug("StringToInt Err: %v", err)
20 | }
21 |
22 | return num
23 | }
24 |
25 | func StringToInt64(str string) int64 {
26 | num, err := strconv.ParseInt(str, 10, 64)
27 | if err != nil {
28 | logs.Debug("StringToInt Err: %v", err)
29 | }
30 |
31 | return num
32 | }
33 |
34 | func Booltonumber(b bool) int {
35 | result := 0
36 | if b {
37 | result = 1
38 | }
39 | return result
40 | }
41 |
--------------------------------------------------------------------------------
/common/types/dataTrance.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import "strconv"
4 |
5 | func InterfaceToInt(i interface{}) int {
6 | switch i.(type) {
7 | case int:
8 | return i.(int)
9 | case string:
10 | iStr := i.(string)
11 | iInt, _ := strconv.Atoi(iStr)
12 | return iInt
13 | case int64:
14 | return int(i.(int64))
15 | default:
16 | return 0
17 | }
18 | }
19 |
20 | func InterfaceToInt64(i interface{}) int64 {
21 | switch i.(type) {
22 | case int:
23 | return i.(int64)
24 | case string:
25 | iStr := i.(string)
26 |
27 | iInt, _ := strconv.ParseInt(iStr, 10, 64)
28 | return iInt
29 | case int64:
30 | return i.(int64)
31 | default:
32 | return 0
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/common/types/id.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "math/rand"
5 | "time"
6 | )
7 |
8 | func GenerateUID() int64 {
9 | rand.Seed(time.Now().UnixNano())
10 | min := 100000 // 最小值(6位数)
11 | max := 999999 // 最大值(6位数)
12 | return int64(rand.Intn(max-min+1) + min)
13 | }
14 |
--------------------------------------------------------------------------------
/common/types/slice.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import "reflect"
4 |
5 | func Contains(arr interface{}, target interface{}) bool {
6 | arrValue := reflect.ValueOf(arr)
7 | if arrValue.Kind() != reflect.Slice {
8 | panic("not a slice")
9 | }
10 |
11 | targetValue := reflect.ValueOf(target)
12 | for i := 0; i < arrValue.Len(); i++ {
13 | if reflect.DeepEqual(arrValue.Index(i).Interface(), targetValue.Interface()) {
14 | return true
15 | }
16 | }
17 |
18 | return false
19 | }
20 |
--------------------------------------------------------------------------------
/common/types/time.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import "time"
4 |
5 | const (
6 | TimeFormatDate = "2006-01-02 15:04:05"
7 | )
8 |
9 | func GetDayStartEn() (start time.Time, end time.Time) {
10 | now := time.Now()
11 | start = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local)
12 | end = time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 50, 999, time.Local)
13 | return
14 | }
15 |
16 | func GetMonthStartEnd() (start time.Time, end time.Time) {
17 | now := time.Now()
18 | start = now.AddDate(0, 0, -now.Day()+1)
19 | end = now.AddDate(0, 1, -now.Day())
20 | return
21 | }
22 |
--------------------------------------------------------------------------------
/config/prod.yml:
--------------------------------------------------------------------------------
1 | port: 8899
2 |
3 | db:
4 | type: mysql
5 | host: 127.0.0.1:3306
6 | hostR1: 127.0.0.1:3306
7 | user: root
8 | password: 12345
9 | name: chatgpt_web_new_go
10 |
11 | redis:
12 | addr: 127.0.0.1:6379
13 |
14 | gpt:
15 | proxy: # 代理支持 socks5h://x.x.x.x 或者 http://x.x.x.x
16 |
--------------------------------------------------------------------------------
/docs/openai.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module chatgpt-web-new-go
2 |
3 | go 1.18
4 |
5 | require (
6 | github.com/SpenserCai/sd-webui-go v0.4.3
7 | github.com/fsnotify/fsnotify v1.6.0
8 | github.com/gin-gonic/gin v1.9.1
9 | github.com/go-redis/redis v6.15.9+incompatible
10 | github.com/golang-jwt/jwt v3.2.2+incompatible
11 | github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
12 | github.com/mattn/go-sqlite3 v1.14.16
13 | github.com/robfig/cron/v3 v3.0.1
14 | github.com/sashabaranov/go-openai v1.9.5
15 | github.com/spf13/pflag v1.0.5
16 | github.com/spf13/viper v1.15.0
17 | go.uber.org/zap v1.24.0
18 | golang.org/x/crypto v0.12.0
19 | golang.org/x/net v0.14.0
20 | gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
21 | gorm.io/driver/mysql v1.5.1
22 | gorm.io/driver/sqlite v1.5.1
23 | gorm.io/gen v0.3.23
24 | gorm.io/gorm v1.25.4
25 | gorm.io/plugin/dbresolver v1.4.7
26 | )
27 |
28 | require (
29 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
30 | github.com/bytedance/sonic v1.9.1 // indirect
31 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
32 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect
33 | github.com/gin-contrib/sse v0.1.0 // indirect
34 | github.com/go-logr/logr v1.2.4 // indirect
35 | github.com/go-logr/stdr v1.2.2 // indirect
36 | github.com/go-openapi/analysis v0.21.4 // indirect
37 | github.com/go-openapi/errors v0.20.4 // indirect
38 | github.com/go-openapi/jsonpointer v0.19.5 // indirect
39 | github.com/go-openapi/jsonreference v0.20.0 // indirect
40 | github.com/go-openapi/loads v0.21.2 // indirect
41 | github.com/go-openapi/runtime v0.26.0 // indirect
42 | github.com/go-openapi/spec v0.20.8 // indirect
43 | github.com/go-openapi/strfmt v0.21.7 // indirect
44 | github.com/go-openapi/swag v0.22.4 // indirect
45 | github.com/go-openapi/validate v0.22.1 // indirect
46 | github.com/go-playground/locales v0.14.1 // indirect
47 | github.com/go-playground/universal-translator v0.18.1 // indirect
48 | github.com/go-playground/validator/v10 v10.14.0 // indirect
49 | github.com/go-sql-driver/mysql v1.7.1 // indirect
50 | github.com/goccy/go-json v0.10.2 // indirect
51 | github.com/hashicorp/hcl v1.0.0 // indirect
52 | github.com/jinzhu/inflection v1.0.0 // indirect
53 | github.com/jinzhu/now v1.1.5 // indirect
54 | github.com/jonboulle/clockwork v0.4.0 // indirect
55 | github.com/josharian/intern v1.0.0 // indirect
56 | github.com/json-iterator/go v1.1.12 // indirect
57 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect
58 | github.com/leodido/go-urn v1.2.4 // indirect
59 | github.com/lestrrat-go/strftime v1.0.6 // indirect
60 | github.com/magiconair/properties v1.8.7 // indirect
61 | github.com/mailru/easyjson v0.7.7 // indirect
62 | github.com/mattn/go-isatty v0.0.19 // indirect
63 | github.com/mitchellh/mapstructure v1.5.0 // indirect
64 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
65 | github.com/modern-go/reflect2 v1.0.2 // indirect
66 | github.com/oklog/ulid v1.3.1 // indirect
67 | github.com/onsi/ginkgo v1.16.5 // indirect
68 | github.com/onsi/gomega v1.27.7 // indirect
69 | github.com/opentracing/opentracing-go v1.2.0 // indirect
70 | github.com/pelletier/go-toml/v2 v2.0.8 // indirect
71 | github.com/pkg/errors v0.9.1 // indirect
72 | github.com/spf13/afero v1.9.3 // indirect
73 | github.com/spf13/cast v1.5.0 // indirect
74 | github.com/spf13/jwalterweatherman v1.1.0 // indirect
75 | github.com/subosito/gotenv v1.4.2 // indirect
76 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
77 | github.com/ugorji/go/codec v1.2.11 // indirect
78 | go.mongodb.org/mongo-driver v1.11.3 // indirect
79 | go.opentelemetry.io/otel v1.14.0 // indirect
80 | go.opentelemetry.io/otel/trace v1.14.0 // indirect
81 | go.uber.org/atomic v1.9.0 // indirect
82 | go.uber.org/multierr v1.8.0 // indirect
83 | golang.org/x/arch v0.3.0 // indirect
84 | golang.org/x/mod v0.12.0 // indirect
85 | golang.org/x/sys v0.11.0 // indirect
86 | golang.org/x/text v0.12.0 // indirect
87 | golang.org/x/tools v0.12.0 // indirect
88 | google.golang.org/protobuf v1.30.0 // indirect
89 | gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
90 | gopkg.in/ini.v1 v1.67.0 // indirect
91 | gopkg.in/yaml.v2 v2.4.0 // indirect
92 | gopkg.in/yaml.v3 v3.0.1 // indirect
93 | gorm.io/datatypes v1.2.0 // indirect
94 | gorm.io/hints v1.1.2 // indirect
95 | )
96 |
--------------------------------------------------------------------------------
/model/action.gen.go:
--------------------------------------------------------------------------------
1 | // Code generated by gorm.io/gen. DO NOT EDIT.
2 | // Code generated by gorm.io/gen. DO NOT EDIT.
3 | // Code generated by gorm.io/gen. DO NOT EDIT.
4 |
5 | package model
6 |
7 | import (
8 | "time"
9 | )
10 |
11 | const TableNameAction = "action"
12 |
13 | // Action mapped from table
14 | type Action struct {
15 | ID int64 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"`
16 | UserID int64 `gorm:"column:user_id;not null;default:9" json:"user_id"`
17 | Type string `gorm:"column:type" json:"type"`
18 | Describe string `gorm:"column:describe;not null" json:"describe"`
19 | IP string `gorm:"column:ip;not null" json:"ip"`
20 | CreateTime time.Time `gorm:"column:create_time;not null;default:CURRENT_TIMESTAMP" json:"create_time"`
21 | UpdateTime time.Time `gorm:"column:update_time;not null;default:CURRENT_TIMESTAMP" json:"update_time"`
22 | IsDelete int32 `gorm:"column:is_delete;not null" json:"is_delete"`
23 | }
24 |
25 | // TableName Action's table name
26 | func (*Action) TableName() string {
27 | return TableNameAction
28 | }
29 |
--------------------------------------------------------------------------------
/model/aikey.gen.go:
--------------------------------------------------------------------------------
1 | // Code generated by gorm.io/gen. DO NOT EDIT.
2 | // Code generated by gorm.io/gen. DO NOT EDIT.
3 | // Code generated by gorm.io/gen. DO NOT EDIT.
4 |
5 | package model
6 |
7 | import (
8 | "time"
9 | )
10 |
11 | const TableNameAikey = "aikey"
12 |
13 | // Aikey mapped from table
14 | type Aikey struct {
15 | ID int64 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"`
16 | Key string `gorm:"column:key;not null" json:"key"`
17 | Host string `gorm:"column:host;not null" json:"host"`
18 | Remarks string `gorm:"column:remarks;not null" json:"remarks"`
19 | Type string `gorm:"column:type;not null;comment:openai sd" json:"type"` // openai sd
20 | Models string `gorm:"column:models;not null;comment:可用模型" json:"models"` // 可用模型
21 | Check int32 `gorm:"column:check;not null;comment:1 检查token可用性 0不检查" json:"check"` // 1 检查token可用性 0不检查
22 | Limit_ float64 `gorm:"column:limit;not null;comment:总限制" json:"limit"` // 总限制
23 | Usage float64 `gorm:"column:usage;not null;comment:已经使用" json:"usage"` // 已经使用
24 | Status int32 `gorm:"column:status;default:1;comment:1 正常 0异常" json:"status"` // 1 正常 0异常
25 | CreateTime time.Time `gorm:"column:create_time;not null;default:CURRENT_TIMESTAMP" json:"create_time"`
26 | UpdateTime time.Time `gorm:"column:update_time;not null;default:CURRENT_TIMESTAMP" json:"update_time"`
27 | IsDelete int32 `gorm:"column:is_delete;not null" json:"is_delete"`
28 | }
29 |
30 | // TableName Aikey's table name
31 | func (*Aikey) TableName() string {
32 | return TableNameAikey
33 | }
34 |
--------------------------------------------------------------------------------
/model/amount_details.gen.go:
--------------------------------------------------------------------------------
1 | // Code generated by gorm.io/gen. DO NOT EDIT.
2 | // Code generated by gorm.io/gen. DO NOT EDIT.
3 | // Code generated by gorm.io/gen. DO NOT EDIT.
4 |
5 | package model
6 |
7 | import (
8 | "time"
9 | )
10 |
11 | const TableNameAmountDetail = "amount_details"
12 |
13 | // AmountDetail mapped from table
14 | type AmountDetail struct {
15 | ID int64 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"`
16 | UserID int64 `gorm:"column:user_id;not null" json:"user_id"`
17 | Type string `gorm:"column:type;not null;comment:提现 or 提成" json:"type"` // 提现 or 提成
18 | CorrelationID int64 `gorm:"column:correlation_id;not null;comment:关联ID" json:"correlation_id"` // 关联ID
19 | OriginalAmount string `gorm:"column:original_amount;not null;comment:原始金额 分" json:"original_amount"` // 原始金额 分
20 | OperateAmount string `gorm:"column:operate_amount;not null;comment:操作金额" json:"operate_amount"` // 操作金额
21 | CurrentAmount string `gorm:"column:current_amount;not null;comment:当前金额" json:"current_amount"` // 当前金额
22 | Remarks string `gorm:"column:remarks;not null;comment:备注" json:"remarks"` // 备注
23 | Status int32 `gorm:"column:status;not null;default:1;comment:1-正常" json:"status"` // 1-正常
24 | CreateTime time.Time `gorm:"column:create_time;not null;default:CURRENT_TIMESTAMP" json:"create_time"`
25 | UpdateTime time.Time `gorm:"column:update_time;not null;default:CURRENT_TIMESTAMP" json:"update_time"`
26 | IsDelete int32 `gorm:"column:is_delete;not null" json:"is_delete"`
27 | }
28 |
29 | // TableName AmountDetail's table name
30 | func (*AmountDetail) TableName() string {
31 | return TableNameAmountDetail
32 | }
33 |
--------------------------------------------------------------------------------
/model/carmi.gen.go:
--------------------------------------------------------------------------------
1 | // Code generated by gorm.io/gen. DO NOT EDIT.
2 | // Code generated by gorm.io/gen. DO NOT EDIT.
3 | // Code generated by gorm.io/gen. DO NOT EDIT.
4 |
5 | package model
6 |
7 | import (
8 | "time"
9 | )
10 |
11 | const TableNameCarmi = "carmi"
12 |
13 | // Carmi mapped from table
14 | type Carmi struct {
15 | ID int64 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"`
16 | IP string `gorm:"column:ip;not null;comment:使用时候的ip" json:"ip"` // 使用时候的ip
17 | UserID int64 `gorm:"column:user_id;not null;comment:使用者" json:"user_id"` // 使用者
18 | Key string `gorm:"column:key;not null;comment:卡密" json:"key"` // 卡密
19 | Value int32 `gorm:"column:value;not null;comment:积分/天数" json:"value"` // 积分/天数
20 | Status int32 `gorm:"column:status;not null;comment:0有效 1使用 2过期" json:"status"` // 0有效 1使用 2过期
21 | Type string `gorm:"column:type;not null;comment:类型 integral/vip_days/svip_days" json:"type"` // 类型 integral/vip_days/svip_days
22 | EndTime string `gorm:"column:end_time;not null;comment:截止时间" json:"end_time"` // 截止时间
23 | Level int32 `gorm:"column:level;not null;comment:卡密充值等级" json:"level"` // 卡密充值等级
24 | CreateTime time.Time `gorm:"column:create_time;not null;default:CURRENT_TIMESTAMP" json:"create_time"`
25 | UpdateTime time.Time `gorm:"column:update_time;not null;default:CURRENT_TIMESTAMP" json:"update_time"`
26 | IsDelete int32 `gorm:"column:is_delete;not null" json:"is_delete"`
27 | }
28 |
29 | // TableName Carmi's table name
30 | func (*Carmi) TableName() string {
31 | return TableNameCarmi
32 | }
33 |
--------------------------------------------------------------------------------
/model/cashback.gen.go:
--------------------------------------------------------------------------------
1 | // Code generated by gorm.io/gen. DO NOT EDIT.
2 | // Code generated by gorm.io/gen. DO NOT EDIT.
3 | // Code generated by gorm.io/gen. DO NOT EDIT.
4 |
5 | package model
6 |
7 | import (
8 | "time"
9 | )
10 |
11 | const TableNameCashback = "cashback"
12 |
13 | // Cashback mapped from table
14 | type Cashback struct {
15 | ID int64 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"`
16 | UserID int64 `gorm:"column:user_id;not null" json:"user_id"`
17 | BenefitID int64 `gorm:"column:benefit_id;primaryKey;comment:受益者" json:"benefit_id"` // 受益者
18 | PayAmount string `gorm:"column:pay_amount;not null;comment:支付金额(分)" json:"pay_amount"` // 支付金额(分)
19 | CommissionRate string `gorm:"column:commission_rate;not null;comment:提成比例(1 - 10000)" json:"commission_rate"` // 提成比例(1 - 10000)
20 | CommissionAmount string `gorm:"column:commission_amount;not null;comment:提成金额(分)" json:"commission_amount"` // 提成金额(分)
21 | Remarks string `gorm:"column:remarks;comment:评论" json:"remarks"` // 评论
22 | OrderID int64 `gorm:"column:order_id;not null" json:"order_id"`
23 | Status int32 `gorm:"column:status;not null;default:3;comment:0异常 1正常 3审核中 6等待下发" json:"status"` // 0异常 1正常 3审核中 6等待下发
24 | CreateTime time.Time `gorm:"column:create_time;not null;default:CURRENT_TIMESTAMP" json:"create_time"`
25 | UpdateTime time.Time `gorm:"column:update_time;not null;default:CURRENT_TIMESTAMP" json:"update_time"`
26 | IsDelete int32 `gorm:"column:is_delete;not null" json:"is_delete"`
27 | }
28 |
29 | // TableName Cashback's table name
30 | func (*Cashback) TableName() string {
31 | return TableNameCashback
32 | }
33 |
--------------------------------------------------------------------------------
/model/config.gen.go:
--------------------------------------------------------------------------------
1 | // Code generated by gorm.io/gen. DO NOT EDIT.
2 | // Code generated by gorm.io/gen. DO NOT EDIT.
3 | // Code generated by gorm.io/gen. DO NOT EDIT.
4 |
5 | package model
6 |
7 | import (
8 | "time"
9 | )
10 |
11 | const TableNameConfig = "config"
12 |
13 | // Config mapped from table
14 | type Config struct {
15 | ID int64 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"`
16 | Name string `gorm:"column:name;primaryKey" json:"name"`
17 | Value string `gorm:"column:value" json:"value"`
18 | Remarks string `gorm:"column:remarks;not null" json:"remarks"`
19 | CreateTime time.Time `gorm:"column:create_time;not null;default:CURRENT_TIMESTAMP" json:"create_time"`
20 | UpdateTime time.Time `gorm:"column:update_time;not null;default:CURRENT_TIMESTAMP" json:"update_time"`
21 | IsDelete int32 `gorm:"column:is_delete;not null" json:"is_delete"`
22 | }
23 |
24 | // TableName Config's table name
25 | func (*Config) TableName() string {
26 | return TableNameConfig
27 | }
28 |
--------------------------------------------------------------------------------
/model/dialog.gen.go:
--------------------------------------------------------------------------------
1 | // Code generated by gorm.io/gen. DO NOT EDIT.
2 | // Code generated by gorm.io/gen. DO NOT EDIT.
3 | // Code generated by gorm.io/gen. DO NOT EDIT.
4 |
5 | package model
6 |
7 | import (
8 | "time"
9 | )
10 |
11 | const TableNameDialog = "dialog"
12 |
13 | // Dialog mapped from table