first commit

This commit is contained in:
benjamin 2024-06-04 21:22:50 +08:00
commit e268852ff1
62 changed files with 4125 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
*.exe
*.so
build
*.log
mini_server*

40
README.md Normal file
View File

@ -0,0 +1,40 @@
# mini_server
#### 介绍
微信小程序后端私有仓库
#### 软件架构
软件架构说明
gorm学习官网https://gorm.io/zh_CN/docs/models.html
#### 安装教程
1. xxxx
2. xxxx
3. xxxx
#### 使用说明
1. xxxx
2. xxxx
3. xxxx
#### 参与贡献
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码
4. 新建 Pull Request
#### 特技
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

27
cert/takway-ai.top.key Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAp5ofFuO1pBRioe3LOCJrYvyMGp3py9gEah7H0thGjClPodNj
tdrLt3UTP2hbDuoEBiiImTDMV+WCWOB0R3xRtq+RnvtKlqXtOvxAcnPrm55jxd3O
kSGIu/PZycY/jG4rxDxDVwKrklpISJnEjmPMwkSLE9tDKIVSp1yvnD8n2bTEo9dq
V4s0FJMJotPOzvVYWcnrRrq2ObJvk/3AdBI/LGEwx+2JuEmf5zcmzITwgICjsZsS
9Yfj4hnudXXbt9+c8opK/bUIuEwdqrrevAsVOTdPEfuTBDX6DYgZTAq6LTWIG+XQ
jG/uhMaXmxBRvLRFIyuApBA6zW68CkLhz/GTqQIDAQABAoIBAAkCPRmQiSWV/Zc7
85MY29zTkP79Q0vfA0hmTydY01yapoUe76/JYESELnyssfhN69lzjvls/g7bb0jd
B7hanlR2K3BNjDzSE7ulQ3GOJDlBJ5Ztzbn6AMzZgU4Eh0tCa2fTisBqjFOu4U5w
YCL8ik0G532HE50Moty2xq8HVgUFnsZqRhaAzloG+VRuNvqTx/xSOotRPS4Ayw1j
NS/jeaKWpBHq0CAZeng1ELiWfDhjojUDeKUthOuNerCU5gp+SbnFyny9OhTP3aqx
k2QO4UJqi3VfCgzPHDbdt3p8QHgjQvcoNBZhQoylk3/bMrp1M7tlXYyf1H25FZrq
wlWAz4ECgYEA3PG0OZAllxXVAunHkwBVKWntsku7T77+BcBGviMM16FfDXwczylV
2mmgd9QDuhDjog3y6Un8yb2E9OjOJCwKhddv1sRYriQKxjkhLJGbFRGtNk+bKq8Y
lhXvH4RyhS41VcAEMvNvvtiuHc7uxLHapp+7Q53TzQ7/ba8HDHdHgAUCgYEAwjHF
QvBeFxKCe1UVe1BEI7CF36BqVnMZsLGKP+lLQLs+HgXv+YXr1UJSZqmwV6gEfyt6
/EaEXFeRnUBUrhg2EUmmEAsQqqk8tEc3AD4KER0vjmXBiikKIXMYzilh7JYU5pqi
NBVqH4u5HoONsXOyaS1QlNyROpER2ZijtlIKalUCgYBoRiJ6sE8tWP5pG5A3Doci
vn2SEi/a2Rbl+/LnNX+QHPbRVGv8UVPVethi9Uq0+9eS2eZ0t5h4Vdt0ApOXermU
pTKBN7VqqlPzbW78q9N3RBptcXCX2n9OBhgO1eF+fWpqh+7zZPSuK3ExXEJPzmiV
DvnAVbJqKtPeHibDeV/jSQKBgDeiurkq1EoX63oaLkVeAaeX80LWWqDZ6QpOUyVs
WTr6ahl/6fi6/Y3jqmfvRa5XXLJyapHMQi+Shw1eWORn21WonAMMqIG8ar/bh05y
d7/fdLh/PquuhlB/ASRPhtaZSPOoacAyQm03QpoTZozocLzhVqzPZGw7E7obmQur
BYDhAoGAQB/JBuShnQJa0XtvSZloiNzoSEc5PjPZYKfMt80VW1NCtIk+RUGRsN6S
V4adJlzwXnQKZSq8LokMD8sgQ227jXHdbKVLSzyCWipREM5UtNH8xg1AetJLxyH7
ATH62gnR5qV1QcIrwGs5Dtw8JlR00gI2xvUvi7XwJHHI1c+XRoY=
-----END RSA PRIVATE KEY-----

62
cert/takway-ai.top.pem Normal file
View File

@ -0,0 +1,62 @@
-----BEGIN CERTIFICATE-----
MIIGBTCCBO2gAwIBAgIQBtj3ZRX9NfSuGIwP9+AbhDANBgkqhkiG9w0BAQsFADBu
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMS0wKwYDVQQDEyRFbmNyeXB0aW9uIEV2ZXJ5d2hlcmUg
RFYgVExTIENBIC0gRzIwHhcNMjQwNTE3MDAwMDAwWhcNMjQwODE0MjM1OTU5WjAY
MRYwFAYDVQQDEw10YWt3YXktYWkudG9wMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAp5ofFuO1pBRioe3LOCJrYvyMGp3py9gEah7H0thGjClPodNjtdrL
t3UTP2hbDuoEBiiImTDMV+WCWOB0R3xRtq+RnvtKlqXtOvxAcnPrm55jxd3OkSGI
u/PZycY/jG4rxDxDVwKrklpISJnEjmPMwkSLE9tDKIVSp1yvnD8n2bTEo9dqV4s0
FJMJotPOzvVYWcnrRrq2ObJvk/3AdBI/LGEwx+2JuEmf5zcmzITwgICjsZsS9Yfj
4hnudXXbt9+c8opK/bUIuEwdqrrevAsVOTdPEfuTBDX6DYgZTAq6LTWIG+XQjG/u
hMaXmxBRvLRFIyuApBA6zW68CkLhz/GTqQIDAQABo4IC8zCCAu8wHwYDVR0jBBgw
FoAUeN+RkF/u3qz2xXXr1UxVU+8kSrYwHQYDVR0OBBYEFG7xcD2xwJgNfg4PlPL7
eALtvIYhMCsGA1UdEQQkMCKCDXRha3dheS1haS50b3CCEXd3dy50YWt3YXktYWku
dG9wMD4GA1UdIAQ3MDUwMwYGZ4EMAQIBMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93
d3cuZGlnaWNlcnQuY29tL0NQUzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI
KwYBBQUHAwEGCCsGAQUFBwMCMIGABggrBgEFBQcBAQR0MHIwJAYIKwYBBQUHMAGG
GGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBKBggrBgEFBQcwAoY+aHR0cDovL2Nh
Y2VydHMuZGlnaWNlcnQuY29tL0VuY3J5cHRpb25FdmVyeXdoZXJlRFZUTFNDQS1H
Mi5jcnQwDAYDVR0TAQH/BAIwADCCAX4GCisGAQQB1nkCBAIEggFuBIIBagFoAHYA
dv+IPwq2+5VRwmHM9Ye6NLSkzbsp3GhCCp/mZ0xaOnQAAAGPhaiLngAABAMARzBF
AiEA0uvWuS89aiRig+3xGXHNyahwV7jNx/uiCklK9klZipACIHpRZvv8iWfo1lW9
ZhZ3UtfpXOseiIKGSbR/bAzZtr7EAHcA2ra/az+1tiKfm8K7XGvocJFxbLtRhIU0
vaQ9MEjX+6sAAAGPhaiLegAABAMASDBGAiEAp365rPS/DmDXRVUuiQ1igB8kLkry
MjMbZT+rvbHC9kICIQCfBvecI6ojgiGzk1U8QT7XpCOB4qAySlmF5Z0tO3v50QB1
AD8XS0/XIkdYlB1lHIS+DRLtkDd/H4Vq68G/KIXs+GRuAAABj4Woi7UAAAQDAEYw
RAIgR66au1GAubh/zc9Eo4i0Cu+wQlLHehJETYD8r1hP7yICIFDq5fGDS45jawLA
qt8IF+5EEiWjLL7/DkxNHLvsnP8qMA0GCSqGSIb3DQEBCwUAA4IBAQDDE7NnYrIe
S2FmcI1hh9encfgviEtfSBvWrUv14F3WCzQKd3HvIAzuuh2aRDpsLBr1xvcPXq8w
2o5IwQMRpe65qFXG7H2pZEp5f0J/qVwXd2h2cZYxImkco/VTNetYvovseJ9LGNnN
Kwrr6XgMd+/KLBzc7m19I6nq2Xvqfa1xFO4o35xyZ9GoYKnH8nn5Cwh/Ux5bRAAD
xuK25DCiLUxXiOzHDngGh5ju4inDEePO2dzYv1TWMp9nLcWoIBp9xZ54aBQRheAm
uSv9CD/xq4vbnRjxMcIn7Ir45HuSqcDL9ghimZ7Crx4fkJiEjaQs3HNaDbrOhKb6
xJ7b42GBvFm+
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEqjCCA5KgAwIBAgIQDeD/te5iy2EQn2CMnO1e0zANBgkqhkiG9w0BAQsFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
MjAeFw0xNzExMjcxMjQ2NDBaFw0yNzExMjcxMjQ2NDBaMG4xCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xLTArBgNVBAMTJEVuY3J5cHRpb24gRXZlcnl3aGVyZSBEViBUTFMgQ0EgLSBH
MjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAO8Uf46i/nr7pkgTDqnE
eSIfCFqvPnUq3aF1tMJ5hh9MnO6Lmt5UdHfBGwC9Si+XjK12cjZgxObsL6Rg1njv
NhAMJ4JunN0JGGRJGSevbJsA3sc68nbPQzuKp5Jc8vpryp2mts38pSCXorPR+sch
QisKA7OSQ1MjcFN0d7tbrceWFNbzgL2csJVQeogOBGSe/KZEIZw6gXLKeFe7mupn
NYJROi2iC11+HuF79iAttMc32Cv6UOxixY/3ZV+LzpLnklFq98XORgwkIJL1HuvP
ha8yvb+W6JislZJL+HLFtidoxmI7Qm3ZyIV66W533DsGFimFJkz3y0GeHWuSVMbI
lfsCAwEAAaOCAU8wggFLMB0GA1UdDgQWBBR435GQX+7erPbFdevVTFVT7yRKtjAf
BgNVHSMEGDAWgBROIlQgGJXm427mD/r6uRLtBhePOTAOBgNVHQ8BAf8EBAMCAYYw
HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1UdEwEB/wQIMAYBAf8C
AQAwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp
Y2VydC5jb20wQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQu
Y29tL0RpZ2lDZXJ0R2xvYmFsUm9vdEcyLmNybDBMBgNVHSAERTBDMDcGCWCGSAGG
/WwBAjAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BT
MAgGBmeBDAECATANBgkqhkiG9w0BAQsFAAOCAQEAoBs1eCLKakLtVRPFRjBIJ9LJ
L0s8ZWum8U8/1TMVkQMBn+CPb5xnCD0GSA6L/V0ZFrMNqBirrr5B241OesECvxIi
98bZ90h9+q/X5eMyOD35f8YTaEMpdnQCnawIwiHx06/0BfiTj+b/XQih+mqt3ZXe
xNCJqKexdiB2IWGSKcgahPacWkk/BAQFisKIFYEqHzV974S3FAz/8LIfD58xnsEN
GfzyIDkH3JrwYZ8caPTf6ZX9M1GrISN8HnWTtdNCH2xEajRa/h9ZBXjUyFKQrGk2
n2hcLrfZSbynEC/pSw/ET7H5nWwckjmAJ1l9fcnbqkU/pf6uMQmnfl0JQjJNSg==
-----END CERTIFICATE-----

52
go.mod Normal file
View File

@ -0,0 +1,52 @@
module mini_server
go 1.21.6
require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/gin-gonic/gin v1.9.1
github.com/google/uuid v1.6.0
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/sirupsen/logrus v1.9.3
github.com/sqids/sqids-go v0.4.1
github.com/unrolled/secure v1.14.0
gorm.io/datatypes v1.2.0
gorm.io/driver/mysql v1.5.6
gorm.io/gorm v1.25.9
)
require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/bytedance/sonic v1.9.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.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.14.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-isatty v0.0.19 // 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.0.8 // indirect
github.com/rogpeppe/go-internal v1.11.0 // 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.11 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

149
go.sum Normal file
View File

@ -0,0 +1,149 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
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.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
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.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
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.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
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.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
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.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.3.0 h1:/NQi8KHMpKWHInxXesC8yD4DhkXPrVhmnwYkjp9AmBA=
github.com/jackc/pgx/v5 v5.3.0/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8=
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/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.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE=
github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ=
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/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sqids/sqids-go v0.4.1 h1:eQKYzmAZbLlRwHeHYPF35QhgxwZHLnlmVj9AkIj/rrw=
github.com/sqids/sqids-go v0.4.1/go.mod h1:EMwHuPQgSNFS0A49jESTfIQS+066XQTVhukrzEPScl8=
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/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.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
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.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/unrolled/secure v1.14.0 h1:u9vJTU/pR4Bny0ntLUMxdfLtmIRGvQf2sEFuA0TG9AE=
github.com/unrolled/secure v1.14.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
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.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
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/datatypes v1.2.0 h1:5YT+eokWdIxhJgWHdrb2zYUimyk0+TaFth+7a0ybzco=
gorm.io/datatypes v1.2.0/go.mod h1:o1dh0ZvjIjhH/bngTpypG6lVRJ5chTBxE09FH/71k04=
gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8=
gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
gorm.io/driver/postgres v1.5.0 h1:u2FXTy14l45qc3UeCJ7QaAXZmZfDDv0YrthvmRq1l0U=
gorm.io/driver/postgres v1.5.0/go.mod h1:FUZXzO+5Uqg5zzwzv4KK49R8lvGIyscBOqYrtI1Ce9A=
gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU=
gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0=
gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig=
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/gorm v1.25.9 h1:wct0gxZIELDk8+ZqF/MVnHLkA1rvYlBWUMv2EdsK1g8=
gorm.io/gorm v1.25.9/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

63
internal/config/config.go Normal file
View File

@ -0,0 +1,63 @@
package config
import (
"github.com/sirupsen/logrus"
)
// 微信全局配置表
type weixin struct {
AppId string
AppSecret string
Site string
HttpTail string
}
func (wx weixin) AppUrl(code string) string {
return wx.Site + "appid=" + wx.AppId + "&secret=" + wx.AppSecret + "&js_code=" + code + wx.HttpTail
}
// 微信小程序配置表
var WeiXin = weixin{
AppId: "wxae661fd0f9edd7da",
AppSecret: "f086d7be25d4cc53bc96f5411cf44548",
Site: "https://api.weixin.qq.com/sns/jscode2session?",
HttpTail: "&grant_type=authorization_code",
}
// 数据库连接信息
// var MysqlDSN = "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=true&loc=Local"
var MysqlDSN = "takway:takway123456@tcp(127.0.0.1:3306)/takway?charset=utf8mb4&parseTime=true&loc=Local"
// 是否使用tls
var TlsEnable = true
var TlsFileKey = "./cert/takway-ai.top.key"
var TlsFilePem = "./cert/takway-ai.top.pem"
// 本程序的服务监听地址
var ServerListenAddress = "0.0.0.0:8002"
// jwtkey
var JwtEnable = true // 是否开启jwt
// var JwtEnable = false // 是否开启jwt仅测试时打开方便使用apifox测试。
var JwtKey = "weilaimengzhu_api_ajsdkfajsldfkjasl"
var JwtApiAuthWhiteList = map[string]string{
"/api/users/wx/login": "POST", // 微信快捷登录
}
// 是否处理跨域请求
var CorsEnable = false
// 设置logrus日志等级和日志文件
var LogLevel = logrus.TraceLevel
var LogFile = "./server.log"
// 是否使用默认用户仅测试时打开方便使用apifox测试。
// var UseDefaultUserId = true
var UseDefaultUserId = false
var DefaultUserId = int32(7) // 7是我的用户ID
// 是否开启文件缓存机制。key为fileIdvalue为*dao.File缓存一段时间减少与mysql交互。
// var FileCacheEnable = true
var FileCacheEnable = false // 由于客户可能会手动修改mysql中的群二维码截图所以这里关闭文件缓存服务避免二维码更新不及时。
// 后续可以考虑提供接口专门更新二维码图片,这样文件缓存开关就可以一直开着。

View File

@ -0,0 +1,15 @@
package dao
import (
"time"
"gorm.io/gorm"
)
// 参考gorm.Model实现
type BaseModel struct {
ID uint64 `gorm:"primarykey"`
CreatedAt time.Time `gorm:"type:datetime(0)"` // 精度维持到秒
UpdatedAt time.Time `gorm:"type:datetime(0)"` // 精度维持到秒
DeletedAt gorm.DeletedAt `gorm:"index;datetime(0)"` // 精度维持到秒
}

37
internal/dao/bluetooth.go Normal file
View File

@ -0,0 +1,37 @@
package dao
import "gorm.io/gorm"
type Bluetooth struct {
ID uint64 `gorm:"primaryKey"`
DeviceId string `gorm:"unique;type varchar(64);not null;comment:'设备Id唯一标识每个蓝牙设备'"`
DeviceName string `gorm:"type varchar(64);comment:'蓝牙名称'"`
DeviceType string `gorm:"type varchar(64);comment:'设备类型'"`
UserId int32 `gorm:"index;comment:'用户ID'"`
}
func AddBluetooth(data *Bluetooth) error {
if err := s.db.Where("device_id = ? AND user_id = ?", data.DeviceId, data.UserId).FirstOrCreate(data).Error; err != nil {
return err
}
return nil
}
func DeleteBluetooth(deviceid string, userid int32) error {
var data *Bluetooth
if err := s.db.Where("device_id = ? AND user_id = ?", deviceid, userid).First(&data).Error; err != nil && err != gorm.ErrRecordNotFound {
return err
}
if err := s.db.Delete(&data).Error; err != nil && err != gorm.ErrRecordNotFound {
return err
}
return nil
}
func GetAllBluetooth(userid int32) ([]*Bluetooth, error) {
var list []*Bluetooth
if err := s.db.Where("user_id = ?", userid).Find(&list).Error; err != nil && err != gorm.ErrRecordNotFound {
return nil, err
}
return list, nil
}

76
internal/dao/character.go Normal file
View File

