diff --git a/.github/desktop-tauri.jpeg b/.github/desktop-tauri.jpeg new file mode 100644 index 00000000..cf4d691e Binary files /dev/null and b/.github/desktop-tauri.jpeg differ diff --git a/.gitignore b/.gitignore index 4b772892..4f7e39cc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ +.idea .vscode -__debug_bin +!*.example +dist/ config.yaml *.log -paopao-api \ No newline at end of file +paopao-ce* diff --git a/Dockerfile b/Dockerfile index f6034422..109cbea6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,9 @@ # build app FROM golang AS build-env -ADD . /paopao-api +ADD . /paopao-ce -WORKDIR /paopao-api +WORKDIR /paopao-ce RUN CGO_ENABLED=0 go build . @@ -14,14 +14,13 @@ ENV TZ=Asia/Shanghai RUN apk update && apk add --no-cache ca-certificates && update-ca-certificates -COPY --from=build-env /paopao-api/paopao-api /usr/bin/paopao-api -COPY --from=build-env /paopao-api/comic.ttf /comic.ttf -COPY --from=build-env /paopao-api/qqwry.dat /qqwry.dat -COPY --from=build-env /paopao-api/configs /configs +COPY --from=build-env /paopao-ce/paopao-ce /usr/bin/paopao-ce +COPY --from=build-env /paopao-ce/assets/comic.ttf /assets/comic.ttf +COPY --from=build-env /paopao-ce/configs /configs EXPOSE 8000 -CMD ["paopao-api"] +CMD ["paopao-ce"] # HEALTHCHECK -HEALTHCHECK --interval=5s --timeout=3s --retries=3 CMD ps -ef | grep paopao-api || exit 1 +HEALTHCHECK --interval=5s --timeout=3s --retries=3 CMD ps -ef | grep paopao-ce || exit 1 diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..4a23ba98 --- /dev/null +++ b/Makefile @@ -0,0 +1,33 @@ +.PHONY: all build run clean fmt help +BUILD_VERSION := $(shell cat version) +BUILD_DATE := $(shell date +'%Y-%m-%d %H:%M:%S') +SHA_SHORT := $(shell git rev-parse --short HEAD) +TAGS = "" +all: fmt build +build: + @go mod download + @echo Build paopao-ce + bash build.sh paopao-ce +run: + @go run -tags '$(TAGS)' -ldflags "-X 'main.version=${BUILD_VERSION}' -X 'main.buildDate=${BUILD_DATE}' -X 'main.commitID=${SHA_SHORT}'" . +clean: + @go clean + @find ./dist -type f -exec rm -r {} + + @find ./tmp -type f -exec rm -r {} + +fmt: + @echo Formatting... + @go fmt ./global/... + @go fmt ./internal/... + @go fmt ./pkg/... + @go vet -composites=false ./global/... + @go vet -composites=false ./internal/... + @go vet -composites=false ./pkg/... +help: + @echo "make: make" + @echo "make run: start api server" + @echo "make build: build executables" + @echo "make build: build executables" + @echo "make run TAGS='embed': start api server and serve embed web frontend" + @echo "make build TAGS='embed': build executables with embed web frontend" +.EXPORT_ALL_VARIABLES: +GO111MODULE = on diff --git a/README.md b/README.md index a9bead35..40ff3323 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@
- [![Go Report Card][goreport-shield]][goreport-url] [![Forks][forks-shield]][forks-url] @@ -28,22 +27,22 @@

+--- - -## 1. 截图预览 - +## 预览 +Web端: [![明色主题][product-light-screenshot]](https://www.paopao.info) [![暗色主题][product-dark-screenshot]](https://www.paopao.info) -更多演示请前往[官网](https://www.paopao.info)体验(谢绝灌水) +更多演示请前往[官网](https://www.paopao.info)体验(谢绝灌水) -

(back to top)

+桌面端: +![](.github/desktop-tauri.jpeg) - -## 2. 快速开始 +

(back to top)