@ -0,0 +1,76 @@
package dao
import (
"gorm.io/gorm"
)
type Character struct {
ID int32 `gorm:"primaryKey;type:int;not null;comment:'角色ID'"`
VoiceId int `gorm:"type:int;default null;comment:'声音ID'"`
AvatarId string `gorm:"type:varchar(32);not null;comment:'角色头像的uuid'"`
BackgroundIds string `gorm:"type:varchar(255);not null;comment:'聊天背景图集合逗号分隔元素是背景图的uuid'"`
Name string `gorm:"type:varchar(32);not null;comment:'角色名称'"`
WakeupWords string `gorm:"type:varchar(255);not null;"`
WorldScenario string `gorm:"type:mediumtext;not null;"`
Description string `gorm:"type:mediumtext;not null;"`
Emojis string `gorm:"type:json;not null;"`
Dialogues string `gorm:"type:mediumtext;not null;"`
}
func (Character) TableName() string { return "character" }
func Character_Insert(data *Character) (int32, error) {
if err := s.db.Create(data).Error; err != nil {
return 0, err
}
return data.ID, nil
}
func Character_Query(characterId int32) (*Character, error) {
var data Character
err := s.db.Where("id = ?", characterId).First(&data).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &data, nil
}
func Character_QueryAllCharacter() ([]*Character, error) {
var list []*Character
err := s.db.Find(&list).Error
if err != nil {
return nil, err
}
return list, nil
}
// 根据角色ID列表查询信息
func Character_QueryList(characterIds []int32) ([]*Character, error) {
var list []*Character
if err := s.db.Find(&list, characterIds).Error; err != nil {
return nil, err
}
return list, nil
}
func Character_Delete(characterId int32) error {
return s.db.Delete(&Character{}, characterId).Error
}
type CharacterBaseInfo struct {
ID int32 `gorm:"id"`
AvatarId string `gorm:"avatar_id"`
Name string `gorm:"name"`
}
// 根据头像的ID列表查询头像和名称信息
func Character_QueryList_BaseInfo(characterIds []int32) ([]*CharacterBaseInfo, error) {
var list []*CharacterBaseInfo
if err := s.db.Model(&Character{}).Where("id in (?)", characterIds).Select("id, avatar_id, name").Scan(&list).Error; err != nil {
return nil, err
}
return list, nil
}

View File

@ -0,0 +1,46 @@
package dao
import (
"time"
"gorm.io/gorm"
)
type ChatRecord struct {
ID uint64 `gorm:"primaryKey;not null;comment:'聊天记录ID'"`
CreatedAt time.Time `gorm:"index;type:datetime(0);comment:'记录时间,由数据库自动创建'"`
UserId int32 `gorm:"index;not null;comment:'用户Id'"`
CharacterId int32 `gorm:"index;not null;comment:'角色Id'"`
Direction int8 `gorm:"comment:'发送方向。1:用户->角色。 2:角色->用户'"`
Message string `gorm:"type:text;not null;comment:'消息内容'"`
}
func ChatRecord_Query(user_id, character_id int32, start_time, stop_time time.Time) ([]*ChatRecord, error) {
var list []*ChatRecord
if err := s.db.Where("user_id = ? and character_id = ?", user_id, character_id).Where("created_at between ? and ?", start_time, stop_time).Find(&list).Error; err != nil && err != gorm.ErrRecordNotFound {
return nil, err
}
return list, nil
}
func ChatRecord_Insert(data *ChatRecord) error {
return s.db.Create(data).Error
}
// 查询用户和所有角色聊天记录。
func ChatRecord_Query_ByUserId(user_id int32, start_time, stop_time time.Time) ([]*ChatRecord, error) {
var list []*ChatRecord
if err := s.db.Where("user_id = ?", user_id).Where("created_at between ? and ?", start_time, stop_time).Find(&list).Error; err != nil && err != gorm.ErrRecordNotFound {
return nil, err
}
return list, nil
}
// 查询用户和角色是否有聊天记录
func ChatRecord_Exists(user_id, character_id int32) (bool, error) {
id := int32(0)
if err := s.db.Model(&ChatRecord{}).Where("user_id = ? and character_id = ?", user_id, character_id).Limit(1).Select("id").Scan(&id).Error; err != nil && err != gorm.ErrRecordNotFound {
return false, err
}
return id > int32(0), nil
}

40
internal/dao/draft.go Normal file
View File

@ -0,0 +1,40 @@
package dao
import (
"time"
"gorm.io/datatypes"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
// 帖子草稿表
type Draft struct {
UserId int32 `gorm:"primaryKey;comment:'发帖人ID'"`
CreatedAt time.Time `gorm:"type:datetime(0)"`
UpdatedAt time.Time `gorm:"type:datetime(0)"`
Title string `gorm:"index;type:varchar(255);comment:'帖子标题'"`
Content string `gorm:"type:text;comment:'帖子内容'"`
RoleIds datatypes.JSON `gorm:"comment:'关联角色'"`
}
// 如果存在,就更新。如果不存在,就创建
func Draft_Save(data *Draft) error {
return s.db.Clauses(clause.OnConflict{Columns: []clause.Column{{Name: "user_id"}}, DoUpdates: clause.AssignmentColumns([]string{"title", "content", "role_ids"})}).Create(&data).Error
}
func Draft_Query(userId int32) (*Draft, error) {
var data Draft
if err := s.db.Where("user_id = ?", userId).First(&data).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &data, nil
}
func Draft_Delete(userId int32) error {
data := Draft{UserId: userId}
return s.db.Delete(&data).Error
}

37
internal/dao/file.go Normal file
View File

@ -0,0 +1,37 @@
package dao
import (
"gorm.io/gorm"
)
type File struct {
BaseModel
UUID string `gorm:"unique;type varchar(32);comment:'文件UUID'"`
UserId int32 `gorm:"comment:'上传者ID'"`
FileName string `gorm:"type:varchar(255);comment:'文件名称'"`
FileSize uint32 `gorm:"comment:'文件大小'"`
FileType string `gorm:"comment:'文件类型对应html中支持的Content-Type类型'"`
FileContent []byte `gorm:"type:mediumblob;comment:'文件内容目前最大为16M'"`
}
func File_Insert(data *File) (uint64, error) {
if err := s.db.Create(data).Error; err != nil {
return 0, err
}
return uint64(data.ID), nil
}
func File_Query(uuid string) (*File, error) {
var data File
if err := s.db.Where("uuid = ?", uuid).First(&data).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &data, nil
}
func File_Delete(uuid string) error {
return s.db.Where("uuid = ?", uuid).Delete(&File{}).Error
}

55
internal/dao/post.go Normal file
View File

@ -0,0 +1,55 @@
package dao
import (
"time"
"gorm.io/datatypes"
"gorm.io/gorm"
)
// 帖子表
type Post struct {
BaseModel
UserId int32 `gorm:"index;comment:'发帖人ID'"`
Title string `gorm:"index;type:varchar(255);comment:'帖子标题'"`
Content string `gorm:"type:text;comment:'帖子内容'"`
RoleIds datatypes.JSON `gorm:"comment:'关联角色'"`
}
func Post_Insert(data *Post) (uint64, error) {
if err := s.db.Create(data).Error; err != nil {
return 0, err
}
return uint64(data.ID), nil
}
func Post_Query(postId uint64) (*Post, error) {
var data Post
if err := s.db.Where("id = ?", postId).First(&data).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &data, nil
}
func Post_Page_Query(start_time, stop_time time.Time, role_id int32) ([]*Post, error) {
var list []*Post
var err error
if role_id == int32(0) { // 不能根据role_id查询
err = s.db.Where("created_at between ? and ?", start_time, stop_time).Find(&list).Error
} else {
err = s.db.Where("created_at between ? and ?", start_time, stop_time).Where(datatypes.JSONArrayQuery("role_ids").Contains(role_id)).Find(&list).Error
}
if err != nil && err != gorm.ErrRecordNotFound {
return nil, err
}
return list, nil
}
func Post_Delete(postId uint64) error {
data := Post{}
data.ID = postId
return s.db.Delete(&data).Error
}

View File

@ -0,0 +1,39 @@
package dao
import (
"time"
"gorm.io/gorm"
)
// 帖子评论和回复表
type PostComment struct {
ID uint64 `gorm:"primaryKey;not null;comment:'评论id'"`
CreatedAt time.Time `gorm:"type:datetime(0)"`
UserId int32 `gorm:"not null;comment:'评论者'"`
PostId uint64 `gorm:"index;not null;comment:'帖子'"`
Content string `gorm:"type:varchar(1024);not null;comment:'评论内容'"`
// CommentId uint64 `gorm:"comment:'评论上级ID作为回复某个评论时使用'"`
}
func PostComment_Insert(data *PostComment) (uint64, error) {
if err := s.db.Create(data).Error; err != nil {
return 0, err
}
return uint64(data.ID), nil
}
// 根据帖子id查询所有的评论
func PostComment_QueryListByPostId(postId uint64) ([]*PostComment, error) {
var list []*PostComment
if err := s.db.Where("post_id = ?", postId).Find(&list).Error; err != nil && err != gorm.ErrRecordNotFound {
return nil, err
}
return list, nil
}
func PostComment_Delete(commentId uint64) error {
data := PostComment{ID: commentId}
return s.db.Delete(&data).Error
}

75
internal/dao/post_like.go Normal file
View File

@ -0,0 +1,75 @@
package dao
import (
"time"
"gorm.io/gorm"
)
// 贴子点赞表
type PostLike struct {
PostId uint64 `gorm:"index;not null;comment:'帖子'"`
UserId int32 `gorm:"index;not null;comment:'点赞者'"`
CreatedAt time.Time `gorm:"type:datetime(0)"`
}
// 点赞。如果已经点过赞了,则忽略。
func PostLike_Like(data *PostLike) error {
return s.db.Where("user_id = ? and post_id = ?", data.UserId, data.PostId).FirstOrCreate(data).Error
}
// 去掉点赞
func PostLike_UnLike(userId int32, postId uint64) error {
return s.db.Where("user_id = ? and post_id = ?", userId, postId).Delete(&PostLike{}).Error
}
// 获取点赞数
func PostLike_Count(postId uint64) (int64, error) {
count := int64(0)
if err := s.db.Model(&PostLike{}).Where("post_id = ?", postId).Count(&count).Error; err != nil && err != gorm.ErrRecordNotFound {
return 0, err
}
return count, nil
}
// 批量获取帖子点赞数
func PostLike_Count_Batch(postIds []uint64) (map[uint64]int64, error) {
type search_result_item struct {
PostId uint64
LikeCount int64
}
var result []*search_result_item
if err := s.db.Model(&PostLike{}).Where("post_id in (?)", postIds).Select("post_id,count(*) as like_count").Group("post_id").Scan(&result).Error; err != nil {
return nil, err
}
reply := make(map[uint64]int64)
for _, item := range result {
reply[item.PostId] = item.LikeCount
}
return reply, nil
}
// 当前用户有没有点赞
func PostLike_Exists(userId int32, postId uint64) (bool, error) {
count := int64(0)
if err := s.db.Model(&PostLike{}).Where("user_id = ? and post_id = ?", userId, postId).Count(&count).Error; err != nil && err != gorm.ErrRecordNotFound {
return false, err
}
return count != int64(0), nil
}
// 批量查询用户有没有点赞
func PostLike_Exists_Batch(userId int32, postIds []uint64) (map[uint64]bool, error) {
var result []uint64
if err := s.db.Model(&PostLike{}).Where("user_id = ?", userId).Where("post_id in (?)", postIds).Select("post_id").Group("post_id").Scan(&result).Error; err != nil {
return nil, err
}
reply := make(map[uint64]bool)
for _, post_id := range result {
reply[post_id] = true
}
return reply, nil
}

76
internal/dao/service.go Normal file
View File

@ -0,0 +1,76 @@
package dao
import (
"fmt"
"mini_server/internal/logger"
"time"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type Service struct {
dsn string
db *gorm.DB
}
var s *Service
// 获取当前时间,精度只精确到秒,去掉毫秒等数据。避免保存到数据库时时间进位
func now_func() time.Time {
tmp := time.Now().Local().Format(time.DateTime)
now, _ := time.ParseInLocation(time.DateTime, tmp, time.Local)
return now
}
// // dsn example: "root:123@tcp(127.0.0.1:3306)/test"
func InitDefaultService(dsn string) error {
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.GormLogger,
NowFunc: now_func,
})
if err != nil {
return fmt.Errorf("open mysql failed, dsn:%v, err:%v", dsn, err)
}
dbs := &Service{dsn: dsn, db: db}
// 自动创建相关的表结构
if err := auto_migrate_all(db); err != nil {
return fmt.Errorf("automigrate failed, err:%v", err)
}
// 数据库连接相关配置
config_connection_info(db)
s = dbs
return nil
}
// 自动创建对应的数据表结构
func auto_migrate_all(db *gorm.DB) error {
return db.AutoMigrate(
//&Character{}, // 后续需要注释掉,避免修改客户创建的数据表
&ChatRecord{},
&File{},
&Draft{},
&PostLike{},
&PostComment{},
&Post{},
&Bluetooth{},
//&UserCharacter{}, // 需要注释掉,避免修改客户创建的数据表
//&User{}, // 需要注释掉,避免修改客户创建的数据表
)
}
// 数据库连接相关配置
func config_connection_info(db *gorm.DB) {
mysqlDb, err := db.DB()
if err != nil {
return
}
mysqlDb.SetMaxIdleConns(10) // 设置连接池的最大限制连接数
mysqlDb.SetMaxOpenConns(100) // 设置连接池中的最大连接数量
}

97
internal/dao/user.go Normal file
View File

@ -0,0 +1,97 @@
package dao
import (
"time"
"gorm.io/gorm"
)
type User struct {
ID int32 `gorm:"primarykey;type:int"`
CreatedAt time.Time `gorm:"type:datetime(0)"`
UpdatedAt time.Time `gorm:"type:datetime(0)"`
DeletedAt gorm.DeletedAt `gorm:"index;type:datetime(0)"`
Username string `gorm:"type:varchar(64);default null;comment:'用户名称'"`
OpenId string `gorm:"type:varchar(255);default null;comment:'用户唯一标识'"`
AvatarId string `gorm:"type:varchar(32);default null;comment:'头像图片uuid'"`
Tags string `gorm:"type:json;default null;comment:'用户标签,json格式'"`
Persona string `gorm:"type:json;default null;comment:'用户个性化数据,json格式'"`
}
func (User) TableName() string { return "user" }
func User_WX_Insert(data *User) (int32, error) {
if err := s.db.Create(data).Error; err != nil {
return 0, err
}
return data.ID, nil
}
func User_WX_Query(openId string) (*User, error) {
var data User
if err := s.db.Where("open_id = ?", openId).First(&data).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &data, nil
}
func User_Query(userId int32) (*User, error) {
var data User
if err := s.db.Where("id = ?", userId).First(&data).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &data, nil
}
// 修改用户名称
func User_Change_Username(userId int32, username string) error {
user := &User{ID: userId}
return s.db.Model(&user).Update("username", username).Error
}
// 修改用户头像
func User_Change_AvatarId(userId int32, avatarId string) error {
user := &User{ID: userId}
return s.db.Model(&user).Update("avatar_id", avatarId).Error
}
// 查询用户信息列表如果不存在则设置为nil
func User_Query_List(userIds []int32) (map[int32]*User, error) {
var list []*User
if err := s.db.Where("id in (?)", userIds).Find(&list).Error; err != nil {
return nil, err
}
reply := make(map[int32]*User)
for _, data := range list {
reply[data.ID] = data
}
return reply, nil
}
type UserBaseInfo struct {
ID int32 `gorm:"column:id"`
Username string `gorm:"column:username"`
AvatarId string `gorm:"column:avatar_id"`
}
// 批量查询用户的名称和头像信息
func User_Query_List_BaseInfo(userIds []int32) (map[int32]*UserBaseInfo, error) {
var list []*UserBaseInfo
if err := s.db.Model(&User{}).Where("id in (?)", userIds).Select("id, username, avatar_id").Find(&list).Error; err != nil && err != gorm.ErrRecordNotFound {
return nil, err
}
m := make(map[int32]*UserBaseInfo)
for _, info := range list {
m[info.ID] = info
}
return m, nil
}

View File

@ -0,0 +1,26 @@
package dao
type UserCharacter struct {
ID int32 `gorm:"primaryKey;type:int;not null;auto_increment;comment:'角色ID'"`
UserId int32 `gorm:"default null;comment:'关联的用户ID'"`
CharacterId int32 `gorm:"default null;comment:'关联的角色ID'"`
Persona string `gorm:"type:json;default null;comment:'个性化配置'"`
}
// 根据userId查询关联的角色表
func UserCharacter_QueryList(userId int32) ([]*UserCharacter, error) {
var list []*UserCharacter
if err := s.db.Where("user_id = ?", userId).Find(&list).Error; err != nil {
return nil, err
}
return list, nil
}
// 根据userId查询关联角色的role_id
func UserCharacter_QueryList_OnlyCharacterId(userId int32) ([]int32, error) {
var list []int32
if err := s.db.Model(&UserCharacter{}).Where("user_id = ?", userId).Select("character_id").Scan(&list).Error; err != nil {
return nil, err
}
return list, nil
}

View File