-### 2.1 技术栈 +## 🛠 技术栈 PaoPao主要由以下优秀的开源项目/工具构建 @@ -54,61 +53,116 @@ PaoPao主要由以下优秀的开源项目/工具构建 * [Vue.js](https://vuejs.org/) * [Vite.js](https://vitejs.dev/) + +## 🏗 快速开始 -### 2.2 环境要求 +### 环境要求 -- Go (1.17+) -- Node.js (14+) -- MySQL (5.7+) -- Redis -- Zinc +* Go (1.17+) +* Node.js (14+) +* MySQL (5.7+) +* Redis +* Zinc -\* Zinc是一款轻量级全文搜索引擎,可以查阅 https://zincsearch.com/ 安装 +\* Zinc是一款轻量级全文搜索引擎,可以查阅 安装 以上环境版本为PaoPao官方的开发版本,仅供参考,其他版本的环境未进行充分测试 -
+### 安装说明 + +***宝塔安装*** + +我们为宝塔用户提供了超详细安装教程 [点此查看](https://www.rocs.me/archives/paopao_bt_install.html) -### 3. 安装说明 +***普通安装*** 克隆代码库 + ```sh git clone https://github.com/rocboss/paopao-ce.git ``` -#### 3.1 后端 + +#### 后端 1. 导入项目根目录下的 `paopao.sql` 文件至MySQL数据库 2. 拷贝项目根目录下 `config.yaml.sample` 文件至 `config.yaml`,按照注释完成配置编辑 -3. 编译后端 +3. 编译后端 + 编译api服务: ```sh - go mod download - go build -o paopao-api . + make build ``` -4. 启动后端 + 编译api服务、内嵌web前端ui; 注意此步骤需要先编译web前端。 ```sh - chmod +x paopao-api - ./paopao-api + make build TAGS='embed' ``` + 编译后在`dist`目录可以找到对应可执行文件。 -#### 3.2 前端 +4. 启动后端 + 运行api服务: + ```sh + make run + ``` + 运行api服务、web前端ui服务: + ```sh + make run TAGS='embed' + ``` + +#### 前端 1. 进入前端目录 `web`,编辑 `.env` 文件中后端服务地址,下载依赖包 + ```sh cd ./web vim .env yarn ``` + 2. 编译前端 + ```sh yarn build ``` build完成后,可以在dist目录获取编译产出,配置nginx指向至该目录即可 -#### 3.3 其他 -建议后端服务使用 `supervisor` 守护进程,并通过nginx反向代理后,提供API给前端服务调用。 -短信通道使用的juhe数据,如果申请不下来,可以考虑替换其他服务商。 -代码结构比较简单,喜欢的朋友欢迎给个Star、贡献PR。 +#### 桌面端 + +1. 进入前端目录 `web`,编辑 `.env` 文件中后端服务地址,下载依赖包 + + ```sh + cd ./web + vim .env + yarn + ``` + +2. 编译前端 + + ```sh + yarn build + ``` + +3. 构建桌面端 + ```sh + yarn tauri build + ``` + 桌面端是使用[Rust](https://www.rust-lang.org/) + [tauri](https://github.com/tauri-apps/tauri)编写 + 的,需要Rust编译环境,具体安装指南请参考[https://www.rust-lang.org/tools/install](https://www.rust-lang.org/tools/install). + +### 其他说明 + +建议后端服务使用 `supervisor` 守护进程,并通过 `nginx` 反向代理后,提供API给前端服务调用。 + +短信通道使用的[聚合数据](https://www.juhe.cn/),如果申请不下来,可以考虑替换其他服务商。 + +代码结构比较简单,很方便扩展 + +## 👯‍♀️ 贡献 + +喜欢的朋友欢迎给个Star、贡献PR。 + +## License + +Distributed under the MIT License. See `LICENSE` for more information. [contributors-shield]: https://img.shields.io/github/contributors/rocboss/paopao-ce?style=flat @@ -125,4 +179,4 @@ PaoPao主要由以下优秀的开源项目/工具构建 [license-url]: https://github.com/rocboss/paopao-ce/blob/master/LICENSE.txt [linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=flat&logo=linkedin&colorB=555 [product-light-screenshot]: https://cdn.rocs.me/static/paopao-light.jpeg -[product-dark-screenshot]: https://cdn.rocs.me/static/paopao-dark.jpeg \ No newline at end of file +[product-dark-screenshot]: https://cdn.rocs.me/static/paopao-dark.jpeg diff --git a/comic.ttf b/assets/comic.ttf similarity index 100% rename from comic.ttf rename to assets/comic.ttf diff --git a/build.sh b/build.sh new file mode 100644 index 00000000..f49c00c8 --- /dev/null +++ b/build.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2155 +set -e +DIST_PREFIX=${1} +DEBUG_MODE=${2} +TARGET_DIR="dist" +PLATFORMS="darwin/amd64 darwin/arm64 linux/amd64 windows/amd64" + +rm -rf ${TARGET_DIR} +mkdir ${TARGET_DIR} + +for pl in ${PLATFORMS}; do + export CGO_ENABLED=0 + export GOOS=$(echo "${pl}" | cut -d'/' -f1) + export GOARCH=$(echo "${pl}" | cut -d'/' -f2) + export TARGET=${TARGET_DIR}/${DIST_PREFIX}_${GOOS}_${GOARCH} + if [ "${GOOS}" == "windows" ]; then + export TARGET=${TARGET_DIR}/${DIST_PREFIX}_${GOOS}_${GOARCH}.exe + fi + + echo "build => ${TARGET}" + if [ "${DEBUG_MODE}" == "debug" ]; then + go build -trimpath -gcflags "all=-N -l" -o "${TARGET}" -tags "${TAGS}" \ + -ldflags "-X 'main.version=${BUILD_VERSION}' \ + -X 'main.buildDate=${BUILD_DATE}' \ + -X 'main.commitID=${SHA_SHORT}'\ + -w -s" + else + go build -trimpath -o "${TARGET}" -tags "${TAGS}" \ + -ldflags "-X 'main.version=${BUILD_VERSION}' \ + -X 'main.buildDate=${BUILD_DATE}' \ + -X 'main.commitID=${SHA_SHORT}'\ + -w -s" + fi +done diff --git a/config.yaml.sample b/config.yaml.sample index f666be2f..a44a84df 100644 --- a/config.yaml.sample +++ b/config.yaml.sample @@ -10,6 +10,8 @@ App: # APP基础设置项 SmsJuheTplVal: "#code#=%d&#m#=%d" AlipayAppID: AlipayPrivateKey: +Runtime: # App运行时功能调节 + DisablePhoneVerify: False # 禁止绑定手机号码时验证短信验证码,为true时任意验证码都可以通过验证 Server: # 服务设置 RunMode: debug HttpIp: 0.0.0.0 diff --git a/global/setting.go b/global/setting.go index 1e26d29c..ac463109 100644 --- a/global/setting.go +++ b/global/setting.go @@ -10,6 +10,7 @@ import ( var ( ServerSetting *setting.ServerSettingS AppSetting *setting.AppSettingS + RuntimeSetting *setting.RuntimeSettingS DatabaseSetting *setting.DatabaseSettingS RedisSetting *setting.RedisSettingS SearchSetting *setting.SearchSettingS diff --git a/go.mod b/go.mod index 3c326ad5..2fc783ce 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/disintegration/imaging v1.6.2 github.com/ethereum/go-ethereum v1.10.16 + github.com/fatih/color v1.13.0 github.com/fbsobreira/gotron-sdk v0.0.0-20211102183839-58a64f4da5f4 github.com/gin-contrib/cors v1.3.1 github.com/gin-gonic/gin v1.7.7 @@ -16,19 +17,18 @@ require ( github.com/go-redis/redis/v8 v8.11.4 github.com/go-resty/resty/v2 v2.7.0 github.com/gofrs/uuid v3.3.0+incompatible - github.com/golang/protobuf v1.5.2 github.com/google/go-cmp v0.5.7 // indirect github.com/satori/go.uuid v1.2.0 // indirect github.com/sirupsen/logrus v1.8.1 github.com/smartwalle/alipay/v3 v3.1.7 github.com/spf13/viper v1.10.1 github.com/ugorji/go v1.2.7 // indirect - github.com/yinheli/mahonia v0.0.0-20131226213531-0eef680515cc // indirect - github.com/yinheli/qqwry v0.0.0-20160229183603-f50680010f4a + github.com/yinheli/mahonia v0.0.0-20131226213531-0eef680515cc golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70 // indirect golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9 // indirect golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 // indirect + google.golang.org/protobuf v1.27.1 gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/resty.v1 v1.12.0 gorm.io/driver/mysql v1.3.2 diff --git a/go.sum b/go.sum index 29160250..9aaf5068 100644 --- a/go.sum +++ b/go.sum @@ -201,6 +201,7 @@ github.com/ethereum/go-ethereum v1.10.16 h1:3oPrumn0bCW/idjcxMn5YYVCdK7VzJYIvwGZ github.com/ethereum/go-ethereum v1.10.16/go.mod h1:Anj6cxczl+AHy63o4X9O8yWNHuN5wMpfb8MAnHkWn7Y= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fbsobreira/gotron-sdk v0.0.0-20211102183839-58a64f4da5f4 h1:zlN+of+l+or/Y6CYgpX99wwaTf4zN0myVj6YAXiE3xk= @@ -485,6 +486,7 @@ github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= @@ -675,8 +677,6 @@ github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6Ut github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yinheli/mahonia v0.0.0-20131226213531-0eef680515cc h1:7VHQaaNwHymWbj8lAcXMYX1qopebSBHwYC3ceXLWONU= github.com/yinheli/mahonia v0.0.0-20131226213531-0eef680515cc/go.mod h1:Pcc297eVCbkDBBVq8FbnI+qDUeIMrHy4Bo7nveAuCAs= -github.com/yinheli/qqwry v0.0.0-20160229183603-f50680010f4a h1:VUPXGL4N1B5xqomxI4vZn0momgleh0hcH0PE/oIOsAI= -github.com/yinheli/qqwry v0.0.0-20160229183603-f50680010f4a/go.mod h1:Zva9ErVtC2arl+9xtHwiXujgejX1S3VpOYExADJ9kio= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/init.go b/init.go index 9382471f..ac7a1178 100644 --- a/init.go +++ b/init.go @@ -8,8 +8,10 @@ import ( "github.com/go-redis/redis/v8" "github.com/rocboss/paopao-ce/global" "github.com/rocboss/paopao-ce/internal/model" + "github.com/rocboss/paopao-ce/internal/service" "github.com/rocboss/paopao-ce/pkg/logger" "github.com/rocboss/paopao-ce/pkg/setting" + "github.com/rocboss/paopao-ce/pkg/zinc" ) func init() { @@ -25,6 +27,8 @@ func init() { if err != nil { log.Fatalf("init.setupDBEngine err: %v", err) } + client := zinc.NewClient(global.SearchSetting) + service.Initialize(global.DBEngine, client) } func setupSetting() error { @@ -41,6 +45,10 @@ func setupSetting() error { if err != nil { return err } + err = setting.ReadSection("Runtime", &global.RuntimeSetting) + if err != nil { + return err + } err = setting.ReadSection("Log", &global.LoggerSetting) if err != nil { return err diff --git a/internal/model/message.go b/internal/model/message.go index 415e3ac4..89a4ae64 100644 --- a/internal/model/message.go +++ b/internal/model/message.go @@ -20,7 +20,7 @@ type Message struct { SenderUserID int64 `json:"sender_user_id"` ReceiverUserID int64 `json:"receiver_user_id"` Type MessageT `json:"type"` - Breif string `json:"breif"` + Brief string `json:"brief"` Content string `json:"content"` PostID int64 `json:"post_id"` CommentID int64 `json:"comment_id"` @@ -34,7 +34,7 @@ type MessageFormated struct { SenderUser *UserFormated `json:"sender_user"` ReceiverUserID int64 `json:"receiver_user_id"` Type MessageT `json:"type"` - Breif string `json:"breif"` + Brief string `json:"brief"` Content string `json:"content"` PostID int64 `json:"post_id"` Post *PostFormated `json:"post"` @@ -57,7 +57,7 @@ func (m *Message) Format() *MessageFormated { SenderUser: &UserFormated{}, ReceiverUserID: m.ReceiverUserID, Type: m.Type, - Breif: m.Breif, + Brief: m.Brief, Content: m.Content, PostID: m.PostID, Post: &PostFormated{}, diff --git a/internal/routers/api/attachment.go b/internal/routers/api/attachment.go index 6ff07630..37cdcec6 100644 --- a/internal/routers/api/attachment.go +++ b/internal/routers/api/attachment.go @@ -53,9 +53,23 @@ func GetImageSize(img image.Rectangle) (int, int) { return width, height } +func fileCheck(uploadType string, size int64) error { + if uploadType != "public/video" && + uploadType != "public/image" && + uploadType != "public/avatar" && + uploadType != "attachment" { + return errcode.InvalidParams + } + + if size > 1024*1024*100 { + return errcode.FileInvalidSize.WithDetails("最大允许100MB") + } + + return nil +} + func UploadAttachment(c *gin.Context) { response := app.NewResponse(c) - svc := service.New(c) uploadType := c.Request.FormValue("type") file, fileHeader, err := c.Request.FormFile("file") @@ -66,16 +80,9 @@ func UploadAttachment(c *gin.Context) { } defer file.Close() - if uploadType != "public/video" && - uploadType != "public/image" && - uploadType != "public/avatar" && - uploadType != "attachment" { - response.ToErrorResponse(errcode.InvalidParams) - return - } - - if fileHeader.Size > 1024*1024*100 { - response.ToErrorResponse(errcode.FileInvalidSize.WithDetails("最大允许100MB")) + if err = fileCheck(uploadType, fileHeader.Size); err != nil { + cErr, _ := err.(*errcode.Error) + response.ToErrorResponse(cErr) return } @@ -129,24 +136,25 @@ func UploadAttachment(c *gin.Context) { attachment.UserID = userID.(int64) } - if uploadType == "public/image" || uploadType == "public/avatar" { - attachment.Type = model.ATTACHMENT_TYPE_IMAGE + var uploadAttachmentTypeMap = map[string]model.AttachmentType{ + "public/image": model.ATTACHMENT_TYPE_IMAGE, + "public/avatar": model.ATTACHMENT_TYPE_IMAGE, + "public/video": model.ATTACHMENT_TYPE_VIDEO, + "attachment": model.ATTACHMENT_TYPE_OTHER, + } - src, err := imaging.Decode(file) + attachment.Type = uploadAttachmentTypeMap[uploadType] + if attachment.Type == model.ATTACHMENT_TYPE_IMAGE { + var src image.Image + src, err = imaging.Decode(file) if err == nil { attachment.ImgWidth, attachment.ImgHeight = GetImageSize(src.Bounds()) } } - if uploadType == "public/video" { - attachment.Type = model.ATTACHMENT_TYPE_VIDEO - } - if uploadType == "attachment" { - attachment.Type = model.ATTACHMENT_TYPE_OTHER - } - attachment, err = svc.CreateAttachment(attachment) + attachment, err = service.CreateAttachment(attachment) if err != nil { - global.Logger.Errorf("svc.CreateAttachment err: %v", err) + global.Logger.Errorf("service.CreateAttachment err: %v", err) response.ToErrorResponse(errcode.FileUploadFailed) } @@ -155,21 +163,20 @@ func UploadAttachment(c *gin.Context) { func DownloadAttachmentPrecheck(c *gin.Context) { response := app.NewResponse(c) - svc := service.New(c) contentID := convert.StrTo(c.Query("id")).MustInt64() // 加载content - content, err := svc.GetPostContentByID(contentID) + content, err := service.GetPostContentByID(contentID) if err != nil { - global.Logger.Errorf("svc.GetPostContentByID err: %v", err) + global.Logger.Errorf("service.GetPostContentByID err: %v", err) response.ToErrorResponse(errcode.InvalidDownloadReq) } user, _ := c.Get("USER") if content.Type == model.CONTENT_TYPE_CHARGE_ATTACHMENT { // 加载post - post, err := svc.GetPost(content.PostID) + post, err := service.GetPost(content.PostID) if err != nil { - global.Logger.Errorf("svc.GetPost err: %v", err) + global.Logger.Errorf("service.GetPost err: %v", err) response.ToResponse(gin.H{ "paid": false, }) @@ -186,7 +193,7 @@ func DownloadAttachmentPrecheck(c *gin.Context) { // 检测是否有购买记录 response.ToResponse(gin.H{ - "paid": svc.CheckPostAttachmentIsPaid(post.ID, user.(*model.User).ID), + "paid": service.CheckPostAttachmentIsPaid(post.ID, user.(*model.User).ID), }) return } @@ -197,14 +204,13 @@ func DownloadAttachmentPrecheck(c *gin.Context) { func DownloadAttachment(c *gin.Context) { response := app.NewResponse(c) - svc := service.New(c) contentID := convert.StrTo(c.Query("id")).MustInt64() // 加载content - content, err := svc.GetPostContentByID(contentID) + content, err := service.GetPostContentByID(contentID) if err != nil { - global.Logger.Errorf("svc.GetPostContentByID err: %v", err) + global.Logger.Errorf("service.GetPostContentByID err: %v", err) response.ToErrorResponse(errcode.InvalidDownloadReq) } @@ -213,9 +219,9 @@ func DownloadAttachment(c *gin.Context) { user, _ := c.Get("USER") // 加载post - post, err := svc.GetPost(content.PostID) + post, err := service.GetPost(content.PostID) if err != nil { - global.Logger.Errorf("svc.GetPost err: %v", err) + global.Logger.Errorf("service.GetPost err: %v", err) response.ToResponse(gin.H{ "paid": false, }) @@ -230,13 +236,13 @@ func DownloadAttachment(c *gin.Context) { } // 检测是否有购买记录 - if svc.CheckPostAttachmentIsPaid(post.ID, user.(*model.User).ID) { + if service.CheckPostAttachmentIsPaid(post.ID, user.(*model.User).ID) { paidFlag = true } if !paidFlag { // 未购买,则尝试购买 - err := svc.BuyPostAttachment(&model.Post{ + err := service.BuyPostAttachment(&model.Post{ Model: &model.Model{ ID: post.ID, }, @@ -244,7 +250,7 @@ func DownloadAttachment(c *gin.Context) { AttachmentPrice: post.AttachmentPrice, }, user.(*model.User)) if err != nil { - global.Logger.Errorf("svc.BuyPostAttachment err: %v", err) + global.Logger.Errorf("service.BuyPostAttachment err: %v", err) if err == errcode.InsuffientDownloadMoney { response.ToErrorResponse(errcode.InsuffientDownloadMoney) diff --git a/internal/routers/api/comment.go b/internal/routers/api/comment.go index 842e57b1..5aba10b5 100644 --- a/internal/routers/api/comment.go +++ b/internal/routers/api/comment.go @@ -11,15 +11,13 @@ import ( ) func GetPostComments(c *gin.Context) { - postID := convert.StrTo(c.Query("id")).MustInt64() response := app.NewResponse(c) - svc := service.New(c) - contents, totalRows, err := svc.GetPostComments(postID, "id ASC", 0, 0) + contents, totalRows, err := service.GetPostComments(postID, "id ASC", 0, 0) if err != nil { - global.Logger.Errorf("svc.GetPostComments err: %v\n", err) + global.Logger.Errorf("service.GetPostComments err: %v\n", err) response.ToErrorResponse(errcode.GetCommentsFailed) return } @@ -38,14 +36,13 @@ func CreatePostComment(c *gin.Context) { } userID, _ := c.Get("UID") - svc := service.New(c) - comment, err := svc.CreatePostComment(userID.(int64), param) + comment, err := service.CreatePostComment(c, userID.(int64), param) if err != nil { if err == errcode.MaxCommentCount { response.ToErrorResponse(errcode.MaxCommentCount) } else { - global.Logger.Errorf("svc.CreatePostComment err: %v\n", err) + global.Logger.Errorf("service.CreatePostComment err: %v\n", err) response.ToErrorResponse(errcode.CreateCommentFailed) } return @@ -64,11 +61,10 @@ func DeletePostComment(c *gin.Context) { return } user, _ := c.Get("USER") - svc := service.New(c) - comment, err := svc.GetPostComment(param.ID) + comment, err := service.GetPostComment(param.ID) if err != nil { - global.Logger.Errorf("svc.GetPostComment err: %v\n", err) + global.Logger.Errorf("service.GetPostComment err: %v\n", err) response.ToErrorResponse(errcode.GetCommentFailed) return } @@ -79,9 +75,9 @@ func DeletePostComment(c *gin.Context) { } // 执行删除 - err = svc.DeletePostComment(comment) + err = service.DeletePostComment(comment) if err != nil { - global.Logger.Errorf("svc.DeletePostComment err: %v\n", err) + global.Logger.Errorf("service.DeletePostComment err: %v\n", err) response.ToErrorResponse(errcode.DeleteCommentFailed) return } @@ -99,11 +95,10 @@ func CreatePostCommentReply(c *gin.Context) { return } user, _ := c.Get("USER") - svc := service.New(c) - comment, err := svc.CreatePostCommentReply(param.CommentID, param.Content, user.(*model.User).ID, param.AtUserID) + comment, err := service.CreatePostCommentReply(c, param.CommentID, param.Content, user.(*model.User).ID, param.AtUserID) if err != nil { - global.Logger.Errorf("svc.CreatePostCommentReply err: %v\n", err) + global.Logger.Errorf("service.CreatePostCommentReply err: %v\n", err) response.ToErrorResponse(errcode.CreateReplyFailed) return } @@ -122,11 +117,10 @@ func DeletePostCommentReply(c *gin.Context) { } user, _ := c.Get("USER") - svc := service.New(c) - reply, err := svc.GetPostCommentReply(param.ID) + reply, err := service.GetPostCommentReply(param.ID) if err != nil { - global.Logger.Errorf("svc.GetPostCommentReply err: %v\n", err) + global.Logger.Errorf("service.GetPostCommentReply err: %v\n", err) response.ToErrorResponse(errcode.GetReplyFailed) return } @@ -137,9 +131,9 @@ func DeletePostCommentReply(c *gin.Context) { } // 执行删除 - err = svc.DeletePostCommentReply(reply) + err = service.DeletePostCommentReply(reply) if err != nil { - global.Logger.Errorf("svc.DeletePostCommentReply err: %v\n", err) + global.Logger.Errorf("service.DeletePostCommentReply err: %v\n", err) response.ToErrorResponse(errcode.DeleteCommentFailed) return } diff --git a/internal/routers/api/home.go b/internal/routers/api/home.go index fb48f07d..65b2ca2e 100644 --- a/internal/routers/api/home.go +++ b/internal/routers/api/home.go @@ -30,12 +30,11 @@ func Version(c *gin.Context) { func SyncSearchIndex(c *gin.Context) { response := app.NewResponse(c) - svc := service.New(c) user, _ := c.Get("USER") if user.(*model.User).IsAdmin { - go svc.PushPostsToSearch() + go service.PushPostsToSearch(c) } response.ToResponse(nil) @@ -44,7 +43,7 @@ func SyncSearchIndex(c *gin.Context) { func GetCaptcha(c *gin.Context) { cap := captcha.New() - if err := cap.SetFont("comic.ttf"); err != nil { + if err := cap.SetFont("assets/comic.ttf"); err != nil { panic(err.Error()) } @@ -77,7 +76,6 @@ func PostCaptcha(c *gin.Context) { response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) return } - svc := service.New(c) // 验证图片验证码 if res, err := global.Redis.Get(c.Request.Context(), "PaoPaoCaptcha:"+param.ImgCaptchaID).Result(); err != nil || res != param.ImgCaptcha { @@ -92,7 +90,7 @@ func PostCaptcha(c *gin.Context) { return } - err := svc.SendPhoneCaptcha(param.Phone) + err := service.SendPhoneCaptcha(c, param.Phone) if err != nil { global.Logger.Errorf("app.SendPhoneCaptcha errs: %v", errs) response.ToErrorResponse(errcode.GetPhoneCaptchaError) diff --git a/internal/routers/api/message.go b/internal/routers/api/message.go index 202e9a83..1242ff86 100644 --- a/internal/routers/api/message.go +++ b/internal/routers/api/message.go @@ -16,9 +16,8 @@ func GetUnreadMsgCount(c *gin.Context) { if u, exists := c.Get("USER"); exists { user = u.(*model.User) } - svc := service.New(c) - count, _ := svc.GetUnreadCount(user.ID) + count, _ := service.GetUnreadCount(user.ID) response.ToResponse(gin.H{ "count": count, @@ -29,11 +28,10 @@ func GetMessages(c *gin.Context) { response := app.NewResponse(c) userID, _ := c.Get("UID") - svc := service.New(c) - messages, totalRows, err := svc.GetMessages(userID.(int64), (app.GetPage(c)-1)*app.GetPageSize(c), app.GetPageSize(c)) + messages, totalRows, err := service.GetMessages(userID.(int64), (app.GetPage(c)-1)*app.GetPageSize(c), app.GetPageSize(c)) if err != nil { - global.Logger.Errorf("svc.GetMessages err: %v\n", err) + global.Logger.Errorf("service.GetMessages err: %v\n", err) response.ToErrorResponse(errcode.GetMessagesFailed) return } @@ -52,10 +50,9 @@ func ReadMessage(c *gin.Context) { } userID, _ := c.Get("UID") - svc := service.New(c) - err := svc.ReadMessage(param.ID, userID.(int64)) + err := service.ReadMessage(param.ID, userID.(int64)) if err != nil { - global.Logger.Errorf("svc.ReadMessage err: %v\n", err) + global.Logger.Errorf("service.ReadMessage err: %v\n", err) response.ToErrorResponse(errcode.ReadMessageFailed) return } @@ -81,17 +78,16 @@ func SendUserWhisper(c *gin.Context) { return } - svc := service.New(c) - _, err := svc.CreateWhisper(&model.Message{ + _, err := service.CreateWhisper(c, &model.Message{ SenderUserID: userID.(int64), ReceiverUserID: param.UserID, Type: model.MESSAGE_WHISPER, - Breif: "给你发送新私信了", + Brief: "给你发送新私信了", Content: param.Content, }) if err != nil { - global.Logger.Errorf("svc.CreateWhisper err: %v\n", err) + global.Logger.Errorf("service.CreateWhisper err: %v\n", err) if err == errcode.TooManyWhisperNum { response.ToErrorResponse(errcode.TooManyWhisperNum) diff --git a/internal/routers/api/post.go b/internal/routers/api/post.go index a7beaaac..226c9c6a 100644 --- a/internal/routers/api/post.go +++ b/internal/routers/api/post.go @@ -22,10 +22,9 @@ func GetPostList(c *gin.Context) { q.Type = "tag" } - svc := service.New(c) if q.Query == "" && q.Type == "search" { // 直接读库 - posts, err := svc.GetPostList(&service.PostListReq{ + posts, err := service.GetPostList(&service.PostListReq{ Conditions: &model.ConditionsT{ "ORDER": "is_top DESC, latest_replied_on DESC", }, @@ -33,20 +32,20 @@ func GetPostList(c *gin.Context) { Limit: app.GetPageSize(c), }) if err != nil { - global.Logger.Errorf("svc.GetPostList err: %v\n", err) + global.Logger.Errorf("service.GetPostList err: %v\n", err) response.ToErrorResponse(errcode.GetPostsFailed) return } - totalRows, _ := svc.GetPostCount(&model.ConditionsT{ + totalRows, _ := service.GetPostCount(&model.ConditionsT{ "ORDER": "latest_replied_on DESC", }) response.ToResponseList(posts, totalRows) } else { - posts, totalRows, err := svc.GetPostListFromSearch(q, (app.GetPage(c)-1)*app.GetPageSize(c), app.GetPageSize(c)) + posts, totalRows, err := service.GetPostListFromSearch(q, (app.GetPage(c)-1)*app.GetPageSize(c), app.GetPageSize(c)) if err != nil { - global.Logger.Errorf("svc.GetPostListFromSearch err: %v\n", err) + global.Logger.Errorf("service.GetPostListFromSearch err: %v\n", err) response.ToErrorResponse(errcode.GetPostsFailed) return } @@ -58,11 +57,10 @@ func GetPost(c *gin.Context) { postID := convert.StrTo(c.Query("id")).MustInt64() response := app.NewResponse(c) - svc := service.New(c) - postFormated, err := svc.GetPost(postID) + postFormated, err := service.GetPost(postID) if err != nil { - global.Logger.Errorf("svc.GetPost err: %v\n", err) + global.Logger.Errorf("service.GetPost err: %v\n", err) response.ToErrorResponse(errcode.GetPostFailed) return } @@ -81,11 +79,10 @@ func CreatePost(c *gin.Context) { } userID, _ := c.Get("UID") - svc := service.New(c) - post, err := svc.CreatePost(userID.(int64), param) + post, err := service.CreatePost(c, userID.(int64), param) if err != nil { - global.Logger.Errorf("svc.CreatePost err: %v\n", err) + global.Logger.Errorf("service.CreatePost err: %v\n", err) response.ToErrorResponse(errcode.CreatePostFailed) return } @@ -104,12 +101,11 @@ func DeletePost(c *gin.Context) { } user, _ := c.Get("USER") - svc := service.New(c) // 获取Post - postFormated, err := svc.GetPost(param.ID) + postFormated, err := service.GetPost(param.ID) if err != nil { - global.Logger.Errorf("svc.GetPost err: %v\n", err) + global.Logger.Errorf("service.GetPost err: %v\n", err) response.ToErrorResponse(errcode.GetPostFailed) return } @@ -119,9 +115,9 @@ func DeletePost(c *gin.Context) { return } - err = svc.DeletePost(param.ID) + err = service.DeletePost(param.ID) if err != nil { - global.Logger.Errorf("svc.DeletePost err: %v\n", err) + global.Logger.Errorf("service.DeletePost err: %v\n", err) response.ToErrorResponse(errcode.DeletePostFailed) return } @@ -133,10 +129,9 @@ func GetPostStar(c *gin.Context) { postID := convert.StrTo(c.Query("id")).MustInt64() response := app.NewResponse(c) - svc := service.New(c) userID, _ := c.Get("UID") - _, err := svc.GetPostStar(postID, userID.(int64)) + _, err := service.GetPostStar(postID, userID.(int64)) if err != nil { response.ToResponse(gin.H{ "status": false, @@ -160,18 +155,17 @@ func PostStar(c *gin.Context) { return } - svc := service.New(c) userID, _ := c.Get("UID") status := false - star, err := svc.GetPostStar(param.ID, userID.(int64)) + star, err := service.GetPostStar(param.ID, userID.(int64)) if err != nil { // 创建Star - svc.CreatePostStar(param.ID, userID.(int64)) + service.CreatePostStar(param.ID, userID.(int64)) status = true } else { // 取消Star - svc.DeletePostStar(star) + service.DeletePostStar(star) } response.ToResponse(gin.H{ @@ -183,10 +177,9 @@ func GetPostCollection(c *gin.Context) { postID := convert.StrTo(c.Query("id")).MustInt64() response := app.NewResponse(c) - svc := service.New(c) userID, _ := c.Get("UID") - _, err := svc.GetPostCollection(postID, userID.(int64)) + _, err := service.GetPostCollection(postID, userID.(int64)) if err != nil { response.ToResponse(gin.H{ "status": false, @@ -210,18 +203,17 @@ func PostCollection(c *gin.Context) { return } - svc := service.New(c) userID, _ := c.Get("UID") status := false - collection, err := svc.GetPostCollection(param.ID, userID.(int64)) + collection, err := service.GetPostCollection(param.ID, userID.(int64)) if err != nil { // 创建collection - svc.CreatePostCollection(param.ID, userID.(int64)) + service.CreatePostCollection(param.ID, userID.(int64)) status = true } else { // 取消Star - svc.DeletePostCollection(collection) + service.DeletePostCollection(collection) } response.ToResponse(gin.H{ @@ -240,12 +232,11 @@ func LockPost(c *gin.Context) { } user, _ := c.Get("USER") - svc := service.New(c) // 获取Post - postFormated, err := svc.GetPost(param.ID) + postFormated, err := service.GetPost(param.ID) if err != nil { - global.Logger.Errorf("svc.GetPost err: %v\n", err) + global.Logger.Errorf("service.GetPost err: %v\n", err) response.ToErrorResponse(errcode.GetPostFailed) return } @@ -254,9 +245,9 @@ func LockPost(c *gin.Context) { response.ToErrorResponse(errcode.NoPermission) return } - err = svc.LockPost(param.ID) + err = service.LockPost(param.ID) if err != nil { - global.Logger.Errorf("svc.LockPost err: %v\n", err) + global.Logger.Errorf("service.LockPost err: %v\n", err) response.ToErrorResponse(errcode.LockPostFailed) return } @@ -277,12 +268,11 @@ func StickPost(c *gin.Context) { } user, _ := c.Get("USER") - svc := service.New(c) // 获取Post - postFormated, err := svc.GetPost(param.ID) + postFormated, err := service.GetPost(param.ID) if err != nil { - global.Logger.Errorf("svc.GetPost err: %v\n", err) + global.Logger.Errorf("service.GetPost err: %v\n", err) response.ToErrorResponse(errcode.GetPostFailed) return } @@ -291,9 +281,9 @@ func StickPost(c *gin.Context) { response.ToErrorResponse(errcode.NoPermission) return } - err = svc.StickPost(param.ID) + err = service.StickPost(param.ID) if err != nil { - global.Logger.Errorf("svc.StickPost err: %v\n", err) + global.Logger.Errorf("service.StickPost err: %v\n", err) response.ToErrorResponse(errcode.LockPostFailed) return } @@ -313,11 +303,9 @@ func GetPostTags(c *gin.Context) { return } - svc := service.New(c) - - tags, err := svc.GetPostTags(¶m) + tags, err := service.GetPostTags(¶m) if err != nil { - global.Logger.Errorf("svc.GetPostTags err: %v\n", err) + global.Logger.Errorf("service.GetPostTags err: %v\n", err) response.ToErrorResponse(errcode.GetPostTagsFailed) return diff --git a/internal/routers/api/user.go b/internal/routers/api/user.go index 92ba4b72..dfbc5c8a 100644 --- a/internal/routers/api/user.go +++ b/internal/routers/api/user.go @@ -16,7 +16,7 @@ import ( "github.com/smartwalle/alipay/v3" ) -// 用户登录 +// Login 用户登录 func Login(c *gin.Context) { param := service.AuthRequest{} response := app.NewResponse(c) @@ -27,10 +27,9 @@ func Login(c *gin.Context) { return } - svc := service.New(c) - user, err := svc.DoLogin(¶m) + user, err := service.DoLogin(c, ¶m) if err != nil { - global.Logger.Errorf("svc.DoLogin err: %v", err) + global.Logger.Errorf("service.DoLogin err: %v", err) response.ToErrorResponse(err.(*errcode.Error)) return } @@ -47,7 +46,7 @@ func Login(c *gin.Context) { }) } -// 用户注册 +// Register 用户注册 func Register(c *gin.Context) { param := service.RegisterRequest{} @@ -59,31 +58,29 @@ func Register(c *gin.Context) { return } - svc := service.New(c) - // 用户名检查 - err := svc.ValidUsername(param.Username) + err := service.ValidUsername(param.Username) if err != nil { - global.Logger.Errorf("svc.Register err: %v", err) + global.Logger.Errorf("service.Register err: %v", err) response.ToErrorResponse(err.(*errcode.Error)) return } // 密码检查 - err = svc.CheckPassword(param.Password) + err = service.CheckPassword(param.Password) if err != nil { - global.Logger.Errorf("svc.Register err: %v", err) + global.Logger.Errorf("service.Register err: %v", err) response.ToErrorResponse(err.(*errcode.Error)) return } - user, err := svc.Register( + user, err := service.Register( param.Username, param.Password, ) if err != nil { - global.Logger.Errorf("svc.Register err: %v", err) + global.Logger.Errorf("service.Register err: %v", err) response.ToErrorResponse(errcode.UserRegisterFailed) return } @@ -98,13 +95,12 @@ func Register(c *gin.Context) { func GetUserInfo(c *gin.Context) { param := service.AuthRequest{} response := app.NewResponse(c) - svc := service.New(c) if username, exists := c.Get("USERNAME"); exists { param.Username = username.(string) } - user, err := svc.GetUserInfo(¶m) + user, err := service.GetUserInfo(¶m) if err != nil { response.ToErrorResponse(errcode.UnauthorizedAuthNotExist) @@ -143,25 +139,23 @@ func ChangeUserPassword(c *gin.Context) { user = u.(*model.User) } - svc := service.New(c) - // 密码检查 - err := svc.CheckPassword(param.Password) + err := service.CheckPassword(param.Password) if err != nil { - global.Logger.Errorf("svc.Register err: %v", err) + global.Logger.Errorf("service.Register err: %v", err) response.ToErrorResponse(err.(*errcode.Error)) return } // 旧密码校验 - if !svc.ValidPassword(user.Password, param.OldPassword, user.Salt) { + if !service.ValidPassword(user.Password, param.OldPassword, user.Salt) { response.ToErrorResponse(errcode.ErrorOldPassword) return } // 更新入库 - user.Password, user.Salt = svc.EncryptPasswordAndSalt(param.Password) - svc.UpdateUserInfo(user) + user.Password, user.Salt = service.EncryptPasswordAndSalt(param.Password) + service.UpdateUserInfo(user) response.ToResponse(nil) } @@ -181,7 +175,6 @@ func ChangeNickname(c *gin.Context) { if u, exists := c.Get("USER"); exists { user = u.(*model.User) } - svc := service.New(c) if utf8.RuneCountInString(param.Nickname) < 2 || utf8.RuneCountInString(param.Nickname) > 12 { response.ToErrorResponse(errcode.NicknameLengthLimit) @@ -190,7 +183,7 @@ func ChangeNickname(c *gin.Context) { // 执行绑定 user.Nickname = param.Nickname - svc.UpdateUserInfo(user) + service.UpdateUserInfo(user) response.ToResponse(nil) } @@ -210,7 +203,6 @@ func ChangeAvatar(c *gin.Context) { if u, exists := c.Get("USER"); exists { user = u.(*model.User) } - svc := service.New(c) if strings.Index(param.Avatar, "https://"+global.AliossSetting.AliossDomain) != 0 { response.ToErrorResponse(errcode.InvalidParams) @@ -219,7 +211,7 @@ func ChangeAvatar(c *gin.Context) { // 执行绑定 user.Avatar = param.Avatar - svc.UpdateUserInfo(user) + service.UpdateUserInfo(user) response.ToResponse(nil) } @@ -239,25 +231,25 @@ func BindUserPhone(c *gin.Context) { if u, exists := c.Get("USER"); exists { user = u.(*model.User) } - svc := service.New(c) // 手机重复性检查 - if svc.CheckPhoneExist(user.ID, param.Phone) { + if service.CheckPhoneExist(user.ID, param.Phone) { response.ToErrorResponse(errcode.ExistedUserPhone) return } // 验证短信验证码 - err := svc.CheckPhoneCaptcha(param.Phone, param.Captcha) - if err != nil { - global.Logger.Errorf("svc.CheckPhoneCaptcha err: %v\n", err) - response.ToErrorResponse(err) - return + if !global.RuntimeSetting.DisablePhoneVerify { + if err := service.CheckPhoneCaptcha(param.Phone, param.Captcha); err != nil { + global.Logger.Errorf("service.CheckPhoneCaptcha err: %v\n", err) + response.ToErrorResponse(err) + return + } } // 执行绑定 user.Phone = param.Phone - svc.UpdateUserInfo(user) + service.UpdateUserInfo(user) response.ToResponse(nil) } @@ -266,10 +258,9 @@ func GetUserProfile(c *gin.Context) { response := app.NewResponse(c) username := c.Query("username") - svc := service.New(c) - user, err := svc.GetUserByUsername(username) + user, err := service.GetUserByUsername(username) if err != nil { - global.Logger.Errorf("svc.GetUserByUsername err: %v\n", err) + global.Logger.Errorf("service.GetUserByUsername err: %v\n", err) response.ToErrorResponse(errcode.NoExistUsername) return } @@ -288,10 +279,9 @@ func GetUserPosts(c *gin.Context) { response := app.NewResponse(c) username := c.Query("username") - svc := service.New(c) - user, err := svc.GetUserByUsername(username) + user, err := service.GetUserByUsername(username) if err != nil { - global.Logger.Errorf("svc.GetUserByUsername err: %v\n", err) + global.Logger.Errorf("service.GetUserByUsername err: %v\n", err) response.ToErrorResponse(errcode.NoExistUsername) return } @@ -301,17 +291,17 @@ func GetUserPosts(c *gin.Context) { "ORDER": "latest_replied_on DESC", } - posts, err := svc.GetPostList(&service.PostListReq{ + posts, err := service.GetPostList(&service.PostListReq{ Conditions: conditions, Offset: (app.GetPage(c) - 1) * app.GetPageSize(c), Limit: app.GetPageSize(c), }) if err != nil { - global.Logger.Errorf("svc.GetPostList err: %v\n", err) + global.Logger.Errorf("service.GetPostList err: %v\n", err) response.ToErrorResponse(errcode.GetPostsFailed) return } - totalRows, _ := svc.GetPostCount(conditions) + totalRows, _ := service.GetPostCount(conditions) response.ToResponseList(posts, totalRows) } @@ -320,11 +310,10 @@ func GetUserCollections(c *gin.Context) { response := app.NewResponse(c) userID, _ := c.Get("UID") - svc := service.New(c) - posts, totalRows, err := svc.GetUserCollections(userID.(int64), (app.GetPage(c)-1)*app.GetPageSize(c), app.GetPageSize(c)) + posts, totalRows, err := service.GetUserCollections(userID.(int64), (app.GetPage(c)-1)*app.GetPageSize(c), app.GetPageSize(c)) if err != nil { - global.Logger.Errorf("svc.GetUserCollections err: %v\n", err) + global.Logger.Errorf("service.GetUserCollections err: %v\n", err) response.ToErrorResponse(errcode.GetCollectionsFailed) return } @@ -336,10 +325,9 @@ func GetUserStars(c *gin.Context) { response := app.NewResponse(c) userID, _ := c.Get("UID") - svc := service.New(c) - posts, totalRows, err := svc.GetUserStars(userID.(int64), (app.GetPage(c)-1)*app.GetPageSize(c), app.GetPageSize(c)) + posts, totalRows, err := service.GetUserStars(userID.(int64), (app.GetPage(c)-1)*app.GetPageSize(c), app.GetPageSize(c)) if err != nil { - global.Logger.Errorf("svc.GetUserStars err: %v\n", err) + global.Logger.Errorf("service.GetUserStars err: %v\n", err) response.ToErrorResponse(errcode.GetCollectionsFailed) return } @@ -351,10 +339,9 @@ func GetSuggestUsers(c *gin.Context) { keyword := c.Query("k") response := app.NewResponse(c) - svc := service.New(c) - usernames, err := svc.GetSuggestUsers(keyword) + usernames, err := service.GetSuggestUsers(keyword) if err != nil { - global.Logger.Errorf("svc.GetSuggestUsers err: %v\n", err) + global.Logger.Errorf("service.GetSuggestUsers err: %v\n", err) response.ToErrorResponse(errcode.GetCollectionsFailed) return } @@ -366,10 +353,9 @@ func GetSuggestTags(c *gin.Context) { keyword := c.Query("k") response := app.NewResponse(c) - svc := service.New(c) - tags, err := svc.GetSuggestTags(keyword) + tags, err := service.GetSuggestTags(keyword) if err != nil { - global.Logger.Errorf("svc.GetSuggestTags err: %v\n", err) + global.Logger.Errorf("service.GetSuggestTags err: %v\n", err) response.ToErrorResponse(errcode.GetCollectionsFailed) return } @@ -389,10 +375,9 @@ func GetUserRechargeLink(c *gin.Context) { // 下单 userID, _ := c.Get("UID") - svc := service.New(c) - recharge, err := svc.CreateRecharge(userID.(int64), param.Amount) + recharge, err := service.CreateRecharge(userID.(int64), param.Amount) if err != nil { - global.Logger.Errorf("svc.CreateRecharge err: %v\n", err) + global.Logger.Errorf("service.CreateRecharge err: %v\n", err) response.ToErrorResponse(errcode.RechargeReqFail) return @@ -456,8 +441,7 @@ func GetUserRechargeResult(c *gin.Context) { id := c.Query("id") userID, _ := c.Get("UID") - svc := service.New(c) - recharge, err := svc.GetRechargeByID(convert.StrTo(id).MustInt64()) + recharge, err := service.GetRechargeByID(convert.StrTo(id).MustInt64()) if err != nil { response.ToErrorResponse(errcode.GetRechargeFailed) return @@ -514,15 +498,14 @@ func AlipayNotify(c *gin.Context) { return } - svc := service.New(c) id := c.Request.Form.Get("out_trade_no") tradeNo := c.Request.Form.Get("trade_no") tradeStatus := c.Request.Form.Get("trade_status") if tradeStatus == "TRADE_SUCCESS" { // 交易支付成功 - err = svc.FinishRecharge(convert.StrTo(id).MustInt64(), tradeNo) + err = service.FinishRecharge(c, convert.StrTo(id).MustInt64(), tradeNo) if err != nil { - global.Logger.Errorf("svc.FinishRecharge err: %v\n", err) + global.Logger.Errorf("service.FinishRecharge err: %v\n", err) response.ToErrorResponse(errcode.RechargeNotifyError) return } @@ -534,11 +517,10 @@ func GetUserWalletBills(c *gin.Context) { response := app.NewResponse(c) userID, _ := c.Get("UID") - svc := service.New(c) - bills, totalRows, err := svc.GetUserWalletBills(userID.(int64), (app.GetPage(c)-1)*app.GetPageSize(c), app.GetPageSize(c)) + bills, totalRows, err := service.GetUserWalletBills(userID.(int64), (app.GetPage(c)-1)*app.GetPageSize(c), app.GetPageSize(c)) if err != nil { - global.Logger.Errorf("svc.GetUserWalletBills err: %v\n", err) + global.Logger.Errorf("service.GetUserWalletBills err: %v\n", err) response.ToErrorResponse(errcode.GetCollectionsFailed) return } diff --git a/internal/routers/router.go b/internal/routers/router.go index 449f3e02..f735bc2a 100644 --- a/internal/routers/router.go +++ b/internal/routers/router.go @@ -10,16 +10,22 @@ import ( ) func NewRouter() *gin.Engine { - r := gin.New() - r.HandleMethodNotAllowed = true - r.Use(gin.Logger()) - r.Use(gin.Recovery()) + e := gin.New() + e.HandleMethodNotAllowed = true + e.Use(gin.Logger()) + e.Use(gin.Recovery()) // 跨域配置 corsConfig := cors.DefaultConfig() corsConfig.AllowAllOrigins = true corsConfig.AddAllowHeaders("Authorization") - r.Use(cors.New(corsConfig)) + e.Use(cors.New(corsConfig)) + + // 按需注册静态资源路由 + registerStatick(e) + + // v1 group api + r := e.Group("/v1") // 获取version r.GET("/", api.Version) @@ -163,18 +169,18 @@ func NewRouter() *gin.Engine { } // 默认404 - r.NoRoute(func(c *gin.Context) { + e.NoRoute(func(c *gin.Context) { c.JSON(http.StatusNotFound, gin.H{ "code": 404, "msg": "Not Found", }) }) // 默认405 - r.NoMethod(func(c *gin.Context) { + e.NoMethod(func(c *gin.Context) { c.JSON(http.StatusMethodNotAllowed, gin.H{ "code": 405, "msg": "Method Not Allowed", }) }) - return r + return e } diff --git a/internal/routers/statick.go b/internal/routers/statick.go new file mode 100644 index 00000000..c20d5059 --- /dev/null +++ b/internal/routers/statick.go @@ -0,0 +1,13 @@ +//go:build !embed +// +build !embed + +package routers + +import ( + "github.com/gin-gonic/gin" +) + +// registerStatick stub function for register static asset route +func registerStatick(e *gin.Engine) { + // empty +} diff --git a/internal/routers/statick_embed.go b/internal/routers/statick_embed.go new file mode 100644 index 00000000..2ea56171 --- /dev/null +++ b/internal/routers/statick_embed.go @@ -0,0 +1,27 @@ +//go:build embed +// +build embed + +package routers + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/rocboss/paopao-ce/web" +) + +// registerStatick register static assets route +func registerStatick(e *gin.Engine) { + routeStatic(e, "/", "/index.html", "/favicon.ico", "/assets/*filepath") +} + +func routeStatic(e *gin.Engine, paths ...string) { + staticHandler := http.FileServer(web.NewFileSystem()) + handler := func(c *gin.Context) { + staticHandler.ServeHTTP(c.Writer, c.Request) + } + for _, path := range paths { + e.GET(path, handler) + e.HEAD(path, handler) + } +} diff --git a/internal/service/attachment.go b/internal/service/attachment.go index 2f4bfc88..053b555c 100644 --- a/internal/service/attachment.go +++ b/internal/service/attachment.go @@ -2,6 +2,6 @@ package service import "github.com/rocboss/paopao-ce/internal/model" -func (svc *Service) CreateAttachment(attachment *model.Attachment) (*model.Attachment, error) { - return svc.dao.CreateAttachment(attachment) +func CreateAttachment(attachment *model.Attachment) (*model.Attachment, error) { + return myDao.CreateAttachment(attachment) } diff --git a/internal/service/avatar.go b/internal/service/avatar.go index 5c666133..3ac302e0 100644 --- a/internal/service/avatar.go +++ b/internal/service/avatar.go @@ -58,7 +58,7 @@ var defaultAvatars = []string{ "https://assets.paopao.info/public/avatar/default/abigail.png", } -func (s *Service) GetRandomAvatar() string { +func GetRandomAvatar() string { rand.Seed(time.Now().UnixMicro()) return defaultAvatars[rand.Intn(len(defaultAvatars))] } diff --git a/internal/service/comment.go b/internal/service/comment.go index 9ef87f67..88cb917c 100644 --- a/internal/service/comment.go +++ b/internal/service/comment.go @@ -4,6 +4,7 @@ import ( "strings" "time" + "github.com/gin-gonic/gin" "github.com/rocboss/paopao-ce/global" "github.com/rocboss/paopao-ce/internal/model" "github.com/rocboss/paopao-ce/pkg/errcode" @@ -28,12 +29,12 @@ type ReplyDelReq struct { ID int64 `json:"id" binding:"required"` } -func (svc *Service) GetPostComments(postID int64, sort string, offset, limit int) ([]*model.CommentFormated, int64, error) { +func GetPostComments(postID int64, sort string, offset, limit int) ([]*model.CommentFormated, int64, error) { conditions := &model.ConditionsT{ "post_id": postID, "ORDER": sort, } - comments, err := svc.dao.GetComments(conditions, offset, limit) + comments, err := myDao.GetComments(conditions, offset, limit) if err != nil { return nil, 0, err @@ -46,17 +47,17 @@ func (svc *Service) GetPostComments(postID int64, sort string, offset, limit int commentIDs = append(commentIDs, comment.ID) } - users, err := svc.dao.GetUsersByIDs(userIDs) + users, err := myDao.GetUsersByIDs(userIDs) if err != nil { return nil, 0, err } - contents, err := svc.dao.GetCommentContentsByIDs(commentIDs) + contents, err := myDao.GetCommentContentsByIDs(commentIDs) if err != nil { return nil, 0, err } - replies, err := svc.dao.GetCommentRepliesByID(commentIDs) + replies, err := myDao.GetCommentRepliesByID(commentIDs) if err != nil { return nil, 0, err } @@ -84,14 +85,14 @@ func (svc *Service) GetPostComments(postID int64, sort string, offset, limit int } // 获取总量 - totalRows, _ := svc.dao.GetCommentCount(conditions) + totalRows, _ := myDao.GetCommentCount(conditions) return commentsFormated, totalRows, nil } -func (svc *Service) CreatePostComment(userID int64, param CommentCreationReq) (*model.Comment, error) { +func CreatePostComment(ctx *gin.Context, userID int64, param CommentCreationReq) (*model.Comment, error) { // 加载Post - post, err := svc.dao.GetPostByID(param.PostID) + post, err := myDao.GetPostByID(param.PostID) if err != nil { return nil, err @@ -100,14 +101,14 @@ func (svc *Service) CreatePostComment(userID int64, param CommentCreationReq) (* if post.CommentCount >= global.AppSetting.MaxCommentCount { return nil, errcode.MaxCommentCount } - ip := svc.ctx.ClientIP() + ip := ctx.ClientIP() comment := &model.Comment{ PostID: post.ID, UserID: userID, IP: ip, IPLoc: util.GetIPLoc(ip), } - comment, err = svc.dao.CreateComment(comment) + comment, err = myDao.CreateComment(comment) if err != nil { return nil, err } @@ -127,41 +128,41 @@ func (svc *Service) CreatePostComment(userID int64, param CommentCreationReq) (* Type: item.Type, Sort: item.Sort, } - svc.dao.CreateCommentContent(postContent) + myDao.CreateCommentContent(postContent) } // 更新Post回复数 post.CommentCount++ post.LatestRepliedOn = time.Now().Unix() - svc.dao.UpdatePost(post) + myDao.UpdatePost(post) // 更新索引 - go svc.PushPostToSearch(post) + go PushPostToSearch(post) // 创建用户消息提醒 - postMaster, err := svc.dao.GetUserByID(post.UserID) + postMaster, err := myDao.GetUserByID(post.UserID) if err == nil && postMaster.ID != userID { - go svc.dao.CreateMessage(&model.Message{ + go myDao.CreateMessage(&model.Message{ SenderUserID: userID, ReceiverUserID: postMaster.ID, Type: model.MESSAGE_COMMENT, - Breif: "在泡泡中评论了你", + Brief: "在泡泡中评论了你", PostID: post.ID, CommentID: comment.ID, }) } for _, u := range param.Users { - user, err := svc.dao.GetUserByUsername(u) + user, err := myDao.GetUserByUsername(u) if err != nil || user.ID == userID || user.ID == postMaster.ID { continue } // 创建消息提醒 - go svc.dao.CreateMessage(&model.Message{ + go myDao.CreateMessage(&model.Message{ SenderUserID: userID, ReceiverUserID: user.ID, Type: model.MESSAGE_COMMENT, - Breif: "在泡泡评论中@了你", + Brief: "在泡泡评论中@了你", PostID: post.ID, CommentID: comment.ID, }) @@ -170,37 +171,38 @@ func (svc *Service) CreatePostComment(userID int64, param CommentCreationReq) (* return comment, nil } -func (svc *Service) GetPostComment(id int64) (*model.Comment, error) { - return svc.dao.GetCommentByID(id) +func GetPostComment(id int64) (*model.Comment, error) { + return myDao.GetCommentByID(id) } -func (svc *Service) DeletePostComment(comment *model.Comment) error { +func DeletePostComment(comment *model.Comment) error { // 加载post - post, err := svc.dao.GetPostByID(comment.PostID) + post, err := myDao.GetPostByID(comment.PostID) if err == nil { // 更新post回复数 post.CommentCount-- - svc.dao.UpdatePost(post) + myDao.UpdatePost(post) } - return svc.dao.DeleteComment(comment) + return myDao.DeleteComment(comment) } -func (svc *Service) CreatePostCommentReply(commentID int64, content string, userID, atUserID int64) (*model.CommentReply, error) { +func createPostPreHandler(commentID int64, userID, atUserID int64) (*model.Post, *model.Comment, int64, + error) { // 加载Comment - comment, err := svc.dao.GetCommentByID(commentID) + comment, err := myDao.GetCommentByID(commentID) if err != nil { - return nil, err + return nil, nil, atUserID, err } // 加载comment的post - post, err := svc.dao.GetPostByID(comment.PostID) + post, err := myDao.GetPostByID(comment.PostID) if err != nil { - return nil, err + return nil, nil, atUserID, err } if post.CommentCount >= global.AppSetting.MaxCommentCount { - return nil, errcode.MaxCommentCount + return nil, nil, atUserID, errcode.MaxCommentCount } if userID == atUserID { @@ -209,14 +211,27 @@ func (svc *Service) CreatePostCommentReply(commentID int64, content string, user if atUserID > 0 { // 检测目前用户是否存在 - users, _ := svc.dao.GetUsersByIDs([]int64{atUserID}) + users, _ := myDao.GetUsersByIDs([]int64{atUserID}) if len(users) == 0 { atUserID = 0 } } + return post, comment, atUserID, nil +} + +func CreatePostCommentReply(ctx *gin.Context, commentID int64, content string, userID, atUserID int64) (*model.CommentReply, error) { + var ( + post *model.Post + comment *model.Comment + err error + ) + if post, comment, atUserID, err = createPostPreHandler(commentID, userID, atUserID); err != nil { + return nil, err + } + // 创建评论 - ip := svc.ctx.ClientIP() + ip := ctx.ClientIP() reply := &model.CommentReply{ CommentID: commentID, UserID: userID, @@ -226,7 +241,7 @@ func (svc *Service) CreatePostCommentReply(commentID int64, content string, user IPLoc: util.GetIPLoc(ip), } - reply, err = svc.dao.CreateCommentReply(reply) + reply, err = myDao.CreateCommentReply(reply) if err != nil { return nil, err } @@ -234,45 +249,45 @@ func (svc *Service) CreatePostCommentReply(commentID int64, content string, user // 更新Post回复数 post.CommentCount++ post.LatestRepliedOn = time.Now().Unix() - svc.dao.UpdatePost(post) + myDao.UpdatePost(post) // 更新索引 - go svc.PushPostToSearch(post) + go PushPostToSearch(post) // 创建用户消息提醒 - commentMaster, err := svc.dao.GetUserByID(comment.UserID) + commentMaster, err := myDao.GetUserByID(comment.UserID) if err == nil && commentMaster.ID != userID { - go svc.dao.CreateMessage(&model.Message{ + go myDao.CreateMessage(&model.Message{ SenderUserID: userID, ReceiverUserID: commentMaster.ID, Type: model.MESSAGE_REPLY, - Breif: "在泡泡评论下回复了你", + Brief: "在泡泡评论下回复了你", PostID: post.ID, CommentID: comment.ID, ReplyID: reply.ID, }) } - postMaster, err := svc.dao.GetUserByID(post.UserID) + postMaster, err := myDao.GetUserByID(post.UserID) if err == nil && postMaster.ID != userID && commentMaster.ID != postMaster.ID { - go svc.dao.CreateMessage(&model.Message{ + go myDao.CreateMessage(&model.Message{ SenderUserID: userID, ReceiverUserID: postMaster.ID, Type: model.MESSAGE_REPLY, - Breif: "在泡泡评论下发布了新回复", + Brief: "在泡泡评论下发布了新回复", PostID: post.ID, CommentID: comment.ID, ReplyID: reply.ID, }) } if atUserID > 0 { - user, err := svc.dao.GetUserByID(atUserID) + user, err := myDao.GetUserByID(atUserID) if err == nil && user.ID != userID && commentMaster.ID != user.ID && postMaster.ID != user.ID { // 创建消息提醒 - go svc.dao.CreateMessage(&model.Message{ + go myDao.CreateMessage(&model.Message{ SenderUserID: userID, ReceiverUserID: user.ID, Type: model.MESSAGE_REPLY, - Breif: "在泡泡评论的回复中@了你", + Brief: "在泡泡评论的回复中@了你", PostID: post.ID, CommentID: comment.ID, ReplyID: reply.ID, @@ -283,24 +298,24 @@ func (svc *Service) CreatePostCommentReply(commentID int64, content string, user return reply, nil } -func (svc *Service) GetPostCommentReply(id int64) (*model.CommentReply, error) { - return svc.dao.GetCommentReplyByID(id) +func GetPostCommentReply(id int64) (*model.CommentReply, error) { + return myDao.GetCommentReplyByID(id) } -func (svc *Service) DeletePostCommentReply(reply *model.CommentReply) error { - err := svc.dao.DeleteCommentReply(reply) +func DeletePostCommentReply(reply *model.CommentReply) error { + err := myDao.DeleteCommentReply(reply) if err != nil { return err } // 加载Comment - comment, err := svc.dao.GetCommentByID(reply.CommentID) + comment, err := myDao.GetCommentByID(reply.CommentID) if err != nil { return err } // 加载comment的post - post, err := svc.dao.GetPostByID(comment.PostID) + post, err := myDao.GetPostByID(comment.PostID) if err != nil { return err } @@ -308,10 +323,10 @@ func (svc *Service) DeletePostCommentReply(reply *model.CommentReply) error { // 更新Post回复数 post.CommentCount-- post.LatestRepliedOn = time.Now().Unix() - svc.dao.UpdatePost(post) + myDao.UpdatePost(post) // 更新索引 - go svc.PushPostToSearch(post) + go PushPostToSearch(post) return nil } diff --git a/internal/service/message.go b/internal/service/message.go index 2c420cd7..64e0cd4a 100644 --- a/internal/service/message.go +++ b/internal/service/message.go @@ -4,13 +4,14 @@ import ( "fmt" "time" + "github.com/gin-gonic/gin" "github.com/rocboss/paopao-ce/global" "github.com/rocboss/paopao-ce/internal/model" "github.com/rocboss/paopao-ce/pkg/convert" "github.com/rocboss/paopao-ce/pkg/errcode" ) -// 当日单用户私信总数限制(TODO 配置化、积分兑换等) +// MAX_WHISPER_NUM_DAILY 当日单用户私信总数限制(TODO 配置化、积分兑换等) const MAX_WHISPER_NUM_DAILY = 20 type ReadMessageReq struct { @@ -21,37 +22,37 @@ type WhisperReq struct { Content string `json:"content" binding:"required"` } -// 创建私信 -func (svc *Service) CreateWhisper(msg *model.Message) (*model.Message, error) { +// CreateWhisper 创建私信 +func CreateWhisper(c *gin.Context, msg *model.Message) (*model.Message, error) { whisperKey := fmt.Sprintf("WhisperTimes:%d", msg.SenderUserID) // 今日频次限制 - if res, _ := global.Redis.Get(svc.ctx, whisperKey).Result(); convert.StrTo(res).MustInt() >= MAX_WHISPER_NUM_DAILY { + if res, _ := global.Redis.Get(c, whisperKey).Result(); convert.StrTo(res).MustInt() >= MAX_WHISPER_NUM_DAILY { return nil, errcode.TooManyWhisperNum } // 创建私信 - msg, err := svc.dao.CreateMessage(msg) + msg, err := myDao.CreateMessage(msg) if err != nil { return nil, err } // 写入当日(自然日)计数缓存 - global.Redis.Incr(svc.ctx, whisperKey).Result() + global.Redis.Incr(c, whisperKey).Result() currentTime := time.Now() endTime := time.Date(currentTime.Year(), currentTime.Month(), currentTime.Day(), 23, 59, 59, 0, currentTime.Location()) - global.Redis.Expire(svc.ctx, whisperKey, endTime.Sub(currentTime)) + global.Redis.Expire(c, whisperKey, endTime.Sub(currentTime)) return msg, err } -func (svc *Service) GetUnreadCount(userID int64) (int64, error) { - return svc.dao.GetUnreadCount(userID) +func GetUnreadCount(userID int64) (int64, error) { + return myDao.GetUnreadCount(userID) } -func (svc *Service) ReadMessage(id, userID int64) error { +func ReadMessage(id, userID int64) error { // 获取message - message, err := svc.dao.GetMessageByID(id) + message, err := myDao.GetMessageByID(id) if err != nil { return err } @@ -61,20 +62,20 @@ func (svc *Service) ReadMessage(id, userID int64) error { } // 已读消息 - return svc.dao.ReadMessage(message) + return myDao.ReadMessage(message) } -func (svc *Service) GetMessages(userID int64, offset, limit int) ([]*model.MessageFormated, int64, error) { +func GetMessages(userID int64, offset, limit int) ([]*model.MessageFormated, int64, error) { conditions := &model.ConditionsT{ "receiver_user_id": userID, "ORDER": "id DESC", } - messages, err := svc.dao.GetMessages(conditions, offset, limit) + messages, err := myDao.GetMessages(conditions, offset, limit) for _, mf := range messages { if mf.SenderUserID > 0 { - user, err := svc.dao.GetUserByID(mf.SenderUserID) + user, err := myDao.GetUserByID(mf.SenderUserID) if err == nil { mf.SenderUser = user.Format() } @@ -82,17 +83,17 @@ func (svc *Service) GetMessages(userID int64, offset, limit int) ([]*model.Messa } if mf.PostID > 0 { - post, err := svc.GetPost(mf.PostID) + post, err := GetPost(mf.PostID) if err == nil { mf.Post = post if mf.CommentID > 0 { - comment, err := svc.GetPostComment(mf.CommentID) + comment, err := GetPostComment(mf.CommentID) if err == nil { mf.Comment = comment if mf.ReplyID > 0 { - reply, err := svc.GetPostCommentReply(mf.ReplyID) + reply, err := GetPostCommentReply(mf.ReplyID) if err == nil { mf.Reply = reply } @@ -108,7 +109,7 @@ func (svc *Service) GetMessages(userID int64, offset, limit int) ([]*model.Messa } // 获取总量 - totalRows, _ := svc.dao.GetMessageCount(conditions) + totalRows, _ := myDao.GetMessageCount(conditions) return messages, totalRows, nil } diff --git a/internal/service/post.go b/internal/service/post.go index ea89dbc9..7554c2ed 100644 --- a/internal/service/post.go +++ b/internal/service/post.go @@ -7,6 +7,7 @@ import ( "strings" "time" + "github.com/gin-gonic/gin" "github.com/rocboss/paopao-ce/global" "github.com/rocboss/paopao-ce/internal/dao" "github.com/rocboss/paopao-ce/internal/model" @@ -39,26 +40,50 @@ type PostCreationReq struct { type PostDelReq struct { ID int64 `json:"id" binding:"required"` } + type PostLockReq struct { ID int64 `json:"id" binding:"required"` } + type PostStickReq struct { ID int64 `json:"id" binding:"required"` } + type PostStarReq struct { ID int64 `json:"id" binding:"required"` } + type PostCollectionReq struct { ID int64 `json:"id" binding:"required"` } + type PostContentItem struct { Content string `json:"content" binding:"required"` Type model.PostContentT `json:"type" binding:"required"` Sort int64 `json:"sort" binding:"required"` } -func (svc *Service) CreatePost(userID int64, param PostCreationReq) (*model.Post, error) { - ip := svc.ctx.ClientIP() +// Check 检查PostContentItem属性 +func (p *PostContentItem) Check() error { + // 检查附件是否是本站资源 + if p.Type == model.CONTENT_TYPE_IMAGE || p.Type == model.CONTENT_TYPE_VIDEO || p.Type == model. + CONTENT_TYPE_ATTACHMENT { + if strings.Index(p.Content, "https://"+global.AliossSetting.AliossDomain) != 0 { + return fmt.Errorf("附件非本站资源") + } + } + // 检查链接是否合法 + if p.Type == model.CONTENT_TYPE_LINK { + if strings.Index(p.Content, "http://") != 0 && strings.Index(p.Content, "https://") != 0 { + return fmt.Errorf("链接不合法") + } + } + + return nil +} + +func CreatePost(c *gin.Context, userID int64, param PostCreationReq) (*model.Post, error) { + ip := c.ClientIP() post := &model.Post{ UserID: userID, @@ -67,7 +92,7 @@ func (svc *Service) CreatePost(userID int64, param PostCreationReq) (*model.Post IPLoc: util.GetIPLoc(ip), AttachmentPrice: param.AttachmentPrice, } - post, err := svc.dao.CreatePost(post) + post, err := myDao.CreatePost(post) if err != nil { return nil, err } @@ -78,22 +103,13 @@ func (svc *Service) CreatePost(userID int64, param PostCreationReq) (*model.Post UserID: userID, Tag: t, } - svc.dao.CreateTag(tag) + myDao.CreateTag(tag) } for _, item := range param.Contents { - - // 检查附件是否是本站资源 - if item.Type == model.CONTENT_TYPE_IMAGE || item.Type == model.CONTENT_TYPE_VIDEO || item.Type == model.CONTENT_TYPE_ATTACHMENT { - if strings.Index(item.Content, "https://"+global.AliossSetting.AliossDomain) != 0 { - continue - } - } - // 检查链接是否合法 - if item.Type == model.CONTENT_TYPE_LINK { - if strings.Index(item.Content, "http://") != 0 && strings.Index(item.Content, "https://") != 0 { - continue - } + if err = item.Check(); err != nil { + // 属性非法 + continue } if item.Type == model.CONTENT_TYPE_ATTACHMENT && param.AttachmentPrice > 0 { @@ -107,25 +123,25 @@ func (svc *Service) CreatePost(userID int64, param PostCreationReq) (*model.Post Type: item.Type, Sort: item.Sort, } - svc.dao.CreatePostContent(postContent) + myDao.CreatePostContent(postContent) } // 推送Search - go svc.PushPostToSearch(post) + go PushPostToSearch(post) // 创建用户消息提醒 for _, u := range param.Users { - user, err := svc.dao.GetUserByUsername(u) + user, err := myDao.GetUserByUsername(u) if err != nil || user.ID == userID { continue } // 创建消息提醒 - go svc.dao.CreateMessage(&model.Message{ + go myDao.CreateMessage(&model.Message{ SenderUserID: userID, ReceiverUserID: user.ID, Type: model.MESSAGE_POST, - Breif: "在新发布的泡泡动态中@了你", + Brief: "在新发布的泡泡动态中@了你", PostID: post.ID, }) } @@ -133,8 +149,8 @@ func (svc *Service) CreatePost(userID int64, param PostCreationReq) (*model.Post return post, nil } -func (svc *Service) DeletePost(id int64) error { - post, _ := svc.dao.GetPostByID(id) +func DeletePost(id int64) error { + post, _ := myDao.GetPostByID(id) // tag删除 tags := strings.Split(post.Tags, ",") @@ -143,25 +159,25 @@ func (svc *Service) DeletePost(id int64) error { tag := &model.Tag{ Tag: t, } - svc.dao.DeleteTag(tag) + myDao.DeleteTag(tag) } - err := svc.dao.DeletePost(post) + err := myDao.DeletePost(post) if err != nil { return err } // 删除索引 - go svc.DeleteSearchPost(post) + go DeleteSearchPost(post) return nil } -func (svc *Service) LockPost(id int64) error { - post, _ := svc.dao.GetPostByID(id) +func LockPost(id int64) error { + post, _ := myDao.GetPostByID(id) - err := svc.dao.LockPost(post) + err := myDao.LockPost(post) if err != nil { return err @@ -170,10 +186,10 @@ func (svc *Service) LockPost(id int64) error { return nil } -func (svc *Service) StickPost(id int64) error { - post, _ := svc.dao.GetPostByID(id) +func StickPost(id int64) error { + post, _ := myDao.GetPostByID(id) - err := svc.dao.StickPost(post) + err := myDao.StickPost(post) if err != nil { return err @@ -182,109 +198,109 @@ func (svc *Service) StickPost(id int64) error { return nil } -func (svc *Service) GetPostStar(postID, userID int64) (*model.PostStar, error) { - return svc.dao.GetUserPostStar(postID, userID) +func GetPostStar(postID, userID int64) (*model.PostStar, error) { + return myDao.GetUserPostStar(postID, userID) } -func (svc *Service) CreatePostStar(postID, userID int64) (*model.PostStar, error) { +func CreatePostStar(postID, userID int64) (*model.PostStar, error) { // 加载Post - post, err := svc.dao.GetPostByID(postID) + post, err := myDao.GetPostByID(postID) if err != nil { return nil, err } - star, err := svc.dao.CreatePostStar(postID, userID) + star, err := myDao.CreatePostStar(postID, userID) if err != nil { return nil, err } // 更新Post点赞数 post.UpvoteCount++ - svc.dao.UpdatePost(post) + myDao.UpdatePost(post) // 更新索引 - go svc.PushPostToSearch(post) + go PushPostToSearch(post) return star, nil } -func (svc *Service) DeletePostStar(star *model.PostStar) error { - err := svc.dao.DeletePostStar(star) +func DeletePostStar(star *model.PostStar) error { + err := myDao.DeletePostStar(star) if err != nil { return err } // 加载Post - post, err := svc.dao.GetPostByID(star.PostID) + post, err := myDao.GetPostByID(star.PostID) if err != nil { return err } // 更新Post点赞数 post.UpvoteCount-- - svc.dao.UpdatePost(post) + myDao.UpdatePost(post) // 更新索引 - go svc.PushPostToSearch(post) + go PushPostToSearch(post) return nil } -func (svc *Service) GetPostCollection(postID, userID int64) (*model.PostCollection, error) { - return svc.dao.GetUserPostCollection(postID, userID) +func GetPostCollection(postID, userID int64) (*model.PostCollection, error) { + return myDao.GetUserPostCollection(postID, userID) } -func (svc *Service) CreatePostCollection(postID, userID int64) (*model.PostCollection, error) { +func CreatePostCollection(postID, userID int64) (*model.PostCollection, error) { // 加载Post - post, err := svc.dao.GetPostByID(postID) + post, err := myDao.GetPostByID(postID) if err != nil { return nil, err } - collection, err := svc.dao.CreatePostCollection(postID, userID) + collection, err := myDao.CreatePostCollection(postID, userID) if err != nil { return nil, err } // 更新Post点赞数 post.CollectionCount++ - svc.dao.UpdatePost(post) + myDao.UpdatePost(post) // 更新索引 - go svc.PushPostToSearch(post) + go PushPostToSearch(post) return collection, nil } -func (svc *Service) DeletePostCollection(collection *model.PostCollection) error { - err := svc.dao.DeletePostCollection(collection) +func DeletePostCollection(collection *model.PostCollection) error { + err := myDao.DeletePostCollection(collection) if err != nil { return err } // 加载Post - post, err := svc.dao.GetPostByID(collection.PostID) + post, err := myDao.GetPostByID(collection.PostID) if err != nil { return err } // 更新Post点赞数 post.CollectionCount-- - svc.dao.UpdatePost(post) + myDao.UpdatePost(post) // 更新索引 - go svc.PushPostToSearch(post) + go PushPostToSearch(post) return nil } -func (svc *Service) GetPost(id int64) (*model.PostFormated, error) { - post, err := svc.dao.GetPostByID(id) +func GetPost(id int64) (*model.PostFormated, error) { + post, err := myDao.GetPostByID(id) if err != nil { return nil, err } - postContents, err := svc.dao.GetPostContentsByIDs([]int64{post.ID}) + postContents, err := myDao.GetPostContentsByIDs([]int64{post.ID}) if err != nil { return nil, err } - users, err := svc.dao.GetUsersByIDs([]int64{post.UserID}) + users, err := myDao.GetUsersByIDs([]int64{post.UserID}) if err != nil { return nil, err } @@ -302,21 +318,21 @@ func (svc *Service) GetPost(id int64) (*model.PostFormated, error) { return postFormated, nil } -func (svc *Service) GetPostContentByID(id int64) (*model.PostContent, error) { - return svc.dao.GetPostContentByID(id) +func GetPostContentByID(id int64) (*model.PostContent, error) { + return myDao.GetPostContentByID(id) } -func (svc *Service) GetPostList(req *PostListReq) ([]*model.PostFormated, error) { - posts, err := svc.dao.GetPosts(req.Conditions, req.Offset, req.Limit) +func GetPostList(req *PostListReq) ([]*model.PostFormated, error) { + posts, err := myDao.GetPosts(req.Conditions, req.Offset, req.Limit) if err != nil { return nil, err } - return svc.FormatPosts(posts) + return FormatPosts(posts) } -func (svc *Service) FormatPosts(posts []*model.Post) ([]*model.PostFormated, error) { +func FormatPosts(posts []*model.Post) ([]*model.PostFormated, error) { postIds := []int64{} userIds := []int64{} for _, post := range posts { @@ -324,12 +340,12 @@ func (svc *Service) FormatPosts(posts []*model.Post) ([]*model.PostFormated, err userIds = append(userIds, post.UserID) } - postContents, err := svc.dao.GetPostContentsByIDs(postIds) + postContents, err := myDao.GetPostContentsByIDs(postIds) if err != nil { return nil, err } - users, err := svc.dao.GetUsersByIDs(userIds) + users, err := myDao.GetUsersByIDs(userIds) if err != nil { return nil, err } @@ -356,17 +372,17 @@ func (svc *Service) FormatPosts(posts []*model.Post) ([]*model.PostFormated, err return postsFormated, nil } -func (svc *Service) GetPostCount(conditions *model.ConditionsT) (int64, error) { - return svc.dao.GetPostCount(conditions) +func GetPostCount(conditions *model.ConditionsT) (int64, error) { + return myDao.GetPostCount(conditions) } -func (svc *Service) GetPostListFromSearch(q *dao.QueryT, offset, limit int) ([]*model.PostFormated, int64, error) { - queryResult, err := svc.dao.QueryAll(q, global.SearchSetting.ZincIndex, offset, limit) +func GetPostListFromSearch(q *dao.QueryT, offset, limit int) ([]*model.PostFormated, int64, error) { + queryResult, err := myDao.QueryAll(q, global.SearchSetting.ZincIndex, offset, limit) if err != nil { return nil, 0, err } - posts, err := svc.FormatZincPost(queryResult) + posts, err := FormatZincPost(queryResult) if err != nil { return nil, 0, err } @@ -374,13 +390,13 @@ func (svc *Service) GetPostListFromSearch(q *dao.QueryT, offset, limit int) ([]* return posts, queryResult.Hits.Total.Value, nil } -func (svc *Service) GetPostListFromSearchByQuery(query string, offset, limit int) ([]*model.PostFormated, int64, error) { - queryResult, err := svc.dao.QuerySearch(global.SearchSetting.ZincIndex, query, offset, limit) +func GetPostListFromSearchByQuery(query string, offset, limit int) ([]*model.PostFormated, int64, error) { + queryResult, err := myDao.QuerySearch(global.SearchSetting.ZincIndex, query, offset, limit) if err != nil { return nil, 0, err } - posts, err := svc.FormatZincPost(queryResult) + posts, err := FormatZincPost(queryResult) if err != nil { return nil, 0, err } @@ -388,14 +404,14 @@ func (svc *Service) GetPostListFromSearchByQuery(query string, offset, limit int return posts, queryResult.Hits.Total.Value, nil } -func (svc *Service) PushPostToSearch(post *model.Post) { +func PushPostToSearch(post *model.Post) { indexName := global.SearchSetting.ZincIndex postFormated := post.Format() postFormated.User = &model.UserFormated{ ID: post.UserID, } - contents, _ := svc.dao.GetPostContentsByIDs([]int64{post.ID}) + contents, _ := myDao.GetPostContentsByIDs([]int64{post.ID}) for _, content := range contents { postFormated.Contents = append(postFormated.Contents, content.Format()) } @@ -436,31 +452,31 @@ func (svc *Service) PushPostToSearch(post *model.Post) { "modified_on": post.ModifiedOn, }) - svc.dao.BulkPushDoc(data) + myDao.BulkPushDoc(data) } -func (svc *Service) DeleteSearchPost(post *model.Post) error { +func DeleteSearchPost(post *model.Post) error { indexName := global.SearchSetting.ZincIndex - return svc.dao.DelDoc(indexName, fmt.Sprintf("%d", post.ID)) + return myDao.DelDoc(indexName, fmt.Sprintf("%d", post.ID)) } -func (svc *Service) PushPostsToSearch() { - if ok, _ := global.Redis.SetNX(svc.ctx, "JOB_PUSH_TO_SEARCH", 1, time.Hour).Result(); ok { +func PushPostsToSearch(c *gin.Context) { + if ok, _ := global.Redis.SetNX(c, "JOB_PUSH_TO_SEARCH", 1, time.Hour).Result(); ok { splitNum := 1000 - totalRows, _ := svc.GetPostCount(&model.ConditionsT{}) + totalRows, _ := GetPostCount(&model.ConditionsT{}) pages := math.Ceil(float64(totalRows) / float64(splitNum)) nums := int(pages) indexName := global.SearchSetting.ZincIndex // 创建索引 - svc.dao.CreateSearchIndex(indexName) + myDao.CreateSearchIndex(indexName) for i := 0; i < nums; i++ { data := []map[string]interface{}{} - posts, _ := svc.GetPostList(&PostListReq{ + posts, _ := GetPostList(&PostListReq{ Conditions: &model.ConditionsT{}, Offset: i * splitNum, Limit: splitNum, @@ -499,15 +515,15 @@ func (svc *Service) PushPostsToSearch() { } if len(data) > 0 { - svc.dao.BulkPushDoc(data) + myDao.BulkPushDoc(data) } } - global.Redis.Del(svc.ctx, "JOB_PUSH_TO_SEARCH") + global.Redis.Del(c, "JOB_PUSH_TO_SEARCH") } } -func (svc *Service) FormatZincPost(queryResult *zinc.QueryResultT) ([]*model.PostFormated, error) { +func FormatZincPost(queryResult *zinc.QueryResultT) ([]*model.PostFormated, error) { posts := []*model.PostFormated{} for _, hit := range queryResult.Hits.Hits { item := &model.PostFormated{} @@ -525,12 +541,12 @@ func (svc *Service) FormatZincPost(queryResult *zinc.QueryResultT) ([]*model.Pos postIds = append(postIds, post.ID) userIds = append(userIds, post.UserID) } - postContents, err := svc.dao.GetPostContentsByIDs(postIds) + postContents, err := myDao.GetPostContentsByIDs(postIds) if err != nil { return nil, err } - users, err := svc.dao.GetUsersByIDs(userIds) + users, err := myDao.GetUsersByIDs(userIds) if err != nil { return nil, err } @@ -555,7 +571,7 @@ func (svc *Service) FormatZincPost(queryResult *zinc.QueryResultT) ([]*model.Pos return posts, nil } -func (svc *Service) GetPostTags(param *PostTagsReq) ([]*model.TagFormated, error) { +func GetPostTags(param *PostTagsReq) ([]*model.TagFormated, error) { num := param.Num if num > global.AppSetting.MaxPageSize { num = global.AppSetting.MaxPageSize @@ -575,7 +591,7 @@ func (svc *Service) GetPostTags(param *PostTagsReq) ([]*model.TagFormated, error } } - tags, err := svc.dao.GetTags(conditions, 0, num) + tags, err := myDao.GetTags(conditions, 0, num) if err != nil { return nil, err } @@ -586,7 +602,7 @@ func (svc *Service) GetPostTags(param *PostTagsReq) ([]*model.TagFormated, error userIds = append(userIds, tag.UserID) } - users, _ := svc.dao.GetUsersByIDs(userIds) + users, _ := myDao.GetUsersByIDs(userIds) tagsFormated := []*model.TagFormated{} for _, tag := range tags { @@ -602,8 +618,8 @@ func (svc *Service) GetPostTags(param *PostTagsReq) ([]*model.TagFormated, error return tagsFormated, nil } -func (svc *Service) CheckPostAttachmentIsPaid(postID, userID int64) bool { - bill, err := svc.dao.GetPostAttatchmentBill(postID, userID) +func CheckPostAttachmentIsPaid(postID, userID int64) bool { + bill, err := myDao.GetPostAttatchmentBill(postID, userID) return err == nil && bill.Model != nil && bill.ID > 0 } diff --git a/internal/service/service.go b/internal/service/service.go index e0b3db36..d9b91b40 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -1,26 +1,15 @@ package service import ( - "github.com/gin-gonic/gin" - "github.com/rocboss/paopao-ce/global" "github.com/rocboss/paopao-ce/internal/dao" "github.com/rocboss/paopao-ce/pkg/zinc" + "gorm.io/gorm" ) -type Service struct { - ctx *gin.Context - dao *dao.Dao -} - -func New(ctx *gin.Context) Service { - svc := Service{ctx: ctx} - svc.dao = dao.New(global.DBEngine, &zinc.ZincClient{ - ZincClientConfig: &zinc.ZincClientConfig{ - ZincHost: global.SearchSetting.ZincHost, - ZincUser: global.SearchSetting.ZincUser, - ZincPassword: global.SearchSetting.ZincPassword, - }, - }) +var ( + myDao *dao.Dao +) - return svc +func Initialize(engine *gorm.DB, client *zinc.ZincClient) { + myDao = dao.New(engine, client) } diff --git a/internal/service/sign.go b/internal/service/sign.go index 53aa58e4..6d5ef510 100644 --- a/internal/service/sign.go +++ b/internal/service/sign.go @@ -8,7 +8,7 @@ import ( "github.com/rocboss/paopao-ce/pkg/util" ) -func (svc *Service) GetParamSign(param map[string]interface{}, secretKey string) string { +func GetParamSign(param map[string]interface{}, secretKey string) string { signRaw := "" rawStrs := []string{} diff --git a/internal/service/user.go b/internal/service/user.go index 5cdbc208..e055d0f4 100644 --- a/internal/service/user.go +++ b/internal/service/user.go @@ -7,6 +7,7 @@ import ( "time" "unicode/utf8" + "github.com/gin-gonic/gin" "github.com/gofrs/uuid" "github.com/rocboss/paopao-ce/global" "github.com/rocboss/paopao-ce/internal/model" @@ -32,6 +33,7 @@ type AuthRequest struct { Username string `json:"username" form:"username" binding:"required"` Password string `json:"password" form:"password" binding:"required"` } + type RegisterRequest struct { Username string `json:"username" form:"username" binding:"required"` Password string `json:"password" form:"password" binding:"required"` @@ -41,9 +43,11 @@ type ChangePasswordReq struct { Password string `json:"password" form:"password" binding:"required"` OldPassword string `json:"old_password" form:"old_password" binding:"required"` } + type ChangeNicknameReq struct { Nickname string `json:"nickname" form:"nickname" binding:"required"` } + type ChangeAvatarReq struct { Avatar string `json:"avatar" form:"avatar" binding:"required"` } @@ -51,36 +55,36 @@ type ChangeAvatarReq struct { const LOGIN_ERR_KEY = "PaoPaoUserLoginErr" const MAX_LOGIN_ERR_TIMES = 10 -// 用户认证 -func (svc *Service) DoLogin(param *AuthRequest) (*model.User, error) { - user, err := svc.dao.GetUserByUsername(param.Username) +// DoLogin 用户认证 +func DoLogin(ctx *gin.Context, param *AuthRequest) (*model.User, error) { + user, err := myDao.GetUserByUsername(param.Username) if err != nil { return nil, errcode.UnauthorizedAuthNotExist } if user.Model != nil && user.ID > 0 { - if errTimes, err := global.Redis.Get(svc.ctx, fmt.Sprintf("%s:%d", LOGIN_ERR_KEY, user.ID)).Result(); err == nil { + if errTimes, err := global.Redis.Get(ctx, fmt.Sprintf("%s:%d", LOGIN_ERR_KEY, user.ID)).Result(); err == nil { if convert.StrTo(errTimes).MustInt() >= MAX_LOGIN_ERR_TIMES { return nil, errcode.TooManyLoginError } } // 对比密码是否正确 - if svc.ValidPassword(user.Password, param.Password, user.Salt) { + if ValidPassword(user.Password, param.Password, user.Salt) { if user.Status == model.UserStatusClosed { return nil, errcode.UserHasBeenBanned } // 清空登录计数 - global.Redis.Del(svc.ctx, fmt.Sprintf("%s:%d", LOGIN_ERR_KEY, user.ID)) + global.Redis.Del(ctx, fmt.Sprintf("%s:%d", LOGIN_ERR_KEY, user.ID)) return user, nil } // 登录错误计数 - _, err = global.Redis.Incr(svc.ctx, fmt.Sprintf("%s:%d", LOGIN_ERR_KEY, user.ID)).Result() + _, err = global.Redis.Incr(ctx, fmt.Sprintf("%s:%d", LOGIN_ERR_KEY, user.ID)).Result() if err == nil { - global.Redis.Expire(svc.ctx, fmt.Sprintf("%s:%d", LOGIN_ERR_KEY, user.ID), time.Hour).Result() + global.Redis.Expire(ctx, fmt.Sprintf("%s:%d", LOGIN_ERR_KEY, user.ID), time.Hour).Result() } return nil, errcode.UnauthorizedAuthFailed @@ -89,18 +93,18 @@ func (svc *Service) DoLogin(param *AuthRequest) (*model.User, error) { return nil, errcode.UnauthorizedAuthNotExist } -// 检查密码是否一致 -func (svc *Service) ValidPassword(dbPassword, password, salt string) bool { +// ValidPassword 检查密码是否一致 +func ValidPassword(dbPassword, password, salt string) bool { return strings.Compare(dbPassword, util.EncodeMD5(util.EncodeMD5(password)+salt)) == 0 } -// 检测用户权限 -func (svc *Service) CheckStatus(user *model.User) bool { +// CheckStatus 检测用户权限 +func CheckStatus(user *model.User) bool { return user.Status == model.UserStatusNormal } -// 验证用户 -func (svc *Service) ValidUsername(username string) error { +// ValidUsername 验证用户 +func ValidUsername(username string) error { // 检测用户是否合规 if utf8.RuneCountInString(username) < 3 || utf8.RuneCountInString(username) > 12 { return errcode.UsernameLengthLimit @@ -111,7 +115,7 @@ func (svc *Service) ValidUsername(username string) error { } // 重复检查 - user, _ := svc.dao.GetUserByUsername(username) + user, _ := myDao.GetUserByUsername(username) if user.Model != nil && user.ID > 0 { return errcode.UsernameHasExisted @@ -120,8 +124,8 @@ func (svc *Service) ValidUsername(username string) error { return nil } -// 密码检查 -func (svc *Service) CheckPassword(password string) error { +// CheckPassword 密码检查 +func CheckPassword(password string) error { // 检测用户是否合规 if utf8.RuneCountInString(password) < 6 || utf8.RuneCountInString(password) > 16 { return errcode.PasswordLengthLimit @@ -130,9 +134,9 @@ func (svc *Service) CheckPassword(password string) error { return nil } -// 验证手机验证码 -func (svc *Service) CheckPhoneCaptcha(phone, captcha string) *errcode.Error { - c, err := svc.dao.GetLatestPhoneCaptcha(phone) +// CheckPhoneCaptcha 验证手机验证码 +func CheckPhoneCaptcha(phone, captcha string) *errcode.Error { + c, err := myDao.GetLatestPhoneCaptcha(phone) if err != nil { return errcode.ErrorPhoneCaptcha } @@ -150,14 +154,14 @@ func (svc *Service) CheckPhoneCaptcha(phone, captcha string) *errcode.Error { } // 更新检测次数 - svc.dao.UsePhoneCaptcha(c) + myDao.UsePhoneCaptcha(c) return nil } -// 检测手机号是否存在 -func (svc *Service) CheckPhoneExist(uid int64, phone string) bool { - u, err := svc.dao.GetUserByPhone(phone) +// CheckPhoneExist 检测手机号是否存在 +func CheckPhoneExist(uid int64, phone string) bool { + u, err := myDao.GetUserByPhone(phone) if err != nil { return false } @@ -173,28 +177,28 @@ func (svc *Service) CheckPhoneExist(uid int64, phone string) bool { return true } -// 密码加密&生成salt -func (svc *Service) EncryptPasswordAndSalt(password string) (string, string) { +// EncryptPasswordAndSalt 密码加密&生成salt +func EncryptPasswordAndSalt(password string) (string, string) { salt := uuid.Must(uuid.NewV4()).String()[:8] password = util.EncodeMD5(util.EncodeMD5(password) + salt) return password, salt } -// 用户注册 -func (svc *Service) Register(username, password string) (*model.User, error) { - password, salt := svc.EncryptPasswordAndSalt(password) +// Register 用户注册 +func Register(username, password string) (*model.User, error) { + password, salt := EncryptPasswordAndSalt(password) user := &model.User{ Nickname: username, Username: username, Password: password, - Avatar: svc.GetRandomAvatar(), + Avatar: GetRandomAvatar(), Salt: salt, Status: model.UserStatusNormal, } - user, err := svc.dao.CreateUser(user) + user, err := myDao.CreateUser(user) if err != nil { return nil, err } @@ -202,9 +206,9 @@ func (svc *Service) Register(username, password string) (*model.User, error) { return user, nil } -// 获取用户信息 -func (svc *Service) GetUserInfo(param *AuthRequest) (*model.User, error) { - user, err := svc.dao.GetUserByUsername(param.Username) +// GetUserInfo 获取用户信息 +func GetUserInfo(param *AuthRequest) (*model.User, error) { + user, err := myDao.GetUserByUsername(param.Username) if err != nil { return nil, err @@ -217,8 +221,8 @@ func (svc *Service) GetUserInfo(param *AuthRequest) (*model.User, error) { return nil, errcode.UnauthorizedAuthNotExist } -func (svc *Service) GetUserByUsername(username string) (*model.User, error) { - user, err := svc.dao.GetUserByUsername(username) +func GetUserByUsername(username string) (*model.User, error) { + user, err := myDao.GetUserByUsername(username) if err != nil { return nil, err @@ -231,18 +235,18 @@ func (svc *Service) GetUserByUsername(username string) (*model.User, error) { return nil, errcode.NoExistUsername } -// 更新用户信息 -func (svc *Service) UpdateUserInfo(user *model.User) error { - return svc.dao.UpdateUser(user) +// UpdateUserInfo 更新用户信息 +func UpdateUserInfo(user *model.User) error { + return myDao.UpdateUser(user) } -// 获取用户收藏列表 -func (svc *Service) GetUserCollections(userID int64, offset, limit int) ([]*model.PostFormated, int64, error) { - collections, err := svc.dao.GetUserPostCollections(userID, offset, limit) +// GetUserCollections 获取用户收藏列表 +func GetUserCollections(userID int64, offset, limit int) ([]*model.PostFormated, int64, error) { + collections, err := myDao.GetUserPostCollections(userID, offset, limit) if err != nil { return nil, 0, err } - totalRows, err := svc.dao.GetUserPostCollectionCount(userID) + totalRows, err := myDao.GetUserPostCollectionCount(userID) if err != nil { return nil, 0, err } @@ -252,7 +256,7 @@ func (svc *Service) GetUserCollections(userID int64, offset, limit int) ([]*mode } // 获取Posts - posts, err := svc.dao.GetPosts(&model.ConditionsT{ + posts, err := myDao.GetPosts(&model.ConditionsT{ "id IN ?": postIDs, "ORDER": "id DESC", }, 0, 0) @@ -260,7 +264,7 @@ func (svc *Service) GetUserCollections(userID int64, offset, limit int) ([]*mode return nil, 0, err } - postsFormated, err := svc.FormatPosts(posts) + postsFormated, err := FormatPosts(posts) if err != nil { return nil, 0, err } @@ -268,13 +272,13 @@ func (svc *Service) GetUserCollections(userID int64, offset, limit int) ([]*mode return postsFormated, totalRows, nil } -// 获取用户点赞列表 -func (svc *Service) GetUserStars(userID int64, offset, limit int) ([]*model.PostFormated, int64, error) { - stars, err := svc.dao.GetUserPostStars(userID, offset, limit) +// GetUserStars 获取用户点赞列表 +func GetUserStars(userID int64, offset, limit int) ([]*model.PostFormated, int64, error) { + stars, err := myDao.GetUserPostStars(userID, offset, limit) if err != nil { return nil, 0, err } - totalRows, err := svc.dao.GetUserPostStarCount(userID) + totalRows, err := myDao.GetUserPostStarCount(userID) if err != nil { return nil, 0, err } @@ -284,7 +288,7 @@ func (svc *Service) GetUserStars(userID int64, offset, limit int) ([]*model.Post } // 获取Posts - posts, err := svc.dao.GetPosts(&model.ConditionsT{ + posts, err := myDao.GetPosts(&model.ConditionsT{ "id IN ?": postIDs, "ORDER": "id DESC", }, 0, 0) @@ -292,7 +296,7 @@ func (svc *Service) GetUserStars(userID int64, offset, limit int) ([]*model.Post return nil, 0, err } - postsFormated, err := svc.FormatPosts(posts) + postsFormated, err := FormatPosts(posts) if err != nil { return nil, 0, err } @@ -300,13 +304,13 @@ func (svc *Service) GetUserStars(userID int64, offset, limit int) ([]*model.Post return postsFormated, totalRows, nil } -// 获取用户账单列表 -func (svc *Service) GetUserWalletBills(userID int64, offset, limit int) ([]*model.WalletStatement, int64, error) { - bills, err := svc.dao.GetUserWalletBills(userID, offset, limit) +// GetUserWalletBills 获取用户账单列表 +func GetUserWalletBills(userID int64, offset, limit int) ([]*model.WalletStatement, int64, error) { + bills, err := myDao.GetUserWalletBills(userID, offset, limit) if err != nil { return nil, 0, err } - totalRows, err := svc.dao.GetUserWalletBillCount(userID) + totalRows, err := myDao.GetUserWalletBillCount(userID) if err != nil { return nil, 0, err } @@ -314,28 +318,28 @@ func (svc *Service) GetUserWalletBills(userID int64, offset, limit int) ([]*mode return bills, totalRows, nil } -// 发送短信验证码 -func (svc *Service) SendPhoneCaptcha(phone string) error { +// SendPhoneCaptcha 发送短信验证码 +func SendPhoneCaptcha(ctx *gin.Context, phone string) error { - err := svc.dao.SendPhoneCaptcha(phone) + err := myDao.SendPhoneCaptcha(phone) if err != nil { return err } // 写入计数缓存 - global.Redis.Incr(svc.ctx, "PaoPaoSmsCaptcha:"+phone).Result() + global.Redis.Incr(ctx, "PaoPaoSmsCaptcha:"+phone).Result() currentTime := time.Now() endTime := time.Date(currentTime.Year(), currentTime.Month(), currentTime.Day(), 23, 59, 59, 0, currentTime.Location()) - global.Redis.Expire(svc.ctx, "PaoPaoSmsCaptcha:"+phone, endTime.Sub(currentTime)) + global.Redis.Expire(ctx, "PaoPaoSmsCaptcha:"+phone, endTime.Sub(currentTime)) return nil } -// 根据关键词获取用户推荐 -func (svc *Service) GetSuggestUsers(keyword string) ([]string, error) { - users, err := svc.dao.GetUsersByKeyword(keyword) +// GetSuggestUsers 根据关键词获取用户推荐 +func GetSuggestUsers(keyword string) ([]string, error) { + users, err := myDao.GetUsersByKeyword(keyword) if err != nil { return nil, err } @@ -348,9 +352,9 @@ func (svc *Service) GetSuggestUsers(keyword string) ([]string, error) { return usernames, nil } -// 根据关键词获取标签推荐 -func (svc *Service) GetSuggestTags(keyword string) ([]string, error) { - tags, err := svc.dao.GetTagsByKeyword(keyword) +// GetSuggestTags 根据关键词获取标签推荐 +func GetSuggestTags(keyword string) ([]string, error) { + tags, err := myDao.GetTagsByKeyword(keyword) if err != nil { return nil, err } diff --git a/internal/service/wallet.go b/internal/service/wallet.go index 60526937..8c5da819 100644 --- a/internal/service/wallet.go +++ b/internal/service/wallet.go @@ -3,6 +3,7 @@ package service import ( "time" + "github.com/gin-gonic/gin" "github.com/rocboss/paopao-ce/global" "github.com/rocboss/paopao-ce/internal/model" "github.com/rocboss/paopao-ce/pkg/errcode" @@ -12,17 +13,17 @@ type RechargeReq struct { Amount int64 `json:"amount" form:"amount" binding:"required"` } -func (svc *Service) GetRechargeByID(id int64) (*model.WalletRecharge, error) { - return svc.dao.GetRechargeByID(id) +func GetRechargeByID(id int64) (*model.WalletRecharge, error) { + return myDao.GetRechargeByID(id) } -func (svc *Service) CreateRecharge(userID, amount int64) (*model.WalletRecharge, error) { - return svc.dao.CreateRecharge(userID, amount) +func CreateRecharge(userID, amount int64) (*model.WalletRecharge, error) { + return myDao.CreateRecharge(userID, amount) } -func (svc *Service) FinishRecharge(id int64, tradeNo string) error { - if ok, _ := global.Redis.SetNX(svc.ctx, "PaoPaoRecharge:"+tradeNo, 1, time.Second*5).Result(); ok { - recharge, err := svc.dao.GetRechargeByID(id) +func FinishRecharge(ctx *gin.Context, id int64, tradeNo string) error { + if ok, _ := global.Redis.SetNX(ctx, "PaoPaoRecharge:"+tradeNo, 1, time.Second*5).Result(); ok { + recharge, err := myDao.GetRechargeByID(id) if err != nil { return err } @@ -30,8 +31,8 @@ func (svc *Service) FinishRecharge(id int64, tradeNo string) error { if recharge.TradeStatus != "TRADE_SUCCESS" { // 标记为已付款 - err := svc.dao.HandleRechargeSuccess(recharge, tradeNo) - defer global.Redis.Del(svc.ctx, "PaoPaoRecharge:"+tradeNo) + err := myDao.HandleRechargeSuccess(recharge, tradeNo) + defer global.Redis.Del(ctx, "PaoPaoRecharge:"+tradeNo) if err != nil { return err @@ -43,11 +44,11 @@ func (svc *Service) FinishRecharge(id int64, tradeNo string) error { return nil } -func (svc *Service) BuyPostAttachment(post *model.Post, user *model.User) error { +func BuyPostAttachment(post *model.Post, user *model.User) error { if user.Balance < post.AttachmentPrice { return errcode.InsuffientDownloadMoney } // 执行购买 - return svc.dao.HandlePostAttachmentBought(post, user) + return myDao.HandlePostAttachmentBought(post, user) } diff --git a/main.go b/main.go index 3bf3441f..8aa7adf9 100644 --- a/main.go +++ b/main.go @@ -4,9 +4,15 @@ import ( "fmt" "net/http" + "github.com/fatih/color" "github.com/gin-gonic/gin" "github.com/rocboss/paopao-ce/global" "github.com/rocboss/paopao-ce/internal/routers" + "github.com/rocboss/paopao-ce/pkg/util" +) + +var ( + version, buildDate, commitID string ) func main() { @@ -21,6 +27,9 @@ func main() { MaxHeaderBytes: 1 << 20, } - fmt.Printf("\x1b[36m%s\x1b[0m\n", "PaoPao service listen on http://"+global.ServerSetting.HttpIp+":"+global.ServerSetting.HttpPort) + util.PrintHelloBanner(fmt.Sprintf("paopao %s (build:%s %s)", version, commitID, buildDate)) + fmt.Fprintf(color.Output, "PaoPao service listen on %s\n", + color.GreenString(fmt.Sprintf("http://%s:%s", global.ServerSetting.HttpIp, global.ServerSetting.HttpPort)), + ) s.ListenAndServe() } diff --git a/paopao.sql b/paopao.sql index c134f55d..d78496d5 100644 --- a/paopao.sql +++ b/paopao.sql @@ -111,7 +111,7 @@ CREATE TABLE `p_message` ( `sender_user_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '发送方用户ID', `receiver_user_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '接收方用户ID', `type` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '通知类型,1动态,2评论,3回复,4私信,99系统通知', - `breif` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '摘要说明', + `brief` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '摘要说明', `content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '详细内容', `post_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '动态ID', `comment_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '评论ID', diff --git a/pkg/setting/settting.go b/pkg/setting/settting.go index 216ada5a..bb22198b 100644 --- a/pkg/setting/settting.go +++ b/pkg/setting/settting.go @@ -51,6 +51,10 @@ type AppSettingS struct { AlipayPrivateKey string } +type RuntimeSettingS struct { + DisablePhoneVerify bool +} + type SearchSettingS struct { ZincHost string ZincIndex string diff --git a/pkg/sign/sign.go b/pkg/sign/sign.go index ebce41d1..717b8e54 100644 --- a/pkg/sign/sign.go +++ b/pkg/sign/sign.go @@ -6,8 +6,8 @@ import ( "time" "github.com/fbsobreira/gotron-sdk/pkg/proto/core" - "github.com/golang/protobuf/proto" "github.com/rocboss/paopao-ce/pkg/crypto" + "google.golang.org/protobuf/proto" ) // SignTransaction 签名交易 diff --git a/pkg/util/banner.go b/pkg/util/banner.go new file mode 100644 index 00000000..d41b5ae1 --- /dev/null +++ b/pkg/util/banner.go @@ -0,0 +1,12 @@ +package util + +import "fmt" + +func PrintHelloBanner(text string) { + + fmt.Println(" ____ __ __ ____ __ __ ") + fmt.Println("( _ \\ / _\\ / \\( _ \\ / _\\ / \\ ") + fmt.Println(" ) __// \\( O )) __// \\( O )") + fmt.Println("(__) \\_/\\_/ \\__/(__) \\_/\\_/ \\__/ ") + fmt.Println(text) +} diff --git a/pkg/util/ip.go b/pkg/util/ip.go index 5e657944..3aaa13c1 100644 --- a/pkg/util/ip.go +++ b/pkg/util/ip.go @@ -1,12 +1,8 @@ package util -import ( - "github.com/yinheli/qqwry" -) +import "github.com/rocboss/paopao-ce/pkg/util/iploc" func GetIPLoc(ip string) string { - q := qqwry.NewQQwry("qqwry.dat") - q.Find(ip) - - return q.Country + country, _ := iploc.Find(ip) + return country } diff --git a/pkg/util/iploc/iploc.go b/pkg/util/iploc/iploc.go new file mode 100644 index 00000000..0a103738 --- /dev/null +++ b/pkg/util/iploc/iploc.go @@ -0,0 +1,128 @@ +package iploc + +import ( + _ "embed" + "encoding/binary" + "net" + + "github.com/yinheli/mahonia" +) + +// Note: This file is a modified version of https://github.com/yinheli/qqwry. + +//go:embed qqwry.dat +var qqwry []byte + +const ( + cIndexLen = 7 + cRedirectMode1 = 0x01 + cRedirectMode2 = 0x02 +) + +// Find get country and city base ip +func Find(ip string) (string, string) { + offset := searchIndex(binary.BigEndian.Uint32(net.ParseIP(ip).To4())) + if offset <= 0 { + return "", "" + } + var country, area []byte + mode := readMode(offset + 4) + if mode == cRedirectMode1 { + countryOffset := readUInt24(offset + 5) + mode = readMode(countryOffset) + if mode == cRedirectMode2 { + c := readUInt24(countryOffset + 1) + country = readString(c) + countryOffset += 4 + } else { + country = readString(countryOffset) + countryOffset += uint32(len(country) + 1) + } + area = readArea(countryOffset) + } else if mode == cRedirectMode2 { + countryOffset := readUInt24(offset + 5) + country = readString(countryOffset) + area = readArea(offset + 8) + } else { + country = readString(offset + 4) + area = readArea(offset + uint32(5+len(country))) + } + enc := mahonia.NewDecoder("gbk") + Country := enc.ConvertString(string(country)) + City := enc.ConvertString(string(area)) + return Country, City +} + +func readMode(offset uint32) byte { + return qqwry[offset] +} + +func readArea(offset uint32) []byte { + mode := readMode(offset) + if mode == cRedirectMode1 || mode == cRedirectMode2 { + areaOffset := readUInt24(offset + 1) + if areaOffset == 0 { + return []byte("") + } else { + return readString(areaOffset) + } + } else { + return readString(offset) + } +} + +func readString(offset uint32) []byte { + data := make([]byte, 0, 30) + for { + if qqwry[offset] == 0 { + break + } + data = append(data, qqwry[offset]) + offset++ + } + return data +} + +func searchIndex(ip uint32) uint32 { + header := qqwry[:8] + start := binary.LittleEndian.Uint32(header[:4]) + end := binary.LittleEndian.Uint32(header[4:]) + for { + mid := getMiddleOffset(start, end) + buf := qqwry[mid : mid+cIndexLen] + ipaddr := binary.LittleEndian.Uint32(buf[:4]) + if end-start == cIndexLen { + offset := byte3ToUInt32(buf[4:]) + // TODO: 这里可能有bug,需要优化 + if ip < binary.LittleEndian.Uint32(qqwry[end:end+4]) { + return offset + } else { + return 0 + } + } + // 找到的比较大,向前移 + if ipaddr > ip { + end = mid + } else if ipaddr < ip { // 找到的比较小,向后移 + start = mid + } else { + return byte3ToUInt32(buf[4:]) + } + } +} + +func readUInt24(offset uint32) uint32 { + return byte3ToUInt32(qqwry[offset : offset+3]) +} + +func getMiddleOffset(start uint32, end uint32) uint32 { + records := ((end - start) / cIndexLen) >> 1 + return start + records*cIndexLen +} + +func byte3ToUInt32(data []byte) uint32 { + i := uint32(data[0]) & 0xff + i |= (uint32(data[1]) << 8) & 0xff00 + i |= (uint32(data[2]) << 16) & 0xff0000 + return i +} diff --git a/pkg/util/iploc/iploc_test.go b/pkg/util/iploc/iploc_test.go new file mode 100644 index 00000000..4754e86d --- /dev/null +++ b/pkg/util/iploc/iploc_test.go @@ -0,0 +1,25 @@ +package iploc + +import ( + "testing" +) + +func TestFind(t *testing.T) { + for _, data := range []struct { + ip string + country string + city string + }{ + {ip: "127.0.0.1", country: "本机地址", city: " CZ88.NET"}, + {ip: "180.89.94.9", country: "北京市", city: "鹏博士宽带"}, + } { + country, city := Find(data.ip) + t.Logf("ip:%v, country:%v, city:%v", data.ip, country, city) + if country != data.country { + t.Errorf("find ip:%s expect country: %s got: %s", data.ip, data.country, country) + } + if city != data.city { + t.Errorf("find ip:%s expect city: %s got: %s", data.ip, data.city, city) + } + } +} diff --git a/qqwry.dat b/pkg/util/iploc/qqwry.dat similarity index 100% rename from qqwry.dat rename to pkg/util/iploc/qqwry.dat diff --git a/pkg/zinc/zinc.go b/pkg/zinc/zinc.go index 7cf4c86e..a7d9cc8d 100644 --- a/pkg/zinc/zinc.go +++ b/pkg/zinc/zinc.go @@ -8,6 +8,7 @@ import ( "time" "github.com/go-resty/resty/v2" + "github.com/rocboss/paopao-ce/pkg/setting" ) type ZincClient struct { @@ -49,14 +50,17 @@ type QueryResultT struct { TimedOut bool `json:"timed_out"` Hits *HitsResultT `json:"hits"` } + type HitsResultT struct { Total *HitsResultTotalT `json:"total"` MaxScore float64 `json:"max_score"` Hits []*HitItem `json:"hits"` } + type HitsResultTotalT struct { Value int64 `json:"value"` } + type HitItem struct { Index string `json:"_index"` Type string `json:"_type"` @@ -66,6 +70,17 @@ type HitItem struct { Source interface{} `json:"_source"` } +// NewClient 获取ZincClient新实例 +func NewClient(conf *setting.SearchSettingS) *ZincClient { + return &ZincClient{ + ZincClientConfig: &ZincClientConfig{ + ZincHost: conf.ZincHost, + ZincUser: conf.ZincUser, + ZincPassword: conf.ZincPassword, + }, + } +} + // 创建索引 func (c *ZincClient) CreateIndex(name string, p *ZincIndexProperty) bool { data := &ZincIndex{ diff --git a/version b/version new file mode 100644 index 00000000..e69de29b diff --git a/web/.gitignore b/web/.gitignore index ba6b09ec..5e36b5b9 100644 --- a/web/.gitignore +++ b/web/.gitignore @@ -11,6 +11,7 @@ node_modules dist dist-ssr *.local +.env # Editor directories and files .vscode/* diff --git a/web/embed.go b/web/embed.go new file mode 100644 index 00000000..4866a53a --- /dev/null +++ b/web/embed.go @@ -0,0 +1,19 @@ +//go:build embed +// +build embed + +package web + +import ( + "embed" + "io/fs" + "net/http" +) + +//go:embed dist/* +var files embed.FS + +// NewFileSystem get an embed static assets http.FileSystem instance. +func NewFileSystem() http.FileSystem { + subfs, _ := fs.Sub(files, "dist") + return http.FS(subfs) +} diff --git a/web/package.json b/web/package.json index 78bd809a..d93e4413 100644 --- a/web/package.json +++ b/web/package.json @@ -5,7 +5,8 @@ "scripts": { "dev": "vite", "build": "vite build", - "preview": "vite preview" + "preview": "vite preview", + "tauri": "tauri" }, "dependencies": { "@vicons/carbon": "^0.12.0", @@ -29,6 +30,7 @@ "vuex": "^4.0.2" }, "devDependencies": { + "@tauri-apps/cli": "^1.0.0-rc.7", "@types/node": "^17.0.35", "@types/qrcode": "^1.4.2", "@vitejs/plugin-vue": "^2.3.1", diff --git a/web/src-tauri/.gitignore b/web/src-tauri/.gitignore new file mode 100644 index 00000000..25279840 --- /dev/null +++ b/web/src-tauri/.gitignore @@ -0,0 +1,5 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +WixTools +Cargo.lock diff --git a/web/src-tauri/Cargo.toml b/web/src-tauri/Cargo.toml new file mode 100644 index 00000000..f1f34a5d --- /dev/null +++ b/web/src-tauri/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "paopao" +version = "0.1.0" +description = "Paopao App" +authors = ["Rocboss"] +license = "MIT License" +repository = "https://github.com/rocboss/paopao-ce" +edition = "2021" +rust-version = "1.57" + +[build-dependencies] +tauri-build = { version = "1.0.0-rc.4", features = [] } + +[dependencies] +tauri = { version = "1.0.0-rc.4", features = ["api-all"] } + +[features] +# by default Tauri runs in production mode +# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL +default = [ "custom-protocol" ] +# this feature is used used for production builds where `devPath` points to the filesystem +# DO NOT remove this +custom-protocol = [ "tauri/custom-protocol" ] diff --git a/web/src-tauri/build.rs b/web/src-tauri/build.rs new file mode 100644 index 00000000..d860e1e6 --- /dev/null +++ b/web/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build() +} diff --git a/web/src-tauri/icons/128x128.png b/web/src-tauri/icons/128x128.png new file mode 100644 index 00000000..8976d45d Binary files /dev/null and b/web/src-tauri/icons/128x128.png differ diff --git a/web/src-tauri/icons/128x128@2x.png b/web/src-tauri/icons/128x128@2x.png new file mode 100644 index 00000000..826a74fc Binary files /dev/null and b/web/src-tauri/icons/128x128@2x.png differ diff --git a/web/src-tauri/icons/32x32.png b/web/src-tauri/icons/32x32.png new file mode 100644 index 00000000..96e054be Binary files /dev/null and b/web/src-tauri/icons/32x32.png differ diff --git a/web/src-tauri/icons/icon.icns b/web/src-tauri/icons/icon.icns new file mode 100644 index 00000000..80eebbf3 Binary files /dev/null and b/web/src-tauri/icons/icon.icns differ diff --git a/web/src-tauri/icons/icon.ico b/web/src-tauri/icons/icon.ico new file mode 100644 index 00000000..92783607 Binary files /dev/null and b/web/src-tauri/icons/icon.ico differ diff --git a/web/src-tauri/src/main.rs b/web/src-tauri/src/main.rs new file mode 100644 index 00000000..6c858d61 --- /dev/null +++ b/web/src-tauri/src/main.rs @@ -0,0 +1,52 @@ +#![cfg_attr( + all(not(debug_assertions), target_os = "windows"), + windows_subsystem = "windows" +)] + +use tauri::api::shell; +use tauri::{CustomMenuItem, Manager, Menu, MenuEntry, MenuItem, Submenu}; + +fn main() { + let _ctx = tauri::generate_context!(); + + tauri::Builder::default() + .menu(Menu::with_items([ + #[cfg(target_os = "macos")] + MenuEntry::Submenu(Submenu::new( + &_ctx.package_info().name, + Menu::with_items([ + MenuItem::Separator.into(), + MenuItem::Services.into(), + MenuItem::Separator.into(), + MenuItem::Hide.into(), + MenuItem::HideOthers.into(), + MenuItem::ShowAll.into(), + MenuItem::Separator.into(), + MenuItem::Quit.into(), + ]), + )), + MenuEntry::Submenu(Submenu::new( + "Window", + Menu::with_items([MenuItem::Minimize.into(), MenuItem::Zoom.into()]), + )), + // You should always have a Help menu on macOS because it will automatically + // show a menu search field + MenuEntry::Submenu(Submenu::new( + "Help", + Menu::with_items([CustomMenuItem::new("Learn More", "Learn More").into()]), + )), + ])) + .on_menu_event(|event| { + let event_name = event.menu_item_id(); + event.window().emit("menu", event_name).unwrap(); + match event_name { + "Learn More" => { + let link = "https://github.com/rocboss/paopao-ce".to_string(); + shell::open(&event.window().shell_scope(), link, None).unwrap(); + } + _ => {} + } + }) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} diff --git a/web/src-tauri/tauri.conf.json b/web/src-tauri/tauri.conf.json new file mode 100644 index 00000000..e28b7816 --- /dev/null +++ b/web/src-tauri/tauri.conf.json @@ -0,0 +1,74 @@ +{ + "package": { + "productName": "Paopao", + "version": "0.1.0" + }, + "build": { + "distDir": "../dist", + "beforeDevCommand": "", + "beforeBuildCommand": "", + "withGlobalTauri": true + }, + "tauri": { + "bundle": { + "active": true, + "targets": "all", + "identifier": "tauri.paopao.info", + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ], + "resources": [], + "externalBin": [], + "copyright": "", + "category": "Social Networking", + "shortDescription": "", + "longDescription": "", + "deb": { + "depends": [] + }, + "macOS": { + "frameworks": [], + "minimumSystemVersion": "", + "exceptionDomain": "", + "signingIdentity": null, + "providerShortName": null, + "entitlements": null + }, + "windows": { + "certificateThumbprint": null, + "digestAlgorithm": "sha256", + "timestampUrl": "" + } + }, + "updater": { + "active": false + }, + "allowlist": { + "all": true, + "http": { + "all": true, + "request": true, + "scope": ["https://**", "http://**"] + }, + "notification": { + "all": true + } + }, + "windows": [ + { + "title": "泡泡 - 闲了,冒个泡", + "width": 1080, + "height": 800, + "resizable": true, + "fullscreen": false + } + ], + "security": { + "csp": null + } + } +} diff --git a/web/src/api/auth.ts b/web/src/api/auth.ts index e6513e92..4d5a066d 100644 --- a/web/src/api/auth.ts +++ b/web/src/api/auth.ts @@ -1,62 +1,39 @@ -import request from '@/utils/request'; +import { request } from '@/utils/request'; - -/** - * 用户登录 - * @param {Object} params - * - @param {string} username - * - @param {string} password - * @returns Promise - */ -export const userLogin = (params: NetParams.AuthUserLogin = {}) => { +/** 用户登录 */ +export const userLogin = (params: NetParams.AuthUserLogin): Promise => { return request({ method: 'post', - url: '/auth/login', + url: '/v1/auth/login', data: params, - }) as unknown as Promise; + }); }; -/** - * 注册用户 - * @param {Object} params - * - @param {string} username - * - @param {string} password - * @returns Promise - */ -export const userRegister = (params = {}) => { +/** 注册用户 */ +export const userRegister = (params: NetParams.AuthUserRegister): Promise => { return request({ method: 'post', - url: '/auth/register', + url: '/v1/auth/register', data: params, }); }; -/** - * 用户信息 - * @param {Object} params - * @returns Promise - */ -export const userInfo = (token = '') => { +/** 用户信息 */ +export const userInfo = (token: NetParams.AuthUserInfo = ""): Promise => { return request({ method: 'get', - url: '/user/info', + url: '/v1/user/info', headers: { Authorization: `Bearer ${token}`, }, }); }; -/** - * 修改用户密码 - * @param {Object} params - * - @param {string} password 新密码 - * - @param {string} old_password 旧密码 - * @returns Promise - */ -export const updateUserPassword = (data: any) => { +/** 修改用户密码,该接口暂时未使用 */ +export const updateUserPassword = (data: NetParams.AuthUpdateUserPassword): Promise => { return request({ method: 'post', - url: '/api/user/password', + url: '/v1/api/user/password', data, }); }; diff --git a/web/src/api/post.ts b/web/src/api/post.ts index 3488eed5..7f2bc408 100644 --- a/web/src/api/post.ts +++ b/web/src/api/post.ts @@ -1,222 +1,145 @@ -import request from '@/utils/request'; +import { request } from '@/utils/request'; -/** - * 获取动态列表 - * @param {Object} params - * @returns Promise - */ -export const getPosts = (params: NetParams.PostGetPosts) => { +/** 获取动态列表 */ +export const getPosts = (params: NetParams.PostGetPosts): Promise => { return request({ method: 'get', - url: '/posts', + url: '/v1/posts', params - }) as unknown as Promise; + }); }; -/** - * 获取标签列表 - * @param {Object} params - * @returns Promise - */ -export const getTags = (params: NetParams.PostGetTags) => { +/** 获取标签列表 */ +export const getTags = (params: NetParams.PostGetTags): Promise => { return request({ method: 'get', - url: '/tags', + url: '/v1/tags', params - }) as unknown as Promise; + }); }; -/** - * 获取动态详情 - * @param {Object} params - * @returns Promise - */ -export const getPost = (params: NetParams.PostGetPost) => { +/** 获取动态详情 */ +export const getPost = (params: NetParams.PostGetPost): Promise => { return request({ method: 'get', - url: '/post', + url: '/v1/post', params - }) as unknown as Promise; + }); }; -/** - * 获取动态点赞状态 - * @param {Object} params - * @returns Promise - */ -export const getPostStar = (params: NetParams.PostPostStar) => { +/** 获取动态点赞状态 */ +export const getPostStar = (params: NetParams.PostPostStar): Promise => { return request({ method: 'get', - url: '/post/star', + url: '/v1/post/star', params - }) as unknown as Promise; + }); }; -/** - * 动态点赞 - * @param {Object} data - * @returns Promise - */ -export const postStar = (data: NetParams.PostPostStar) => { +/** 动态点赞 */ +export const postStar = (data: NetParams.PostPostStar): Promise => { return request({ method: 'post', - url: '/post/star', + url: '/v1/post/star', data - }) as unknown as Promise; + }); }; -/** - * 获取动态收藏状态 - * @param {Object} params - * @returns Promise - */ -export const getPostCollection = (params: NetParams.PostGetPostCollection) => { +/** 获取动态收藏状态 */ +export const getPostCollection = (params: NetParams.PostGetPostCollection): Promise => { return request({ method: 'get', - url: '/post/collection', + url: '/v1/post/collection', params - }) as unknown as Promise; + }); }; -/** - * 动态收藏 - * @param {Object} data - * @returns Promise - */ -export const postCollection = (data: NetParams.PostPostCollection) => { +/** 动态收藏 */ +export const postCollection = (data: NetParams.PostPostCollection): Promise => { return request({ method: 'post', - url: '/post/collection', + url: '/v1/post/collection', data - }) as unknown as Promise; + }); }; -/** - * 获取动态评论列表 - * @param {Object} params - * @returns Promise - */ -export const getPostComments = (params: NetParams.PostGetPostComments) => { +/** 获取动态评论列表 */ +export const getPostComments = (params: NetParams.PostGetPostComments): Promise => { return request({ method: 'get', - url: '/post/comments', + url: '/v1/post/comments', params - }) as unknown as Promise; + }); }; -/** - * 发布动态 - * @param {Object} data - * - @param {array} contents 内容 - * - @param {array} users at用户 - * - @param {array} tags 话题 - * @returns Promise - */ -export const createPost = (data: NetParams.PostCreatePost) => { +/** 发布动态 */ +export const createPost = (data: NetParams.PostCreatePost): Promise => { return request({ method: 'post', - url: '/post', + url: '/v1/post', data - }) as unknown as Promise; + }); }; -/** - * 删除动态 - * @param {Object} data - * - @param {number} id - * @returns Promise - */ -export const deletePost = (data: any) => { +/** 删除动态 */ +export const deletePost = (data: NetParams.PostDeletePost): Promise => { return request({ method: 'delete', - url: '/post', + url: '/v1/post', data }); }; -/** - * 锁定/解锁动态 - * @param {Object} data - * - @param {number} id - * @returns Promise - */ -export const lockPost = (data: NetParams.PostLockPost) => { +/** 锁定/解锁动态 */ +export const lockPost = (data: NetParams.PostLockPost): Promise => { return request({ method: 'post', - url: '/post/lock', + url: '/v1/post/lock', data - }) as unknown as Promise; + }); }; -/** - * 置顶/取消置顶动态 - * @param {Object} data - * - @param {number} id - * @returns Promise - */ -export const stickPost = (data: NetParams.PostStickPost) => { +/** 置顶/取消置顶动态 */ +export const stickPost = (data: NetParams.PostStickPost): Promise => { return request({ method: 'post', - url: '/post/stick', + url: '/v1/post/stick', data - }) as unknown as Promise; + }); }; -/** - * 发布动态评论 - * @param {Object} data - * - @param {array} contents 内容 - * - @param {array} users at用户 - * @returns Promise - */ -export const createComment = (data: any) => { +/** 发布动态评论 */ +export const createComment = (data: NetParams.PostCreateComment): Promise => { return request({ method: 'post', - url: '/post/comment', + url: '/v1/post/comment', data }); }; -/** - * 删除评论 - * @param {Object} data - * - @param {number} id - * @returns Promise - */ -export const deleteComment = (data: any) => { +/** 删除评论 */ +export const deleteComment = (data: NetParams.PostDeleteComment): Promise => { return request({ method: 'delete', - url: '/post/comment', + url: '/v1/post/comment', data }); }; -/** - * 发布评论回复 - * @param {Object} data - * - @param {string} content 内容 - * - @param {number} comment_id 评论ID - * - @param {number} at_user_id at用户ID - * @returns Promise - */ -export const createCommentReply = (data: any) => { +/** 发布评论回复 */ +export const createCommentReply = (data: NetParams.PostCreateCommentReply): Promise => { return request({ method: 'post', - url: '/post/comment/reply', + url: '/v1/post/comment/reply', data }); }; -/** - * 删除评论回复 - * @param {Object} data - * - @param {number} id 评论ID - * @returns Promise - */ -export const deleteCommentReply = (data: any) => { +/** 删除评论回复 */ +export const deleteCommentReply = (data: NetParams.PostDeleteCommentReply): Promise => { return request({ method: 'delete', - url: '/post/comment/reply', + url: '/v1/post/comment/reply', data }); -}; \ No newline at end of file +}; diff --git a/web/src/api/user.ts b/web/src/api/user.ts index 46cfe889..ea6c1adf 100644 --- a/web/src/api/user.ts +++ b/web/src/api/user.ts @@ -1,16 +1,12 @@ -import request from '@/utils/request'; +import { request } from '@/utils/request'; -/** - * 获取验证码 - * @param {Object} params - * @returns Promise - */ -export const getCaptcha = (params: NetParams.UserGetCaptcha = {}) => { +/** 获取验证码 */ +export const getCaptcha = (params: NetParams.UserGetCaptcha = {}): Promise => { return request({ method: 'get', - url: '/captcha', + url: '/v1/captcha', params - }) as unknown as Promise; + }); }; /** @@ -21,7 +17,7 @@ export const getCaptcha = (params: NetParams.UserGetCaptcha = {}) => { export const sendCaptcha = (data: any) => { return request({ method: 'post', - url: '/captcha', + url: '/v1/captcha', data }); }; @@ -31,12 +27,12 @@ export const sendCaptcha = (data: any) => { * @param {Object} data * @returns Promise */ -export const sendUserWhisper = (data: NetParams.UserWhisper) => { +export const sendUserWhisper = (data: NetParams.UserWhisper): Promise => { return request({ method: 'post', - url: '/user/whisper', + url: '/v1/user/whisper', data - }) as unknown as Promise; + }); }; /** @@ -44,36 +40,28 @@ export const sendUserWhisper = (data: NetParams.UserWhisper) => { * @param {Object} data * @returns Promise */ -export const bindUserPhone = (data: NetParams.UserBindUserPhone) => { +export const bindUserPhone = (data: NetParams.UserBindUserPhone): Promise => { return request({ method: 'post', - url: '/user/phone', + url: '/v1/user/phone', data - }) as unknown as Promise; + }); }; -/** - * 更改密码 - * @param {Object} data - * @returns Promise - */ -export const changePassword = (data: any) => { +/** 更改密码 */ +export const changePassword = (data: NetParams.UserChangePassword): Promise => { return request({ method: 'post', - url: '/user/password', + url: '/v1/user/password', data }); }; -/** - * 更改昵称 - * @param {Object} data - * @returns Promise - */ -export const changeNickname = (data: any) => { +/** 更改昵称 */ +export const changeNickname = (data: NetParams.UserChangeNickname): Promise => { return request({ method: 'post', - url: '/user/nickname', + url: '/v1/user/nickname', data }); }; @@ -86,35 +74,27 @@ export const changeNickname = (data: any) => { export const changeAvatar = (data: any) => { return request({ method: 'post', - url: '/user/avatar', + url: '/v1/user/avatar', data }); }; -/** - * 获取未读消息数 - * @param {Object} params - * @returns Promise - */ -export const getUnreadMsgCount = (params: NetParams.UserGetUnreadMsgCount = {}) => { +/** 获取未读消息数 */ +export const getUnreadMsgCount = (params: NetParams.UserGetUnreadMsgCount = {}): Promise => { return request({ method: 'get', - url: '/user/msgcount/unread', + url: '/v1/user/msgcount/unread', params - }) as unknown as Promise; + }); }; -/** - * 获取消息列表 - * @param {Object} params - * @returns Promise - */ -export const getMessages = (params: NetParams.UserGetMessages) => { +/** 获取消息列表 */ +export const getMessages = (params: NetParams.UserGetMessages): Promise => { return request({ method: 'get', - url: '/user/messages', + url: '/v1/user/messages', params - }) as unknown as Promise; + }); }; /** @@ -125,74 +105,54 @@ export const getMessages = (params: NetParams.UserGetMessages) => { export const readMessage = (data: any) => { return request({ method: 'post', - url: '/user/message/read', + url: '/v1/user/message/read', data }); }; -/** - * 获取收藏列表 - * @param {Object} params - * @returns Promise - */ -export const getCollections = (params: NetParams.UserGetCollections) => { +/** 获取收藏列表 */ +export const getCollections = (params: NetParams.UserGetCollections): Promise => { return request({ method: 'get', - url: '/user/collections', + url: '/v1/user/collections', params - }) as unknown as Promise; + }); }; -/** - * 获取点赞列表 - * @param {Object} params - * @returns Promise - */ -export const getStars = (params: NetParams.UserGetStars) => { +/** 获取点赞列表 */ +export const getStars = (params: NetParams.UserGetStars): Promise => { return request({ method: 'get', - url: '/user/stars', + url: '/v1/user/stars', params - }) as unknown as Promise; + }); }; -/** - * 获取用户基础信息 - * @param {Object} params - * @returns Promise - */ -export const getUserProfile = (params: NetParams.UserGetUserProfile) => { +/** 获取用户基础信息 */ +export const getUserProfile = (params: NetParams.UserGetUserProfile): Promise => { return request({ method: 'get', - url: '/user/profile', + url: '/v1/user/profile', params - }) as unknown as Promise; + }); }; -/** - * 获取点赞列表 - * @param {Object} params - * @returns Promise - */ -export const getUserPosts = (params: NetParams.UserGetUserPosts) => { +/** 获取用户帖子列表 */ +export const getUserPosts = (params: NetParams.UserGetUserPosts): Promise => { return request({ method: 'get', - url: '/user/posts', + url: '/v1/user/posts', params - }) as unknown as Promise; + }); }; -/** - * 获取账单列表 - * @param {Object} params - * @returns Promise - */ -export const getBills = (params: NetParams.UserGetBills) => { +/** 获取账单列表 */ +export const getBills = (params: NetParams.UserGetBills): Promise => { return request({ method: 'get', - url: '/user/wallet/bills', + url: '/v1/user/wallet/bills', params - }) as unknown as Promise; + }); }; /** @@ -200,12 +160,12 @@ export const getBills = (params: NetParams.UserGetBills) => { * @param {Object} data * @returns Promise */ -export const reqRecharge = (data: NetParams.UserReqRecharge) => { +export const reqRecharge = (data: NetParams.UserReqRecharge): Promise => { return request({ method: 'post', - url: '/user/recharge', + url: '/v1/user/recharge', data - }) as unknown as Promise; + }); }; /** @@ -213,12 +173,12 @@ export const reqRecharge = (data: NetParams.UserReqRecharge) => { * @param {Object} params * @returns Promise */ -export const getRecharge = (params: NetParams.UserGetRecharge) => { +export const getRecharge = (params: NetParams.UserGetRecharge): Promise => { return request({ method: 'get', - url: '/user/recharge', + url: '/v1/user/recharge', params - }) as unknown as Promise; + }); }; /** @@ -226,12 +186,12 @@ export const getRecharge = (params: NetParams.UserGetRecharge) => { * @param {Object} params * @returns Promise */ -export const getSuggestUsers = (params: { k: string }) => { +export const getSuggestUsers = (params: { k: string }): Promise => { return request({ method: 'get', - url: '/suggest/users', + url: '/v1/suggest/users', params - }) as unknown as Promise; + }); }; /** @@ -239,12 +199,12 @@ export const getSuggestUsers = (params: { k: string }) => { * @param {Object} params * @returns Promise */ -export const getSuggestTags = (params: { k: string }) => { +export const getSuggestTags = (params: { k: string }): Promise => { return request({ method: 'get', - url: '/suggest/tags', + url: '/v1/suggest/tags', params - }) as unknown as Promise; + }); }; /** @@ -252,12 +212,12 @@ export const getSuggestTags = (params: { k: string }) => { * @param {Object} params * @returns Promise */ -export const precheckAttachment = (params: NetParams.UserPrecheckAttachment) => { +export const precheckAttachment = (params: NetParams.UserPrecheckAttachment): Promise => { return request({ method: 'get', - url: '/attachment/precheck', + url: '/v1/attachment/precheck', params - }) as unknown as Promise; + }); }; /** @@ -265,10 +225,10 @@ export const precheckAttachment = (params: NetParams.UserPrecheckAttachment) => * @param {Object} params * @returns Promise */ -export const getAttachment = (params: NetParams.UserGetAttachment) => { +export const getAttachment = (params: NetParams.UserGetAttachment): Promise => { return request({ method: 'get', - url: '/attachment', + url: '/v1/attachment', params - }) as unknown as Promise; + }); }; diff --git a/web/src/components/comment-item.vue b/web/src/components/comment-item.vue index ad93562a..c5b4f2ee 100644 --- a/web/src/components/comment-item.vue +++ b/web/src/components/comment-item.vue @@ -113,20 +113,20 @@ const replyAtUsername = ref(''); const replyComposeRef = ref(); const emit = defineEmits<{ - (e: "reload"): void + (e: 'reload'): void }>(); const props = withDefaults(defineProps<{ comment: Item.CommentProps }>(), {}) const comment = computed(() => { - let comment = Object.assign( + let comment: Item.CommentComponentProps = Object.assign( { texts: [], imgs: [], }, props.comment - ) as {texts: Item.CommentProps[], imgs: Item.CommentProps[]} & Item.CommentProps; + ); comment.contents.map((content :any) => { if (+content.type === 1 || +content.type === 2) { comment.texts.push(content); diff --git a/web/src/components/compose-comment.vue b/web/src/components/compose-comment.vue index 3bcb36fd..e6c9fded 100644 --- a/web/src/components/compose-comment.vue +++ b/web/src/components/compose-comment.vue @@ -170,7 +170,7 @@ import { parsePostTag } from '@/utils/content'; import type { MentionOption, UploadFileInfo, UploadInst } from 'naive-ui'; const emit = defineEmits<{ - (e: "post-success"): void + (e: 'post-success'): void }>(); const props = withDefaults(defineProps<{ lock: number, @@ -265,7 +265,7 @@ const finishUpload = ({ file, event }: any): any => { imageContents.value.push({ id: file.id, content: data.data.content, - }); + } as Item.CommentItemProps); } } } catch (error) { diff --git a/web/src/components/compose-reply.vue b/web/src/components/compose-reply.vue index 2f19810e..cc22b195 100644 --- a/web/src/components/compose-reply.vue +++ b/web/src/components/compose-reply.vue @@ -50,11 +50,11 @@ const props = withDefaults(defineProps<{ }>(), { commentId: 0, atUserid: 0, - atUsername: "" + atUsername: '' }); const emit = defineEmits<{ - (e: "reload"): void, - (e: "reset"): void + (e: 'reload'): void, + (e: 'reset'): void }>(); const inputInstRef = ref(); const showReply = ref(false); diff --git a/web/src/components/compose.vue b/web/src/components/compose.vue index 7fa46214..db8fca0c 100644 --- a/web/src/components/compose.vue +++ b/web/src/components/compose.vue @@ -265,7 +265,7 @@ const uploadType = ref('public/image'); const fileQueue = ref([]); const imageContents = ref([]); const videoContents = ref([]); -const attachmentContents = ref([]); +const attachmentContents = ref([]); const uploadGateway = import.meta.env.VITE_HOST + '/attachment'; const uploadToken = ref(); @@ -402,19 +402,19 @@ const finishUpload = ({ file, event }: any): any => { imageContents.value.push({ id: file.id, content: data.data.content, - }); + } as Item.CommentItemProps); } if (uploadType.value === 'public/video') { videoContents.value.push({ id: file.id, content: data.data.content, - }); + } as Item.CommentItemProps); } if (uploadType.value === 'attachment') { attachmentContents.value.push({ id: file.id, content: data.data.content, - }); + } as Item.AttachmentProps); } } } catch (error) { diff --git a/web/src/components/message-item.vue b/web/src/components/message-item.vue index bb873603..e01f6a71 100644 --- a/web/src/components/message-item.vue +++ b/web/src/components/message-item.vue @@ -46,11 +46,11 @@