@ -0,0 +1,149 @@
package logger
import (
"context"
"errors"
"fmt"
"time"
"github.com/sirupsen/logrus"
"gorm.io/gorm/logger"
"gorm.io/gorm/utils"
)
// Colors
const (
Reset = "\033[0m"
Red = "\033[31m"
Green = "\033[32m"
Yellow = "\033[33m"
Blue = "\033[34m"
Magenta = "\033[35m"
Cyan = "\033[36m"
White = "\033[37m"
BlueBold = "\033[34;1m"
MagentaBold = "\033[35;1m"
RedBold = "\033[31;1m"
YellowBold = "\033[33;1m"
)
// New initialize logger
func NewGormLogger(logursLogger *logrus.Logger, config logger.Config) logger.Interface {
var (
infoStr = "%s[info] "
warnStr = "%s[warn] "
errStr = "%s[error] "
traceStr = "%s[%.3fms] [rows:%v] %s"
traceWarnStr = "%s %s[%.3fms] [rows:%v] %s"
traceErrStr = "%s %s[%.3fms] [rows:%v] %s"
)
if config.Colorful {
infoStr = Green + "%s" + Reset + Green + "[info] " + Reset
warnStr = BlueBold + "%s" + Reset + Magenta + "[warn] " + Reset
errStr = Magenta + "%s" + Reset + Red + "[error] " + Reset
traceStr = Green + "%s" + Reset + Yellow + "[%.3fms] " + BlueBold + "[rows:%v]" + Reset + " %s"
traceWarnStr = Green + "%s " + Yellow + "%s" + Reset + RedBold + "[%.3fms] " + Yellow + "[rows:%v]" + Magenta + " %s" + Reset
traceErrStr = RedBold + "%s " + MagentaBold + "%s" + Reset + Yellow + "[%.3fms] " + BlueBold + "[rows:%v]" + Reset + " %s"
}
switch config.LogLevel {
case logger.Silent:
logursLogger.SetLevel(logrus.FatalLevel)
case logger.Error:
logursLogger.SetLevel(logrus.ErrorLevel)
case logger.Warn:
logursLogger.SetLevel(logrus.WarnLevel)
case logger.Info:
logursLogger.SetLevel(logrus.TraceLevel)
}
return &gormLogger{
Logger: logursLogger,
Config: config,
infoStr: infoStr,
warnStr: warnStr,
errStr: errStr,
traceStr: traceStr,
traceWarnStr: traceWarnStr,
traceErrStr: traceErrStr,
}
}
type gormLogger struct {
Logger *logrus.Logger
logger.Config
infoStr, warnStr, errStr string
traceStr, traceErrStr, traceWarnStr string
}
// LogMode log mode
func (l *gormLogger) LogMode(level logger.LogLevel) logger.Interface {
newlogger := *l
newlogger.LogLevel = level
return &newlogger
}
// Info print info
func (l *gormLogger) Info(ctx context.Context, msg string, data ...interface{}) {
if l.LogLevel >= logger.Info {
l.Logger.Infof(l.infoStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
}
}
// Warn print warn messages
func (l *gormLogger) Warn(ctx context.Context, msg string, data ...interface{}) {
if l.LogLevel >= logger.Warn {
l.Logger.Warnf(l.warnStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
}
}
// Error print error messages
func (l *gormLogger) Error(ctx context.Context, msg string, data ...interface{}) {
if l.LogLevel >= logger.Error {
l.Logger.Errorf(l.errStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
}
}
// Trace print sql message
//
//nolint:cyclop
func (l *gormLogger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
if l.LogLevel <= logger.Silent {
return
}
elapsed := time.Since(begin)
switch {
case err != nil && l.LogLevel >= logger.Error && (!errors.Is(err, logger.ErrRecordNotFound) || !l.IgnoreRecordNotFoundError):
sql, rows := fc()
if rows == -1 {
l.Logger.Errorf(l.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, "-", sql)
} else {
l.Logger.Errorf(l.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, rows, sql)
}
case elapsed > l.SlowThreshold && l.SlowThreshold != 0 && l.LogLevel >= logger.Warn:
sql, rows := fc()
slowLog := fmt.Sprintf("SLOW SQL >= %v", l.SlowThreshold)
if rows == -1 {
l.Logger.Warnf(l.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, "-", sql)
} else {
l.Logger.Warnf(l.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, rows, sql)
}
case l.LogLevel == logger.Info:
sql, rows := fc()
if rows == -1 {
l.Logger.Infof(l.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, "-", sql)
} else {
l.Logger.Infof(l.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, rows, sql)
}
}
}
// ParamsFilter filter params
func (l *gormLogger) ParamsFilter(ctx context.Context, sql string, params ...interface{}) (string, []interface{}) {
if l.Config.ParameterizedQueries {
return sql, nil
}
return sql, params
}

56
internal/logger/log.go Normal file
View File

@ -0,0 +1,56 @@
package logger
import (
"fmt"
"io"
"mini_server/internal/config"
"os"
"time"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"gorm.io/gorm/logger"
)
var GinLogger io.Writer
var GormLogger logger.Interface
func create_file() *os.File {
f, err := os.OpenFile(config.LogFile, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
if err != nil {
fmt.Printf("日志文件打开失败err:%v", err)
os.Exit(-1)
}
return f
}
func Init_log() {
f := create_file()
// writer := io.MultiWriter(f, os.Stdout)
writer := io.MultiWriter(f)
logrus.SetLevel(config.LogLevel)
//设置日志格式,使用自定义格式
logrus.SetFormatter(&MyFormatter{for_server: true})
// 设置业务日志
gin.DisableConsoleColor()
logrus.SetOutput(writer)
// 设置GIN日志文件
GinLogger = io.MultiWriter(writer)
gormLogrus := logrus.New()
gormLogrus.SetFormatter(&MyFormatter{for_gorm: true})
gormLogrus.SetOutput(writer)
// 设置GORM日志文件
GormLogger = NewGormLogger(
gormLogrus,
logger.Config{
SlowThreshold: time.Second, // 慢SQL阈值
LogLevel: logger.Info,
Colorful: false, // 禁用彩色打印
IgnoreRecordNotFoundError: true, // 不打印未查询到的日志
},
)
}

View File

@ -0,0 +1,32 @@
package logger
import (
"bytes"
"fmt"
"time"
"github.com/sirupsen/logrus"
)
type MyFormatter struct {
for_server bool
for_gorm bool
}
func (m *MyFormatter) Format(entry *logrus.Entry) ([]byte, error) {
var b *bytes.Buffer
if entry.Buffer != nil {
b = entry.Buffer
} else {
b = &bytes.Buffer{}
}
timestamp := entry.Time.Format(time.DateTime)
var line string
if m.for_gorm {
line = fmt.Sprintf("[GORM ] [%s] [%s] %s\r\n", timestamp, entry.Level, entry.Message)
} else {
line = fmt.Sprintf("[SERVER] [%s] [%s] %s\r\n", timestamp, entry.Level, entry.Message)
}
b.WriteString(line)
return b.Bytes(), nil
}

View File

@ -0,0 +1,29 @@
package cors
import (
"net/http"
"github.com/gin-gonic/gin"
)
func CorsMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 设置允许的方法
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
// 设置允许的头
c.Writer.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
// 设置允许的来源,* 表示允许所有来源
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
// 如果是OPTIONS请求直接返回不继续处理
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(http.StatusOK)
return
}
// 继续处理请求
c.Next()
}
}

View File

@ -0,0 +1,62 @@
package jwt
import (
"mini_server/internal/config"
"mini_server/internal/dao"
"net/http"
"time"
"github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin"
)
var jwtKey = []byte(config.JwtKey)
type Claims struct {
Username string `json:"username"`
ID int32 `json:"id"`
jwt.StandardClaims
}
func CreateToken(user *dao.User) (string, error) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, &Claims{
ID: user.ID,
Username: user.Username,
StandardClaims: jwt.StandardClaims{
ExpiresAt: jwt.TimeFunc().Add(time.Hour * 24).Unix(), // Token expires in 24 hours
IssuedAt: jwt.TimeFunc().Unix(),
},
})
return token.SignedString(jwtKey)
}
func AuthenticateToken(tokenString string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
return jwtKey, nil
})
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
return claims, nil
} else {
return nil, err
}
}
func AuthMiddleware() gin.HandlerFunc {
return func(ctx *gin.Context) {
if !withinWhiteList(ctx.Request.URL, ctx.Request.Method) {
tokenString := ctx.GetHeader("Authorization")
if tokenString == "" {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}
claims, err := AuthenticateToken(tokenString)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}
ctx.Set("user", claims)
}
ctx.Next()
}
}

View File

@ -0,0 +1,15 @@
package jwt
import (
"mini_server/internal/config"
"net/url"
)
func withinWhiteList(url *url.URL, method string) bool {
target := config.JwtApiAuthWhiteList
path := url.Path
if v, ok := target[path]; ok && (method == v) {
return true
}
return false
}

View File

@ -0,0 +1,25 @@
package tls
import (
"mini_server/internal/config"
"github.com/gin-gonic/gin"
"github.com/unrolled/secure"
)
func TlsHandler() gin.HandlerFunc {
return func(c *gin.Context) {
secureMiddleware := secure.New(secure.Options{
SSLRedirect: true,
SSLHost: config.ServerListenAddress,
})
err := secureMiddleware.Process(c.Writer, c.Request)
// If there was an error, do not continue.
if err != nil {
return
}
c.Next()
}
}

View File

@ -0,0 +1,31 @@
package otherapi
import (
"bytes"
"encoding/json"
"io"
"net/http"
)
func http_post(url string, request, reply any) error {
jsonData, err := json.Marshal(request)
if err != nil {
return err
}
httpRequest, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(jsonData))
if err != nil {
return err
}
httpRequest.Header.Set("Content-Type", "application/json")
client := &http.Client{}
response, err := client.Do(httpRequest)
if err != nil {
return err
}
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
if err != nil {
return err
}
return json.Unmarshal(body, reply)
}

View File

@ -0,0 +1,101 @@
package bluetooth
import (
"fmt"
"mini_server/internal/dao"
"mini_server/internal/server/help"
"mini_server/internal/server/httpreply"
"net/http"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
type CreateRequest struct {
DeviceId string `gorm:"unique;type varchar(64);not null;comment:'设备Id唯一标识每个蓝牙设备'"`
DeviceName string `gorm:"type varchar(64);comment:'蓝牙名称'"`
DeviceType string `gorm:"type varchar(64);comment:'设备类型'"`
}
// 添加角色
func Create(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
var request CreateRequest
if err := c.ShouldBindJSON(&request); err != nil {
reply.Status = http.StatusBadRequest
reply.Message = fmt.Sprintf("invalid json data,err:%v", err)
return
}
userid, res := help.GetUserId(c)
if !res {
reply.Status = http.StatusInternalServerError
reply.Message = "登录信息已失效"
return
}
data := dao.Bluetooth{
DeviceId: request.DeviceId,
DeviceName: request.DeviceName,
DeviceType: request.DeviceType,
UserId: userid,
}
err := dao.AddBluetooth(&data)
if err != nil {
logrus.Errorf("dao.Bluetooth_Insert failed, err:%v", err)
reply.Status = http.StatusInternalServerError
reply.Message = "添加蓝牙失败"
return
}
reply.Data = data
}
func QueryAll(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
userid, res := help.GetUserId(c)
if !res {
reply.Status = http.StatusInternalServerError
reply.Message = "登录信息已失效"
return
}
data, err := dao.GetAllBluetooth(userid)
if err != nil {
reply.Status = http.StatusInternalServerError
reply.Message = "蓝牙查询异常"
return
}
reply.Data = data
}
func Delete(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
var request CreateRequest
if err := c.ShouldBindJSON(&request); err != nil {
reply.Status = http.StatusBadRequest
reply.Message = fmt.Sprintf("invalid json data,err:%v", err)
return
}
userid, res := help.GetUserId(c)
if !res {
reply.Status = http.StatusInternalServerError
reply.Message = "登录信息已失效"
return
}
err := dao.DeleteBluetooth(request.DeviceId, userid)
if err != nil {
logrus.Errorf("dao.Bluetooth_Delete failed, err:%v", err)
reply.Status = http.StatusInternalServerError
reply.Message = "删除蓝牙失败"
return
}
reply.Data = err
}

View File

@ -0,0 +1,6 @@
package bluetooth
import "github.com/gin-gonic/gin"
// 查询用户保存的蓝牙列表
func List(c *gin.Context) {}

View File

@ -0,0 +1,6 @@
package bluetooth
import "github.com/gin-gonic/gin"
// 上传蓝牙信息
func Upload(c *gin.Context) {}

View File

@ -0,0 +1,51 @@
package chatrecord
import (
"fmt"
"mini_server/internal/dao"
"mini_server/internal/server/help"
"mini_server/internal/server/httpreply"
"net/http"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
type ExistsRequest struct {
RoleId int32 `form:"role_id" binding:"min=1"` // 角色ID
}
type ExistsReply struct {
Exists bool `json:"exists"`
}
func Exists(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
//解析请求参数
var request ExistsRequest
if err := c.ShouldBind(&request); err != nil {
reply.Status = http.StatusBadRequest
reply.Message = fmt.Sprintf("invalid json data, err:%v", err)
return
}
userId, ok := help.GetUserId(c)
if !ok {
reply.Status = http.StatusNotFound
reply.Message = "无法获取用户信息"
return
}
// 在数据库中查询,是否存在角色和用户的聊天记录。
exists, err := dao.ChatRecord_Exists(userId, request.RoleId)
if err != nil {
logrus.Errorf("数据库查询是否存在聊天记录时失败userId:%d,roleId:%d,err:%v", userId, request.RoleId, err)
reply.Status = http.StatusInternalServerError
reply.Message = "数据库查询是否存在聊天记录时失败"
return
}
reply.Data = ExistsReply{Exists: exists}
}

View File

@ -0,0 +1,77 @@
package chatrecord
import (
"fmt"
"mini_server/internal/dao"
"mini_server/internal/server/help"
"mini_server/internal/server/httpreply"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
type ChatRecordInfo struct {
ChatRecordId uint64 `json:"chat_record_id"`
UserId int32 `json:"user_id"`
RoleId int32 `json:"role_id"`
Direction int8 `json:"direction"`
Message string `json:"message"`
CreatedAt string `json:"create_at"` // 使用string保存在设置时不带时区
}
type HistoryRequest struct {
RoleId int32 `form:"role_id" binding:"required,gt=0"`
StartTime time.Time `form:"start_time" binding:"required" time_format:"2006-01-02 15:04:05"`
StopTime time.Time `form:"stop_time" binding:"required,gtfield=StartTime" time_format:"2006-01-02 15:04:05"`
}
type HistoryReply struct {
List []*ChatRecordInfo `json:"list"`
}
// 聊天记录历史查询,支持分页查询
func History(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
userId, ok := help.GetUserId(c)
if !ok {
reply.Status = http.StatusNotFound
reply.Message = "无法获取用户信息"
return
}
var request HistoryRequest
if err := c.ShouldBind(&request); err != nil {
reply.Status = http.StatusBadRequest
reply.Message = fmt.Sprintf("invalid query args, err:%v", err)
return
}
list, err := dao.ChatRecord_Query(userId, request.RoleId, request.StartTime, request.StopTime)
if err != nil {
logrus.Errorf("聊天记录查询失败request:%v,err:%v", request, err)
reply.Status = http.StatusInternalServerError
reply.Message = "聊天记录查询失败"
return
}
chatRecordList := make([]*ChatRecordInfo, len(list))
for i, data := range list {
var info ChatRecordInfo
copy_from_mysql(data, &info)
chatRecordList[i] = &info
}
reply.Data = &HistoryReply{List: chatRecordList}
}
func copy_from_mysql(data *dao.ChatRecord, info *ChatRecordInfo) {
info.ChatRecordId = data.ID
info.UserId = data.UserId
info.RoleId = data.CharacterId
info.Direction = data.Direction
info.Message = data.Message
info.CreatedAt = data.CreatedAt.Format(time.DateTime)
}

View File

@ -0,0 +1,93 @@
package chatrecord
import (
"fmt"
"mini_server/internal/dao"
"mini_server/internal/server/help"
"mini_server/internal/server/httpreply"
"mini_server/internal/server/role"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
type HistoryAllRequest struct {
StartTime time.Time `form:"start_time" binding:"required" time_format:"2006-01-02 15:04:05"`
StopTime time.Time `form:"stop_time" binding:"required,gtfield=StartTime" time_format:"2006-01-02 15:04:05"`
}
type HistoryAllItem struct {
RoleInfo *role.RoleInfo `json:"role_info"`
List []*ChatRecordInfo `json:"list"`
}
type HistoryAllReply struct {
RoleList []*HistoryAllItem `json:"role_list"`
}
// 查询用户和全部角色的聊天记录。
func HistoryAll(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
userId, ok := help.GetUserId(c)
if !ok {
reply.Status = http.StatusNotFound
reply.Message = "无法获取用户信息"
return
}
var request HistoryAllRequest
if err := c.ShouldBind(&request); err != nil {
reply.Status = http.StatusBadRequest
reply.Message = fmt.Sprintf("invalid query args, err:%v", err)
return
}
// 根据用户查询聊天记录。
list, err := dao.ChatRecord_Query_ByUserId(userId, request.StartTime, request.StopTime)
if err != nil {
logrus.Errorf("聊天记录查询失败request:%v,err:%v", request, err)
reply.Status = http.StatusInternalServerError
reply.Message = "聊天记录查询失败"
return
}
role_list := make([]*HistoryAllItem, 0)
mapExistsRole := map[int32]int{} // key是role_idvalue是列表中的下标。
role_ids := make([]int32, 0)
for _, data := range list {
role_id := data.CharacterId
index, ok := mapExistsRole[role_id]
if !ok { // 不存在,则添加。
index = len(role_list)
mapExistsRole[role_id] = index
role_list = append(role_list, &HistoryAllItem{RoleInfo: nil, List: make([]*ChatRecordInfo, 0)})
role_ids = append(role_ids, role_id)
}
var info ChatRecordInfo
copy_from_mysql(data, &info)
role_list[index].List = append(role_list[index].List, &info)
}
if len(role_ids) > 0 {
// 根据角色id列表查询角色的头像和名称
role_infos, err := dao.Character_QueryList(role_ids)
if err != nil {
logrus.Errorf("查询角色基础信息失败,role_ids:%v,err:%v", role_ids, err)
reply.Status = http.StatusInternalServerError
reply.Message = "查询角色基础信息失败"
return
}
for _, role_info := range role_infos {
role_id := role_info.ID
var detail role.RoleInfo
role.Copy_mysql_data(role_info, &detail)
role_list[mapExistsRole[role_id]].RoleInfo = &detail
}
}
reply.Data = &HistoryAllReply{RoleList: role_list}
}

View File

@ -0,0 +1,51 @@
package chatrecord
import (
"fmt"
"mini_server/internal/dao"
"mini_server/internal/server/help"
"mini_server/internal/server/httpreply"
"net/http"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
type UploadRequest struct {
RoleId int32 `json:"role_id" binding:"required,gt=0"`
Direction *int `json:"direction" binding:"required,oneof=0 1"` // 整数required时无法接收0值会报错。需要定义为指针类型
Message string `json:"message" binding:"required,min=1"`
}
// 上传聊天记录
func Upload(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
userId, ok := help.GetUserId(c)
if !ok {
reply.Status = http.StatusNotFound
reply.Message = "无法获取用户信息"
return
}
var info UploadRequest
if err := c.ShouldBindJSON(&info); err != nil {
reply.Status = http.StatusBadRequest
reply.Message = fmt.Sprintf("invalid json data:%v", err)
return
}
data := dao.ChatRecord{
UserId: userId,
CharacterId: info.RoleId,
Direction: int8(*info.Direction),
Message: info.Message,
}
if err := dao.ChatRecord_Insert(&data); err != nil {
logrus.Errorf("保存聊天记录失败info:%v,err:%v", info, err)
reply.Status = http.StatusInternalServerError
reply.Message = "聊天记录保存失败"
return
}
}

View File

@ -0,0 +1,36 @@
package file
import (
"fmt"
"mini_server/internal/dao"
"mini_server/internal/server/httpreply"
"net/http"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
type DeleteURI struct {
FileId string `uri:"file_id" binding:"len=32"`
}
// 文件删除接口
func Delete(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
var param DeleteURI
if err := c.ShouldBindUri(&param); err != nil {
reply.Status = http.StatusBadRequest
reply.Message = fmt.Sprintf("invalid uri path,err:%v", err)
return
}
if err := dao.File_Delete(param.FileId); err != nil {
logrus.Errorf("删除文件失败文件id:%s,err:%v", param.FileId, err)
reply.Status = http.StatusInternalServerError
reply.Message = "数据库删除失败"
return
}
}

View File

@ -0,0 +1,62 @@
package file
import (
"fmt"
"mini_server/internal/config"
"mini_server/internal/dao"
"mini_server/internal/server/help"
"net/http"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
type DownloadURI struct {
FileId string `uri:"file_id" binding:"len=32"`
}
// 文件下载接口
func Download(c *gin.Context) {
var param DownloadURI
if err := c.ShouldBindUri(&param); err != nil {
c.JSON(http.StatusBadRequest, fmt.Sprintf("invalid download uri,err:%v", err))
return
}
fileId := param.FileId
var data *dao.File
var err error
var ok bool
if config.FileCacheEnable { // 从文件缓存中查询
if data, ok = help.GetFileCache(fileId); ok {
logrus.Tracef("从内存缓存中获取文件数据file_uuid:%v", fileId)
}
}
if data == nil { // 未获取到文件缓存数据
logrus.Tracef("从msyql中获取文件数据file_uuid:%v", fileId)
data, err = dao.File_Query(param.FileId)
if err != nil {
logrus.Errorf("文件下载失败,fileId:%s,err:%v", param.FileId, err)
c.JSON(http.StatusInternalServerError, "文件下载失败")
return
}
if data == nil { // 文件不存在
logrus.Errorf("文件下载失败文件file_id:%v不存在", param.FileId)
c.JSON(http.StatusNotFound, "文件不存在")
return
}
if config.FileCacheEnable { // 更新到文件缓存中
help.SetFileCache(fileId, data)
}
}
logrus.Tracef("文件%s下载成功文件大小:%d数据库中blob实际大小:%d", data.FileName, data.FileSize, len(data.FileContent))
c.Header("Content-Disposition", "attachment;filename="+data.FileName)
c.Header("Content-Type", data.FileType)
c.Header("Accept-Length", fmt.Sprintf("%d", data.FileSize))
c.Writer.WriteHeader(http.StatusOK)
c.Writer.Write(data.FileContent)
}

View File

@ -0,0 +1,83 @@
package file
import (
"bytes"
"encoding/hex"
"io"
"log"
"mini_server/internal/dao"
"mini_server/internal/server/help"
"mini_server/internal/server/httpreply"
"net/http"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/sirupsen/logrus"
)
type UploadReply struct {
FileId string `json:"file_id"` // 文件uuid返回file_uuid
}
// 文件上传接口
func Upload(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
userId, ok := help.GetUserId(c)
if !ok {
reply.Status = http.StatusNotFound
reply.Message = "无法获取用户信息"
return
}
file, err := c.FormFile("file")
if err != nil {
reply.Status = http.StatusBadRequest
reply.Message = "无法获取文件信息"
return
}
// 读取文件中的数据,并写入到数据库中
reader, _ := file.Open()
defer reader.Close()
// 添加文件大小限制目前使用mediumblob有16M存储限制
if file.Size >= int64(1024*1024*16) {
reply.Status = http.StatusRequestEntityTooLarge
reply.Message = "上传文件超过限制目前最多支持16M文件上传"
return
}
// 拷贝数据
buf := bytes.NewBuffer(nil)
if _, err := io.Copy(buf, reader); err != nil {
reply.Status = http.StatusInternalServerError
reply.Message = "copy file failed"
return
}
fileType := http.DetectContentType(buf.Bytes())
log.Printf("文件上传请求,文件名:%v, 文件大小:%d, 文件类型:%v", file.Filename, file.Size, fileType)
// 将数据写入数据库中
bytesUUID, _ := uuid.NewUUID()
uuid := hex.EncodeToString(bytesUUID[:])
data := &dao.File{
UUID: uuid, // 作为唯一标识
UserId: userId,
FileType: fileType,
FileName: file.Filename,
FileSize: uint32(file.Size),
FileContent: buf.Bytes(),
}
if _, err = dao.File_Insert(data); err != nil {
logrus.Errorf("dao.File_Insert failed,err:%v", err)
reply.Status = http.StatusInternalServerError
reply.Message = "文件上传保存失败"
return
}
reply.Data = &UploadReply{FileId: uuid}
}

View File

@ -0,0 +1,26 @@
package help
import (
"mini_server/internal/dao"
"time"
"github.com/patrickmn/go-cache"
)
// 添加文件缓存功能,将文件数据保存到内存中,减少与数据库的交互,提高性能。
var fileCache *cache.Cache
func init() {
fileCache = cache.New(time.Hour, time.Minute*30) // 设置超时时间和清理时间
}
func GetFileCache(file_uuid string) (*dao.File, bool) {
if v, found := fileCache.Get(file_uuid); found {
return v.(*dao.File), true
}
return nil, false
}
func SetFileCache(file_uuid string, data *dao.File) {
fileCache.Set(file_uuid, data, cache.DefaultExpiration)
}

View File

@ -0,0 +1,29 @@
package help
import (
"math/rand"
"time"
"github.com/sqids/sqids-go"
)
var sqlids_sqlids *sqids.Sqids
func init() {
// 设置随机数种子
rand.NewSource(time.Now().UnixNano())
sqlids_sqlids, _ = sqids.New()
}
// 保证每秒id唯一。同一秒内由随机数区分可能出现重复。
// 注: 此微信用户名,只是展示名称,不能作为用户唯一标识使用,后期用户也可以修改。所有并没有严格唯一性要求,只要不容易重复即可。
func GetUniqueIdEachSecond() string {
// 生成一个0到1000之间的随机数
randomNum := rand.Intn(1001)
// 获取当前时间戳,精度秒即可。
ts := time.Now().Unix()
// 根据整数数组生成唯一的短id
id, _ := sqlids_sqlids.Encode([]uint64{uint64(ts), uint64(randomNum)})
return id
}

View File

@ -0,0 +1,34 @@
package help
import (
"mini_server/internal/config"
"mini_server/internal/middleware/jwt"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
func GetUserId(c *gin.Context) (userId int32, ok bool) {
defer func() {
if !ok && config.UseDefaultUserId {
userId = config.DefaultUserId
ok = true
}
}()
v, ok := c.Get("user")
logrus.Tracef("GetUserInfo, path:%v,method:%v,user v:%#v", c.Request.URL.Path, c.Request.Method, v)
if !ok {
return
}
cliams, ok := v.(*jwt.Claims)
if !ok {
return
}
if cliams.ID == int32(0) {
return
}
userId = cliams.ID
ok = true
return
}

View File

@ -0,0 +1,27 @@
package httpreply
import (
"net/http"
"github.com/gin-gonic/gin"
)
const (
Code_OK = 200
Code_Err = -1
)
type ReplyData struct {
Status int `json:"status"` // 状态码 200代表成功
Message string `json:"message"` // 消息
Data any `json:"data"` // 实体数据
}
func NewDefaultReplyData() *ReplyData { return &ReplyData{Status: 200, Message: "", Data: nil} }
func NewReplyData(status int, message string, data any) *ReplyData {
return &ReplyData{Status: status, Message: message, Data: data}
}
func Reply(c *gin.Context, data *ReplyData) {
c.JSON(http.StatusOK, data)
}

View File

@ -0,0 +1,160 @@
package post
import (
"fmt"
"mini_server/internal/dao"
"mini_server/internal/server/help"
"mini_server/internal/server/httpreply"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
// 帖子评论处理逻辑
type UploadCommentRequest struct {
Content string `json:"content" bingding:"min=1"`
}
// 发布帖子
func UploadComment(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
userId, ok := help.GetUserId(c)
if !ok {
reply.Status = http.StatusNotFound
reply.Message = "无法获取用户信息"
return
}
var param QueryURI
if err := c.ShouldBindUri(&param); err != nil {
reply.Status = http.StatusBadRequest
reply.Message = fmt.Sprintf("invalid uri path,err:%v", err)
return
}
var request UploadCommentRequest
if err := c.ShouldBindJSON(&request); err != nil {
reply.Status = http.StatusBadRequest
reply.Message = fmt.Sprintf("bad request, err:%v", err)
return
}
data := dao.PostComment{
UserId: userId,
PostId: param.PostId,
Content: request.Content,
}
if _, err := dao.PostComment_Insert(&data); err != nil {
logrus.Errorf("评论上传失败request:%v", request)
reply.Status = http.StatusInternalServerError
reply.Message = "评论上传失败"
return
}
}
type QueryCommentURI struct {
PostId uint64 `uri:"post_id" binding:"min=1"`
}
type CommentInfo struct {
CommentId uint64 `json:"comment_id"`
Content string `json:"content"`
Time string `json:"time"`
UserId int32 `json:"user_id"`
Username string `json:"username"`
AvatarId string `json:"avatar_id"`
}
type QueryCommentReply struct {
CommentList []*CommentInfo `json:"comment_list"`
}
// 查询帖子所有评论
func QueryComment(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
var param QueryCommentURI
if err := c.ShouldBindUri(&param); err != nil {
reply.Status = http.StatusBadRequest
reply.Message = fmt.Sprintf("invalid uri path,err:%v", err)
return
}
list, err := dao.PostComment_QueryListByPostId(param.PostId)
if err != nil {
logrus.Errorf("查询评论列表失败,post_id:%v", param.PostId)
reply.Status = http.StatusInternalServerError
reply.Message = "查询评论列表失败"
return
}
mapUserIdExists := make(map[int32]bool)
listUserId := make([]int32, 0)
commentList := make([]*CommentInfo, len(list))
for i, data := range list {
user_id := data.UserId
commentList[i] = &CommentInfo{
CommentId: data.ID,
Content: data.Content,
Time: data.CreatedAt.Format(time.DateTime),
UserId: data.UserId,
}
if !mapUserIdExists[user_id] {
listUserId = append(listUserId, user_id)
}
}
if len(listUserId) > 0 {
// 批量查询评论用户的头像和名称
mapBaseInfo, err := dao.User_Query_List_BaseInfo(listUserId)
if err != nil {
logrus.Errorf("查询评论用户基本信息失败,listUserId:%v,err:%v", listUserId, err)
reply.Status = http.StatusInternalServerError
reply.Message = "查询评论用户基本信息失败"
return
}
for i, data := range list {
user_id := data.UserId
commentList[i].Username = mapBaseInfo[user_id].Username
commentList[i].AvatarId = mapBaseInfo[user_id].AvatarId
}
}
reply.Data = QueryCommentReply{CommentList: commentList}
}
type DeleteCommentURI struct {
PostId uint64 `uri:"post_id" binding:"min=1"`
CommentId uint64 `uri:"comment_id" binding:"min=1"`
}
// 删除评论
func DeleteComment(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
userId, ok := help.GetUserId(c)
if !ok {
reply.Status = http.StatusNotFound
reply.Message = "无法获取用户信息"
return
}
var param DeleteCommentURI
if err := c.ShouldBindUri(&param); err != nil {
reply.Status = http.StatusBadRequest
reply.Message = fmt.Sprintf("invalid uri path,err:%v", err)
return
}
if err := dao.PostComment_Delete(param.CommentId); err != nil {
logrus.Errorf("删除评论失败userId:%d,param:%v,err:%v", userId, param, err)
reply.Status = http.StatusInternalServerError
reply.Message = "删除评论失败"
return
}
}

View File

@ -0,0 +1,113 @@
package post
import (
"fmt"
"mini_server/internal/dao"
"mini_server/internal/server/help"
"mini_server/internal/server/httpreply"
"net/http"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"gorm.io/datatypes"
)
// 草稿处理逻辑。
type DraftInfo struct {
UserId int32 `json:"user_id"` // 发帖者
Title string `json:"title"`
Content string `json:"content"`
RoleIds datatypes.JSON `json:"role_ids"` // 帖子关联的角色id列表
}
type QueryDraftReply struct {
DraftInfo *DraftInfo `json:"draft_info"`
}
// 上传草稿
func SaveDraft(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
userId, ok := help.GetUserId(c)
if !ok {
reply.Status = http.StatusNotFound
reply.Message = "无法获取用户信息"
return
}
var request UploadRequest
if err := c.ShouldBindJSON(&request); err != nil {
reply.Status = http.StatusBadRequest
reply.Message = fmt.Sprintf("bad request, err:%v", err)
return
}
// 将数据写入数据库中
data := dao.Draft{
UserId: userId,
Title: request.Title,
Content: request.Content,
RoleIds: request.RoleIds,
}
if err := dao.Draft_Save(&data); err != nil {
logrus.Errorf("保存草稿失败userId:%d,err:%v", userId, err)
reply.Status = http.StatusInternalServerError
reply.Message = "保存草稿失败"
return
}
}
// 删除草稿
func DeleteDraft(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
userId, ok := help.GetUserId(c)
if !ok {
reply.Status = http.StatusNotFound
reply.Message = "无法获取用户信息"
return
}
if err := dao.Draft_Delete(userId); err != nil {
logrus.Errorf("删除草稿失败userId:%d,err:%v", userId, err)
reply.Status = http.StatusInternalServerError
reply.Message = "删除草稿失败"
return
}
}
// 查询草稿
func QueryDraft(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
userId, ok := help.GetUserId(c)
if !ok {
reply.Status = http.StatusNotFound
reply.Message = "无法获取用户信息"
return
}
data, err := dao.Draft_Query(userId)
if err != nil {
logrus.Errorf("查询草稿失败userId:%d,err:%v", userId, err)
reply.Status = http.StatusInternalServerError
reply.Message = "查询草稿失败"
return
}
queryDraftReply := QueryDraftReply{DraftInfo: nil}
if data != nil {
queryDraftReply.DraftInfo = &DraftInfo{
UserId: data.UserId,
Title: data.Title,
Content: data.Content,
RoleIds: data.RoleIds,
}
}
reply.Data = queryDraftReply
}

View File

@ -0,0 +1,39 @@
package post
import (
"fmt"
"mini_server/internal/dao"
"mini_server/internal/server/help"
"mini_server/internal/server/httpreply"
"net/http"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
func Like(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
userId, ok := help.GetUserId(c)
if !ok {
reply.Status = http.StatusNotFound
reply.Message = "无法获取用户信息"
return
}
var param QueryURI
if err := c.ShouldBindUri(&param); err != nil {
reply.Status = http.StatusBadRequest
reply.Message = fmt.Sprintf("invalid uri path,err:%v", err)
return
}
data := dao.PostLike{UserId: userId, PostId: param.PostId}
if err := dao.PostLike_Like(&data); err != nil {
logrus.Errorf("贴子点赞插入数据库异常userId:%d,postId:%d,err:%v", userId, param.PostId, err)
reply.Status = http.StatusInternalServerError
reply.Message = "贴子点赞异常"
return
}
}

View File

@ -0,0 +1,97 @@
package post
import (
"fmt"
"mini_server/internal/dao"
"mini_server/internal/server/help"
"mini_server/internal/server/httpreply"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
// 帖子列表查询接口。支持按照时间查询,以及角色查询。
type ListParam struct {
StartTime time.Time `form:"start_time" binding:"required" time_format:"2006-01-02 15:04:05"`
StopTime time.Time `form:"stop_time" binding:"required,gtfield=StartTime" time_format:"2006-01-02 15:04:05"`
RoleId *int32 `form:"role_id" binding:"min=0"`
}
type ListReply struct {
PostList []*PostInfo `json:"post_list"`
}
func List(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
userId, ok := help.GetUserId(c)
if !ok {
reply.Status = http.StatusNotFound
reply.Message = "无法获取用户信息"
return
}
var param ListParam
if err := c.ShouldBindQuery(&param); err != nil {
reply.Status = http.StatusBadRequest
reply.Message = fmt.Sprintf("invalid query args,err:%v", err)
return
}
list, err := dao.Post_Page_Query(param.StartTime, param.StopTime, *param.RoleId)
if err != nil {
logrus.Errorf("帖子列表查询失败param:%v,error:%v", param, err)
reply.Status = http.StatusInternalServerError
reply.Message = "帖子列表查询失败"
return
}
// 批量查询帖子点赞信息
postIds := make([]uint64, len(list))
for i, data := range list {
postIds[i] = uint64(data.ID)
}
userIds := make([]int32, len(list))
for i, data := range list {
userIds[i] = data.UserId
}
like_count_map, err := dao.PostLike_Count_Batch(postIds)
if err != nil {
logrus.Errorf("帖子列表点赞数查询失败userId:%d,postIds:%v,err:%v", userId, postIds, err)
reply.Status = http.StatusInternalServerError
reply.Message = "帖子列表点赞数查询失败"
return
}
// 批量查询用户是否点赞
user_like_map, err := dao.PostLike_Exists_Batch(userId, postIds)
if err != nil {
logrus.Errorf("帖子列表用户点赞信息查询失败userId:%d,postId:%v,err:%v", userId, postIds, err)
reply.Status = http.StatusInternalServerError
reply.Message = "帖子列表用户点赞信息查询失败"
return
}
// 批量查询用户名和用户头像
user_info_map, err := dao.User_Query_List(userIds)
if err != nil {
logrus.Errorf("批量查询帖子发布者信息失败postIds:%v,userIds:%v,err:%v", postIds, userId, err)
reply.Status = http.StatusInternalServerError
reply.Message = "批量查询帖子发布者信息失败"
return
}
postList := make([]*PostInfo, len(list))
for i, data := range list {
var info PostInfo
current_post_id := uint64(data.ID)
current_user_id := data.UserId
copy_from_mysql(data, like_count_map[current_post_id], user_like_map[current_post_id], user_info_map[current_user_id], &info)
postList[i] = &info
}
reply.Data = &ListReply{PostList: postList}
}

View File

@ -0,0 +1,109 @@
package post
import (
"fmt"
"mini_server/internal/dao"
"mini_server/internal/server/help"
"mini_server/internal/server/httpreply"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"gorm.io/datatypes"
)
type PostInfo struct {
PostId uint64 `json:"post_id"`
UserId int32 `json:"user_id"` // 发帖者
UserAvatarId string `json:"user_avatar_id"` // 用户头像
Username string `json:"username"` // 用户名称
CreatedAt string `json:"created_at"` // 发帖时间
Title string `json:"title"`
Content string `json:"content"`
LikeCount int `json:"like_count"` // 点赞数
IsLike bool `json:"is_like"` // 当前用户是否点赞了
RoleIds datatypes.JSON `json:"role_ids"` // 帖子关联的角色id列表
}
type QueryURI struct {
PostId uint64 `uri:"post_id" binding:"min=1"`
}
type QueryReply struct {
PostInfo *PostInfo `json:"post_info"`
}
// 帖子查询接口
func Query(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
userId, ok := help.GetUserId(c)
if !ok {
reply.Status = http.StatusNotFound
reply.Message = "无法获取用户信息"
return
}
var param QueryURI
if err := c.ShouldBindUri(&param); err != nil {
reply.Status = http.StatusBadRequest
reply.Message = fmt.Sprintf("invalid uri path,err:%v", err)
return
}
data, err := dao.Post_Query(param.PostId)
if err != nil {
logrus.Errorf("帖子数据库查询失败post_id:%d,err:%v", param.PostId, err)
reply.Status = http.StatusInternalServerError
reply.Message = "帖子查询异常"
return
}
// 从数据库中查询贴子的点赞情况。
count, err := dao.PostLike_Count(param.PostId)
if err != nil {
logrus.Errorf("贴子点赞数查询失败post_id:%d,err:%v", param.PostId, err)
reply.Status = http.StatusInternalServerError
reply.Message = "贴子点赞数查询异常"
return
}
/// 查询当前用户,是否点赞了。
is_like, err := dao.PostLike_Exists(userId, param.PostId)
if err != nil {
logrus.Errorf("贴子用户是否点赞查询失败user_id:%d,post_id:%d,err:%v", userId, param.PostId, err)
reply.Status = http.StatusInternalServerError
reply.Message = "贴子用户是否点赞查询失败"
return
}
// 查询用户名和头像id
user_info, err := dao.User_Query(data.UserId)
if err != nil {
logrus.Errorf("查询帖子发布者信息失败user_id:%d,post_id:%d,err:%v", userId, param.PostId, err)
reply.Status = http.StatusInternalServerError
reply.Message = "查询帖子发布者信息失败"
return
}
var info PostInfo
copy_from_mysql(data, count, is_like, user_info, &info)
reply.Data = &QueryReply{PostInfo: &info}
}
func copy_from_mysql(data *dao.Post, like_count int64, is_like bool, user_info *dao.User, detail *PostInfo) {
detail.PostId = uint64(data.ID)
detail.UserId = data.UserId
detail.Title = data.Title
detail.Content = data.Content
detail.RoleIds = data.RoleIds
detail.LikeCount = int(like_count)
detail.IsLike = is_like
detail.CreatedAt = data.CreatedAt.Format(time.DateTime)
if user_info != nil {
detail.UserAvatarId = user_info.AvatarId
detail.Username = user_info.Username
}
}

View File

@ -0,0 +1,38 @@
package post
import (
"fmt"
"mini_server/internal/dao"
"mini_server/internal/server/help"
"mini_server/internal/server/httpreply"
"net/http"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
func UnLike(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
userId, ok := help.GetUserId(c)
if !ok {
reply.Status = http.StatusNotFound
reply.Message = "无法获取用户信息"
return
}
var param QueryURI
if err := c.ShouldBindUri(&param); err != nil {
reply.Status = http.StatusBadRequest
reply.Message = fmt.Sprintf("invalid uri path,err:%v", err)
return
}
if err := dao.PostLike_UnLike(userId, param.PostId); err != nil {
logrus.Errorf("贴子取消点赞删除数据库异常userId:%d,postId:%d,err:%v", userId, param.PostId, err)
reply.Status = http.StatusInternalServerError
reply.Message = "贴子取消点赞异常"
return
}
}

View File

@ -0,0 +1,68 @@
package post
import (
"fmt"
"mini_server/internal/dao"
"mini_server/internal/server/help"
"mini_server/internal/server/httpreply"
"net/http"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"gorm.io/datatypes"
)
// 发布帖子
type UploadRequest struct {
Title string `json:"title" binding:"min=1"`
Content string `json:"content" binding:"min=1"`
RoleIds datatypes.JSON `json:"role_ids"` // 关联角色ID
}
type UploadReply struct {
PostId uint64 `json:"post_id"` // 文件ID
}
// 帖子上传接口
func Upload(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
userId, ok := help.GetUserId(c)
if !ok {
reply.Status = http.StatusNotFound
reply.Message = "无法获取用户信息"
return
}
var request UploadRequest
if err := c.ShouldBindJSON(&request); err != nil {
reply.Status = http.StatusBadRequest
reply.Message = fmt.Sprintf("bad request, err:%v", err)
return
}
// 将数据写入数据库中
data := dao.Post{
UserId: userId,
Title: request.Title,
Content: request.Content,
RoleIds: request.RoleIds,
}
id, err := dao.Post_Insert(&data)
if err != nil {
logrus.Errorf("上传帖子到数据库中失败,err:%v", err)
reply.Status = http.StatusInternalServerError
reply.Message = "上传帖子数据失败"
return
}
// 帖子发布成功之后,自动删除草稿
if err := dao.Draft_Delete(userId); err != nil {
logrus.Errorf("删除草稿失败userId:%d,err:%v", userId, err)
}
reply.Data = &UploadReply{PostId: id}
}

View File

@ -0,0 +1,67 @@
package role
import (
"fmt"
"mini_server/internal/dao"
"mini_server/internal/server/httpreply"
"net/http"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
// 角色添加
type CreateRequest struct {
VoiceId int `json:"voice_id" bingding:"gt=0"`
RoleName string `json:"role_name"`
WakeupWords string `json:"wakeup_words" binding:"min=1"`
WorldScenario string `json:"world_scenario" binding:"min=1"`
Description string `json:"description" binding:"min=1"`
Emojis string `json:"emojis"` // json格式
Dialogues string `json:"dialogues" binding:"min=1"`
AvatarId string `json:"avatar_id" binding:"min=0"` /// 头像图片路径
BackgroundIds string `json:"background_ids" binding:"min=0"` // 背景图列表,以逗号分隔
}
type CreateReply struct {
RoleId int32 `json:"role_id"` // 角色ID
}
// 添加角色
func Create(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
var request CreateRequest
if err := c.ShouldBindJSON(&request); err != nil {
reply.Status = http.StatusBadRequest
reply.Message = fmt.Sprintf("invalid json data,err:%v", err)
return
}
// 将数据写入数据库中
data := dao.Character{
VoiceId: request.VoiceId,
Name: request.RoleName,
WakeupWords: request.WakeupWords,
WorldScenario: request.WorldScenario,
Description: request.Description,
Emojis: request.Emojis,
Dialogues: request.Dialogues,
AvatarId: request.AvatarId,
BackgroundIds: request.BackgroundIds,
}
if data.Emojis == "" {
data.Emojis = "{}"
}
roleId, err := dao.Character_Insert(&data)
if err != nil {
logrus.Errorf("dao.Character_Insert failed, err:%v", err)
reply.Status = http.StatusInternalServerError
reply.Message = "添加角色失败"
return
}
reply.Data = &CreateReply{RoleId: roleId}
}

View File

@ -0,0 +1,28 @@
package role
/*
type DeleteURI struct {
RoleId int32 `uri:"role_id" binding:"gt=0"`
}
// 角色删除接口
func Delete(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
var param DeleteURI
if err := c.ShouldBindUri(&param); err != nil {
reply.Status = http.StatusBadRequest
reply.Message = fmt.Sprintf("invalid uri,err:%v", err)
return
}
if err := dao.Character_Delete(param.RoleId); err != nil {
reply.Status = http.StatusInternalServerError
reply.Message = "角色删除失败"
return
}
}
*/

View File

@ -0,0 +1,67 @@
package role
import "mini_server/internal/dao"
type RoleInfo struct {
RoleId uint64 `json:"role_id"`
VoiceId int `json:"voice_id"`
RoleName string `json:"role_name"`
WakeupWords string `json:"wakeup_words"`
WorldScenario string `json:"world_scenario"`
Description string `json:"description"`
Emojis string `json:"emojis"`
Dialogues string `json:"dialogues"`
AvatarId string `json:"avatar_id"` /// 头像图片路径
BackgroundIds string `json:"background_ids"` // 背景图列表,以逗号分隔
}
/*
type QueryURI struct {
RoleId int32 `uri:"role_id" binding:"gt=0"`
}
type QueryReply struct {
RoleInfo *RoleInfo `json:"role_info"`
}
// 单个角色查询接口
func Query(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
var param QueryURI
if err := c.ShouldBindUri(&param); err != nil {
reply.Status = http.StatusBadRequest
reply.Message = fmt.Sprintf("invalid uri, err:%v", err)
return
}
data, err := dao.Character_Query(param.RoleId)
if err != nil {
logrus.Errorf("dao.Character_Query return err:%v", err)
reply.Status = http.StatusInternalServerError
reply.Message = "查询角色信息失败"
return
}
var queryReply QueryReply
if data != nil {
var detail RoleInfo
Copy_mysql_data(data, &detail)
queryReply.RoleInfo = &detail
}
reply.Data = &queryReply
}
*/
func Copy_mysql_data(data *dao.Character, detail *RoleInfo) {
detail.BackgroundIds = data.BackgroundIds
detail.AvatarId = data.AvatarId
detail.RoleId = uint64(data.ID)
detail.VoiceId = data.VoiceId
detail.RoleName = data.Name
detail.WakeupWords = data.WakeupWords
detail.WorldScenario = data.WorldScenario
detail.Description = data.Description
detail.Emojis = data.Emojis
detail.Dialogues = data.Dialogues
}

View File

@ -0,0 +1,32 @@
package role
import (
"mini_server/internal/dao"
"mini_server/internal/server/httpreply"
"net/http"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
// 角色列表查询接口,查询全部
func QueryAllRole(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
list, err := dao.Character_QueryAllCharacter()
if err != nil {
logrus.Errorf("角色列表查询失败,err:%v", err)
reply.Status = http.StatusInternalServerError
reply.Message = "角色列表查询失败"
return
}
roleList := make([]*RoleInfo, len(list))
for i, data := range list {
var detail RoleInfo
Copy_mysql_data(data, &detail)
roleList[i] = &detail
}
reply.Data = roleList
}

View File

@ -0,0 +1,59 @@
package role
import (
"mini_server/internal/dao"
"mini_server/internal/server/help"
"mini_server/internal/server/httpreply"
"net/http"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
type QueryListReply struct {
RoleList []*RoleInfo `json:"role_list"`
}
// 角色列表查询接口根据用户ID查询
func QueryList(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
userId, ok := help.GetUserId(c)
if !ok {
reply.Status = http.StatusNotFound
reply.Message = "无法获取用户信息"
return
}
// 1. 需要去表user_character中查询关联的角色ID
list, err := dao.UserCharacter_QueryList(userId)
if err != nil {
logrus.Errorf("查询用户关联角色列表失败,err:%v", err)
reply.Status = http.StatusInternalServerError
reply.Message = "查询用户关联角色列表失败"
return
}
// 2. 根据角色ID列表查询角色详情
roleIds := make([]int32, len(list))
for i, data := range list {
roleIds[i] = data.CharacterId
}
roleList, err := dao.Character_QueryList(roleIds)
if err != nil {
logrus.Errorf("查询角色列表失败,userId:%v,roleIds:%v,err:%v", userId, roleIds, err)
reply.Status = http.StatusInternalServerError
reply.Message = "查询角色列表失败"
return
}
replyroleList := make([]*RoleInfo, len(list))
for i, data := range roleList {
var detail RoleInfo
Copy_mysql_data(data, &detail)
replyroleList[i] = &detail
}
reply.Data = roleList
}

129
internal/server/server.go Normal file
View File

@ -0,0 +1,129 @@
package server
import (
"mini_server/internal/config"
"mini_server/internal/dao"
"mini_server/internal/logger"
"mini_server/internal/middleware/cors"
"mini_server/internal/middleware/jwt"
"mini_server/internal/middleware/tls"
"mini_server/internal/server/bluetooth"
chatrecord "mini_server/internal/server/chat_record"
"mini_server/internal/server/file"
"mini_server/internal/server/post"
"mini_server/internal/server/role"
"mini_server/internal/server/user"
"mini_server/internal/server/wx"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
type Server struct {
e *gin.Engine
address string
}
func NewServer(address, dsn string) (*Server, error) {
err := dao.InitDefaultService(dsn)
if err != nil {
return nil, err
}
gin.DefaultWriter = logger.GinLogger
e := gin.Default()
srv := &Server{e: e, address: address}
srv.init_v1_handler()
return srv, nil
}
func (s *Server) GetGinEngine() *gin.Engine { return s.e }
func (s *Server) Run() error {
if config.TlsEnable { // 启用tls
return s.e.RunTLS(s.address, config.TlsFilePem, config.TlsFileKey)
}
return s.e.Run(s.address)
}
func (s *Server) init_v1_handler() {
if config.TlsEnable { // 是否开启https
logrus.Info("use TLS")
s.e.Use(tls.TlsHandler())
}
api := s.e.Group("api")
if config.JwtEnable { // 是否开启jwt校验
logrus.Info("use JWT")
api.Use(jwt.AuthMiddleware())
}
if config.CorsEnable { // 是否开启跨域处理
logrus.Info("use CORS")
api.Use(cors.CorsMiddleware())
}
users := api.Group("users")
{
// users.GET("/:user_id", user.Query) // 获取用户信息。小程序未使用。
users.PUT("/username", user.ChangeUsername) // 更新用户名称
users.PUT("/avatar_id", user.ChangeAvatarId) // 更新用户头像
users.POST("/wx/login", wx.WxLogin) // 微信登录
}
// 聊天相关功能
chats := api.Group("chat_records")
{
chats.GET("/", chatrecord.History) // 获取单个角色历史聊天记录
chats.GET("/all", chatrecord.HistoryAll) // 获取所有角色历史聊天记录
chats.POST("/", chatrecord.Upload) // 发送聊天消息,进行存储
chats.GET("/exists", chatrecord.Exists) // 判断用户与角色是否有聊天记录
}
// 帖子相关接口
posts := api.Group("post")
{
posts.GET("/", post.List) // 获取社区帖子列表,支持按照时间查询,支持根据角色筛选
posts.GET("/:post_id", post.Query) // 获取帖子详情
posts.POST("/", post.Upload) // 发布帖子
posts.POST("/:post_id/like", post.Like) // 帖子点赞
posts.DELETE("/:post_id/like", post.UnLike) // 帖子取消点赞
posts.GET("/:post_id/comments/", post.QueryComment) // 获取帖子的全部评论
posts.POST("/:post_id/comments/", post.UploadComment) // 添加一条评论
posts.DELETE("/:post_id/comments/:comment_id", post.DeleteComment) // 删除评论
}
// 草稿相关接口
drafts := api.Group("drafts")
{
drafts.GET("/", post.QueryDraft) // 获取草稿。目前草稿的处理逻辑是,一个用户只保存一张草稿。
drafts.POST("/", post.SaveDraft) // 保存草稿
drafts.DELETE("/", post.DeleteDraft) // 删除草稿
}
// 图片和音频等文件相关功能。
files := api.Group("files")
{
files.POST("/", file.Upload) // 文件上传
files.DELETE("/:file_id", file.Delete) // 文件删除
}
s.e.GET("/api/files/:file_id", file.Download) // 文件下载 /////////// 注:为了保证图片通过<image>标签下载未设置header。文件相关接口不受jwt限制。
// 蓝牙接口
bluetooths := api.Group("bluetooths")
{
bluetooths.GET("/", bluetooth.QueryAll) // 查询蓝牙接口列表
bluetooths.POST("/", bluetooth.Create) // 增加某个蓝牙的具体信息
bluetooths.DELETE("/", bluetooth.Delete) // 删除某个蓝牙的具体信息
}
// 角色接口
roles := api.Group("roles")
{
roles.GET("/all", role.QueryAllRole) // 获取所有角色
roles.POST("/", role.Create) // 添加角色
roles.GET("/", role.QueryList) // 获取用户绑定的角色列表
// roles.DELETE("/:role_id", role.Delete) // 删除角色。 小程序未使用此接口。
// roles.GET("/:role_id", role.Query) // 获取角色详情。 小程序未使用此接口。
}
}

View File

@ -0,0 +1,47 @@
package user
import (
"fmt"
"mini_server/internal/dao"
"mini_server/internal/server/help"
"mini_server/internal/server/httpreply"
"net/http"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
type ChangeAvatarIdRequest struct {
AvatarId string `json:"avatar_id" binding:"min=1"` // 修改的头像id
}
func ChangeAvatarId(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
//解析请求参数
var request ChangeAvatarIdRequest
if err := c.ShouldBindJSON(&request); err != nil {
reply.Status = http.StatusBadRequest
reply.Message = fmt.Sprintf("invalid json data, err:%v", err)
return
}
userId, ok := help.GetUserId(c)
if !ok {
reply.Status = http.StatusNotFound
reply.Message = "无法获取用户信息"
return
}
logrus.Tracef("修改用户头像userId:%d,请求参数:%v", userId, request)
if err := dao.User_Change_AvatarId(userId, request.AvatarId); err != nil {
logrus.Errorf("更新用户头像失败userId:%d,err:%v", userId, err)
reply.Status = http.StatusInternalServerError
reply.Message = "更新用户头像失败"
return
}
full_lastest_userinfo(reply, userId)
}

View File

@ -0,0 +1,76 @@
package user
import (
"fmt"
"mini_server/internal/dao"
"mini_server/internal/server/help"
"mini_server/internal/server/httpreply"
"net/http"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
/*
func ChangePersona(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
var param QueryURI
if err := c.ShouldBindUri(&param); err != nil {
reply.Status = http.StatusInternalServerError
reply.Message = fmt.Sprintf("invalid uri, err:%v", err)
return
}
persona := make(map[string]interface{})
if err := c.ShouldBindJSON(&persona); err != nil {
reply.Status = http.StatusInternalServerError
reply.Message = fmt.Sprintf("invalid json,err:%v", err)
return
}
if err := dao.User_Change_Persona(param.UserId, persona); err != nil {
logrus.Errorf("dao.User_Change_Persona失败,userId:%v,persona:%v,err:%v", param.UserId, persona, err)
reply.Status = http.StatusInternalServerError
reply.Message = "更新个性化信息失败"
return
}
}
*/
type ChangeUsernameRequest struct {
Username string `json:"username" binding:"min=1"` // 修改的用户名
}
func ChangeUsername(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
//解析请求参数
var request ChangeUsernameRequest
if err := c.ShouldBindJSON(&request); err != nil {
reply.Status = http.StatusBadRequest
reply.Message = fmt.Sprintf("invalid json data, err:%v", err)
return
}
userId, ok := help.GetUserId(c)
if !ok {
reply.Status = http.StatusNotFound
reply.Message = "无法获取用户信息"
return
}
logrus.Tracef("修改用户名userId:%d,请求参数:%v", userId, request)
if err := dao.User_Change_Username(userId, request.Username); err != nil {
logrus.Errorf("更新用户名称失败userId:%d,err:%v", userId, err)
reply.Status = http.StatusInternalServerError
reply.Message = "更新用户名称失败"
return
}
full_lastest_userinfo(reply, userId)
}

View File

@ -0,0 +1,68 @@
package user
import (
"fmt"
"mini_server/internal/dao"
"mini_server/internal/server/httpreply"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
type UserInfo struct {
UserId uint64 `json:"user_id"`
AvatarId string `json:"avatar_id"`
Username string `json:"username"`
CreatedAt string `json:"created_at"`
Tags string `json:"tags"`
Persona string `json:"persona"`
}
type QueryURI struct {
UserId int32 `uri:"user_id" binding:"gt=0"`
}
type QueryReply struct {
UserInfo *UserInfo `json:"user_info"`
}
func Query(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
var param QueryURI
if err := c.ShouldBindUri(&param); err != nil {
reply.Status = http.StatusInternalServerError
reply.Message = fmt.Sprintf("invalid uri, err:%v", err)
return
}
full_lastest_userinfo(reply, param.UserId)
}
func Copy_User_Info(data *dao.User, info *UserInfo) {
info.UserId = uint64(data.ID)
info.AvatarId = data.AvatarId
info.Username = data.Username
info.CreatedAt = data.CreatedAt.Format(time.DateTime)
info.Tags = data.Tags
info.Persona = data.Persona
}
// 查询数据库,并填充最新的用户信息
func full_lastest_userinfo(reply *httpreply.ReplyData, userId int32) {
// 查询最新用户信息,并返回
data, err := dao.User_Query(userId)
if err != nil || data == nil {
logrus.Errorf("查询用户最新信息失败userId:%d,err:%v", userId, err)
reply.Status = http.StatusInternalServerError
reply.Message = "查询用户信息失败"
return
}
var queryReply QueryReply
var info UserInfo
Copy_User_Info(data, &info)
queryReply.UserInfo = &info
reply.Data = queryReply
}

147
internal/server/wx/login.go Normal file
View File

@ -0,0 +1,147 @@
package wx
import (
"encoding/json"
"fmt"
"io"
"mini_server/internal/config"
"mini_server/internal/dao"
"mini_server/internal/middleware/jwt"
"mini_server/internal/server/help"
"mini_server/internal/server/httpreply"
"mini_server/internal/server/user"
"net/http"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
type LoginRequest struct {
Code string `json:"code" binding:"min=1"` // 临时会话登录code
}
type LoginReply struct {
Token string `json:"token"` // 返回登录成功的jwt token
UserInfo *user.UserInfo `json:"user_info"`
}
// 微信服务器,返回的数据结构
type WxHttpReply struct {
Errcode int32 `json:"errcode"` // 错误码
Errmsg string `json:"errmsg"` // 错误信息
OpenId string `json:"openid"` // 用户唯一标识
// Unionid string `json:"unionid"` // 用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回,详见微信 UnionID
SessionKey string `json:"session_key"` // 会话密钥
}
func WxLogin(c *gin.Context) {
reply := httpreply.NewDefaultReplyData()
defer httpreply.Reply(c, reply)
//解析请求参数
var request LoginRequest
if err := c.ShouldBindJSON(&request); err != nil {
reply.Status = http.StatusBadRequest
reply.Message = fmt.Sprintf("invalid json data, err:%v", err)
return
}
logrus.Tracef("微信登录,请求参数:%v", request)
// 从微信查询openId
openId, err := get_wx_login_open_id(request.Code)
if err != nil {
logrus.Errorf("请求微信获取OpenId失败err:%v", err)
reply.Status = http.StatusServiceUnavailable
reply.Message = "第三方服务错误"
return
}
// 首先查询,是否存在
data, err := dao.User_WX_Query(openId)
if err != nil {
logrus.Errorf("微信登录,数据库查询失败,err%v", err)
reply.Status = http.StatusInternalServerError
reply.Message = "用户查询失败"
return
}
if data == nil { // 用户不存在,则需要添加创建
default_wx_username, err := get_default_wx_username()
if err != nil {
logrus.Errorf("生成微信默认用户名失败,err:%v", err)
reply.Status = http.StatusInternalServerError
reply.Message = "生成微信默认用户名失败"
return
}
data = &dao.User{
AvatarId: "",
OpenId: openId,
Username: default_wx_username, // 默认名称
Persona: "{}",
Tags: "{}",
}
if _, err = dao.User_WX_Insert(data); err != nil {
logrus.Errorf("微信登录,数据库用户添加失败,err%v", err)
reply.Status = http.StatusInternalServerError
reply.Message = "用户添加失败"
return
}
}
// 生成token
token, err := jwt.CreateToken(data)
if err != nil {
logrus.Errorf("微信登录生成token失败err:%v", err)
reply.Status = http.StatusInternalServerError
reply.Message = "token生成失败"
return
}
// 返回用户信息
var info user.UserInfo
user.Copy_User_Info(data, &info)
loginReply := LoginReply{
Token: token,
UserInfo: &info,
}
reply.Data = &loginReply
logrus.Tracef("微信登录成功,应答:%v", reply)
}
func get_wx_login_open_id(code string) (string, error) {
//接口请求url拼接
url := config.WeiXin.AppUrl(code)
// 转发请求到微信接口
resp, err := http.Get(url)
if err != nil {
return "", fmt.Errorf("请求微信登录接口失败err:%v", err)
}
defer resp.Body.Close()
// 读取数据
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("读取微信登录应答数据失败err:%v", err)
}
// 解码数据并赋值给返回的respData
respData := new(WxHttpReply)
json.Unmarshal(body, &respData)
openId := respData.OpenId
// 检测openid是否为空
if openId == "" {
return "", fmt.Errorf("读取微信登录应答数据openId为空")
}
return openId, nil
}
// 注册时返回随机名称
func get_default_wx_username() (string, error) {
id := help.GetUniqueIdEachSecond()
return "微信用户" + id, nil
}

BIN
main Executable file

Binary file not shown.

25
main.go Normal file
View File

@ -0,0 +1,25 @@
package main
import (
"mini_server/internal/config"
"mini_server/internal/logger"
"mini_server/internal/server"
"github.com/sirupsen/logrus"
)
func init() {
logger.Init_log()
}
func main() {
address := config.ServerListenAddress
dsn := config.MysqlDSN
if srv, err := server.NewServer(address, dsn); err != nil {
logrus.Errorf("服务初始化失败,错误信息:%v", err)
} else if err = srv.Run(); err != nil {
logrus.Errorf("服务启动失败,错误信息:%v", err)
}
}

447
nohup.out Normal file
View File

@ -0,0 +1,447 @@
[GIN-debug] [ERROR] listen tcp 0.0.0.0:8002: bind: address already in use
[mysql] 2024/05/21 15:34:38 connection.go:49: closing bad idle connection: unexpected read from socket
2024/05/21 16:00:38 http: TLS handshake error from 152.89.198.50:65074: tls: first record does not look like a TLS handshake
2024/05/21 17:10:17 http: TLS handshake error from 91.240.118.187:65375: tls: first record does not look like a TLS handshake
2024/05/21 17:28:09 文件上传请求,文件名:2Wi3IZxiuZJNa27edf8f931dc10ea680d20a3ca53006.jpg, 文件大小:87088, 文件类型:image/jpeg
2024/05/21 17:28:28 文件上传请求,文件名:cj8XTQATw9Vka27edf8f931dc10ea680d20a3ca53006.jpg, 文件大小:87088, 文件类型:image/jpeg
2024/05/22 04:11:22 http: TLS handshake error from 39.98.166.194:21295: EOF
2024/05/22 04:16:39 http: TLS handshake error from 39.98.70.188:47820: tls: first record does not look like a TLS handshake
[mysql] 2024/05/22 10:14:12 connection.go:49: closing bad idle connection: unexpected read from socket
2024/05/22 10:55:11 文件上传请求,文件名:tmp_096f57f8db6ad391c82dc4032e2ff2e9.jpg, 文件大小:720427, 文件类型:image/jpeg
2024/05/22 14:36:10 http: TLS handshake error from 139.162.185.210:34080: EOF
2024/05/22 17:19:08 http: TLS handshake error from 223.104.68.207:25987: EOF
2024/05/22 19:05:31 http: TLS handshake error from 111.7.96.150:24250: read tcp 192.168.20.202:8002->111.7.96.150:24250: read: connection reset by peer
2024/05/22 19:06:04 http: TLS handshake error from 36.99.136.137:6598: local error: tls: unexpected message
2024/05/22 22:59:09 http: TLS handshake error from 78.128.114.22:65525: tls: first record does not look like a TLS handshake
2024/05/23 00:24:22 http: TLS handshake error from 170.64.155.138:58608: EOF
2024/05/23 03:54:22 http: TLS handshake error from 185.122.204.79:64772: tls: first record does not look like a TLS handshake
2024/05/23 05:05:51 http: TLS handshake error from 39.98.89.14:30636: EOF
2024/05/23 05:14:59 http: TLS handshake error from 39.98.177.174:55832: tls: first record does not look like a TLS handshake
2024/05/23 07:36:12 http: TLS handshake error from 115.231.78.7:33511: tls: first record does not look like a TLS handshake
2024/05/23 09:55:00 http: TLS handshake error from 39.100.94.106:36678: EOF
2024/05/23 09:55:32 http: TLS handshake error from 39.100.94.106:41686: EOF
2024/05/23 09:55:32 http: TLS handshake error from 39.100.94.106:41576: EOF
2024/05/23 09:55:32 http: TLS handshake error from 39.100.94.106:41688: EOF
2024/05/23 17:22:08 http: TLS handshake error from 185.234.216.112:65102: tls: first record does not look like a TLS handshake
2024/05/23 23:26:35 http: TLS handshake error from 39.98.82.242:13117: EOF
2024/05/23 23:41:38 http: TLS handshake error from 47.92.228.62:49328: tls: first record does not look like a TLS handshake
2024/05/23 23:56:19 http: TLS handshake error from 162.216.241.155:35998: unexpected EOF
2024/05/23 23:56:20 http: TLS handshake error from 162.216.241.155:34382: tls: unsupported SSLv2 handshake received
2024/05/23 23:58:57 http: TLS handshake error from 162.216.241.155:393: tls: unsupported SSLv2 handshake received
2024/05/23 23:59:01 http: TLS handshake error from 162.216.241.155:46564: read tcp 192.168.20.202:8002->162.216.241.155:46564: read: connection reset by peer
2024/05/23 23:59:01 http: TLS handshake error from 162.216.241.155:46570: read tcp 192.168.20.202:8002->162.216.241.155:46570: read: connection reset by peer
2024/05/23 23:59:01 http: TLS handshake error from 162.216.241.155:46586: EOF
2024/05/23 23:59:01 http: TLS handshake error from 162.216.241.155:46600: tls: client requested unsupported application protocols ([http/0.9 http/1.0 spdy/1 spdy/2 spdy/3 h2c hq])
2024/05/23 23:59:01 http: TLS handshake error from 162.216.241.155:46612: tls: client requested unsupported application protocols ([hq h2c spdy/3 spdy/2 spdy/1 http/1.0 http/0.9])
2024/05/23 23:59:01 http: TLS handshake error from 162.216.241.155:46626: read tcp 192.168.20.202:8002->162.216.241.155:46626: read: connection reset by peer
2024/05/23 23:59:02 http: TLS handshake error from 162.216.241.155:46634: read tcp 192.168.20.202:8002->162.216.241.155:46634: read: connection reset by peer
2024/05/23 23:59:02 http: TLS handshake error from 162.216.241.155:46640: read tcp 192.168.20.202:8002->162.216.241.155:46640: read: connection reset by peer
2024/05/23 23:59:02 http: TLS handshake error from 162.216.241.155:46648: EOF
2024/05/23 23:59:02 http: TLS handshake error from 162.216.241.155:46654: read tcp 192.168.20.202:8002->162.216.241.155:46654: read: connection reset by peer
2024/05/23 23:59:02 http: TLS handshake error from 162.216.241.155:46666: EOF
2024/05/24 00:00:12 http: TLS handshake error from 162.216.241.155:55340: remote error: tls: unknown certificate
2024/05/24 03:03:07 http: TLS handshake error from 172.233.52.247:60000: read tcp 192.168.20.202:8002->172.233.52.247:60000: read: connection reset by peer
2024/05/24 12:35:09 http: TLS handshake error from 178.159.37.98:64758: tls: first record does not look like a TLS handshake
2024/05/24 13:26:53 http: TLS handshake error from 78.128.114.22:65268: tls: first record does not look like a TLS handshake
2024/05/24 16:20:44 http: TLS handshake error from 78.128.114.22:64939: tls: first record does not look like a TLS handshake
2024/05/24 20:34:47 http: TLS handshake error from 39.98.93.197:29537: EOF
2024/05/25 10:34:03 http: TLS handshake error from 78.128.114.22:61543: tls: first record does not look like a TLS handshake
2024/05/25 13:51:15 http: TLS handshake error from 178.159.37.98:2403: tls: first record does not look like a TLS handshake
2024/05/26 00:05:41 http: TLS handshake error from 154.212.141.222:10190: EOF
2024/05/26 00:05:42 http: TLS handshake error from 154.212.141.222:10664: EOF
2024/05/26 00:05:42 http: TLS handshake error from 154.212.141.222:10888: EOF
2024/05/26 00:05:42 http: TLS handshake error from 154.212.141.222:11082: tls: client requested unsupported application protocols ([http/0.9 http/1.0 spdy/1 spdy/2 spdy/3 h2c hq])
2024/05/26 00:05:42 http: TLS handshake error from 154.212.141.222:11204: tls: client requested unsupported application protocols ([hq h2c spdy/3 spdy/2 spdy/1 http/1.0 http/0.9])
2024/05/26 00:05:42 http: TLS handshake error from 154.212.141.222:11282: read tcp 192.168.20.202:8002->154.212.141.222:11282: read: connection reset by peer
2024/05/26 00:05:42 http: TLS handshake error from 154.212.141.222:11350: EOF
2024/05/26 00:05:42 http: TLS handshake error from 154.212.141.222:11440: EOF
2024/05/26 00:05:42 http: TLS handshake error from 154.212.141.222:11578: read tcp 192.168.20.202:8002->154.212.141.222:11578: read: connection reset by peer
2024/05/26 00:05:43 http: TLS handshake error from 154.212.141.222:11706: read tcp 192.168.20.202:8002->154.212.141.222:11706: read: connection reset by peer
2024/05/26 00:05:43 http: TLS handshake error from 154.212.141.222:11806: read tcp 192.168.20.202:8002->154.212.141.222:11806: read: connection reset by peer
2024/05/26 00:55:26 http: TLS handshake error from 121.62.21.146:49739: tls: first record does not look like a TLS handshake
2024/05/26 12:22:21 http: TLS handshake error from 78.128.114.22:3396: tls: first record does not look like a TLS handshake
2024/05/26 15:18:04 http: TLS handshake error from 185.234.216.112:65128: tls: first record does not look like a TLS handshake
2024/05/26 16:28:35 http: TLS handshake error from 115.231.78.9:8477: tls: first record does not look like a TLS handshake
2024/05/26 17:55:22 http: TLS handshake error from 39.108.88.65:51018: unexpected EOF
2024/05/26 17:55:22 http: TLS handshake error from 39.108.88.65:55918: tls: unsupported SSLv2 handshake received
2024/05/26 17:55:51 http: TLS handshake error from 39.108.88.65:45302: tls: unsupported SSLv2 handshake received
2024/05/26 17:55:51 http: TLS handshake error from 39.108.88.65:45312: tls: first record does not look like a TLS handshake
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36676: tls: unsupported SSLv2 handshake received
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36666: tls: no cipher suite supported by both client and server
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36736: tls: client offered only unsupported versions: []
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36750: tls: no cipher suite supported by both client and server
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36714: tls: no cipher suite supported by both client and server
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36720: tls: no cipher suite supported by both client and server
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36704: EOF
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36694: EOF
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36752: tls: no cipher suite supported by both client and server
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36744: EOF
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36790: tls: no cipher suite supported by both client and server
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36822: tls: no cipher suite supported by both client and server
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36792: tls: no cipher suite supported by both client and server
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36776: EOF
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36806: EOF
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36842: tls: client requested unsupported application protocols ([http/0.9 http/1.0 spdy/1 spdy/2 spdy/3 stun.turn stun.nat-discovery h2c webrtc c-webrtc ftp imap pop3 managesieve coap xmpp-client xmpp-server acme-tls/1 mqtt dot ntske/1 sunrpc h3 smb irc nntp nnsp doq grpc-exp])
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36826: tls: no cipher suite supported by both client and server
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36856: EOF
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36690: EOF
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36760: EOF
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36868: EOF
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36874: EOF
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36876: EOF
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36890: EOF
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36892: tls: no cipher suite supported by both client and server
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36928: tls: no cipher suite supported by both client and server
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36906: EOF
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36922: EOF
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36936: EOF
2024/05/26 17:56:03 http: TLS handshake error from 39.108.88.65:36938: tls: no cipher suite supported by both client and server
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:36976: tls: no cipher suite supported by both client and server
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:36958: tls: no cipher suite supported by both client and server
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:36950: EOF
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:36974: EOF
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:36984: tls: no cipher suite supported by both client and server
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:36988: tls: no cipher suite supported by both client and server
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:36990: EOF
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:37004: EOF
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:37002: EOF
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:37012: tls: no cipher suite supported by both client and server
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:37054: tls: no cipher suite supported by both client and server
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:37032: tls: no cipher suite supported by both client and server
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:37020: EOF
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:37042: EOF
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:37062: tls: no cipher suite supported by both client and server
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:37082: tls: no cipher suite supported by both client and server
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:37070: tls: no cipher suite supported by both client and server
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:37068: EOF
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:37094: tls: client offered only unsupported versions: []
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:37078: EOF
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:37104: EOF
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:37112: EOF
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:37122: EOF
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:37120: EOF
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:37134: tls: no cipher suite supported by both client and server
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:37138: tls: no cipher suite supported by both client and server
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:37142: EOF
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:37148: EOF
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:37160: tls: no cipher suite supported by both client and server
2024/05/26 17:56:04 http: TLS handshake error from 39.108.88.65:37176: tls: no cipher suite supported by both client and server
2024/05/26 17:56:05 http: TLS handshake error from 39.108.88.65:37184: EOF
2024/05/26 17:56:05 http: TLS handshake error from 39.108.88.65:37188: EOF
2024/05/26 17:56:05 http: TLS handshake error from 39.108.88.65:37190: tls: no cipher suite supported by both client and server
2024/05/26 17:56:05 http: TLS handshake error from 39.108.88.65:37206: tls: no cipher suite supported by both client and server
2024/05/26 17:56:05 http: TLS handshake error from 39.108.88.65:37220: tls: no cipher suite supported by both client and server
2024/05/26 17:56:05 http: TLS handshake error from 39.108.88.65:37192: EOF
2024/05/26 17:56:05 http: TLS handshake error from 39.108.88.65:37222: tls: no cipher suite supported by both client and server
2024/05/26 17:56:05 http: TLS handshake error from 39.108.88.65:37256: tls: no cipher suite supported by both client and server
2024/05/26 17:56:05 http: TLS handshake error from 39.108.88.65:37242: tls: no cipher suite supported by both client and server
2024/05/26 17:56:05 http: TLS handshake error from 39.108.88.65:37226: EOF
2024/05/26 17:56:05 http: TLS handshake error from 39.108.88.65:37268: EOF
2024/05/26 17:56:05 http: TLS handshake error from 39.108.88.65:37274: EOF
2024/05/26 17:56:05 http: TLS handshake error from 39.108.88.65:37280: EOF
2024/05/26 17:56:05 http: TLS handshake error from 39.108.88.65:37294: EOF
2024/05/26 17:56:05 http: TLS handshake error from 39.108.88.65:37312: EOF
2024/05/26 17:56:05 http: TLS handshake error from 39.108.88.65:37300: EOF
2024/05/26 17:56:05 http: TLS handshake error from 39.108.88.65:37324: EOF
2024/05/26 17:56:05 http: TLS handshake error from 39.108.88.65:37344: EOF
2024/05/26 17:56:05 http: TLS handshake error from 39.108.88.65:37328: EOF
2024/05/26 17:56:05 http: TLS handshake error from 39.108.88.65:37350: tls: no cipher suite supported by both client and server
2024/05/26 17:56:05 http: TLS handshake error from 39.108.88.65:37370: EOF
2024/05/26 17:56:05 http: TLS handshake error from 39.108.88.65:37354: EOF
2024/05/26 17:56:05 http: TLS handshake error from 39.108.88.65:37372: tls: no cipher suite supported by both client and server
2024/05/26 17:56:06 http: TLS handshake error from 39.108.88.65:37376: EOF
2024/05/26 17:56:06 http: TLS handshake error from 39.108.88.65:37374: EOF
2024/05/26 17:56:06 http: TLS handshake error from 39.108.88.65:37386: EOF
2024/05/26 17:56:06 http: TLS handshake error from 39.108.88.65:37384: EOF
2024/05/26 17:56:06 http: TLS handshake error from 39.108.88.65:37392: EOF
2024/05/26 17:56:06 http: TLS handshake error from 39.108.88.65:37402: EOF
2024/05/26 17:56:06 http: TLS handshake error from 39.108.88.65:37414: EOF
2024/05/26 17:56:06 http: TLS handshake error from 39.108.88.65:37418: EOF
2024/05/26 17:56:06 http: TLS handshake error from 39.108.88.65:37442: EOF
2024/05/26 17:56:06 http: TLS handshake error from 39.108.88.65:37426: EOF
2024/05/26 17:56:06 http: TLS handshake error from 39.108.88.65:37452: EOF
2024/05/26 17:56:06 http: TLS handshake error from 39.108.88.65:37484: EOF
2024/05/26 17:56:06 http: TLS handshake error from 39.108.88.65:37480: EOF
2024/05/26 17:56:06 http: TLS handshake error from 39.108.88.65:37468: EOF
2024/05/26 17:56:06 http: TLS handshake error from 39.108.88.65:37494: EOF
2024/05/26 17:56:06 http: TLS handshake error from 39.108.88.65:37498: EOF
2024/05/26 17:56:07 http: TLS handshake error from 39.108.88.65:37512: EOF
2024/05/26 17:56:07 http: TLS handshake error from 39.108.88.65:37516: EOF
2024/05/26 17:56:07 http: TLS handshake error from 39.108.88.65:37530: EOF
2024/05/26 17:56:07 http: TLS handshake error from 39.108.88.65:37536: EOF
2024/05/26 17:56:07 http: TLS handshake error from 39.108.88.65:37540: EOF
2024/05/26 17:56:07 http: TLS handshake error from 39.108.88.65:37554: EOF
2024/05/26 17:56:08 http: TLS handshake error from 39.108.88.65:42024: EOF
2024/05/26 17:56:08 http: TLS handshake error from 39.108.88.65:42030: EOF
2024/05/26 17:56:08 http: TLS handshake error from 39.108.88.65:42042: tls: client offered only unsupported versions: []
2024/05/26 17:56:08 http: TLS handshake error from 39.108.88.65:36664: EOF
[mysql] 2024/05/26 19:41:58 connection.go:49: closing bad idle connection: unexpected read from socket
[mysql] 2024/05/26 19:41:58 connection.go:49: closing bad idle connection: unexpected read from socket
2024/05/27 04:13:17 http: TLS handshake error from 85.209.133.200:57572: tls: first record does not look like a TLS handshake
2024/05/27 04:37:07 http: TLS handshake error from 52.83.91.65:43268: tls: first record does not look like a TLS handshake
2024/05/27 07:32:14 http: TLS handshake error from 223.113.128.194:47910: EOF
2024/05/27 07:32:26 http: TLS handshake error from 223.113.128.194:38640: read tcp 192.168.20.202:8002->223.113.128.194:38640: read: connection reset by peer
2024/05/27 07:32:26 http: TLS handshake error from 223.113.128.194:39086: EOF
2024/05/27 07:32:27 http: TLS handshake error from 223.113.128.194:39292: EOF
2024/05/27 07:32:27 http: TLS handshake error from 223.113.128.194:39660: EOF
2024/05/27 07:32:27 http: TLS handshake error from 223.113.128.194:40228: tls: client requested unsupported application protocols ([http/0.9 http/1.0 spdy/1 spdy/2 spdy/3 h2c hq])
2024/05/27 07:32:27 http: TLS handshake error from 223.113.128.194:40546: tls: client requested unsupported application protocols ([hq h2c spdy/3 spdy/2 spdy/1 http/1.0 http/0.9])
2024/05/27 07:32:27 http: TLS handshake error from 223.113.128.194:40892: EOF
2024/05/27 07:32:28 http: TLS handshake error from 223.113.128.194:41182: EOF
2024/05/27 07:32:28 http: TLS handshake error from 223.113.128.194:41452: EOF
2024/05/27 07:32:28 http: TLS handshake error from 223.113.128.194:41670: EOF
2024/05/27 07:32:28 http: TLS handshake error from 223.113.128.194:41956: EOF
2024/05/27 11:08:51 http: TLS handshake error from 87.236.176.84:60715: EOF
2024/05/27 11:09:25 http: TLS handshake error from 87.236.176.106:56505: EOF
2024/05/27 11:09:58 http: TLS handshake error from 87.236.176.93:35489: EOF
2024/05/27 11:10:32 http: TLS handshake error from 87.236.176.91:39803: tls: client requested unsupported application protocols ([http/0.9 http/1.0 spdy/1 spdy/2 spdy/3 h2c hq])
2024/05/27 11:11:49 http: TLS handshake error from 87.236.176.93:42575: tls: client requested unsupported application protocols ([hq h2c spdy/3 spdy/2 spdy/1 http/1.0 http/0.9])
2024/05/27 11:12:22 http: TLS handshake error from 87.236.176.105:51975: EOF
2024/05/27 11:12:56 http: TLS handshake error from 87.236.176.84:60991: EOF
2024/05/27 11:13:30 http: TLS handshake error from 87.236.176.85:39577: EOF
2024/05/27 11:14:03 http: TLS handshake error from 87.236.176.97:36585: EOF
2024/05/27 11:14:37 http: TLS handshake error from 87.236.176.90:60779: EOF
2024/05/27 11:26:34 http: TLS handshake error from 178.159.37.98:64812: tls: first record does not look like a TLS handshake
2024/05/27 12:42:40 http: TLS handshake error from 85.209.11.113:64237: tls: first record does not look like a TLS handshake
2024/05/27 14:33:21 http: TLS handshake error from 52.83.91.65:57994: tls: first record does not look like a TLS handshake
2024/05/27 18:31:51 http: TLS handshake error from 78.128.114.22:65130: tls: first record does not look like a TLS handshake
[mysql] 2024/05/27 20:26:47 connection.go:49: closing bad idle connection: unexpected read from socket
2024/05/27 21:01:32 http: TLS handshake error from 115.231.78.7:46874: tls: first record does not look like a TLS handshake
2024/05/27 22:08:14 http: TLS handshake error from 111.7.96.149:13476: read tcp 192.168.20.202:8002->111.7.96.149:13476: read: connection reset by peer
2024/05/27 22:08:48 http: TLS handshake error from 36.99.136.128:48245: local error: tls: unexpected message
2024/05/27 22:10:35 http: TLS handshake error from 36.40.78.26:57164: read tcp 192.168.20.202:8002->36.40.78.26:57164: read: connection reset by peer
2024/05/28 00:32:50 http: TLS handshake error from 94.156.64.245:49034: tls: first record does not look like a TLS handshake
2024/05/28 02:39:05 http: TLS handshake error from 101.251.238.169:56693: read tcp 192.168.20.202:8002->101.251.238.169:56693: read: connection reset by peer
2024/05/28 02:47:21 http: TLS handshake error from 185.234.216.112:63390: tls: first record does not look like a TLS handshake
2024/05/28 06:05:23 http: TLS handshake error from 31.7.62.234:64943: tls: first record does not look like a TLS handshake
2024/05/28 14:02:32 http: TLS handshake error from 78.128.114.22:65435: tls: first record does not look like a TLS handshake
2024/05/28 14:25:57 http: TLS handshake error from 178.159.37.98:65383: tls: first record does not look like a TLS handshake
2024/05/28 14:30:04 http: TLS handshake error from 185.234.216.112:64609: tls: first record does not look like a TLS handshake
[mysql] 2024/05/28 21:26:07 connection.go:49: closing bad idle connection: unexpected read from socket
2024/05/28 21:49:41 文件上传请求,文件名:1590231450028.jpg, 文件大小:51569, 文件类型:image/jpeg
2024/05/28 22:22:14 文件上传请求,文件名:87onykKRLtFy19a0a7202d28622c9cff3fa76820e176.png, 文件大小:379182, 文件类型:image/png
2024/05/28 22:23:40 文件上传请求,文件名:FUBZG0VCx9Si19a0a7202d28622c9cff3fa76820e176.png, 文件大小:379182, 文件类型:image/png
2024/05/29 10:30:46 http: TLS handshake error from 79.124.56.202:62722: tls: first record does not look like a TLS handshake
2024/05/29 10:54:19 http: TLS handshake error from 79.124.56.202:3523: tls: first record does not look like a TLS handshake
[mysql] 2024/05/29 11:28:35 connection.go:49: closing bad idle connection: unexpected read from socket
2024/05/29 11:31:52 文件上传请求,文件名:1590231450028.jpg, 文件大小:51569, 文件类型:image/jpeg
2024/05/29 12:55:39 http: TLS handshake error from 185.234.216.112:64830: tls: first record does not look like a TLS handshake
2024/05/29 15:28:42 http: TLS handshake error from 148.153.45.234:54210: EOF
2024/05/29 23:33:30 http: TLS handshake error from 79.124.56.202:64548: tls: first record does not look like a TLS handshake
2024/05/30 01:03:09 http: TLS handshake error from 185.234.216.112:63180: tls: first record does not look like a TLS handshake
2024/05/30 13:56:49 http: TLS handshake error from 185.234.216.112:63513: tls: first record does not look like a TLS handshake
2024/05/30 14:57:27 http: TLS handshake error from 87.236.176.140:38815: read tcp 192.168.20.202:8002->87.236.176.140:38815: read: connection reset by peer
2024/05/30 14:58:00 http: TLS handshake error from 87.236.176.143:49801: EOF
2024/05/30 14:58:40 http: TLS handshake error from 87.236.176.135:50543: read tcp 192.168.20.202:8002->87.236.176.135:50543: read: connection reset by peer
2024/05/30 14:59:13 http: TLS handshake error from 87.236.176.141:46163: tls: client requested unsupported application protocols ([http/0.9 http/1.0 spdy/1 spdy/2 spdy/3 h2c hq])
2024/05/30 14:59:49 http: TLS handshake error from 87.236.176.111:42803: tls: client requested unsupported application protocols ([hq h2c spdy/3 spdy/2 spdy/1 http/1.0 http/0.9])
2024/05/30 15:01:03 http: TLS handshake error from 87.236.176.120:45599: EOF
2024/05/30 15:01:37 http: TLS handshake error from 87.236.176.137:38505: read tcp 192.168.20.202:8002->87.236.176.137:38505: read: connection reset by peer
2024/05/30 15:02:11 http: TLS handshake error from 87.236.176.141:38265: EOF
2024/05/30 15:02:46 http: TLS handshake error from 87.236.176.127:44825: EOF
2024/05/30 15:03:21 http: TLS handshake error from 87.236.176.111:58583: EOF
2024/05/30 15:33:13 http: TLS handshake error from 39.98.76.173:7606: EOF
2024/05/30 20:16:28 http: TLS handshake error from 111.7.96.148:54806: read tcp 192.168.20.202:8002->111.7.96.148:54806: read: connection reset by peer
2024/05/30 20:17:20 http: TLS handshake error from 36.99.136.128:51220: local error: tls: unexpected message
2024/05/30 20:17:21 http: TLS handshake error from 111.7.96.164:20962: read tcp 192.168.20.202:8002->111.7.96.164:20962: read: connection reset by peer
2024/05/30 23:53:38 http: TLS handshake error from 115.231.78.7:61077: tls: first record does not look like a TLS handshake
2024/05/31 05:16:52 http: TLS handshake error from 52.83.92.55:52118: tls: first record does not look like a TLS handshake
2024/05/31 05:27:50 http: TLS handshake error from 161.189.192.94:47200: tls: first record does not look like a TLS handshake
2024/05/31 05:55:11 http: TLS handshake error from 47.92.65.140:43138: unexpected EOF
2024/05/31 05:55:11 http: TLS handshake error from 47.92.65.140:43166: tls: unsupported SSLv2 handshake received
2024/05/31 05:55:39 http: TLS handshake error from 47.92.65.140:59788: tls: unsupported SSLv2 handshake received
2024/05/31 05:55:39 http: TLS handshake error from 47.92.65.140:59792: tls: first record does not look like a TLS handshake
2024/05/31 05:55:50 http: TLS handshake error from 47.92.65.140:49446: tls: no cipher suite supported by both client and server
2024/05/31 05:55:50 http: TLS handshake error from 47.92.65.140:49452: tls: unsupported SSLv2 handshake received
2024/05/31 05:55:50 http: TLS handshake error from 47.92.65.140:49478: tls: client offered only unsupported versions: []
2024/05/31 05:55:50 http: TLS handshake error from 47.92.65.140:49502: tls: no cipher suite supported by both client and server
2024/05/31 05:55:50 http: TLS handshake error from 47.92.65.140:49464: tls: no cipher suite supported by both client and server
2024/05/31 05:55:50 http: TLS handshake error from 47.92.65.140:49490: tls: no cipher suite supported by both client and server
2024/05/31 05:55:50 http: TLS handshake error from 47.92.65.140:49418: EOF
2024/05/31 05:55:50 http: TLS handshake error from 47.92.65.140:49438: EOF
2024/05/31 05:55:50 http: TLS handshake error from 47.92.65.140:49482: EOF
2024/05/31 05:55:50 http: TLS handshake error from 47.92.65.140:49522: tls: no cipher suite supported by both client and server
2024/05/31 05:55:50 http: TLS handshake error from 47.92.65.140:49570: tls: no cipher suite supported by both client and server
2024/05/31 05:55:51 http: TLS handshake error from 47.92.65.140:49520: EOF
2024/05/31 05:55:51 http: TLS handshake error from 47.92.65.140:49582: tls: client requested unsupported application protocols ([http/0.9 http/1.0 spdy/1 spdy/2 spdy/3 stun.turn stun.nat-discovery h2c webrtc c-webrtc ftp imap pop3 managesieve coap xmpp-client xmpp-server acme-tls/1 mqtt dot ntske/1 sunrpc h3 smb irc nntp nnsp doq grpc-exp])
2024/05/31 05:55:51 http: TLS handshake error from 47.92.65.140:49596: EOF
2024/05/31 05:55:51 http: TLS handshake error from 47.92.65.140:49514: EOF
2024/05/31 05:55:51 http: TLS handshake error from 47.92.65.140:49416: EOF
2024/05/31 05:55:51 http: TLS handshake error from 47.92.65.140:49608: EOF
2024/05/31 05:55:51 http: TLS handshake error from 47.92.65.140:49610: EOF
2024/05/31 05:55:51 http: TLS handshake error from 47.92.65.140:49614: tls: no cipher suite supported by both client and server
2024/05/31 05:55:51 http: TLS handshake error from 47.92.65.140:49622: tls: no cipher suite supported by both client and server
2024/05/31 05:55:51 http: TLS handshake error from 47.92.65.140:49632: tls: no cipher suite supported by both client and server
2024/05/31 05:55:51 http: TLS handshake error from 47.92.65.140:49644: EOF
2024/05/31 05:55:51 http: TLS handshake error from 47.92.65.140:49654: tls: client offered only unsupported versions: []
2024/05/31 05:55:51 http: TLS handshake error from 47.92.65.140:49658: tls: no cipher suite supported by both client and server
2024/05/31 05:55:51 http: TLS handshake error from 47.92.65.140:49670: tls: no cipher suite supported by both client and server
2024/05/31 05:55:51 http: TLS handshake error from 47.92.65.140:49680: tls: no cipher suite supported by both client and server
2024/05/31 05:55:51 http: TLS handshake error from 47.92.65.140:49532: tls: no cipher suite supported by both client and server
2024/05/31 05:55:51 http: TLS handshake error from 47.92.65.140:49686: tls: no cipher suite supported by both client and server
2024/05/31 05:55:51 http: TLS handshake error from 47.92.65.140:49554: tls: no cipher suite supported by both client and server
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49538: read tcp 192.168.20.202:8002->47.92.65.140:49538: read: connection reset by peer
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49698: EOF
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49704: tls: no cipher suite supported by both client and server
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49700: EOF
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49720: EOF
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49734: EOF
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49774: tls: no cipher suite supported by both client and server
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49744: EOF
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49780: tls: no cipher suite supported by both client and server
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49758: EOF
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49790: EOF
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49798: EOF
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49804: tls: no cipher suite supported by both client and server
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49800: tls: no cipher suite supported by both client and server
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49834: tls: no cipher suite supported by both client and server
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49824: EOF
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49816: EOF
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49864: tls: no cipher suite supported by both client and server
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49874: tls: no cipher suite supported by both client and server
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49880: tls: no cipher suite supported by both client and server
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49850: EOF
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49870: EOF
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49882: tls: no cipher suite supported by both client and server
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49898: tls: no cipher suite supported by both client and server
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49900: EOF
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49922: EOF
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49912: EOF
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49932: tls: no cipher suite supported by both client and server
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49948: tls: no cipher suite supported by both client and server
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49954: EOF
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49952: EOF
2024/05/31 05:55:52 http: TLS handshake error from 47.92.65.140:49960: EOF
2024/05/31 05:55:53 http: TLS handshake error from 47.92.65.140:49968: EOF
2024/05/31 05:55:53 http: TLS handshake error from 47.92.65.140:49982: EOF
2024/05/31 05:55:53 http: TLS handshake error from 47.92.65.140:49996: EOF
2024/05/31 05:55:53 http: TLS handshake error from 47.92.65.140:49998: EOF
2024/05/31 05:55:53 http: TLS handshake error from 47.92.65.140:50020: tls: no cipher suite supported by both client and server
2024/05/31 05:55:53 http: TLS handshake error from 47.92.65.140:50006: EOF
2024/05/31 05:55:53 http: TLS handshake error from 47.92.65.140:50026: EOF
2024/05/31 05:55:53 http: TLS handshake error from 47.92.65.140:50048: tls: no cipher suite supported by both client and server
2024/05/31 05:55:53 http: TLS handshake error from 47.92.65.140:50034: EOF
2024/05/31 05:55:53 http: TLS handshake error from 47.92.65.140:50058: tls: no cipher suite supported by both client and server
2024/05/31 05:55:53 http: TLS handshake error from 47.92.65.140:50056: EOF
2024/05/31 05:55:53 http: TLS handshake error from 47.92.65.140:50074: EOF
2024/05/31 05:55:53 http: TLS handshake error from 47.92.65.140:50102: tls: no cipher suite supported by both client and server
2024/05/31 05:55:53 http: TLS handshake error from 47.92.65.140:50090: EOF
2024/05/31 05:55:53 http: TLS handshake error from 47.92.65.140:50110: EOF
2024/05/31 05:55:53 http: TLS handshake error from 47.92.65.140:50124: EOF
2024/05/31 05:55:53 http: TLS handshake error from 47.92.65.140:50126: EOF
2024/05/31 05:55:53 http: TLS handshake error from 47.92.65.140:50138: EOF
2024/05/31 05:55:53 http: TLS handshake error from 47.92.65.140:50140: EOF
2024/05/31 05:55:53 http: TLS handshake error from 47.92.65.140:50144: EOF
2024/05/31 05:55:54 http: TLS handshake error from 47.92.65.140:50156: EOF
2024/05/31 05:55:54 http: TLS handshake error from 47.92.65.140:50166: EOF
2024/05/31 05:55:54 http: TLS handshake error from 47.92.65.140:50178: EOF
2024/05/31 05:55:54 http: TLS handshake error from 47.92.65.140:50180: EOF
2024/05/31 05:55:54 http: TLS handshake error from 47.92.65.140:50206: tls: no cipher suite supported by both client and server
2024/05/31 05:55:54 http: TLS handshake error from 47.92.65.140:50194: EOF
2024/05/31 05:55:54 http: TLS handshake error from 47.92.65.140:50232: tls: no cipher suite supported by both client and server
2024/05/31 05:55:54 http: TLS handshake error from 47.92.65.140:50222: EOF
2024/05/31 05:55:54 http: TLS handshake error from 47.92.65.140:50242: EOF
2024/05/31 05:55:54 http: TLS handshake error from 47.92.65.140:50258: EOF
2024/05/31 05:55:54 http: TLS handshake error from 47.92.65.140:50264: EOF
2024/05/31 05:55:54 http: TLS handshake error from 47.92.65.140:50266: EOF
2024/05/31 05:55:54 http: TLS handshake error from 47.92.65.140:50270: EOF
2024/05/31 05:55:54 http: TLS handshake error from 47.92.65.140:50280: EOF
2024/05/31 05:55:55 http: TLS handshake error from 47.92.65.140:50296: EOF
2024/05/31 05:55:55 http: TLS handshake error from 47.92.65.140:50300: EOF
2024/05/31 05:55:55 http: TLS handshake error from 47.92.65.140:50316: EOF
2024/05/31 05:55:55 http: TLS handshake error from 47.92.65.140:50326: EOF
2024/05/31 05:55:55 http: TLS handshake error from 47.92.65.140:50334: EOF
2024/05/31 05:55:55 http: TLS handshake error from 47.92.65.140:49378: EOF
2024/05/31 05:55:55 http: TLS handshake error from 47.92.65.140:49386: EOF
2024/05/31 05:55:55 http: TLS handshake error from 47.92.65.140:49390: EOF
2024/05/31 05:55:56 http: TLS handshake error from 47.92.65.140:49400: EOF
2024/05/31 05:55:56 http: TLS handshake error from 47.92.65.140:49416: EOF
2024/05/31 05:55:56 http: TLS handshake error from 47.92.65.140:49426: EOF
2024/05/31 05:55:56 http: TLS handshake error from 47.92.65.140:49428: EOF
2024/05/31 05:55:56 http: TLS handshake error from 47.92.65.140:49438: tls: client offered only unsupported versions: []
2024/05/31 05:55:56 http: TLS handshake error from 47.92.65.140:49414: EOF
2024/05/31 12:39:35 http: TLS handshake error from 85.209.11.113:63985: tls: first record does not look like a TLS handshake
2024/05/31 13:29:55 http: TLS handshake error from 79.124.56.202:64545: tls: first record does not look like a TLS handshake
2024/05/31 16:23:30 http: TLS handshake error from 185.234.216.112:64460: tls: first record does not look like a TLS handshake
2024/06/01 02:43:30 http: TLS handshake error from 213.226.123.98:64873: tls: first record does not look like a TLS handshake
2024/06/01 11:24:57 http: TLS handshake error from 79.124.56.202:64076: tls: first record does not look like a TLS handshake
2024/06/01 14:28:13 http: TLS handshake error from 79.124.56.202:65124: tls: first record does not look like a TLS handshake
2024/06/01 18:16:43 http: TLS handshake error from 213.226.123.98:64798: tls: first record does not look like a TLS handshake
2024/06/01 22:25:07 http: TLS handshake error from 206.81.24.227:44996: read tcp 192.168.20.202:8002->206.81.24.227:44996: read: connection reset by peer
2024/06/01 22:25:08 http: TLS handshake error from 206.81.24.227:45012: read tcp 192.168.20.202:8002->206.81.24.227:45012: read: connection reset by peer
2024/06/01 22:25:08 http: TLS handshake error from 206.81.24.227:45016: read tcp 192.168.20.202:8002->206.81.24.227:45016: read: connection reset by peer
2024/06/01 22:25:09 http: TLS handshake error from 206.81.24.227:45018: tls: client requested unsupported application protocols ([http/0.9 http/1.0 spdy/1 spdy/2 spdy/3 h2c hq])
2024/06/01 22:25:09 http: TLS handshake error from 206.81.24.227:45028: tls: client requested unsupported application protocols ([hq h2c spdy/3 spdy/2 spdy/1 http/1.0 http/0.9])
2024/06/01 22:25:10 http: TLS handshake error from 206.81.24.227:45038: read tcp 192.168.20.202:8002->206.81.24.227:45038: read: connection reset by peer
2024/06/01 22:25:11 http: TLS handshake error from 206.81.24.227:45046: read tcp 192.168.20.202:8002->206.81.24.227:45046: read: connection reset by peer
2024/06/01 22:25:12 http: TLS handshake error from 206.81.24.227:45058: read tcp 192.168.20.202:8002->206.81.24.227:45058: read: connection reset by peer
2024/06/01 22:25:12 http: TLS handshake error from 206.81.24.227:45070: read tcp 192.168.20.202:8002->206.81.24.227:45070: read: connection reset by peer
2024/06/01 22:25:13 http: TLS handshake error from 206.81.24.227:45078: read tcp 192.168.20.202:8002->206.81.24.227:45078: read: connection reset by peer
2024/06/02 00:48:59 http: TLS handshake error from 172.235.174.210:60000: read tcp 192.168.20.202:8002->172.235.174.210:60000: read: connection reset by peer
2024/06/02 02:20:17 http: TLS handshake error from 118.193.56.149:54516: EOF
2024/06/02 02:20:37 http: TLS handshake error from 118.193.56.149:47666: read tcp 192.168.20.202:8002->118.193.56.149:47666: read: connection reset by peer
2024/06/02 02:20:37 http: TLS handshake error from 118.193.56.149:47676: read tcp 192.168.20.202:8002->118.193.56.149:47676: read: connection reset by peer
2024/06/02 02:20:37 http: TLS handshake error from 118.193.56.149:47684: read tcp 192.168.20.202:8002->118.193.56.149:47684: read: connection reset by peer
2024/06/02 02:20:37 http: TLS handshake error from 118.193.56.149:47698: tls: client requested unsupported application protocols ([http/0.9 http/1.0 spdy/1 spdy/2 spdy/3 h2c hq])
2024/06/02 02:20:37 http: TLS handshake error from 118.193.56.149:47710: tls: client requested unsupported application protocols ([hq h2c spdy/3 spdy/2 spdy/1 http/1.0 http/0.9])
2024/06/02 02:20:38 http: TLS handshake error from 118.193.56.149:47724: read tcp 192.168.20.202:8002->118.193.56.149:47724: read: connection reset by peer
2024/06/02 02:20:38 http: TLS handshake error from 118.193.56.149:47728: read tcp 192.168.20.202:8002->118.193.56.149:47728: read: connection reset by peer
2024/06/02 02:20:38 http: TLS handshake error from 118.193.56.149:47738: read tcp 192.168.20.202:8002->118.193.56.149:47738: read: connection reset by peer
2024/06/02 02:20:38 http: TLS handshake error from 118.193.56.149:47752: read tcp 192.168.20.202:8002->118.193.56.149:47752: read: connection reset by peer
2024/06/02 02:20:39 http: TLS handshake error from 118.193.56.149:47758: read tcp 192.168.20.202:8002->118.193.56.149:47758: read: connection reset by peer
2024/06/02 10:00:17 http: TLS handshake error from 213.226.123.98:63162: tls: first record does not look like a TLS handshake
2024/06/02 11:07:39 http: TLS handshake error from 115.231.78.9:11949: tls: first record does not look like a TLS handshake
2024/06/02 12:05:00 http: TLS handshake error from 152.89.198.254:63533: tls: first record does not look like a TLS handshake
2024/06/02 13:26:15 http: TLS handshake error from 79.124.56.202:64139: tls: first record does not look like a TLS handshake
2024/06/02 14:42:51 http: TLS handshake error from 178.159.37.98:2669: tls: first record does not look like a TLS handshake
[mysql] 2024/06/02 18:05:24 connection.go:49: closing bad idle connection: unexpected read from socket
[mysql] 2024/06/02 18:05:24 connection.go:49: closing bad idle connection: unexpected read from socket
2024/06/02 19:02:42 http: TLS handshake error from 87.236.176.208:60789: EOF
2024/06/02 19:03:55 http: TLS handshake error from 87.236.176.213:52835: read tcp 192.168.20.202:8002->87.236.176.213:52835: read: connection reset by peer
2024/06/02 19:04:29 http: TLS handshake error from 87.236.176.217:58165: EOF
2024/06/02 19:04:52 http: TLS handshake error from 79.124.56.202:65005: tls: first record does not look like a TLS handshake
2024/06/02 19:06:23 http: TLS handshake error from 87.236.176.213:35043: tls: client requested unsupported application protocols ([http/0.9 http/1.0 spdy/1 spdy/2 spdy/3 h2c hq])
2024/06/02 19:06:56 http: TLS handshake error from 87.236.176.195:41495: tls: client requested unsupported application protocols ([hq h2c spdy/3 spdy/2 spdy/1 http/1.0 http/0.9])
2024/06/02 19:07:30 http: TLS handshake error from 87.236.176.210:48675: read tcp 192.168.20.202:8002->87.236.176.210:48675: read: connection reset by peer
2024/06/02 19:08:44 http: TLS handshake error from 87.236.176.203:40213: read tcp 192.168.20.202:8002->87.236.176.203:40213: read: connection reset by peer
2024/06/02 19:09:57 http: TLS handshake error from 87.236.176.206:51169: EOF
2024/06/02 19:10:31 http: TLS handshake error from 87.236.176.189:42549: EOF
2024/06/02 19:11:04 http: TLS handshake error from 87.236.176.183:45873: EOF
2024/06/03 01:58:40 http: TLS handshake error from 213.226.123.98:65404: tls: first record does not look like a TLS handshake
2024/06/03 02:02:28 http: TLS handshake error from 111.162.158.53:57399: EOF
2024/06/03 02:02:38 http: TLS handshake error from 180.212.93.10:52777: EOF
2024/06/03 02:02:44 http: TLS handshake error from 118.81.86.60:53559: EOF
2024/06/03 02:02:48 http: TLS handshake error from 171.120.158.108:32526: read tcp 192.168.20.202:8002->171.120.158.108:32526: read: connection reset by peer
2024/06/03 02:02:49 http: TLS handshake error from 27.98.228.73:42586: tls: client requested unsupported application protocols ([http/0.9 http/1.0 spdy/1 spdy/2 spdy/3 h2c hq])
2024/06/03 02:02:49 http: TLS handshake error from 110.177.176.191:58517: tls: client requested unsupported application protocols ([hq h2c spdy/3 spdy/2 spdy/1 http/1.0 http/0.9])
2024/06/03 02:02:49 http: TLS handshake error from 221.213.12.184:64390: read tcp 192.168.20.202:8002->221.213.12.184:64390: read: connection reset by peer
2024/06/03 02:02:51 http: TLS handshake error from 221.213.12.230:43586: EOF
2024/06/03 02:02:51 http: TLS handshake error from 60.16.204.89:37869: read tcp 192.168.20.202:8002->60.16.204.89:37869: read: connection reset by peer
2024/06/03 02:02:54 http: TLS handshake error from 124.90.51.219:54040: EOF
2024/06/03 02:02:55 http: TLS handshake error from 171.8.138.202:21988: read tcp 192.168.20.202:8002->171.8.138.202:21988: read: connection reset by peer
2024/06/03 02:02:57 http: TLS handshake error from 111.224.6.7:28407: read tcp 192.168.20.202:8002->111.224.6.7:28407: read: connection reset by peer
2024/06/03 02:03:03 http: TLS handshake error from 124.90.48.175:40575: EOF
2024/06/03 02:05:52 http: TLS handshake error from 175.30.48.170:65225: read tcp 192.168.20.202:8002->175.30.48.170:65225: read: connection timed out
2024/06/03 10:07:12 http: TLS handshake error from 154.212.141.203:58604: EOF
2024/06/03 10:07:12 http: TLS handshake error from 154.212.141.203:58624: read tcp 192.168.20.202:8002->154.212.141.203:58624: read: connection reset by peer
2024/06/03 10:07:12 http: TLS handshake error from 154.212.141.203:58658: EOF
2024/06/03 10:07:12 http: TLS handshake error from 154.212.141.203:58752: tls: client requested unsupported application protocols ([http/0.9 http/1.0 spdy/1 spdy/2 spdy/3 h2c hq])
2024/06/03 10:07:12 http: TLS handshake error from 154.212.141.203:58776: tls: client requested unsupported application protocols ([hq h2c spdy/3 spdy/2 spdy/1 http/1.0 http/0.9])
2024/06/03 10:07:12 http: TLS handshake error from 154.212.141.203:58806: read tcp 192.168.20.202:8002->154.212.141.203:58806: read: connection reset by peer
2024/06/03 10:07:12 http: TLS handshake error from 154.212.141.203:58822: EOF
2024/06/03 10:07:12 http: TLS handshake error from 154.212.141.203:58868: read tcp 192.168.20.202:8002->154.212.141.203:58868: read: connection reset by peer
2024/06/03 10:07:12 http: TLS handshake error from 154.212.141.203:58886: EOF
2024/06/03 10:07:13 http: TLS handshake error from 154.212.141.203:58910: read tcp 192.168.20.202:8002->154.212.141.203:58910: read: connection reset by peer
2024/06/03 10:07:13 http: TLS handshake error from 154.212.141.203:58924: EOF
2024/06/03 10:28:09 http: TLS handshake error from 178.159.37.98:2201: tls: first record does not look like a TLS handshake
2024/06/03 14:37:43 http: TLS handshake error from 79.124.56.202:61708: tls: first record does not look like a TLS handshake
2024/06/03 15:22:07 http: TLS handshake error from 79.124.56.202:62208: tls: first record does not look like a TLS handshake
2024/06/03 19:30:22 http: TLS handshake error from 39.100.34.21:9929: EOF
2024/06/04 02:03:45 http: TLS handshake error from 79.124.56.202:65187: tls: first record does not look like a TLS handshake
2024/06/04 02:52:56 http: TLS handshake error from 170.64.234.117:53702: EOF
2024/06/04 05:30:07 http: TLS handshake error from 161.189.192.94:38938: tls: first record does not look like a TLS handshake
2024/06/04 05:36:52 http: TLS handshake error from 161.189.134.11:53366: tls: first record does not look like a TLS handshake
2024/06/04 07:02:52 http: TLS handshake error from 31.7.62.234:59603: tls: first record does not look like a TLS handshake
2024/06/04 09:40:20 http: TLS handshake error from 39.100.20.124:11732: EOF
2024/06/04 12:05:20 http: TLS handshake error from 39.98.186.243:27542: EOF
2024/06/04 14:13:20 http: TLS handshake error from 79.124.56.202:60759: tls: first record does not look like a TLS handshake

211
takway_structure_backup.sql Normal file

File diff suppressed because one or more lines are too long