Merge branch 'main' into 'fix/typo'

# Conflicts:
#   ui/src/i18n/locales/en.json
This commit is contained in:
Dong Feng 2022-11-03 06:38:56 +00:00
commit e8a5677b26
144 changed files with 4589 additions and 1192 deletions

3
.gitignore vendored
View File

@ -21,4 +21,5 @@ Thumbs*.db
tmp
vendor/
.husky
answer-data/
/answer-data/
/answer

View File

@ -1,4 +1,8 @@
# Contributing to answer
## Coding and documentation Style
To be developed.
## Submitting Modifications
To be developed.

View File

@ -6,40 +6,54 @@ COPY . /answer
WORKDIR /answer
RUN make install-ui-packages ui && mv ui/build /tmp
FROM golang:1.18 AS golang-builder
LABEL maintainer="aichy"
# stage2 build the main binary within static resource
FROM golang:1.19-alpine AS golang-builder
LABEL maintainer="aichy@sf.com"
ARG GOPROXY
ENV GOPROXY ${GOPROXY:-direct}
ENV GOPATH /go
ENV GOROOT /usr/local/go
ENV PACKAGE github.com/answerdev/answer
ENV GOPROXY https://goproxy.cn,direct
ENV BUILD_DIR ${GOPATH}/src/${PACKAGE}
# Build
ARG TAGS="sqlite sqlite_unlock_notify"
ENV TAGS "bindata timetzdata $TAGS"
ARG CGO_EXTRA_CFLAGS
COPY . ${BUILD_DIR}
WORKDIR ${BUILD_DIR}
COPY --from=node-builder /tmp/build ${BUILD_DIR}/ui/build
RUN make clean build && \
cp answer /usr/bin/answer && \
mkdir -p /data/upfiles && chmod 777 /data/upfiles && \
mkdir -p /data/i18n && chmod 777 /data/i18n && cp -r i18n/*.yaml /data/i18n
RUN apk --no-cache add build-base git \
&& make clean build \
&& cp answer /usr/bin/answer
RUN mkdir -p /data/upfiles && chmod 777 /data/upfiles \
&& mkdir -p /data/i18n && cp -r i18n/*.yaml /data/i18n
# stage3 copy the binary and resource files into fresh container
FROM alpine
LABEL maintainer="maintainers@sf.com"
FROM debian:bullseye
ENV TZ "Asia/Shanghai"
RUN sed -i 's/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list \
&& sed -i 's/security.debian.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list \
&& echo "Asia/Shanghai" > /etc/timezone \
&& apt -y update \
&& apt -y upgrade \
&& apt -y install ca-certificates openssl tzdata curl netcat dumb-init \
&& apt -y autoremove \
&& mkdir -p /tmp/cache
COPY --from=golang-builder /data /data
VOLUME /data
RUN apk update \
&& apk --no-cache add \
bash \
ca-certificates \
curl \
dumb-init \
gettext \
openssh \
sqlite \
gnupg \
&& echo "Asia/Shanghai" > /etc/timezone
COPY --from=golang-builder /usr/bin/answer /usr/bin/answer
COPY --from=golang-builder /data /data
COPY /script/entrypoint.sh /entrypoint.sh
RUN chmod 755 /entrypoint.sh
VOLUME /data
EXPOSE 80
ENTRYPOINT ["/entrypoint.sh"]

View File

@ -6,9 +6,9 @@ Before installing Answer, you need to install the base environment first.
You can then install Answer in several ways:
- Deploy with Docker
- binary installation
- Source installation
- [Deploy with Docker](#Docker-compose-for-Answer)
- [Binary installation](#Install-Answer-using-binary)
- [Source installation](#Compile-the-image)
## Docker-compose for Answer
```bash
@ -17,15 +17,15 @@ $ wget https://raw.githubusercontent.com/answerdev/answer/main/docker-compose.ya
$ docker-compose up
```
browser open URL [http://127.0.0.1:9080/](http://127.0.0.1:9080/).
In browser, open URL [http://127.0.0.1:9080/](http://127.0.0.1:9080/).
You can log in with the default administrator username( **`admin@admin.com`** ) and password( **`admin`** ).
You can log in with the default administrator username (**`admin@admin.com`**) and password (**`admin`**).
## Docker for Answer
Visit Docker Hub or GitHub Container registry to see all available images and tags.
Visit [Docker Hub](https://hub.docker.com/r/answerdev/answer) or GitHub Container registry to see all available images and tags.
### Usage
To keep your data out of Docker container, we do a volume (/var/data -> /data) here, and you can change it based on your situation.
To persist data beyond the life of a Docker container, use a volume (/var/data -> /data). You can modify this based on your situation.
```
# Pull image from Docker Hub.
@ -35,9 +35,9 @@ $ docker pull answerdev/answer:latest
$ mkdir -p /var/data
# Run the image first
$ docker run --name=answer -p 9080:80 -v /var/data:/data answer/answer
$ docker run --name=answer -p 9080:80 -v /var/data:/data answerdev/answer
# After the first startup, a configuration file will be generated in the /var/data directory
# After successful first startup, a configuration file will be generated in the /var/data directory
# /var/data/conf/config.yaml
# Need to modify the Mysql database address in the configuration file
vim /var/data/conf/config.yaml
@ -46,34 +46,32 @@ vim /var/data/conf/config.yaml
# connection: [username]:[password]@tcp([host]:[port])/[DbName]
...
# After configuring the configuration file, you can start the mirror again to start the service
# After configuring the configuration file, you can start the container again to start the service
$ docker start answer
```
## Binary for Answer
## Install Answer using binary
1. Unzip the compressed package
2. Use the command cd to enter the directory you just created
3. Execute the command ./answer init
4. Answer will generate a ./data directory in the current directory
5. Enter the data directory and modify the config.yaml file
6. Modify the database connection address to your database connection address
connection: [username]:[password]@tcp([host]:[port])/[DbName]
7. Exit the data directory and execute ./answer run -c ./data/conf/config.yaml
2. Use the command `cd` to enter the directory you just created
3. Execute the command `./answer init`
4. Answer will generate a `./data` directory in the current directory
5. Enter the `data` directory and modify the `config.yaml` file
6. Modify the database connection identify your database connection information
`connection: [username]:[password]@tcp([host]:[port])/[DbName]`
7. Use `cd ..` to return the directory from step 2, and execute `./answer run -c ./data/conf/config.yaml`
## Available Commands
Usage: answer [command]
Usage: `answer [command]`
- help: Help about any command
- init: Init answer application
- run: Run answer application
- check: Check answer required environment
- dump: Backup answer data
- `help`: Help about any command
- `init`: Init answer application
- `run`: Run answer application
- `check`: Check answer required environment
- `dump`: Backup answer data
## config.yaml Description
Here is a sample/default config.yaml file, as would be created from `answer init`.
```
server:
http:
@ -97,9 +95,9 @@ service_config:
```
## Compile the image
If you have modified the source files and want to repackage the image, you can use the following statement to repackage the image
If you have modified the source files and want to repackage the image, you can use the following to repackage the image
```
docker build -t answer:v1.0.0 .
```
## common problem
1. The project cannot be started, answer the main program startup depends on the configuration file config.yaml, the internationalization translation directory/i18n, the upload file storage directory/upfiles, you need to ensure that the configuration file is loaded when the project starts, answer run -c config.yaml and the correct config.yaml The configuration items that specify the i18n and upfiles directories
1. The project cannot be started: the main program startup depends on proper configuraiton of the configuration file, `config.yaml`, as well as the internationalization translation directory (`i18n`), and the upload file storage directory (`upfiles`). Ensure that the configuration file is loaded when the project starts, such as when using `answer run -c config.yaml` and that the `config.yaml` correctly specifies the i18n and upfiles directories.

View File

@ -35,7 +35,7 @@ $ docker pull answerdev/answer:latest
$ mkdir -p /var/data
# 先运行一遍镜像
$ docker run --name=answer -p 9080:80 -v /var/data:/data answer/answer
$ docker run --name=answer -p 9080:80 -v /var/data:/data answerdev/answer
# 第一次启动后会在/var/data 目录下生成配置文件
# /var/data/conf/config.yaml

View File

@ -7,12 +7,19 @@ DOCKER_CMD=docker
#GO_ENV=CGO_ENABLED=0
Revision=$(shell git rev-parse --short HEAD)
GO_FLAGS=-ldflags="-X main.Version=$(VERSION) -X main.Revision=$(Revision) -X 'main.Time=`date`' -extldflags -static"
GO_FLAGS=-ldflags="-X main.Version=$(VERSION) -X 'main.Revision=$(Revision)' -X 'main.Time=`date`' -extldflags -static"
GO=$(GO_ENV) $(shell which go)
build:
@$(GO_ENV) $(GO) build $(GO_FLAGS) -o $(BIN) $(DIR_SRC)
# https://dev.to/thewraven/universal-macos-binaries-with-go-1-16-3mm3
universal:
@GOOS=darwin GOARCH=amd64 $(GO_ENV) $(GO) build $(GO_FLAGS) -o ${BIN}_amd64 $(DIR_SRC)
@GOOS=darwin GOARCH=arm64 $(GO_ENV) $(GO) build $(GO_FLAGS) -o ${BIN}_arm64 $(DIR_SRC)
@lipo -create -output ${BIN} ${BIN}_amd64 ${BIN}_arm64
@rm -f ${BIN}_amd64 ${BIN}_arm64
generate:
go get github.com/google/wire/cmd/wire@latest
go generate ./...
@ -23,6 +30,7 @@ test:
# clean all build result
clean:
@$(GO) clean ./...
@rm -f $(BIN)

View File

@ -4,13 +4,14 @@
# Answer - Build Q&A community
An open-source knowledge based community software. You can use it to quickly build your Q&A community for product technical support, customer support, user communication, and more.
An open-source knowledge-based community software. You can use it to quickly build your Q&A community for product technical support, customer support, user communication, and more.
To learn more about the project, visit [answer.dev](https://answer.dev).
[![LICENSE](https://img.shields.io/badge/License-Apache-green)](https://github.com/answerdev/answer/blob/main/LICENSE)
[![Language](https://img.shields.io/badge/Language-Go-blue.svg)](https://golang.org/)
[![Language](https://img.shields.io/badge/Language-React-blue.svg)](https://reactjs.org/)
[![Go Report Card](https://goreportcard.com/badge/github.com/answerdev/answer)](https://goreportcard.com/report/github.com/answerdev/answer)
## Screenshots
@ -18,15 +19,13 @@ To learn more about the project, visit [answer.dev](https://answer.dev).
## Quick start
### Running with docker-compose
### Running with docker
```bash
mkdir answer && cd answer
wget https://raw.githubusercontent.com/answerdev/answer/main/docker-compose.yaml
docker-compose up
docker run -d -p 9080:80 -v $PWD/answer-data:/data --name answer answerdev/answer:latest
```
For more information you can see [INSTALL.md](./INSTALL.md)
For more information, see [INSTALL.md](./INSTALL.md)
## Contributing
@ -36,4 +35,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for ways to get started.
## License
[Apache](https://github.com/answerdev/answer/blob/main/LICENSE)
[Apache License 2.0](https://github.com/answerdev/answer/blob/main/LICENSE)

View File

@ -11,6 +11,7 @@
[![LICENSE](https://img.shields.io/badge/License-Apache-green)](https://github.com/answerdev/answer/blob/main/LICENSE)
[![Language](https://img.shields.io/badge/Language-Go-blue.svg)](https://golang.org/)
[![Language](https://img.shields.io/badge/Language-React-blue.svg)](https://reactjs.org/)
[![Go Report Card](https://goreportcard.com/badge/github.com/answerdev/answer)](https://goreportcard.com/report/github.com/answerdev/answer)
## 截图
@ -18,12 +19,10 @@
## 快速开始
### 使用 docker-compose 快速搭建
### 使用 docker 快速搭建
```bash
mkdir answer && cd answer
wget https://raw.githubusercontent.com/answerdev/answer/main/docker-compose.yaml
docker-compose up
docker run -d -p 9080:80 -v $PWD/answer-data:/data --name answer answerdev/answer:latest
```
其他安装配置细节请参考 [INSTALL.md](./INSTALL.md)
@ -36,4 +35,4 @@ docker-compose up
## License
[Apache](https://github.com/answerdev/answer/blob/main/LICENSE)
[Apache License 2.0](https://github.com/answerdev/answer/blob/main/LICENSE)

View File

@ -48,7 +48,7 @@ To run answer, use:
Use: "run",
Short: "Run the application",
Long: `Run the application`,
Run: func(cmd *cobra.Command, args []string) {
Run: func(_ *cobra.Command, _ []string) {
runApp()
},
}
@ -58,7 +58,7 @@ To run answer, use:
Use: "init",
Short: "init answer application",
Long: `init answer application`,
Run: func(cmd *cobra.Command, args []string) {
Run: func(_ *cobra.Command, _ []string) {
cli.InstallAllInitialEnvironment(dataDirPath)
c, err := readConfig()
if err != nil {
@ -79,7 +79,7 @@ To run answer, use:
Use: "upgrade",
Short: "upgrade Answer version",
Long: `upgrade Answer version`,
Run: func(cmd *cobra.Command, args []string) {
Run: func(_ *cobra.Command, _ []string) {
c, err := readConfig()
if err != nil {
fmt.Println("read config failed: ", err.Error())
@ -98,7 +98,7 @@ To run answer, use:
Use: "dump",
Short: "back up data",
Long: `back up data`,
Run: func(cmd *cobra.Command, args []string) {
Run: func(_ *cobra.Command, _ []string) {
fmt.Println("Answer is backing up data")
c, err := readConfig()
if err != nil {
@ -119,7 +119,7 @@ To run answer, use:
Use: "check",
Short: "checking the required environment",
Long: `Check if the current environment meets the startup requirements`,
Run: func(cmd *cobra.Command, args []string) {
Run: func(_ *cobra.Command, _ []string) {
fmt.Println("Start checking the required environment...")
if cli.CheckConfigFile(configFilePath) {
fmt.Println("config file exists [✔]")

View File

@ -14,9 +14,9 @@ import (
"github.com/answerdev/answer/internal/base/translator"
"github.com/answerdev/answer/internal/controller"
"github.com/answerdev/answer/internal/controller_backyard"
"github.com/answerdev/answer/internal/repo"
"github.com/answerdev/answer/internal/repo/activity"
"github.com/answerdev/answer/internal/repo/activity_common"
"github.com/answerdev/answer/internal/repo/answer"
"github.com/answerdev/answer/internal/repo/auth"
"github.com/answerdev/answer/internal/repo/captcha"
"github.com/answerdev/answer/internal/repo/collection"
@ -26,10 +26,13 @@ import (
"github.com/answerdev/answer/internal/repo/export"
"github.com/answerdev/answer/internal/repo/meta"
"github.com/answerdev/answer/internal/repo/notification"
"github.com/answerdev/answer/internal/repo/question"
"github.com/answerdev/answer/internal/repo/rank"
"github.com/answerdev/answer/internal/repo/reason"
"github.com/answerdev/answer/internal/repo/report"
"github.com/answerdev/answer/internal/repo/revision"
"github.com/answerdev/answer/internal/repo/search_common"
"github.com/answerdev/answer/internal/repo/site_info"
"github.com/answerdev/answer/internal/repo/tag"
"github.com/answerdev/answer/internal/repo/unique"
"github.com/answerdev/answer/internal/repo/user"
@ -92,11 +95,11 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
configRepo := config.NewConfigRepo(dataData)
userRepo := user.NewUserRepo(dataData, configRepo)
uniqueIDRepo := unique.NewUniqueIDRepo(dataData)
activityRepo := repo.NewActivityRepo(dataData, uniqueIDRepo, configRepo)
activityRepo := activity_common.NewActivityRepo(dataData, uniqueIDRepo, configRepo)
userRankRepo := rank.NewUserRankRepo(dataData, configRepo)
userActiveActivityRepo := activity.NewUserActiveActivityRepo(dataData, activityRepo, userRankRepo, configRepo)
emailRepo := export.NewEmailRepo(dataData)
siteInfoRepo := repo.NewSiteInfo(dataData)
siteInfoRepo := site_info.NewSiteInfo(dataData)
emailService := export2.NewEmailService(configRepo, emailRepo, siteInfoRepo)
userService := service.NewUserService(userRepo, userActiveActivityRepo, emailService, authService, serviceConf)
captchaRepo := captcha.NewCaptchaRepo(dataData)
@ -106,8 +109,8 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
commentRepo := comment.NewCommentRepo(dataData, uniqueIDRepo)
commentCommonRepo := comment.NewCommentCommonRepo(dataData, uniqueIDRepo)
userCommon := usercommon.NewUserCommon(userRepo)
answerRepo := repo.NewAnswerRepo(dataData, uniqueIDRepo, userRankRepo, activityRepo)
questionRepo := repo.NewQuestionRepo(dataData, uniqueIDRepo)
answerRepo := answer.NewAnswerRepo(dataData, uniqueIDRepo, userRankRepo, activityRepo)
questionRepo := question.NewQuestionRepo(dataData, uniqueIDRepo)
tagRepo := tag.NewTagRepo(dataData, uniqueIDRepo)
objService := object_info.NewObjService(answerRepo, questionRepo, commentCommonRepo, tagRepo)
voteRepo := activity_common.NewVoteRepo(dataData, activityRepo)
@ -130,7 +133,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
followController := controller.NewFollowController(followService)
collectionRepo := collection.NewCollectionRepo(dataData, uniqueIDRepo)
collectionGroupRepo := collection.NewCollectionGroupRepo(dataData)
tagRelRepo := tag.NewTagListRepo(dataData)
tagRelRepo := tag.NewTagRelRepo(dataData)
tagCommonService := tagcommon.NewTagCommonService(tagRepo, tagRelRepo, revisionService)
collectionCommon := collectioncommon.NewCollectionCommon(collectionRepo)
answerCommon := answercommon.NewAnswerCommon(answerRepo)
@ -146,7 +149,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
questionController := controller.NewQuestionController(questionService, rankService)
answerService := service.NewAnswerService(answerRepo, questionRepo, questionCommon, userCommon, collectionCommon, userRepo, revisionService, answerActivityService, answerCommon, voteRepo)
answerController := controller.NewAnswerController(answerService, rankService)
searchRepo := repo.NewSearchRepo(dataData, uniqueIDRepo, userCommon)
searchRepo := search_common.NewSearchRepo(dataData, uniqueIDRepo, userCommon)
searchService := service.NewSearchService(searchRepo, tagRepo, userCommon, followRepo)
searchController := controller.NewSearchController(searchService)
serviceRevisionService := service.NewRevisionService(revisionRepo, userCommon, questionCommon, answerService)
@ -156,7 +159,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
reportHandle := report_handle_backyard.NewReportHandle(questionCommon, commentRepo, configRepo)
reportBackyardService := report_backyard.NewReportBackyardService(reportRepo, userCommon, commonRepo, answerRepo, questionRepo, commentCommonRepo, reportHandle, configRepo)
controller_backyardReportController := controller_backyard.NewReportController(reportBackyardService)
userBackyardRepo := user.NewUserBackyardRepo(dataData)
userBackyardRepo := user.NewUserBackyardRepo(dataData, authRepo)
userBackyardService := user_backyard.NewUserBackyardService(userBackyardRepo)
userBackyardController := controller_backyard.NewUserBackyardController(userBackyardService)
reasonRepo := reason.NewReasonRepo(configRepo)

View File

@ -3,10 +3,10 @@ server:
addr: 0.0.0.0:80
data:
database:
driver: "mysql"
connection: root:root@tcp(db:3306)/answer
driver: "sqlite3"
connection: "/data/sqlite3/answer.db"
cache:
file_path: "/tmp/cache/cache.db"
file_path: "/data/cache/cache.db"
i18n:
bundle_dir: "/data/i18n"
swaggerui:

View File

@ -1,29 +1,12 @@
version: "3.9"
version: "3"
services:
answer:
image: answerdev/answer:latest
image: answerdev/answer
ports:
- '9080:80'
restart: on-failure
depends_on:
db:
condition: service_healthy
links:
- db
volumes:
- ./answer-data/data:/data
db:
image: mariadb:10.4.7
ports:
- '13306:3306'
restart: on-failure
environment:
MYSQL_DATABASE: answer
MYSQL_ROOT_PASSWORD: root
healthcheck:
test: [ "CMD", "mysqladmin" ,"ping", "-uroot", "-proot"]
timeout: 20s
retries: 10
command: ['mysqld', '--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci', '--skip-character-set-client-handshake']
volumes:
- ./answer-data/mysql:/var/lib/mysql
- answer-data:/data
volumes:
answer-data:

View File

@ -56,6 +56,18 @@ const docTemplate = `{
"description": "user status",
"name": "status",
"in": "query"
},
{
"type": "string",
"description": "answer id or question title",
"name": "query",
"in": "query"
},
{
"type": "string",
"description": "question id",
"name": "question_id",
"in": "query"
}
],
"responses": {
@ -173,6 +185,12 @@ const docTemplate = `{
"description": "user status",
"name": "status",
"in": "query"
},
{
"type": "string",
"description": "question id or title",
"name": "query",
"in": "query"
}
],
"responses": {
@ -709,19 +727,12 @@ const docTemplate = `{
},
{
"type": "string",
"description": "username",
"name": "username",
"in": "query"
},
{
"type": "string",
"description": "email",
"name": "e_mail",
"description": "search query: email, username or id:[id]",
"name": "query",
"in": "query"
},
{
"enum": [
"normal",
"suspended",
"deleted",
"inactive"
@ -3303,7 +3314,7 @@ const docTemplate = `{
"ApiKeyAuth": []
}
],
"description": "GetUserInfoByUserID",
"description": "get user info, if user no login response http code is 200, but user info is null",
"consumes": [
"application/json"
],
@ -3326,7 +3337,7 @@ const docTemplate = `{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/schema.GetUserResp"
"$ref": "#/definitions/schema.GetUserToSetShowResp"
}
}
}
@ -4108,6 +4119,23 @@ const docTemplate = `{
}
}
},
"schema.AvatarInfo": {
"type": "object",
"properties": {
"custom": {
"type": "string",
"maxLength": 200
},
"gravatar": {
"type": "string",
"maxLength": 200
},
"type": {
"type": "string",
"maxLength": 100
}
}
},
"schema.CloseQuestionReq": {
"type": "object",
"required": [
@ -4836,6 +4864,102 @@ const docTemplate = `{
}
}
},
"schema.GetUserToSetShowResp": {
"type": "object",
"properties": {
"access_token": {
"description": "access token",
"type": "string"
},
"answer_count": {
"description": "answer count",
"type": "integer"
},
"authority_group": {
"description": "authority group",
"type": "integer"
},
"avatar": {
"$ref": "#/definitions/schema.AvatarInfo"
},
"bio": {
"description": "bio markdown",
"type": "string"
},
"bio_html": {
"description": "bio html",
"type": "string"
},
"created_at": {
"description": "create time",
"type": "integer"
},
"display_name": {
"description": "display name",
"type": "string"
},
"e_mail": {
"description": "email",
"type": "string"
},
"follow_count": {
"description": "follow count",
"type": "integer"
},
"id": {
"description": "user id",
"type": "string"
},
"ip_info": {
"description": "ip info",
"type": "string"
},
"is_admin": {
"description": "is admin",
"type": "boolean"
},
"last_login_date": {
"description": "last login date",
"type": "integer"
},
"location": {
"description": "location",
"type": "string"
},
"mail_status": {
"description": "mail status(1 pass 2 to be verified)",
"type": "integer"
},
"mobile": {
"description": "mobile",
"type": "string"
},
"notice_status": {
"description": "notice status(1 on 2off)",
"type": "integer"
},
"question_count": {
"description": "question count",
"type": "integer"
},
"rank": {
"description": "rank",
"type": "integer"
},
"status": {
"description": "user status",
"type": "string"
},
"username": {
"description": "username",
"type": "string"
},
"website": {
"description": "website",
"type": "string"
}
}
},
"schema.GetVoteWithPageResp": {
"type": "object",
"properties": {
@ -4962,7 +5086,7 @@ const docTemplate = `{
"type": "integer"
},
"tag": {
"description": "Tags []string ` + "`" + `json:\"tags\" form:\"tags\"` + "`" + ` //Search tag",
"description": "Tags []string ` + "`" + `json:\"tags\" form:\"tags\"` + "`" + ` // Search tag",
"type": "string"
},
"username": {
@ -5318,8 +5442,7 @@ const docTemplate = `{
"properties": {
"avatar": {
"description": "avatar",
"type": "string",
"maxLength": 500
"$ref": "#/definitions/schema.AvatarInfo"
},
"bio": {
"description": "bio",
@ -5516,6 +5639,14 @@ const docTemplate = `{
"e_mail"
],
"properties": {
"captcha_code": {
"type": "string",
"maxLength": 500
},
"captcha_id": {
"type": "string",
"maxLength": 500
},
"e_mail": {
"type": "string",
"maxLength": 500

View File

@ -44,6 +44,18 @@
"description": "user status",
"name": "status",
"in": "query"
},
{
"type": "string",
"description": "answer id or question title",
"name": "query",
"in": "query"
},
{
"type": "string",
"description": "question id",
"name": "question_id",
"in": "query"
}
],
"responses": {
@ -161,6 +173,12 @@
"description": "user status",
"name": "status",
"in": "query"
},
{
"type": "string",
"description": "question id or title",
"name": "query",
"in": "query"
}
],
"responses": {
@ -697,19 +715,12 @@
},
{
"type": "string",
"description": "username",
"name": "username",
"in": "query"
},
{
"type": "string",
"description": "email",
"name": "e_mail",
"description": "search query: email, username or id:[id]",
"name": "query",
"in": "query"
},
{
"enum": [
"normal",
"suspended",
"deleted",
"inactive"
@ -3291,7 +3302,7 @@
"ApiKeyAuth": []
}
],
"description": "GetUserInfoByUserID",
"description": "get user info, if user no login response http code is 200, but user info is null",
"consumes": [
"application/json"
],
@ -3314,7 +3325,7 @@
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/schema.GetUserResp"
"$ref": "#/definitions/schema.GetUserToSetShowResp"
}
}
}
@ -4096,6 +4107,23 @@
}
}
},
"schema.AvatarInfo": {
"type": "object",
"properties": {
"custom": {
"type": "string",
"maxLength": 200
},
"gravatar": {
"type": "string",
"maxLength": 200
},
"type": {
"type": "string",
"maxLength": 100
}
}
},
"schema.CloseQuestionReq": {
"type": "object",
"required": [
@ -4824,6 +4852,102 @@
}
}
},
"schema.GetUserToSetShowResp": {
"type": "object",
"properties": {
"access_token": {
"description": "access token",
"type": "string"
},
"answer_count": {
"description": "answer count",
"type": "integer"
},
"authority_group": {
"description": "authority group",
"type": "integer"
},
"avatar": {
"$ref": "#/definitions/schema.AvatarInfo"
},
"bio": {
"description": "bio markdown",
"type": "string"
},
"bio_html": {
"description": "bio html",
"type": "string"
},
"created_at": {
"description": "create time",
"type": "integer"
},
"display_name": {
"description": "display name",
"type": "string"
},
"e_mail": {
"description": "email",
"type": "string"
},
"follow_count": {
"description": "follow count",
"type": "integer"
},
"id": {
"description": "user id",
"type": "string"
},
"ip_info": {
"description": "ip info",
"type": "string"
},
"is_admin": {
"description": "is admin",
"type": "boolean"
},
"last_login_date": {
"description": "last login date",
"type": "integer"
},
"location": {
"description": "location",
"type": "string"
},
"mail_status": {
"description": "mail status(1 pass 2 to be verified)",
"type": "integer"
},
"mobile": {
"description": "mobile",
"type": "string"
},
"notice_status": {
"description": "notice status(1 on 2off)",
"type": "integer"
},
"question_count": {
"description": "question count",
"type": "integer"
},
"rank": {
"description": "rank",
"type": "integer"
},
"status": {
"description": "user status",
"type": "string"
},
"username": {
"description": "username",
"type": "string"
},
"website": {
"description": "website",
"type": "string"
}
}
},
"schema.GetVoteWithPageResp": {
"type": "object",
"properties": {
@ -4950,7 +5074,7 @@
"type": "integer"
},
"tag": {
"description": "Tags []string `json:\"tags\" form:\"tags\"` //Search tag",
"description": "Tags []string `json:\"tags\" form:\"tags\"` // Search tag",
"type": "string"
},
"username": {
@ -5306,8 +5430,7 @@
"properties": {
"avatar": {
"description": "avatar",
"type": "string",
"maxLength": 500
"$ref": "#/definitions/schema.AvatarInfo"
},
"bio": {
"description": "bio",
@ -5504,6 +5627,14 @@
"e_mail"
],
"properties": {
"captcha_code": {
"type": "string",
"maxLength": 500
},
"captcha_id": {
"type": "string",
"maxLength": 500
},
"e_mail": {
"type": "string",
"maxLength": 500

View File

@ -139,6 +139,18 @@ definitions:
description: title
type: string
type: object
schema.AvatarInfo:
properties:
custom:
maxLength: 200
type: string
gravatar:
maxLength: 200
type: string
type:
maxLength: 100
type: string
type: object
schema.CloseQuestionReq:
properties:
close_msg:
@ -671,6 +683,77 @@ definitions:
description: website
type: string
type: object
schema.GetUserToSetShowResp:
properties:
access_token:
description: access token
type: string
answer_count:
description: answer count
type: integer
authority_group:
description: authority group
type: integer
avatar:
$ref: '#/definitions/schema.AvatarInfo'
bio:
description: bio markdown
type: string
bio_html:
description: bio html
type: string
created_at:
description: create time
type: integer
display_name:
description: display name
type: string
e_mail:
description: email
type: string
follow_count:
description: follow count
type: integer
id:
description: user id
type: string
ip_info:
description: ip info
type: string
is_admin:
description: is admin
type: boolean
last_login_date:
description: last login date
type: integer
location:
description: location
type: string
mail_status:
description: mail status(1 pass 2 to be verified)
type: integer
mobile:
description: mobile
type: string
notice_status:
description: notice status(1 on 2off)
type: integer
question_count:
description: question count
type: integer
rank:
description: rank
type: integer
status:
description: user status
type: string
username:
description: username
type: string
website:
description: website
type: string
type: object
schema.GetVoteWithPageResp:
properties:
answer_id:
@ -763,7 +846,7 @@ definitions:
description: Search page size
type: integer
tag:
description: Tags []string `json:"tags" form:"tags"` //Search
description: Tags []string `json:"tags" form:"tags"` // Search
tag
type: string
username:
@ -1013,9 +1096,8 @@ definitions:
schema.UpdateInfoRequest:
properties:
avatar:
$ref: '#/definitions/schema.AvatarInfo'
description: avatar
maxLength: 500
type: string
bio:
description: bio
maxLength: 4096
@ -1159,6 +1241,12 @@ definitions:
type: object
schema.UserChangeEmailSendCodeReq:
properties:
captcha_code:
maxLength: 500
type: string
captcha_id:
maxLength: 500
type: string
e_mail:
maxLength: 500
type: string
@ -1302,6 +1390,14 @@ paths:
in: query
name: status
type: string
- description: answer id or question title
in: query
name: query
type: string
- description: question id
in: query
name: question_id
type: string
produces:
- application/json
responses:
@ -1375,6 +1471,10 @@ paths:
in: query
name: status
type: string
- description: question id or title
in: query
name: query
type: string
produces:
- application/json
responses:
@ -1700,17 +1800,12 @@ paths:
in: query
name: page_size
type: integer
- description: username
- description: 'search query: email, username or id:[id]'
in: query
name: username
type: string
- description: email
in: query
name: e_mail
name: query
type: string
- description: user status
enum:
- normal
- suspended
- deleted
- inactive
@ -3283,7 +3378,8 @@ paths:
get:
consumes:
- application/json
description: GetUserInfoByUserID
description: get user info, if user no login response http code is 200, but
user info is null
produces:
- application/json
responses:
@ -3294,7 +3390,7 @@ paths:
- $ref: '#/definitions/handler.RespBody'
- properties:
data:
$ref: '#/definitions/schema.GetUserResp'
$ref: '#/definitions/schema.GetUserToSetShowResp'
type: object
security:
- ApiKeyAuth: []

59
go.mod
View File

@ -17,22 +17,23 @@ require (
github.com/google/wire v0.5.0
github.com/jinzhu/copier v0.3.5
github.com/jinzhu/now v1.1.5
github.com/lib/pq v1.10.2
github.com/mattn/go-sqlite3 v2.0.3+incompatible
github.com/lib/pq v1.10.7
github.com/mattn/go-sqlite3 v1.14.16
github.com/mojocn/base64Captcha v1.3.5
github.com/ory/dockertest/v3 v3.9.1
github.com/segmentfault/pacman v1.0.1
github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20220929065758-260b3093a347
github.com/segmentfault/pacman/contrib/conf/viper v0.0.0-20220929065758-260b3093a347
github.com/segmentfault/pacman/contrib/i18n v0.0.0-20220929065758-260b3093a347
github.com/segmentfault/pacman/contrib/log/zap v0.0.0-20220929065758-260b3093a347
github.com/segmentfault/pacman/contrib/server/http v0.0.0-20220929065758-260b3093a347
github.com/spf13/cobra v1.5.0
github.com/stretchr/testify v1.8.0
github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20221018072427-a15dd1434e05
github.com/segmentfault/pacman/contrib/conf/viper v0.0.0-20221018072427-a15dd1434e05
github.com/segmentfault/pacman/contrib/i18n v0.0.0-20221018072427-a15dd1434e05
github.com/segmentfault/pacman/contrib/log/zap v0.0.0-20221018072427-a15dd1434e05
github.com/segmentfault/pacman/contrib/server/http v0.0.0-20221018072427-a15dd1434e05
github.com/spf13/cobra v1.6.1
github.com/stretchr/testify v1.8.1
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a
github.com/swaggo/gin-swagger v1.5.3
github.com/swaggo/swag v1.8.6
golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be
golang.org/x/net v0.0.0-20220927171203-f486391704dc
github.com/swaggo/swag v1.8.7
golang.org/x/crypto v0.1.0
golang.org/x/net v0.1.0
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
xorm.io/builder v0.3.12
xorm.io/core v0.7.3
@ -40,19 +41,31 @@ require (
)
require (
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/andybalholm/brotli v1.0.1 // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/containerd/continuity v0.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/docker/cli v20.10.14+incompatible // indirect
github.com/docker/docker v20.10.7+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/spec v0.20.7 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
@ -62,14 +75,19 @@ require (
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nicksnyder/go-i18n/v2 v2.2.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/opencontainers/runc v1.1.2 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/spf13/afero v1.9.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
@ -78,13 +96,16 @@ require (
github.com/subosito/gotenv v1.4.1 // indirect
github.com/syndtr/goleveldb v1.0.0 // indirect
github.com/ugorji/go/codec v1.2.7 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.23.0 // indirect
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 // indirect
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.12 // indirect
golang.org/x/image v0.1.0 // indirect
golang.org/x/sys v0.1.0 // indirect
golang.org/x/text v0.4.0 // indirect
golang.org/x/tools v0.2.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/ini.v1 v1.67.0 // indirect

158
go.sum
View File

@ -39,6 +39,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU=
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
@ -49,6 +51,10 @@ github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
@ -62,8 +68,9 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/anargu/gin-brotli v0.0.0-20220116052358-12bf532d5267 h1:vDHsaEcs/Q0dwetADENtwus6W1ccaZ9h3KBTm0d2X0g=
github.com/anargu/gin-brotli v0.0.0-20220116052358-12bf532d5267/go.mod h1:Yj3yPP/vi87JjwylUTCMyd6FrOfGqP1AHk0305hDm2o=
github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc=
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
@ -82,11 +89,15 @@ github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgIS
github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
@ -95,15 +106,22 @@ github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnht
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg=
github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -111,6 +129,14 @@ github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/docker/cli v20.10.14+incompatible h1:dSBKJOVesDgHo7rbxlYjYsXe7gPzrTT+/cKQgpDAazg=
github.com/docker/cli v20.10.14+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/docker v20.10.7+incompatible h1:Z6O9Nhsjv+ayUEeI1IojKbYcsGdgYSNqxe1s2MYzUhQ=
github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
@ -127,10 +153,11 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
@ -182,12 +209,16 @@ github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGF
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
@ -251,6 +282,8 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -296,8 +329,11 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
@ -368,6 +404,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@ -395,8 +432,9 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
@ -424,8 +462,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
@ -437,6 +475,9 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk=
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -446,6 +487,7 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mojocn/base64Captcha v1.3.5 h1:Qeilr7Ta6eDtG4S+tQuZ5+hO+QHbiGAJdi4PfoagaA0=
github.com/mojocn/base64Captcha v1.3.5/go.mod h1:/tTTXn4WTpX9CfrmipqRytCpJ27Uw3G6I7NcP2WwcmY=
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
@ -466,6 +508,14 @@ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v1.1.2 h1:2VSZwLx5k/BfsBxMMipG/LYUnmqOD/BPkIVgQUcTlLw=
github.com/opencontainers/runc v1.1.2/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc=
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
@ -474,6 +524,8 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/ory/dockertest/v3 v3.9.1 h1:v4dkG+dlu76goxMiTT2j8zV7s4oPPEppKT8K8p2f1kY=
github.com/ory/dockertest/v3 v3.9.1/go.mod h1:42Ir9hmvaAPm0Mgibk6mBPi7SFvTXxEcnztDYOJ//uM=
github.com/otiai10/copy v1.7.0/go.mod h1:rmRl6QPdJj6EiUqXQ/4Nn2lLXoNQjFCQbbNrxgc/t3U=
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
@ -535,18 +587,19 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
github.com/segmentfault/pacman v1.0.1 h1:GFdvPtNxvVVjnDM4ty02D/+4unHwG9PmjcOZSc2wRXE=
github.com/segmentfault/pacman v1.0.1/go.mod h1:5lNp5REd8QMThmBUvR3Fi9Y3AsOB4GRq7soCB4QLqOs=
github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20220929065758-260b3093a347 h1:0xWBBXHHuemzMY61KYJXh7F5FW/4K8g98RYKNXodTCc=
github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20220929065758-260b3093a347/go.mod h1:rmf1TCwz67dyM+AmTwSd1BxTo2AOYHj262lP93bOZbs=
github.com/segmentfault/pacman/contrib/conf/viper v0.0.0-20220929065758-260b3093a347 h1:WpnEbmZFE8FYIgvseX+NJtDgGJlM1KSaKJhoxJywUgo=
github.com/segmentfault/pacman/contrib/conf/viper v0.0.0-20220929065758-260b3093a347/go.mod h1:prPjFam7MyZ5b3S9dcDOt2tMPz6kf7C9c243s9zSwPY=
github.com/segmentfault/pacman/contrib/i18n v0.0.0-20220929065758-260b3093a347 h1:Q29Ky9ZUGhdLIygfX6jwPYeEa7Wqn8o3f1NJWb8LvvE=
github.com/segmentfault/pacman/contrib/i18n v0.0.0-20220929065758-260b3093a347/go.mod h1:5Afm+OQdau/HQqSOp/ALlSUp0vZsMMMbv//kJhxuoi8=
github.com/segmentfault/pacman/contrib/log/zap v0.0.0-20220929065758-260b3093a347 h1:7Adjc296AKv32dg88S0T8t9K3+N+PFYLSCctpPnCUr0=
github.com/segmentfault/pacman/contrib/log/zap v0.0.0-20220929065758-260b3093a347/go.mod h1:L4GqtXLoR73obTYqUQIzfkm8NG8pvZafxFb6KZFSSHk=
github.com/segmentfault/pacman/contrib/server/http v0.0.0-20220929065758-260b3093a347 h1:CfuRhTPK2CBQIZruq5ceuTVthspe8U1FDjWXXI2RWdo=
github.com/segmentfault/pacman/contrib/server/http v0.0.0-20220929065758-260b3093a347/go.mod h1:UjNiOFYv1uGCq1ZCcONaKq4eE7MW3nbgpLqgl8f9N40=
github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20221018072427-a15dd1434e05 h1:rXsXgC/HR7m4V425l9pDBW/qxJv6zCh6pEvvO1ZCNsI=
github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20221018072427-a15dd1434e05/go.mod h1:rmf1TCwz67dyM+AmTwSd1BxTo2AOYHj262lP93bOZbs=
github.com/segmentfault/pacman/contrib/conf/viper v0.0.0-20221018072427-a15dd1434e05 h1:BlqTgc3/MYKG6vMI2MI+6o+7P4Gy5PXlawu185wPXAk=
github.com/segmentfault/pacman/contrib/conf/viper v0.0.0-20221018072427-a15dd1434e05/go.mod h1:prPjFam7MyZ5b3S9dcDOt2tMPz6kf7C9c243s9zSwPY=
github.com/segmentfault/pacman/contrib/i18n v0.0.0-20221018072427-a15dd1434e05 h1:gFCY9KUxhYg+/MXNcDYl4ILK+R1SG78FtaSR3JqZNYY=
github.com/segmentfault/pacman/contrib/i18n v0.0.0-20221018072427-a15dd1434e05/go.mod h1:5Afm+OQdau/HQqSOp/ALlSUp0vZsMMMbv//kJhxuoi8=
github.com/segmentfault/pacman/contrib/log/zap v0.0.0-20221018072427-a15dd1434e05 h1:jcGZU2juv0L3eFEkuZYV14ESLUlWfGMWnP0mjOfrSZc=
github.com/segmentfault/pacman/contrib/log/zap v0.0.0-20221018072427-a15dd1434e05/go.mod h1:L4GqtXLoR73obTYqUQIzfkm8NG8pvZafxFb6KZFSSHk=
github.com/segmentfault/pacman/contrib/server/http v0.0.0-20221018072427-a15dd1434e05 h1:91is1nKNbfTOl8CvMYiFgg4c5Vmol+5mVmMV/jDXD+A=
github.com/segmentfault/pacman/contrib/server/http v0.0.0-20221018072427-a15dd1434e05/go.mod h1:UjNiOFYv1uGCq1ZCcONaKq4eE7MW3nbgpLqgl8f9N40=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
@ -554,6 +607,9 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
@ -563,11 +619,12 @@ github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcD
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU=
@ -579,6 +636,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@ -586,8 +644,9 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a h1:kAe4YSu0O0UFn1DowNo2MY5p6xzqtJ/wQ7LZynSvGaY=
@ -595,8 +654,9 @@ github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a/go.mod h1:lKJPbtWzJ9J
github.com/swaggo/gin-swagger v1.5.3 h1:8mWmHLolIbrhJJTflsaFoZzRBYVmEE7JZGIq08EiC0Q=
github.com/swaggo/gin-swagger v1.5.3/go.mod h1:3XJKSfHjDMB5dBo/0rrTXidPmgLeqsX89Yp4uA50HpI=
github.com/swaggo/swag v1.8.1/go.mod h1:ugemnJsPZm/kRwFUnzBlbHRd0JY9zE1M4F+uy2pAaPQ=
github.com/swaggo/swag v1.8.6 h1:2rgOaLbonWu1PLP6G+/rYjSvPg0jQE0HtrEKuE380eg=
github.com/swaggo/swag v1.8.6/go.mod h1:jMLeXOOmYyjk8PvHTsXBdrubsNd9gUJTTCzL5iBnseg=
github.com/swaggo/swag v1.8.7 h1:2K9ivTD3teEO+2fXV6zrZKDqk5IuU2aJtBDo8U7omWU=
github.com/swaggo/swag v1.8.7/go.mod h1:ezQVUUhly8dludpVk+/PuwJWvLLanB13ygV5Pr9enSk=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@ -608,12 +668,21 @@ github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
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=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
@ -663,10 +732,11 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A=
golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -681,8 +751,8 @@ golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMx
golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 h1:Lj6HJGCSn5AjxRAH2+r35Mir4icalbqku+CLUtjnvXY=
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
golang.org/x/image v0.1.0 h1:r8Oj8ZA2Xy12/b5KZYj3tuv7NG/fBz3TwQVvpJ9l8Rk=
golang.org/x/image v0.1.0/go.mod h1:iyPr49SD/G/TBxYVB/9RRtGUT5eNbo2u4NamWeQcD5c=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -705,7 +775,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -749,8 +820,9 @@ golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220927171203-f486391704dc h1:FxpXZdoBqT8RjqTy6i1E8nXHhW21wK7ptQ/EPIGxzPQ=
golang.org/x/net v0.0.0-20220927171203-f486391704dc/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -771,6 +843,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -788,12 +861,14 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -811,6 +886,7 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -818,6 +894,7 @@ golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -827,12 +904,17 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI=
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -843,8 +925,9 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -865,6 +948,7 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@ -895,6 +979,7 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
@ -904,11 +989,13 @@ golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -1013,6 +1100,7 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
@ -1051,6 +1139,8 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
gotest.tools/v3 v3.2.0 h1:I0DwBVMGAx26dttAj1BtJLAkVGncrkkUXfJLC4Flt/I=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -1,91 +1,93 @@
base:
success:
other: "success"
other: "Success."
unknown:
other: "unknown error"
other: "Unknown error."
request_format_error:
other: "request format is not valid"
other: "Request format is not valid."
unauthorized_error:
other: "unauthorized"
other: "Unauthorized."
database_error:
other: "data server error"
other: "Data server error."
email:
other: "email"
other: "Email"
password:
other: "password"
other: "Password"
email_or_password_wrong_error: &email_or_password_wrong
other: "email or password wrong"
other: "Email and password do not match."
error:
admin:
email_or_password_wrong: *email_or_password_wrong
answer:
not_found:
other: "answer not found"
other: "Answer do not found."
comment:
edit_without_permission:
other: "comment not allowed to edit"
other: "Comment are not allowed to edit."
not_found:
other: "comment not found"
other: "Comment not found."
email:
duplicate:
other: "email already exists"
other: "Email already exists."
need_to_be_verified:
other: "email should be verified"
other: "Email should be verified."
verify_url_expired:
other: "email verified url is expired, please resend the email"
other: "Email verified URL has expired, please resend the email."
lang:
not_found:
other: "language not found"
other: "Language file not found."
object:
captcha_verification_failed:
other: "captcha wrong"
other: "Captcha wrong."
disallow_follow:
other: "You are not allowed to follow"
other: "You are not allowed to follow."
disallow_vote:
other: "You are not allowed to vote"
other: "You are not allowed to vote."
disallow_vote_your_self:
other: "You can't vote for your own post!"
other: "You can't vote for your own post."
not_found:
other: "object not found"
other: "Object not found."
verification_failed:
other: "verification failed"
other: "Verification failed."
email_or_password_incorrect:
other: "email or password incorrect"
other: "Email and password do not match."
old_password_verification_failed:
other: "the old password verification failed"
other: "The old password verification failed"
new_password_same_as_previous_setting:
other: "The new password is the same as the previous setting"
other: "The new password is the same as the previous one."
question:
not_found:
other: "question not found"
other: "Question not found."
rank:
fail_to_meet_the_condition:
other: "rank fail to meet the condition"
other: "Rank fail to meet the condition."
report:
handle_failed:
other: "report handle failed"
other: "Report handle failed."
not_found:
other: "report not found"
other: "Report not found."
tag:
not_found:
other: "tag not found"
other: "Tag not found."
theme:
not_found:
other: "theme not found"
other: "Theme not found."
user:
email_or_password_wrong:
other: *email_or_password_wrong
not_found:
other: "user not found"
other: "User not found."
suspended:
other: "user is suspended"
other: "User has been suspended."
username_invalid:
other: "username is invalid"
other: "Username is invalid."
username_duplicate:
other: "username is already in use"
other: "Username is already in use."
set_avatar:
other: "Avatar set failed."
report:
spam:
@ -161,10 +163,10 @@ notification:
mention_you:
other: "mentioned you"
your_question_is_closed:
other: "your question has been closed"
other: "Your question has been closed"
your_question_was_deleted:
other: "your question has been deleted"
other: "Your question has been deleted"
your_answer_was_deleted:
other: "your answer has been deleted"
other: "Your answer has been deleted"
your_comment_was_deleted:
other: "your comment has been deleted"
other: "Your comment has been deleted"

170
i18n/it_IT.yaml Normal file
View File

@ -0,0 +1,170 @@
base:
success:
other: "Successo"
unknown:
other: "Errore sconosciuto"
request_format_error:
other: "Il formato della richiesta non è valido"
unauthorized_error:
other: "Non autorizzato"
database_error:
other: "Errore server dati"
email:
other: "email"
password:
other: "password"
email_or_password_wrong_error: &email_or_password_wrong
other: "Email o password errati"
error:
admin:
email_or_password_wrong: *email_or_password_wrong
answer:
not_found:
other: "Risposta non trovata"
comment:
edit_without_permission:
other: "Non si hanno di privilegi sufficienti per modificare il commento"
not_found:
other: "Commento non trovato"
email:
duplicate:
other: "email già esistente"
need_to_be_verified:
other: "email deve essere verificata"
verify_url_expired:
other: "l'url di verifica email è scaduto, si prega di reinviare la email"
lang:
not_found:
other: "lingua non trovata"
object:
captcha_verification_failed:
other: "captcha errato"
disallow_follow:
other: "Non sei autorizzato a seguire"
disallow_vote:
other: "non sei autorizzato a votare"
disallow_vote_your_self:
other: "Non puoi votare un tuo post!"
not_found:
other: "oggetto non trovato"
verification_failed:
other: "verifica fallita"
email_or_password_incorrect:
other: "email o password incorretti"
old_password_verification_failed:
other: "la verifica della vecchia password è fallita"
new_password_same_as_previous_setting:
other: "La nuova password è identica alla precedente"
question:
not_found:
other: "domanda non trovata"
rank:
fail_to_meet_the_condition:
other: "Condizioni non valide per il grado"
report:
handle_failed:
other: "Gestione del report fallita"
not_found:
other: "Report non trovato"
tag:
not_found:
other: "Etichetta non trovata"
theme:
not_found:
other: "tema non trovato"
user:
email_or_password_wrong:
other: *email_or_password_wrong
not_found:
other: "utente non trovato"
suspended:
other: "utente sospeso"
username_invalid:
other: "utente non valido"
username_duplicate:
other: "utente già in uso"
report:
spam:
name:
other: "spam"
description:
other: "Questo articolo è una pubblicità o vandalismo. Non è utile o rilevante all'argomento corrente"
rude:
name:
other: "scortese o violento"
description:
other: "Una persona ragionevole trova questo contenuto inappropriato a un discorso rispettoso"
duplicate:
name:
other: "duplicato"
description:
other: "Questa domanda è già stata posta e ha già una risposta."
not_answer:
name:
other: "non è una risposta"
description:
other: "Questo è stato postato come una risposta, ma non sta cercando di rispondere alla domanda. Dovrebbe essere una modifica, un commento, un'altra domanda o cancellato del tutto."
not_need:
name:
other: "non più necessario"
description:
other: "Questo commento è datato, conversazionale o non rilevante a questo articolo."
other:
name:
other: "altro"
description:
other: "Questo articolo richiede una supervisione dello staff per altre ragioni non listate sopra."
question:
close:
duplicate:
name:
other: "spam"
description:
other: "Questa domanda è già stata chiesta o ha già una risposta."
guideline:
name:
other: "motivo legato alla community"
description:
other: "Questa domanda non soddisfa le linee guida della comunità."
multiple:
name:
other: "richiede maggiori dettagli o chiarezza"
description:
other: "Questa domanda attualmente contiene più domande. Deve concentrarsi solamente su un unico problema."
other:
name:
other: "altro"
description:
other: "Questo articolo richiede un'altro motivo non listato sopra."
notification:
action:
update_question:
other: "domanda aggiornata"
answer_the_question:
other: "domanda risposta"
update_answer:
other: "risposta aggiornata"
adopt_answer:
other: "risposta accettata"
comment_question:
other: "domanda commentata"
comment_answer:
other: "risposta commentata"
reply_to_you:
other: "hai ricevuto risposta"
mention_you:
other: "sei stato menzionato"
your_question_is_closed:
other: "la tua domanda è stata chiusa"
your_question_was_deleted:
other: "la tua domanda è stata rimossa"
your_answer_was_deleted:
other: "la tua risposta è stata rimossa"
your_comment_was_deleted:
other: "il tuo commento è stato rimosso"

View File

@ -86,6 +86,8 @@ error:
other: "用户名无效"
username_duplicate:
other: "用户名已被使用"
set_avatar:
other: "头像设置错误"
report:
spam:

View File

@ -3,7 +3,7 @@ package constant
import "time"
const (
Default_PageSize = 20 //Default number of pages
DefaultPageSize = 20 // Default number of pages
UserStatusChangedCacheKey = "answer:user:status:"
UserStatusChangedCacheTime = 7 * 24 * time.Hour
UserTokenCacheKey = "answer:user:token:"

View File

@ -13,7 +13,7 @@ const (
ReportNotAnswerDescription = "report.not_answer.description"
ReportNotNeedName = "report.not_need.name"
ReportNotNeedDescription = "report.not_need.description"
//question close
// question close
QuestionCloseDuplicateName = "question.close.duplicate.name"
QuestionCloseDuplicateDescription = "question.close.duplicate.description"
QuestionCloseGuidelineName = "question.close.guideline.name"
@ -27,8 +27,8 @@ const (
const (
// TODO put this in database
// TODO need reason controller to resolve
QuestionCloseJson = `[{"name":"question.close.duplicate.name","description":"question.close.duplicate.description","source":"question","type":1,"have_content":false,"content_type":""},{"name":"question.close.guideline.name","description":"question.close.guideline.description","source":"question","type":2,"have_content":false,"content_type":""},{"name":"question.close.multiple.name","description":"question.close.multiple.description","source":"question","type":3,"have_content":true,"content_type":"text"},{"name":"question.close.other.name","description":"question.close.other.description","source":"question","type":4,"have_content":true,"content_type":"textarea"}]`
QuestionReportJson = `[{"name":"report.spam.name","description":"report.spam.description","source":"question","type":1,"have_content":false,"content_type":""},{"name":"report.rude.name","description":"report.rude.description","source":"question","type":2,"have_content":false,"content_type":""},{"name":"report.duplicate.name","description":"report.duplicate.description","source":"question","type":3,"have_content":true,"content_type":"text"},{"name":"report.other.name","description":"report.other.description","source":"question","type":4,"have_content":true,"content_type":"textarea"}]`
AnswerReportJson = `[{"name":"report.spam.name","description":"report.spam.description","source":"answer","type":1,"have_content":false,"content_type":""},{"name":"report.rude.name","description":"report.rude.description","source":"answer","type":2,"have_content":false,"content_type":""},{"name":"report.not_answer.name","description":"report.not_answer.description","source":"answer","type":3,"have_content":false,"content_type":""},{"name":"report.other.name","description":"report.other.description","source":"answer","type":4,"have_content":true,"content_type":"textarea"}]`
CommentReportJson = `[{"name":"report.spam.name","description":"report.spam.description","source":"comment","type":1,"have_content":false,"content_type":""},{"name":"report.rude.name","description":"report.rude.description","source":"comment","type":2,"have_content":false,"content_type":""},{"name":"report.not_need.name","description":"report.not_need.description","source":"comment","type":3,"have_content":true,"content_type":"text"},{"name":"report.other.name","description":"report.other.description","source":"comment","type":4,"have_content":true,"content_type":"textarea"}]`
QuestionCloseJSON = `[{"name":"question.close.duplicate.name","description":"question.close.duplicate.description","source":"question","type":1,"have_content":false,"content_type":""},{"name":"question.close.guideline.name","description":"question.close.guideline.description","source":"question","type":2,"have_content":false,"content_type":""},{"name":"question.close.multiple.name","description":"question.close.multiple.description","source":"question","type":3,"have_content":true,"content_type":"text"},{"name":"question.close.other.name","description":"question.close.other.description","source":"question","type":4,"have_content":true,"content_type":"textarea"}]`
QuestionReportJSON = `[{"name":"report.spam.name","description":"report.spam.description","source":"question","type":1,"have_content":false,"content_type":""},{"name":"report.rude.name","description":"report.rude.description","source":"question","type":2,"have_content":false,"content_type":""},{"name":"report.duplicate.name","description":"report.duplicate.description","source":"question","type":3,"have_content":true,"content_type":"text"},{"name":"report.other.name","description":"report.other.description","source":"question","type":4,"have_content":true,"content_type":"textarea"}]`
AnswerReportJSON = `[{"name":"report.spam.name","description":"report.spam.description","source":"answer","type":1,"have_content":false,"content_type":""},{"name":"report.rude.name","description":"report.rude.description","source":"answer","type":2,"have_content":false,"content_type":""},{"name":"report.not_answer.name","description":"report.not_answer.description","source":"answer","type":3,"have_content":false,"content_type":""},{"name":"report.other.name","description":"report.other.description","source":"answer","type":4,"have_content":true,"content_type":"textarea"}]`
CommentReportJSON = `[{"name":"report.spam.name","description":"report.spam.description","source":"comment","type":1,"have_content":false,"content_type":""},{"name":"report.rude.name","description":"report.rude.description","source":"comment","type":2,"have_content":false,"content_type":""},{"name":"report.not_need.name","description":"report.not_need.description","source":"comment","type":3,"have_content":true,"content_type":"text"},{"name":"report.other.name","description":"report.other.description","source":"comment","type":4,"have_content":true,"content_type":"textarea"}]`
)

View File

@ -1,8 +1,10 @@
package data
import (
"path/filepath"
"time"
"github.com/answerdev/answer/pkg/dir"
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
_ "github.com/mattn/go-sqlite3"
@ -35,6 +37,13 @@ func NewDB(debug bool, dataConf *Database) (*xorm.Engine, error) {
if dataConf.Driver == "" {
dataConf.Driver = string(schemas.MYSQL)
}
if dataConf.Driver == string(schemas.SQLITE) {
dbFileDir := filepath.Dir(dataConf.Connection)
log.Debugf("try to create database directory %s", dbFileDir)
if err := dir.CreateDirIfNotExist(dbFileDir); err != nil {
log.Errorf("create database dir failed: %s", err)
}
}
engine, err := xorm.NewEngine(dataConf.Driver, dataConf.Connection)
if err != nil {
return nil, err
@ -69,6 +78,12 @@ func NewCache(c *CacheConf) (cache.Cache, func(), error) {
memCache := memory.NewCache()
if len(c.FilePath) > 0 {
cacheFileDir := filepath.Dir(c.FilePath)
log.Debugf("try to create cache directory %s", cacheFileDir)
err := dir.CreateDirIfNotExist(cacheFileDir)
if err != nil {
log.Errorf("create cache dir failed: %s", err)
}
log.Infof("try to load cache file from %s", c.FilePath)
if err := memory.Load(memCache, c.FilePath); err != nil {
log.Warn(err)

View File

@ -40,7 +40,6 @@ func HandleResponse(ctx *gin.Context, err error, data interface{}) {
respBody.Data = data
}
ctx.JSON(myErr.Code, respBody)
return
}
// BindAndCheck bind request and check

View File

@ -14,9 +14,7 @@ import (
"github.com/segmentfault/pacman/errors"
)
var (
ctxUuidKey = "ctxUuidKey"
)
var ctxUUIDKey = "ctxUuidKey"
// AuthUserMiddleware auth user middleware
type AuthUserMiddleware struct {
@ -44,7 +42,7 @@ func (am *AuthUserMiddleware) Auth() gin.HandlerFunc {
return
}
if userInfo != nil {
ctx.Set(ctxUuidKey, userInfo)
ctx.Set(ctxUUIDKey, userInfo)
}
ctx.Next()
}
@ -82,7 +80,7 @@ func (am *AuthUserMiddleware) MustAuth() gin.HandlerFunc {
ctx.Abort()
return
}
ctx.Set(ctxUuidKey, userInfo)
ctx.Set(ctxUUIDKey, userInfo)
ctx.Next()
}
}
@ -107,7 +105,7 @@ func (am *AuthUserMiddleware) CmsAuth() gin.HandlerFunc {
ctx.Abort()
return
}
ctx.Set(ctxUuidKey, userInfo)
ctx.Set(ctxUUIDKey, userInfo)
}
ctx.Next()
}
@ -115,7 +113,7 @@ func (am *AuthUserMiddleware) CmsAuth() gin.HandlerFunc {
// GetLoginUserIDFromContext get user id from context
func GetLoginUserIDFromContext(ctx *gin.Context) (userID string) {
userInfo, exist := ctx.Get(ctxUuidKey)
userInfo, exist := ctx.Get(ctxUUIDKey)
if !exist {
return ""
}
@ -128,7 +126,7 @@ func GetLoginUserIDFromContext(ctx *gin.Context) (userID string) {
// GetUserInfoFromContext get user info from context
func GetUserInfoFromContext(ctx *gin.Context) (u *entity.UserCacheInfo) {
userInfo, exist := ctx.Get(ctxUuidKey)
userInfo, exist := ctx.Get(ctxUUIDKey)
if !exist {
return nil
}

View File

@ -26,8 +26,9 @@ const (
UserNotFound = "error.user.not_found"
UsernameInvalid = "error.user.username_invalid"
UsernameDuplicate = "error.user.username_duplicate"
UserSetAvatar = "error.user.set_avatar"
EmailDuplicate = "error.email.duplicate"
EmailVerifyUrlExpired = "error.email.verify_url_expired"
EmailVerifyURLExpired = "error.email.verify_url_expired"
EmailNeedToBeVerified = "error.email.need_to_be_verified"
UserSuspended = "error.user.suspended"
ObjectNotFound = "error.object.not_found"

View File

@ -9,7 +9,7 @@ import (
"github.com/go-playground/locales"
english "github.com/go-playground/locales/en"
zhongwen "github.com/go-playground/locales/zh"
"github.com/go-playground/universal-translator"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
"github.com/go-playground/validator/v10/translations/en"
"github.com/go-playground/validator/v10/translations/zh"
@ -31,10 +31,8 @@ type ErrorField struct {
Value string `json:"value"`
}
var (
// GlobalValidatorMapping is a mapping from validator to translator used
GlobalValidatorMapping = make(map[string]*MyValidator, 0)
)
// GlobalValidatorMapping is a mapping from validator to translator used
var GlobalValidatorMapping = make(map[string]*MyValidator, 0)
func init() {
zhTran, zhVal := getTran(zhongwen.New(), i18n.LanguageChinese.Abbr()), createDefaultValidator(i18n.LanguageChinese)

View File

@ -31,7 +31,6 @@ func InstallAllInitialEnvironment(dataDirPath string) {
installUploadDir()
installI18nBundle()
fmt.Println("install all initial environment done")
return
}
func installConfigFile() {
@ -96,7 +95,7 @@ func installI18nBundle() {
}
func writerFile(filePath, content string) error {
file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0666)
file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0o666)
if err != nil {
return err
}

View File

@ -62,9 +62,9 @@ func (ac *AnswerController) RemoveAnswer(ctx *gin.Context) {
// @Success 200 {string} string ""
func (ac *AnswerController) Get(ctx *gin.Context) {
id := ctx.Query("id")
userId := middleware.GetLoginUserIDFromContext(ctx)
userID := middleware.GetLoginUserIDFromContext(ctx)
info, questionInfo, has, err := ac.answerService.Get(ctx, id, userId)
info, questionInfo, has, err := ac.answerService.Get(ctx, id, userID)
if err != nil {
handler.HandleResponse(ctx, err, gin.H{})
return
@ -101,18 +101,18 @@ func (ac *AnswerController) Add(ctx *gin.Context) {
return
}
answerId, err := ac.answerService.Insert(ctx, req)
answerID, err := ac.answerService.Insert(ctx, req)
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
info, questionInfo, has, err := ac.answerService.Get(ctx, answerId, req.UserID)
info, questionInfo, has, err := ac.answerService.Get(ctx, answerID, req.UserID)
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
if !has {
//todo !has
// todo !has
handler.HandleResponse(ctx, nil, nil)
return
}
@ -120,7 +120,6 @@ func (ac *AnswerController) Add(ctx *gin.Context) {
"info": info,
"question": questionInfo,
})
}
// Update godoc
@ -156,7 +155,7 @@ func (ac *AnswerController) Update(ctx *gin.Context) {
return
}
if !has {
//todo !has
// todo !has
handler.HandleResponse(ctx, nil, nil)
return
}

View File

@ -111,6 +111,6 @@ func (nc *NotificationController) GetList(ctx *gin.Context) {
return
}
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
resp, err := nc.notificationService.GetList(ctx, req)
resp, err := nc.notificationService.GetNotificationPage(ctx, req)
handler.HandleResponse(ctx, err, resp)
}

View File

@ -66,7 +66,7 @@ func (qc *QuestionController) CloseQuestion(ctx *gin.Context) {
if handler.BindAndCheck(ctx, req) {
return
}
req.UserId = middleware.GetLoginUserIDFromContext(ctx)
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
err := qc.questionService.CloseQuestion(ctx, req)
handler.HandleResponse(ctx, err, nil)
}
@ -114,7 +114,6 @@ func (qc *QuestionController) SimilarQuestion(ctx *gin.Context) {
"list": list,
"count": count,
})
}
// Index godoc
@ -365,6 +364,7 @@ func (qc *QuestionController) UserCollectionList(ctx *gin.Context) {
// @Param page query int false "page size"
// @Param page_size query int false "page size"
// @Param status query string false "user status" Enums(available, closed, deleted)
// @Param query query string false "question id or title"
// @Success 200 {object} handler.RespBody
// @Router /answer/admin/api/question/page [get]
func (qc *QuestionController) CmsSearchList(ctx *gin.Context) {
@ -390,6 +390,8 @@ func (qc *QuestionController) CmsSearchList(ctx *gin.Context) {
// @Param page query int false "page size"
// @Param page_size query int false "page size"
// @Param status query string false "user status" Enums(available,deleted)
// @Param query query string false "answer id or question title"
// @Param question_id query string false "question id"
// @Success 200 {object} handler.RespBody
// @Router /answer/admin/api/answer/page [get]
func (qc *QuestionController) CmsSearchAnswerList(ctx *gin.Context) {

View File

@ -35,7 +35,8 @@ func NewUserController(
userService *service.UserService,
actionService *action.CaptchaService,
emailService *export.EmailService,
uploaderService *uploader.UploaderService) *UserController {
uploaderService *uploader.UploaderService,
) *UserController {
return &UserController{
authService: authService,
userService: userService,
@ -45,18 +46,25 @@ func NewUserController(
}
}
// GetUserInfoByUserID godoc
// GetUserInfoByUserID get user info, if user no login response http code is 200, but user info is null
// @Summary GetUserInfoByUserID
// @Description GetUserInfoByUserID
// @Description get user info, if user no login response http code is 200, but user info is null
// @Tags User
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Success 200 {object} handler.RespBody{data=schema.GetUserResp}
// @Success 200 {object} handler.RespBody{data=schema.GetUserToSetShowResp}
// @Router /answer/api/v1/user/info [get]
func (uc *UserController) GetUserInfoByUserID(ctx *gin.Context) {
userID := middleware.GetLoginUserIDFromContext(ctx)
token := middleware.ExtractToken(ctx)
// if user is no login return null in data
if len(token) == 0 || len(userID) == 0 {
handler.HandleResponse(ctx, nil, nil)
return
}
resp, err := uc.userService.GetUserInfoByUserID(ctx, token, userID)
handler.HandleResponse(ctx, err, resp)
}
@ -112,7 +120,7 @@ func (uc *UserController) UserEmailLogin(ctx *gin.Context) {
return
}
captchaPass := uc.actionService.ActionRecordVerifyCaptcha(ctx, schema.ActionRecord_Type_Login, ctx.ClientIP(), req.CaptchaID, req.CaptchaCode)
captchaPass := uc.actionService.ActionRecordVerifyCaptcha(ctx, schema.ActionRecordTypeLogin, ctx.ClientIP(), req.CaptchaID, req.CaptchaCode)
if !captchaPass {
resp := schema.UserVerifyEmailErrorResponse{
Key: "captcha_code",
@ -125,7 +133,7 @@ func (uc *UserController) UserEmailLogin(ctx *gin.Context) {
resp, err := uc.userService.EmailLogin(ctx, req)
if err != nil {
_, _ = uc.actionService.ActionRecordAdd(ctx, schema.ActionRecord_Type_Login, ctx.ClientIP())
_, _ = uc.actionService.ActionRecordAdd(ctx, schema.ActionRecordTypeLogin, ctx.ClientIP())
resp := schema.UserVerifyEmailErrorResponse{
Key: "e_mail",
Value: "error.object.email_or_password_incorrect",
@ -134,7 +142,7 @@ func (uc *UserController) UserEmailLogin(ctx *gin.Context) {
handler.HandleResponse(ctx, errors.BadRequest(reason.CaptchaVerificationFailed), resp)
return
}
uc.actionService.ActionRecordDel(ctx, schema.ActionRecord_Type_Login, ctx.ClientIP())
uc.actionService.ActionRecordDel(ctx, schema.ActionRecordTypeLogin, ctx.ClientIP())
handler.HandleResponse(ctx, nil, resp)
}
@ -152,7 +160,7 @@ func (uc *UserController) RetrievePassWord(ctx *gin.Context) {
if handler.BindAndCheck(ctx, req) {
return
}
captchaPass := uc.actionService.ActionRecordVerifyCaptcha(ctx, schema.ActionRecord_Type_Find_Pass, ctx.ClientIP(), req.CaptchaID, req.CaptchaCode)
captchaPass := uc.actionService.ActionRecordVerifyCaptcha(ctx, schema.ActionRecordTypeFindPass, ctx.ClientIP(), req.CaptchaID, req.CaptchaCode)
if !captchaPass {
resp := schema.UserVerifyEmailErrorResponse{
Key: "captcha_code",
@ -162,7 +170,7 @@ func (uc *UserController) RetrievePassWord(ctx *gin.Context) {
handler.HandleResponse(ctx, errors.BadRequest(reason.CaptchaVerificationFailed), resp)
return
}
_, _ = uc.actionService.ActionRecordAdd(ctx, schema.ActionRecord_Type_Find_Pass, ctx.ClientIP())
_, _ = uc.actionService.ActionRecordAdd(ctx, schema.ActionRecordTypeFindPass, ctx.ClientIP())
code, err := uc.userService.RetrievePassWord(ctx, req)
handler.HandleResponse(ctx, err, code)
}
@ -184,13 +192,13 @@ func (uc *UserController) UseRePassWord(ctx *gin.Context) {
req.Content = uc.emailService.VerifyUrlExpired(ctx, req.Code)
if len(req.Content) == 0 {
handler.HandleResponse(ctx, errors.Forbidden(reason.EmailVerifyUrlExpired),
&schema.ForbiddenResp{Type: schema.ForbiddenReasonTypeUrlExpired})
handler.HandleResponse(ctx, errors.Forbidden(reason.EmailVerifyURLExpired),
&schema.ForbiddenResp{Type: schema.ForbiddenReasonTypeURLExpired})
return
}
resp, err := uc.userService.UseRePassWord(ctx, req)
uc.actionService.ActionRecordDel(ctx, schema.ActionRecord_Type_Find_Pass, ctx.ClientIP())
resp, err := uc.userService.UseRePassword(ctx, req)
uc.actionService.ActionRecordDel(ctx, schema.ActionRecordTypeFindPass, ctx.ClientIP())
handler.HandleResponse(ctx, err, resp)
}
@ -245,8 +253,8 @@ func (uc *UserController) UserVerifyEmail(ctx *gin.Context) {
req.Content = uc.emailService.VerifyUrlExpired(ctx, req.Code)
if len(req.Content) == 0 {
handler.HandleResponse(ctx, errors.Forbidden(reason.EmailVerifyUrlExpired),
&schema.ForbiddenResp{Type: schema.ForbiddenReasonTypeUrlExpired})
handler.HandleResponse(ctx, errors.Forbidden(reason.EmailVerifyURLExpired),
&schema.ForbiddenResp{Type: schema.ForbiddenReasonTypeURLExpired})
return
}
@ -256,7 +264,7 @@ func (uc *UserController) UserVerifyEmail(ctx *gin.Context) {
return
}
uc.actionService.ActionRecordDel(ctx, schema.ActionRecord_Type_Email, ctx.ClientIP())
uc.actionService.ActionRecordDel(ctx, schema.ActionRecordTypeEmail, ctx.ClientIP())
handler.HandleResponse(ctx, err, resp)
}
@ -282,7 +290,7 @@ func (uc *UserController) UserVerifyEmailSend(ctx *gin.Context) {
return
}
captchaPass := uc.actionService.ActionRecordVerifyCaptcha(ctx, schema.ActionRecord_Type_Email, ctx.ClientIP(),
captchaPass := uc.actionService.ActionRecordVerifyCaptcha(ctx, schema.ActionRecordTypeEmail, ctx.ClientIP(),
req.CaptchaID, req.CaptchaCode)
if !captchaPass {
resp := schema.UserVerifyEmailErrorResponse{
@ -294,7 +302,7 @@ func (uc *UserController) UserVerifyEmailSend(ctx *gin.Context) {
return
}
uc.actionService.ActionRecordAdd(ctx, schema.ActionRecord_Type_Email, ctx.ClientIP())
uc.actionService.ActionRecordAdd(ctx, schema.ActionRecordTypeEmail, ctx.ClientIP())
err := uc.userService.UserVerifyEmailSend(ctx, userInfo.UserID)
handler.HandleResponse(ctx, err, nil)
}
@ -314,7 +322,7 @@ func (uc *UserController) UserModifyPassWord(ctx *gin.Context) {
if handler.BindAndCheck(ctx, req) {
return
}
req.UserId = middleware.GetLoginUserIDFromContext(ctx)
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
oldPassVerification, err := uc.userService.UserModifyPassWordVerification(ctx, req)
if err != nil {
@ -340,7 +348,7 @@ func (uc *UserController) UserModifyPassWord(ctx *gin.Context) {
handler.HandleResponse(ctx, errors.BadRequest(reason.CaptchaVerificationFailed), resp)
return
}
err = uc.userService.UserModifyPassWord(ctx, req)
err = uc.userService.UserModifyPassword(ctx, req)
handler.HandleResponse(ctx, err, nil)
}
@ -360,7 +368,7 @@ func (uc *UserController) UserUpdateInfo(ctx *gin.Context) {
if handler.BindAndCheck(ctx, req) {
return
}
req.UserId = middleware.GetLoginUserIDFromContext(ctx)
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
err := uc.userService.UpdateInfo(ctx, req)
handler.HandleResponse(ctx, err, nil)
}
@ -438,7 +446,7 @@ func (uc *UserController) ActionRecord(ctx *gin.Context) {
if handler.BindAndCheck(ctx, req) {
return
}
req.Ip = ctx.ClientIP()
req.IP = ctx.ClientIP()
resp, err := uc.actionService.ActionRecord(ctx, req)
handler.HandleResponse(ctx, err, resp)
@ -460,8 +468,8 @@ func (uc *UserController) UserNoticeSet(ctx *gin.Context) {
return
}
req.UserId = middleware.GetLoginUserIDFromContext(ctx)
resp, err := uc.userService.UserNoticeSet(ctx, req.UserId, req.NoticeSwitch)
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
resp, err := uc.userService.UserNoticeSet(ctx, req.UserID, req.NoticeSwitch)
handler.HandleResponse(ctx, err, resp)
}
@ -483,7 +491,7 @@ func (uc *UserController) UserChangeEmailSendCode(ctx *gin.Context) {
// If the user is not logged in, the api cannot be used.
// If the user email is not verified, that also can use this api to modify the email.
captchaPass := uc.actionService.ActionRecordVerifyCaptcha(ctx, schema.ActionRecord_Type_Email, ctx.ClientIP(), req.CaptchaID, req.CaptchaCode)
captchaPass := uc.actionService.ActionRecordVerifyCaptcha(ctx, schema.ActionRecordTypeEmail, ctx.ClientIP(), req.CaptchaID, req.CaptchaCode)
if !captchaPass {
resp := schema.UserVerifyEmailErrorResponse{
Key: "captcha_code",
@ -498,7 +506,7 @@ func (uc *UserController) UserChangeEmailSendCode(ctx *gin.Context) {
handler.HandleResponse(ctx, errors.Unauthorized(reason.UnauthorizedError), nil)
return
}
_, _ = uc.actionService.ActionRecordAdd(ctx, schema.ActionRecord_Type_Email, ctx.ClientIP())
_, _ = uc.actionService.ActionRecordAdd(ctx, schema.ActionRecordTypeEmail, ctx.ClientIP())
err := uc.userService.UserChangeEmailSendCode(ctx, req)
handler.HandleResponse(ctx, err, nil)
}
@ -520,12 +528,12 @@ func (uc *UserController) UserChangeEmailVerify(ctx *gin.Context) {
}
req.Content = uc.emailService.VerifyUrlExpired(ctx, req.Code)
if len(req.Content) == 0 {
handler.HandleResponse(ctx, errors.Forbidden(reason.EmailVerifyUrlExpired),
&schema.ForbiddenResp{Type: schema.ForbiddenReasonTypeUrlExpired})
handler.HandleResponse(ctx, errors.Forbidden(reason.EmailVerifyURLExpired),
&schema.ForbiddenResp{Type: schema.ForbiddenReasonTypeURLExpired})
return
}
err := uc.userService.UserChangeEmailVerify(ctx, req.Content)
uc.actionService.ActionRecordDel(ctx, schema.ActionRecord_Type_Email, ctx.ClientIP())
uc.actionService.ActionRecordDel(ctx, schema.ActionRecordTypeEmail, ctx.ClientIP())
handler.HandleResponse(ctx, err, nil)
}

View File

@ -45,9 +45,8 @@ func (uc *UserBackyardController) UpdateUserStatus(ctx *gin.Context) {
// @Produce json
// @Param page query int false "page size"
// @Param page_size query int false "page size"
// @Param username query string false "username"
// @Param e_mail query string false "email"
// @Param status query string false "user status" Enums(normal, suspended, deleted, inactive)
// @Param query query string false "search query: email, username or id:[id]"
// @Param status query string false "user status" Enums(suspended, deleted, inactive)
// @Success 200 {object} handler.RespBody{data=pager.PageModel{records=[]schema.GetUserPageResp}}
// @Router /answer/admin/api/users/page [get]
func (uc *UserBackyardController) GetUserPage(ctx *gin.Context) {

View File

@ -3,9 +3,9 @@ package entity
import "time"
const (
Answer_Search_OrderBy_Default = "default"
Answer_Search_OrderBy_Time = "updated"
Answer_Search_OrderBy_Vote = "vote"
AnswerSearchOrderByDefault = "default"
AnswerSearchOrderByTime = "updated"
AnswerSearchOrderByVote = "vote"
AnswerStatusAvailable = 1
AnswerStatusDeleted = 10
@ -35,15 +35,17 @@ type Answer struct {
type AnswerSearch struct {
Answer
Order string `json:"order_by" ` // default or updated
Page int `json:"page" form:"page"` //Query number of pages
PageSize int `json:"page_size" form:"page_size"` //Search page size
Page int `json:"page" form:"page"` // Query number of pages
PageSize int `json:"page_size" form:"page_size"` // Search page size
}
type CmsAnswerSearch struct {
Page int `json:"page" form:"page"` //Query number of pages
PageSize int `json:"page_size" form:"page_size"` //Search page size
Status int `json:"-" form:"-"`
StatusStr string `json:"status" form:"status"` //Status 1 Available 2 closed 10 Deleted
Page int `json:"page" form:"page"` // Query number of pages
PageSize int `json:"page_size" form:"page_size"` // Search page size
Status int `json:"-" form:"-"`
StatusStr string `json:"status" form:"status"` // Status 1 Available 2 closed 10 Deleted
Query string `validate:"omitempty,gt=0,lte=100" json:"query" form:"query" ` //Query string
QuestionID string `validate:"omitempty,gt=0,lte=24" json:"question_id" form:"question_id" ` //Query string
}
type AdminSetAnswerStatusRequest struct {

View File

@ -13,7 +13,8 @@ type Revision struct {
Title string `xorm:"not null default '' VARCHAR(255) title"`
Content string `xorm:"not null TEXT content"`
Log string `xorm:"VARCHAR(255) log"`
Status int `xorm:"not null default 1 INT(11) status"`
// Status todo: this field is not used, will be removed in the future
Status int `xorm:"not null default 1 INT(11) status"`
}
// TableName revision table name

View File

@ -40,7 +40,7 @@ type User struct {
Avatar string `xorm:"not null default '' VARCHAR(255) avatar"`
Mobile string `xorm:"not null VARCHAR(20) mobile"`
Bio string `xorm:"not null TEXT bio"`
BioHtml string `xorm:"not null TEXT bio_html"`
BioHTML string `xorm:"not null TEXT bio_html"`
Website string `xorm:"not null default '' VARCHAR(255) website"`
Location string `xorm:"not null default '' VARCHAR(100) location"`
IPInfo string `xorm:"not null default '' VARCHAR(255) ip_info"`
@ -54,6 +54,6 @@ func (User) TableName() string {
type UserSearch struct {
User
Page int `json:"page" form:"page"` //Query number of pages
PageSize int `json:"page_size" form:"page_size"` //Search page size
Page int `json:"page" form:"page"` // Query number of pages
PageSize int `json:"page_size" form:"page_size"` // Search page size
}

View File

@ -8,27 +8,25 @@ import (
"xorm.io/xorm"
)
var (
tables = []interface{}{
&entity.Activity{},
&entity.Answer{},
&entity.Collection{},
&entity.CollectionGroup{},
&entity.Comment{},
&entity.Config{},
&entity.Meta{},
&entity.Notification{},
&entity.Question{},
&entity.Report{},
&entity.Revision{},
&entity.SiteInfo{},
&entity.Tag{},
&entity.TagRel{},
&entity.Uniqid{},
&entity.User{},
&entity.Version{},
}
)
var tables = []interface{}{
&entity.Activity{},
&entity.Answer{},
&entity.Collection{},
&entity.CollectionGroup{},
&entity.Comment{},
&entity.Config{},
&entity.Meta{},
&entity.Notification{},
&entity.Question{},
&entity.Report{},
&entity.Revision{},
&entity.SiteInfo{},
&entity.Tag{},
&entity.TagRel{},
&entity.Uniqid{},
&entity.User{},
&entity.Version{},
}
// InitDB init db
func InitDB(dataConf *data.Database) (err error) {
@ -95,91 +93,91 @@ func initSiteInfo(engine *xorm.Engine) error {
func initConfigTable(engine *xorm.Engine) error {
defaultConfigTable := []*entity.Config{
{1, "answer.accepted", `15`},
{2, "answer.voted_up", `10`},
{3, "question.voted_up", `10`},
{4, "tag.edit_accepted", `2`},
{5, "answer.accept", `2`},
{6, "answer.voted_down_cancel", `2`},
{7, "question.voted_down_cancel", `2`},
{8, "answer.vote_down_cancel", `1`},
{9, "question.vote_down_cancel", `1`},
{10, "user.activated", `1`},
{11, "edit.accepted", `2`},
{12, "answer.vote_down", `-1`},
{13, "question.voted_down", `-2`},
{14, "answer.voted_down", `-2`},
{15, "answer.accept_cancel", `-2`},
{16, "answer.deleted", `-5`},
{17, "question.voted_up_cancel", `-10`},
{18, "answer.voted_up_cancel", `-10`},
{19, "answer.accepted_cancel", `-15`},
{20, "object.reported", `-100`},
{21, "edit.rejected", `-2`},
{22, "daily_rank_limit", `200`},
{23, "daily_rank_limit.exclude", `["answer.accepted"]`},
{24, "user.follow", `0`},
{25, "comment.vote_up", `0`},
{26, "comment.vote_up_cancel", `0`},
{27, "question.vote_down", `0`},
{28, "question.vote_up", `0`},
{29, "question.vote_up_cancel", `0`},
{30, "answer.vote_up", `0`},
{31, "answer.vote_up_cancel", `0`},
{32, "question.follow", `0`},
{33, "email.config", `{"from_name":"answer","from_email":"answer@answer.com","smtp_host":"smtp.answer.org","smtp_port":465,"smtp_password":"answer","smtp_username":"answer@answer.com","smtp_authentication":true,"encryption":"","register_title":"[{{.SiteName}}] Confirm your new account","register_body":"Welcome to {{.SiteName}}<br><br>\n\nClick the following link to confirm and activate your new account:<br>\n<a href='{{.RegisterUrl}}' target='_blank'>{{.RegisterUrl}}</a><br><br>\n\nIf the above link is not clickable, try copying and pasting it into the address bar of your web browser.\n","pass_reset_title":"[{{.SiteName }}] Password reset","pass_reset_body":"Somebody asked to reset your password on [{{.SiteName}}].<br><br>\n\nIf it was not you, you can safely ignore this email.<br><br>\n\nClick the following link to choose a new password:<br>\n<a href='{{.PassResetUrl}}' target='_blank'>{{.PassResetUrl}}</a>\n","change_title":"[{{.SiteName}}] Confirm your new email address","change_body":"Confirm your new email address for {{.SiteName}} by clicking on the following link:<br><br>\n\n<a href='{{.ChangeEmailUrl}}' target='_blank'>{{.ChangeEmailUrl}}</a><br><br>\n\nIf you did not request this change, please ignore this email.\n","test_title":"[{{.SiteName}}] Test Email","test_body":"This is a test email."}`},
{35, "tag.follow", `0`},
{36, "rank.question.add", `0`},
{37, "rank.question.edit", `0`},
{38, "rank.question.delete", `0`},
{39, "rank.question.vote_up", `0`},
{40, "rank.question.vote_down", `0`},
{41, "rank.answer.add", `0`},
{42, "rank.answer.edit", `0`},
{43, "rank.answer.delete", `0`},
{44, "rank.answer.accept", `0`},
{45, "rank.answer.vote_up", `0`},
{46, "rank.answer.vote_down", `0`},
{47, "rank.comment.add", `0`},
{48, "rank.comment.edit", `0`},
{49, "rank.comment.delete", `0`},
{50, "rank.report.add", `0`},
{51, "rank.tag.add", `0`},
{52, "rank.tag.edit", `0`},
{53, "rank.tag.delete", `0`},
{54, "rank.tag.synonym", `0`},
{55, "rank.link.url_limit", `0`},
{56, "rank.vote.detail", `0`},
{57, "reason.spam", `{"name":"spam","description":"This post is an advertisement, or vandalism. It is not useful or relevant to the current topic."}`},
{58, "reason.rude_or_abusive", `{"name":"rude or abusive","description":"A reasonable person would find this content inappropriate for respectful discourse."}`},
{59, "reason.something", `{"name":"something else","description":"This post requires staff attention for another reason not listed above.","content_type":"textarea"}`},
{60, "reason.a_duplicate", `{"name":"a duplicate","description":"This question has been asked before and already has an answer.","content_type":"text"}`},
{61, "reason.not_a_answer", `{"name":"not a answer","description":"This was posted as an answer, but it does not attempt to answer the question. It should possibly be an edit, a comment, another question, or deleted altogether.","content_type":""}`},
{62, "reason.no_longer_needed", `{"name":"no longer needed","description":"This comment is outdated, conversational or not relevant to this post."}`},
{63, "reason.community_specific", `{"name":"a community-specific reason","description":"This question doesnt meet a community guideline."}`},
{64, "reason.not_clarity", `{"name":"needs details or clarity","description":"This question currently includes multiple questions in one. It should focus on one problem only.","content_type":"text"}`},
{65, "reason.normal", `{"name":"normal","description":"A normal post available to everyone."}`},
{66, "reason.normal.user", `{"name":"normal","description":"A normal user can ask and answer questions."}`},
{67, "reason.closed", `{"name":"closed","description":"A closed question cant answer, but still can edit, vote and comment."}`},
{68, "reason.deleted", `{"name":"deleted","description":"All reputation gained and lost will be restored."}`},
{69, "reason.deleted.user", `{"name":"deleted","description":"Delete profile, authentication associations."}`},
{70, "reason.suspended", `{"name":"suspended","description":"A suspended user cant log in."}`},
{71, "reason.inactive", `{"name":"inactive","description":"An inactive user must re-validate their email."}`},
{72, "reason.looks_ok", `{"name":"looks ok","description":"This post is good as-is and not low quality."}`},
{73, "reason.needs_edit", `{"name":"needs edit, and I did it","description":"Improve and correct problems with this post yourself."}`},
{74, "reason.needs_close", `{"name":"needs close","description":"A closed question cant answer, but still can edit, vote and comment."}`},
{75, "reason.needs_delete", `{"name":"needs delete","description":"All reputation gained and lost will be restored."}`},
{76, "question.flag.reasons", `["reason.spam","reason.rude_or_abusive","reason.something","reason.a_duplicate"]`},
{77, "answer.flag.reasons", `["reason.spam","reason.rude_or_abusive","reason.something","reason.not_a_answer"]`},
{78, "comment.flag.reasons", `["reason.spam","reason.rude_or_abusive","reason.something","reason.no_longer_needed"]`},
{79, "question.close.reasons", `["reason.a_duplicate","reason.community_specific","reason.not_clarity","reason.something"]`},
{80, "question.status.reasons", `["reason.normal","reason.closed","reason.deleted"]`},
{81, "answer.status.reasons", `["reason.normal","reason.deleted"]`},
{82, "comment.status.reasons", `["reason.normal","reason.deleted"]`},
{83, "user.status.reasons", `["reason.normal.user","reason.suspended","reason.deleted.user","reason.inactive"]`},
{84, "question.review.reasons", `["reason.looks_ok","reason.needs_edit","reason.needs_close","reason.needs_delete"]`},
{85, "answer.review.reasons", `["reason.looks_ok","reason.needs_edit","reason.needs_delete"]`},
{86, "comment.review.reasons", `["reason.looks_ok","reason.needs_edit","reason.needs_delete"]`},
{ID: 1, Key: "answer.accepted", Value: `15`},
{ID: 2, Key: "answer.voted_up", Value: `10`},
{ID: 3, Key: "question.voted_up", Value: `10`},
{ID: 4, Key: "tag.edit_accepted", Value: `2`},
{ID: 5, Key: "answer.accept", Value: `2`},
{ID: 6, Key: "answer.voted_down_cancel", Value: `2`},
{ID: 7, Key: "question.voted_down_cancel", Value: `2`},
{ID: 8, Key: "answer.vote_down_cancel", Value: `1`},
{ID: 9, Key: "question.vote_down_cancel", Value: `1`},
{ID: 10, Key: "user.activated", Value: `1`},
{ID: 11, Key: "edit.accepted", Value: `2`},
{ID: 12, Key: "answer.vote_down", Value: `-1`},
{ID: 13, Key: "question.voted_down", Value: `-2`},
{ID: 14, Key: "answer.voted_down", Value: `-2`},
{ID: 15, Key: "answer.accept_cancel", Value: `-2`},
{ID: 16, Key: "answer.deleted", Value: `-5`},
{ID: 17, Key: "question.voted_up_cancel", Value: `-10`},
{ID: 18, Key: "answer.voted_up_cancel", Value: `-10`},
{ID: 19, Key: "answer.accepted_cancel", Value: `-15`},
{ID: 20, Key: "object.reported", Value: `-100`},
{ID: 21, Key: "edit.rejected", Value: `-2`},
{ID: 22, Key: "daily_rank_limit", Value: `200`},
{ID: 23, Key: "daily_rank_limit.exclude", Value: `["answer.accepted"]`},
{ID: 24, Key: "user.follow", Value: `0`},
{ID: 25, Key: "comment.vote_up", Value: `0`},
{ID: 26, Key: "comment.vote_up_cancel", Value: `0`},
{ID: 27, Key: "question.vote_down", Value: `0`},
{ID: 28, Key: "question.vote_up", Value: `0`},
{ID: 29, Key: "question.vote_up_cancel", Value: `0`},
{ID: 30, Key: "answer.vote_up", Value: `0`},
{ID: 31, Key: "answer.vote_up_cancel", Value: `0`},
{ID: 32, Key: "question.follow", Value: `0`},
{ID: 33, Key: "email.config", Value: `{"from_name":"answer","from_email":"answer@answer.com","smtp_host":"smtp.answer.org","smtp_port":465,"smtp_password":"answer","smtp_username":"answer@answer.com","smtp_authentication":true,"encryption":"","register_title":"[{{.SiteName}}] Confirm your new account","register_body":"Welcome to {{.SiteName}}<br><br>\n\nClick the following link to confirm and activate your new account:<br>\n<a href='{{.RegisterUrl}}' target='_blank'>{{.RegisterUrl}}</a><br><br>\n\nIf the above link is not clickable, try copying and pasting it into the address bar of your web browser.\n","pass_reset_title":"[{{.SiteName }}] Password reset","pass_reset_body":"Somebody asked to reset your password on [{{.SiteName}}].<br><br>\n\nIf it was not you, you can safely ignore this email.<br><br>\n\nClick the following link to choose a new password:<br>\n<a href='{{.PassResetUrl}}' target='_blank'>{{.PassResetUrl}}</a>\n","change_title":"[{{.SiteName}}] Confirm your new email address","change_body":"Confirm your new email address for {{.SiteName}} by clicking on the following link:<br><br>\n\n<a href='{{.ChangeEmailUrl}}' target='_blank'>{{.ChangeEmailUrl}}</a><br><br>\n\nIf you did not request this change, please ignore this email.\n","test_title":"[{{.SiteName}}] Test Email","test_body":"This is a test email."}`},
{ID: 35, Key: "tag.follow", Value: `0`},
{ID: 36, Key: "rank.question.add", Value: `0`},
{ID: 37, Key: "rank.question.edit", Value: `0`},
{ID: 38, Key: "rank.question.delete", Value: `0`},
{ID: 39, Key: "rank.question.vote_up", Value: `0`},
{ID: 40, Key: "rank.question.vote_down", Value: `0`},
{ID: 41, Key: "rank.answer.add", Value: `0`},
{ID: 42, Key: "rank.answer.edit", Value: `0`},
{ID: 43, Key: "rank.answer.delete", Value: `0`},
{ID: 44, Key: "rank.answer.accept", Value: `0`},
{ID: 45, Key: "rank.answer.vote_up", Value: `0`},
{ID: 46, Key: "rank.answer.vote_down", Value: `0`},
{ID: 47, Key: "rank.comment.add", Value: `0`},
{ID: 48, Key: "rank.comment.edit", Value: `0`},
{ID: 49, Key: "rank.comment.delete", Value: `0`},
{ID: 50, Key: "rank.report.add", Value: `0`},
{ID: 51, Key: "rank.tag.add", Value: `0`},
{ID: 52, Key: "rank.tag.edit", Value: `0`},
{ID: 53, Key: "rank.tag.delete", Value: `0`},
{ID: 54, Key: "rank.tag.synonym", Value: `0`},
{ID: 55, Key: "rank.link.url_limit", Value: `0`},
{ID: 56, Key: "rank.vote.detail", Value: `0`},
{ID: 57, Key: "reason.spam", Value: `{"name":"spam","description":"This post is an advertisement, or vandalism. It is not useful or relevant to the current topic."}`},
{ID: 58, Key: "reason.rude_or_abusive", Value: `{"name":"rude or abusive","description":"A reasonable person would find this content inappropriate for respectful discourse."}`},
{ID: 59, Key: "reason.something", Value: `{"name":"something else","description":"This post requires staff attention for another reason not listed above.","content_type":"textarea"}`},
{ID: 60, Key: "reason.a_duplicate", Value: `{"name":"a duplicate","description":"This question has been asked before and already has an answer.","content_type":"text"}`},
{ID: 61, Key: "reason.not_a_answer", Value: `{"name":"not a answer","description":"This was posted as an answer, but it does not attempt to answer the question. It should possibly be an edit, a comment, another question, or deleted altogether.","content_type":""}`},
{ID: 62, Key: "reason.no_longer_needed", Value: `{"name":"no longer needed","description":"This comment is outdated, conversational or not relevant to this post."}`},
{ID: 63, Key: "reason.community_specific", Value: `{"name":"a community-specific reason","description":"This question doesnt meet a community guideline."}`},
{ID: 64, Key: "reason.not_clarity", Value: `{"name":"needs details or clarity","description":"This question currently includes multiple questions in one. It should focus on one problem only.","content_type":"text"}`},
{ID: 65, Key: "reason.normal", Value: `{"name":"normal","description":"A normal post available to everyone."}`},
{ID: 66, Key: "reason.normal.user", Value: `{"name":"normal","description":"A normal user can ask and answer questions."}`},
{ID: 67, Key: "reason.closed", Value: `{"name":"closed","description":"A closed question cant answer, but still can edit, vote and comment."}`},
{ID: 68, Key: "reason.deleted", Value: `{"name":"deleted","description":"All reputation gained and lost will be restored."}`},
{ID: 69, Key: "reason.deleted.user", Value: `{"name":"deleted","description":"Delete profile, authentication associations."}`},
{ID: 70, Key: "reason.suspended", Value: `{"name":"suspended","description":"A suspended user cant log in."}`},
{ID: 71, Key: "reason.inactive", Value: `{"name":"inactive","description":"An inactive user must re-validate their email."}`},
{ID: 72, Key: "reason.looks_ok", Value: `{"name":"looks ok","description":"This post is good as-is and not low quality."}`},
{ID: 73, Key: "reason.needs_edit", Value: `{"name":"needs edit, and I did it","description":"Improve and correct problems with this post yourself."}`},
{ID: 74, Key: "reason.needs_close", Value: `{"name":"needs close","description":"A closed question cant answer, but still can edit, vote and comment."}`},
{ID: 75, Key: "reason.needs_delete", Value: `{"name":"needs delete","description":"All reputation gained and lost will be restored."}`},
{ID: 76, Key: "question.flag.reasons", Value: `["reason.spam","reason.rude_or_abusive","reason.something","reason.a_duplicate"]`},
{ID: 77, Key: "answer.flag.reasons", Value: `["reason.spam","reason.rude_or_abusive","reason.something","reason.not_a_answer"]`},
{ID: 78, Key: "comment.flag.reasons", Value: `["reason.spam","reason.rude_or_abusive","reason.something","reason.no_longer_needed"]`},
{ID: 79, Key: "question.close.reasons", Value: `["reason.a_duplicate","reason.community_specific","reason.not_clarity","reason.something"]`},
{ID: 80, Key: "question.status.reasons", Value: `["reason.normal","reason.closed","reason.deleted"]`},
{ID: 81, Key: "answer.status.reasons", Value: `["reason.normal","reason.deleted"]`},
{ID: 82, Key: "comment.status.reasons", Value: `["reason.normal","reason.deleted"]`},
{ID: 83, Key: "user.status.reasons", Value: `["reason.normal.user","reason.suspended","reason.deleted.user","reason.inactive"]`},
{ID: 84, Key: "question.review.reasons", Value: `["reason.looks_ok","reason.needs_edit","reason.needs_close","reason.needs_delete"]`},
{ID: 85, Key: "answer.review.reasons", Value: `["reason.looks_ok","reason.needs_edit","reason.needs_delete"]`},
{ID: 86, Key: "comment.review.reasons", Value: `["reason.looks_ok","reason.needs_edit","reason.needs_delete"]`},
}
_, err := engine.Insert(defaultConfigTable)
return err

View File

@ -44,7 +44,6 @@ func NewAnswerActivityRepo(
userRankRepo rank.UserRankRepo,
) activity.AnswerActivityRepo {
return &AnswerActivityRepo{
data: data,
activityRepo: activityRepo,
userRankRepo: userRankRepo,
@ -125,7 +124,8 @@ func (ar *AnswerActivityRepo) DeleteQuestion(ctx context.Context, questionID str
// AcceptAnswer accept other answer
func (ar *AnswerActivityRepo) AcceptAnswer(ctx context.Context,
answerObjID, questionUserID, answerUserID string, isSelf bool) (err error) {
answerObjID, questionUserID, answerUserID string, isSelf bool,
) (err error) {
addActivityList := make([]*entity.Activity, 0)
for _, action := range acceptActionList {
// get accept answer need add rank amount
@ -173,7 +173,7 @@ func (ar *AnswerActivityRepo) AcceptAnswer(ctx context.Context,
}
if exists {
if _, e := session.Where("id = ?", existsActivity.ID).Cols("`cancelled`").
if _, e = session.Where("id = ?", existsActivity.ID).Cols("`cancelled`").
Update(&entity.Activity{Cancelled: entity.ActivityAvailable}); e != nil {
return nil, errors.InternalServer(reason.DatabaseError).WithError(e).WithStack()
}
@ -222,7 +222,8 @@ func (ar *AnswerActivityRepo) AcceptAnswer(ctx context.Context,
// CancelAcceptAnswer accept other answer
func (ar *AnswerActivityRepo) CancelAcceptAnswer(ctx context.Context,
answerObjID, questionUserID, answerUserID string) (err error) {
answerObjID, questionUserID, answerUserID string,
) (err error) {
addActivityList := make([]*entity.Activity, 0)
for _, action := range acceptActionList {
// get accept answer need add rank amount

View File

@ -37,8 +37,8 @@ func NewFollowRepo(
}
}
func (ar *FollowRepo) Follow(ctx context.Context, objectId, userId string) error {
activityType, _, _, err := ar.activityRepo.GetActivityTypeByObjID(ctx, objectId, "follow")
func (ar *FollowRepo) Follow(ctx context.Context, objectID, userID string) error {
activityType, _, _, err := ar.activityRepo.GetActivityTypeByObjID(ctx, objectID, "follow")
if err != nil {
return err
}
@ -51,8 +51,8 @@ func (ar *FollowRepo) Follow(ctx context.Context, objectId, userId string) error
result = nil
has, err = session.Where(builder.Eq{"activity_type": activityType}).
And(builder.Eq{"user_id": userId}).
And(builder.Eq{"object_id": objectId}).
And(builder.Eq{"user_id": userID}).
And(builder.Eq{"object_id": objectID}).
Get(&existsActivity)
if err != nil {
@ -72,8 +72,8 @@ func (ar *FollowRepo) Follow(ctx context.Context, objectId, userId string) error
} else {
// update existing activity with new user id and u object id
_, err = session.Insert(&entity.Activity{
UserID: userId,
ObjectID: objectId,
UserID: userID,
ObjectID: objectID,
ActivityType: activityType,
Cancelled: 0,
Rank: 0,
@ -87,7 +87,7 @@ func (ar *FollowRepo) Follow(ctx context.Context, objectId, userId string) error
}
// start update followers when everything is fine
err = ar.updateFollows(ctx, session, objectId, 1)
err = ar.updateFollows(ctx, session, objectID, 1)
if err != nil {
log.Error(err)
}
@ -98,8 +98,8 @@ func (ar *FollowRepo) Follow(ctx context.Context, objectId, userId string) error
return err
}
func (ar *FollowRepo) FollowCancel(ctx context.Context, objectId, userId string) error {
activityType, _, _, err := ar.activityRepo.GetActivityTypeByObjID(ctx, objectId, "follow")
func (ar *FollowRepo) FollowCancel(ctx context.Context, objectID, userID string) error {
activityType, _, _, err := ar.activityRepo.GetActivityTypeByObjID(ctx, objectID, "follow")
if err != nil {
return err
}
@ -112,8 +112,8 @@ func (ar *FollowRepo) FollowCancel(ctx context.Context, objectId, userId string)
result = nil
has, err = session.Where(builder.Eq{"activity_type": activityType}).
And(builder.Eq{"user_id": userId}).
And(builder.Eq{"object_id": objectId}).
And(builder.Eq{"user_id": userID}).
And(builder.Eq{"object_id": objectID}).
Get(&existsActivity)
if err != nil || !has {
@ -130,24 +130,24 @@ func (ar *FollowRepo) FollowCancel(ctx context.Context, objectId, userId string)
}); err != nil {
return
}
err = ar.updateFollows(ctx, session, objectId, -1)
err = ar.updateFollows(ctx, session, objectID, -1)
return
})
return err
}
func (ar *FollowRepo) updateFollows(ctx context.Context, session *xorm.Session, objectId string, follows int) error {
objectType, err := obj.GetObjectTypeStrByObjectID(objectId)
func (ar *FollowRepo) updateFollows(ctx context.Context, session *xorm.Session, objectID string, follows int) error {
objectType, err := obj.GetObjectTypeStrByObjectID(objectID)
if err != nil {
return err
}
switch objectType {
case "question":
_, err = session.Where("id = ?", objectId).Incr("follow_count", follows).Update(&entity.Question{})
_, err = session.Where("id = ?", objectID).Incr("follow_count", follows).Update(&entity.Question{})
case "user":
_, err = session.Where("id = ?", objectId).Incr("follow_count", follows).Update(&entity.User{})
_, err = session.Where("id = ?", objectID).Incr("follow_count", follows).Update(&entity.User{})
case "tag":
_, err = session.Where("id = ?", objectId).Incr("follow_count", follows).Update(&entity.Tag{})
_, err = session.Where("id = ?", objectID).Incr("follow_count", follows).Update(&entity.Tag{})
default:
err = errors.InternalServer(reason.DisallowFollow).WithMsg("this object can't be followed")
}

View File

@ -2,9 +2,10 @@ package activity
import (
"context"
"github.com/answerdev/answer/pkg/converter"
"strings"
"github.com/answerdev/answer/pkg/converter"
"github.com/answerdev/answer/internal/base/pager"
"github.com/answerdev/answer/internal/service/config"
"github.com/answerdev/answer/internal/service/notice_queue"
@ -42,7 +43,8 @@ func NewVoteRepo(
configRepo config.ConfigRepo,
activityRepo activity_common.ActivityRepo,
userRankRepo rank.UserRankRepo,
voteCommon activity_common.VoteRepo) service.VoteRepo {
voteCommon activity_common.VoteRepo,
) service.VoteRepo {
return &VoteRepo{
data: data,
uniqueIDRepo: uniqueIDRepo,
@ -65,7 +67,7 @@ var LimitDownActions = map[string][]string{
"comment": {"vote_down"},
}
func (vr *VoteRepo) vote(ctx context.Context, objectID string, userID, objectUserId string, actions []string) (resp *schema.VoteResp, err error) {
func (vr *VoteRepo) vote(ctx context.Context, objectID string, userID, objectUserID string, actions []string) (resp *schema.VoteResp, err error) {
resp = &schema.VoteResp{}
_, err = vr.data.DB.Transaction(func(session *xorm.Session) (result any, err error) {
result = nil
@ -75,24 +77,24 @@ func (vr *VoteRepo) vote(ctx context.Context, objectID string, userID, objectUse
insertActivity entity.Activity
has bool
triggerUserID,
activityUserId string
activityUserID string
activityType, deltaRank, hasRank int
)
activityUserId, activityType, deltaRank, hasRank, err = vr.CheckRank(ctx, objectID, objectUserId, userID, action)
activityUserID, activityType, deltaRank, hasRank, err = vr.CheckRank(ctx, objectID, objectUserID, userID, action)
if err != nil {
return
}
triggerUserID = userID
if userID == activityUserId {
if userID == activityUserID {
triggerUserID = "0"
}
// check is voted up
has, _ = session.
Where(builder.Eq{"object_id": objectID}).
And(builder.Eq{"user_id": activityUserId}).
And(builder.Eq{"user_id": activityUserID}).
And(builder.Eq{"trigger_user_id": triggerUserID}).
And(builder.Eq{"activity_type": activityType}).
Get(&existsActivity)
@ -104,7 +106,7 @@ func (vr *VoteRepo) vote(ctx context.Context, objectID string, userID, objectUse
insertActivity = entity.Activity{
ObjectID: objectID,
UserID: activityUserId,
UserID: activityUserID,
TriggerUserID: converter.StringToInt64(triggerUserID),
ActivityType: activityType,
Rank: deltaRank,
@ -114,7 +116,8 @@ func (vr *VoteRepo) vote(ctx context.Context, objectID string, userID, objectUse
// trigger user rank and send notification
if hasRank != 0 {
isReachStandard, err := vr.userRankRepo.TriggerUserRank(ctx, session, activityUserId, deltaRank, activityType)
var isReachStandard bool
isReachStandard, err = vr.userRankRepo.TriggerUserRank(ctx, session, activityUserID, deltaRank, activityType)
if err != nil {
return nil, err
}
@ -122,7 +125,7 @@ func (vr *VoteRepo) vote(ctx context.Context, objectID string, userID, objectUse
insertActivity.Rank = 0
}
vr.sendNotification(ctx, activityUserId, objectUserId, objectID)
vr.sendNotification(ctx, activityUserID, objectUserID, objectID)
}
if has {
@ -163,7 +166,7 @@ func (vr *VoteRepo) vote(ctx context.Context, objectID string, userID, objectUse
return
}
func (vr *VoteRepo) voteCancel(ctx context.Context, objectID string, userID, objectUserId string, actions []string) (resp *schema.VoteResp, err error) {
func (vr *VoteRepo) voteCancel(ctx context.Context, objectID string, userID, objectUserID string, actions []string) (resp *schema.VoteResp, err error) {
resp = &schema.VoteResp{}
_, err = vr.data.DB.Transaction(func(session *xorm.Session) (result any, err error) {
for _, action := range actions {
@ -171,24 +174,24 @@ func (vr *VoteRepo) voteCancel(ctx context.Context, objectID string, userID, obj
existsActivity entity.Activity
has bool
triggerUserID,
activityUserId string
activityUserID string
activityType,
deltaRank, hasRank int
)
result = nil
activityUserId, activityType, deltaRank, hasRank, err = vr.CheckRank(ctx, objectID, objectUserId, userID, action)
activityUserID, activityType, deltaRank, hasRank, err = vr.CheckRank(ctx, objectID, objectUserID, userID, action)
if err != nil {
return
}
triggerUserID = userID
if userID == activityUserId {
if userID == activityUserID {
triggerUserID = "0"
}
has, err = session.
Where(builder.Eq{"user_id": activityUserId}).
Where(builder.Eq{"user_id": activityUserID}).
And(builder.Eq{"trigger_user_id": triggerUserID}).
And(builder.Eq{"activity_type": activityType}).
And(builder.Eq{"object_id": objectID}).
@ -211,12 +214,12 @@ func (vr *VoteRepo) voteCancel(ctx context.Context, objectID string, userID, obj
// trigger user rank and send notification
if hasRank != 0 {
_, err = vr.userRankRepo.TriggerUserRank(ctx, session, activityUserId, -deltaRank, activityType)
_, err = vr.userRankRepo.TriggerUserRank(ctx, session, activityUserID, -deltaRank, activityType)
if err != nil {
return
}
vr.sendNotification(ctx, activityUserId, objectUserId, objectID)
vr.sendNotification(ctx, activityUserID, objectUserID, objectID)
}
// update votes
@ -242,7 +245,7 @@ func (vr *VoteRepo) voteCancel(ctx context.Context, objectID string, userID, obj
return
}
func (vr *VoteRepo) VoteUp(ctx context.Context, objectID string, userID, objectUserId string) (resp *schema.VoteResp, err error) {
func (vr *VoteRepo) VoteUp(ctx context.Context, objectID string, userID, objectUserID string) (resp *schema.VoteResp, err error) {
resp = &schema.VoteResp{}
objectType, err := obj.GetObjectTypeStrByObjectID(objectID)
if err != nil {
@ -256,11 +259,11 @@ func (vr *VoteRepo) VoteUp(ctx context.Context, objectID string, userID, objectU
return
}
_, _ = vr.VoteDownCancel(ctx, objectID, userID, objectUserId)
return vr.vote(ctx, objectID, userID, objectUserId, actions)
_, _ = vr.VoteDownCancel(ctx, objectID, userID, objectUserID)
return vr.vote(ctx, objectID, userID, objectUserID, actions)
}
func (vr *VoteRepo) VoteDown(ctx context.Context, objectID string, userID, objectUserId string) (resp *schema.VoteResp, err error) {
func (vr *VoteRepo) VoteDown(ctx context.Context, objectID string, userID, objectUserID string) (resp *schema.VoteResp, err error) {
resp = &schema.VoteResp{}
objectType, err := obj.GetObjectTypeStrByObjectID(objectID)
if err != nil {
@ -273,14 +276,12 @@ func (vr *VoteRepo) VoteDown(ctx context.Context, objectID string, userID, objec
return
}
_, _ = vr.VoteUpCancel(ctx, objectID, userID, objectUserId)
return vr.vote(ctx, objectID, userID, objectUserId, actions)
_, _ = vr.VoteUpCancel(ctx, objectID, userID, objectUserID)
return vr.vote(ctx, objectID, userID, objectUserID, actions)
}
func (vr *VoteRepo) VoteUpCancel(ctx context.Context, objectID string, userID, objectUserId string) (resp *schema.VoteResp, err error) {
var (
objectType string
)
func (vr *VoteRepo) VoteUpCancel(ctx context.Context, objectID string, userID, objectUserID string) (resp *schema.VoteResp, err error) {
var objectType string
resp = &schema.VoteResp{}
objectType, err = obj.GetObjectTypeStrByObjectID(objectID)
@ -294,13 +295,11 @@ func (vr *VoteRepo) VoteUpCancel(ctx context.Context, objectID string, userID, o
return
}
return vr.voteCancel(ctx, objectID, userID, objectUserId, actions)
return vr.voteCancel(ctx, objectID, userID, objectUserID, actions)
}
func (vr *VoteRepo) VoteDownCancel(ctx context.Context, objectID string, userID, objectUserId string) (resp *schema.VoteResp, err error) {
var (
objectType string
)
func (vr *VoteRepo) VoteDownCancel(ctx context.Context, objectID string, userID, objectUserID string) (resp *schema.VoteResp, err error) {
var objectType string
resp = &schema.VoteResp{}
objectType, err = obj.GetObjectTypeStrByObjectID(objectID)
@ -314,22 +313,22 @@ func (vr *VoteRepo) VoteDownCancel(ctx context.Context, objectID string, userID,
return
}
return vr.voteCancel(ctx, objectID, userID, objectUserId, actions)
return vr.voteCancel(ctx, objectID, userID, objectUserID, actions)
}
func (vr *VoteRepo) CheckRank(ctx context.Context, objectID, objectUserId, userID string, action string) (activityUserId string, activityType, rank, hasRank int, err error) {
func (vr *VoteRepo) CheckRank(ctx context.Context, objectID, objectUserID, userID string, action string) (activityUserID string, activityType, rank, hasRank int, err error) {
activityType, rank, hasRank, err = vr.activityRepo.GetActivityTypeByObjID(ctx, objectID, action)
if err != nil {
return
}
activityUserId = userID
activityUserID = userID
if strings.Contains(action, "voted") {
activityUserId = objectUserId
activityUserID = objectUserID
}
return activityUserId, activityType, rank, hasRank, nil
return activityUserID, activityType, rank, hasRank, nil
}
func (vr *VoteRepo) GetVoteResultByObjectId(ctx context.Context, objectID string) (resp *schema.VoteResp, err error) {
@ -341,7 +340,7 @@ func (vr *VoteRepo) GetVoteResultByObjectId(ctx context.Context, objectID string
activityType int
)
activityType, _, _, err = vr.activityRepo.GetActivityTypeByObjID(ctx, objectID, action)
activityType, _, _, _ = vr.activityRepo.GetActivityTypeByObjID(ctx, objectID, action)
votes, err = vr.data.DB.Where(builder.Eq{"object_id": objectID}).
And(builder.Eq{"activity_type": activityType}).
@ -417,15 +416,15 @@ func (vr *VoteRepo) updateVotes(ctx context.Context, session *xorm.Session, obje
}
// sendNotification send rank triggered notification
func (vr *VoteRepo) sendNotification(ctx context.Context, activityUserId, objectUserId, objectID string) {
func (vr *VoteRepo) sendNotification(ctx context.Context, activityUserID, objectUserID, objectID string) {
objectType, err := obj.GetObjectTypeStrByObjectID(objectID)
if err != nil {
return
}
msg := &schema.NotificationMsg{
ReceiverUserID: activityUserId,
TriggerUserID: objectUserId,
ReceiverUserID: activityUserID,
TriggerUserID: objectUserID,
Type: schema.NotificationTypeAchievement,
ObjectID: objectID,
ObjectType: objectType,

View File

@ -1,4 +1,4 @@
package repo
package activity_common
import (
"context"
@ -37,14 +37,14 @@ func NewActivityRepo(
}
}
func (ar *ActivityRepo) GetActivityTypeByObjID(ctx context.Context, objectId string, action string) (activityType, rank, hasRank int, err error) {
objectKey, err := obj.GetObjectTypeStrByObjectID(objectId)
func (ar *ActivityRepo) GetActivityTypeByObjID(ctx context.Context, objectID string, action string) (activityType, rank, hasRank int, err error) {
objectKey, err := obj.GetObjectTypeStrByObjectID(objectID)
if err != nil {
return
}
confKey := fmt.Sprintf("%s.%s", objectKey, action)
activityType, err = ar.configRepo.GetConfigType(confKey)
activityType, _ = ar.configRepo.GetConfigType(confKey)
rank, err = ar.configRepo.GetInt(confKey)
hasRank = 0
@ -64,7 +64,8 @@ func (ar *ActivityRepo) GetActivityTypeByObjKey(ctx context.Context, objectKey,
}
func (ar *ActivityRepo) GetActivity(ctx context.Context, session *xorm.Session,
objectID, userID string, activityType int) (existsActivity *entity.Activity, exist bool, err error) {
objectID, userID string, activityType int,
) (existsActivity *entity.Activity, exist bool, err error) {
existsActivity = &entity.Activity{}
exist, err = session.
Where(builder.Eq{"object_id": objectID}).

View File

@ -33,27 +33,27 @@ func NewFollowRepo(
}
// GetFollowAmount get object id's follows
func (ar *FollowRepo) GetFollowAmount(ctx context.Context, objectId string) (follows int, err error) {
objectType, err := obj.GetObjectTypeStrByObjectID(objectId)
func (ar *FollowRepo) GetFollowAmount(ctx context.Context, objectID string) (follows int, err error) {
objectType, err := obj.GetObjectTypeStrByObjectID(objectID)
if err != nil {
return 0, err
}
switch objectType {
case "question":
model := &entity.Question{}
_, err = ar.data.DB.Where("id = ?", objectId).Cols("`follow_count`").Get(model)
_, err = ar.data.DB.Where("id = ?", objectID).Cols("`follow_count`").Get(model)
if err == nil {
follows = int(model.FollowCount)
}
case "user":
model := &entity.User{}
_, err = ar.data.DB.Where("id = ?", objectId).Cols("`follow_count`").Get(model)
_, err = ar.data.DB.Where("id = ?", objectID).Cols("`follow_count`").Get(model)
if err == nil {
follows = int(model.FollowCount)
}
case "tag":
model := &entity.Tag{}
_, err = ar.data.DB.Where("id = ?", objectId).Cols("`follow_count`").Get(model)
_, err = ar.data.DB.Where("id = ?", objectID).Cols("`follow_count`").Get(model)
if err == nil {
follows = int(model.FollowCount)
}
@ -95,6 +95,9 @@ func (ar *FollowRepo) GetFollowUserIDs(ctx context.Context, objectID string) (us
func (ar *FollowRepo) GetFollowIDs(ctx context.Context, userID, objectKey string) (followIDs []string, err error) {
followIDs = make([]string, 0)
activityType, err := ar.activityRepo.GetActivityTypeByObjKey(ctx, objectKey, "follow")
if err != nil {
return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
session := ar.data.DB.Select("object_id")
session.Table(entity.Activity{}.TableName())
session.Where("user_id = ? AND activity_type = ?", userID, activityType)
@ -107,14 +110,14 @@ func (ar *FollowRepo) GetFollowIDs(ctx context.Context, userID, objectKey string
}
// IsFollowed check user if follow object or not
func (ar *FollowRepo) IsFollowed(userId, objectId string) (bool, error) {
activityType, _, _, err := ar.activityRepo.GetActivityTypeByObjID(nil, objectId, "follow")
func (ar *FollowRepo) IsFollowed(userID, objectID string) (bool, error) {
activityType, _, _, err := ar.activityRepo.GetActivityTypeByObjID(context.TODO(), objectID, "follow")
if err != nil {
return false, err
}
at := &entity.Activity{}
has, err := ar.data.DB.Where("user_id = ? AND object_id = ? AND activity_type = ?", userId, objectId, activityType).Get(at)
has, err := ar.data.DB.Where("user_id = ? AND object_id = ? AND activity_type = ?", userID, objectID, activityType).Get(at)
if err != nil {
return false, err
}

View File

@ -6,13 +6,11 @@ import (
"github.com/answerdev/answer/internal/base/data"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/service/activity_common"
"github.com/answerdev/answer/internal/service/unique"
)
// VoteRepo activity repository
type VoteRepo struct {
data *data.Data
uniqueIDRepo unique.UniqueIDRepo
activityRepo activity_common.ActivityRepo
}
@ -24,11 +22,14 @@ func NewVoteRepo(data *data.Data, activityRepo activity_common.ActivityRepo) act
}
}
func (vr *VoteRepo) GetVoteStatus(ctx context.Context, objectId, userId string) (status string) {
func (vr *VoteRepo) GetVoteStatus(ctx context.Context, objectID, userID string) (status string) {
for _, action := range []string{"vote_up", "vote_down"} {
at := &entity.Activity{}
activityType, _, _, err := vr.activityRepo.GetActivityTypeByObjID(ctx, objectId, action)
has, err := vr.data.DB.Where("object_id =? AND cancelled=0 AND activity_type=? AND user_id=?", objectId, activityType, userId).Get(at)
activityType, _, _, err := vr.activityRepo.GetActivityTypeByObjID(ctx, objectID, action)
if err != nil {
return ""
}
has, err := vr.data.DB.Where("object_id =? AND cancelled=0 AND activity_type=? AND user_id=?", objectID, activityType, userID).Get(at)
if err != nil {
return ""
}

View File

@ -1,8 +1,11 @@
package repo
package answer
import (
"context"
"strings"
"time"
"unicode"
"xorm.io/builder"
"github.com/answerdev/answer/internal/base/constant"
"github.com/answerdev/answer/internal/base/data"
@ -89,7 +92,8 @@ func (ar *answerRepo) UpdateAnswerStatus(ctx context.Context, answer *entity.Ans
// GetAnswer get answer one
func (ar *answerRepo) GetAnswer(ctx context.Context, id string) (
answer *entity.Answer, exist bool, err error) {
answer *entity.Answer, exist bool, err error,
) {
answer = &entity.Answer{}
exist, err = ar.data.DB.ID(id).Get(answer)
if err != nil {
@ -120,20 +124,20 @@ func (ar *answerRepo) GetAnswerPage(ctx context.Context, page, pageSize int, ans
// UpdateAdopted
// If no answer is selected, the answer id can be 0
func (ar *answerRepo) UpdateAdopted(ctx context.Context, id string, questionId string) error {
if questionId == "" {
func (ar *answerRepo) UpdateAdopted(ctx context.Context, id string, questionID string) error {
if questionID == "" {
return nil
}
var data entity.Answer
data.ID = id
data.Adopted = schema.Answer_Adopted_Failed
_, err := ar.data.DB.Where("question_id =?", questionId).Cols("adopted").Update(&data)
data.Adopted = schema.AnswerAdoptedFailed
_, err := ar.data.DB.Where("question_id =?", questionID).Cols("adopted").Update(&data)
if err != nil {
return err
}
if id != "0" {
data.Adopted = schema.Answer_Adopted_Enable
data.Adopted = schema.AnswerAdoptedEnable
_, err = ar.data.DB.Where("id = ?", id).Cols("adopted").Update(&data)
if err != nil {
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
@ -152,9 +156,9 @@ func (ar *answerRepo) GetByID(ctx context.Context, id string) (*entity.Answer, b
return &resp, has, nil
}
func (ar *answerRepo) GetByUserIdQuestionId(ctx context.Context, userId string, questionId string) (*entity.Answer, bool, error) {
func (ar *answerRepo) GetByUserIDQuestionID(ctx context.Context, userID string, questionID string) (*entity.Answer, bool, error) {
var resp entity.Answer
has, err := ar.data.DB.Where("question_id =? and user_id = ?", questionId, userId).Get(&resp)
has, err := ar.data.DB.Where("question_id =? and user_id = ?", questionID, userID).Get(&resp)
if err != nil {
return &resp, false, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
@ -172,7 +176,7 @@ func (ar *answerRepo) SearchList(ctx context.Context, search *entity.AnswerSearc
search.Page = 0
}
if search.PageSize == 0 {
search.PageSize = constant.Default_PageSize
search.PageSize = constant.DefaultPageSize
}
offset := search.Page * search.PageSize
session := ar.data.DB.Where("")
@ -183,14 +187,14 @@ func (ar *answerRepo) SearchList(ctx context.Context, search *entity.AnswerSearc
if len(search.UserID) > 0 {
session = session.And("user_id = ?", search.UserID)
}
if search.Order == entity.Answer_Search_OrderBy_Time {
switch search.Order {
case entity.AnswerSearchOrderByTime:
session = session.OrderBy("created_at desc")
} else if search.Order == entity.Answer_Search_OrderBy_Vote {
case entity.AnswerSearchOrderByVote:
session = session.OrderBy("vote_count desc")
} else {
default:
session = session.OrderBy("adopted desc,vote_count desc")
}
session = session.And("status = ?", entity.AnswerStatusAvailable)
session = session.Limit(search.PageSize, offset)
@ -202,11 +206,16 @@ func (ar *answerRepo) SearchList(ctx context.Context, search *entity.AnswerSearc
}
func (ar *answerRepo) CmsSearchList(ctx context.Context, search *entity.CmsAnswerSearch) ([]*entity.Answer, int64, error) {
var count int64
var err error
if search.Status == 0 {
search.Status = 1
}
var (
count int64
err error
session = ar.data.DB.Table([]string{entity.Answer{}.TableName(), "a"}).Select("a.*")
)
session.Where(builder.Eq{
"a.status": search.Status,
})
rows := make([]*entity.Answer, 0)
if search.Page > 0 {
search.Page = search.Page - 1
@ -214,13 +223,51 @@ func (ar *answerRepo) CmsSearchList(ctx context.Context, search *entity.CmsAnswe
search.Page = 0
}
if search.PageSize == 0 {
search.PageSize = constant.Default_PageSize
search.PageSize = constant.DefaultPageSize
}
// search by question title like or answer id
if len(search.Query) > 0 {
// check id search
var (
idSearch = false
id = ""
)
if strings.Contains(search.Query, "answer:") {
idSearch = true
id = strings.TrimSpace(strings.TrimPrefix(search.Query, "answer:"))
for _, r := range id {
if !unicode.IsDigit(r) {
idSearch = false
break
}
}
}
if idSearch {
session.And(builder.Eq{
"id": id,
})
} else {
session.Join("LEFT", []string{entity.Question{}.TableName(), "q"}, "q.id = a.question_id")
session.And(builder.Like{
"q.title", search.Query,
})
}
}
// check search by question id
if len(search.QuestionID) > 0 {
session.And(builder.Eq{
"question_id": search.QuestionID,
})
}
offset := search.Page * search.PageSize
session := ar.data.DB.Where("")
session = session.And("status =?", search.Status)
session = session.OrderBy("updated_at desc")
session = session.Limit(search.PageSize, offset)
session.
OrderBy("a.updated_at desc").
Limit(search.PageSize, offset)
count, err = session.FindAndCount(&rows)
if err != nil {
return rows, count, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()

View File

@ -12,11 +12,12 @@ import (
"github.com/segmentfault/pacman/errors"
)
// authRepo activity repository
// authRepo auth repository
type authRepo struct {
data *data.Data
}
// GetUserCacheInfo get user cache info
func (ar *authRepo) GetUserCacheInfo(ctx context.Context, accessToken string) (userInfo *entity.UserCacheInfo, err error) {
userInfoCache, err := ar.data.Cache.GetString(ctx, constant.UserTokenCacheKey+accessToken)
if err != nil {
@ -30,27 +31,7 @@ func (ar *authRepo) GetUserCacheInfo(ctx context.Context, accessToken string) (u
return userInfo, nil
}
func (ar *authRepo) GetUserStatus(ctx context.Context, userID string) (userInfo *entity.UserCacheInfo, err error) {
userInfoCache, err := ar.data.Cache.GetString(ctx, constant.UserStatusChangedCacheKey+userID)
if err != nil {
return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
userInfo = &entity.UserCacheInfo{}
err = json.Unmarshal([]byte(userInfoCache), userInfo)
if err != nil {
return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return userInfo, nil
}
func (ar *authRepo) RemoveUserStatus(ctx context.Context, userID string) (err error) {
err = ar.data.Cache.Del(ctx, constant.UserStatusChangedCacheKey+userID)
if err != nil {
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return nil
}
// SetUserCacheInfo set user cache info
func (ar *authRepo) SetUserCacheInfo(ctx context.Context, accessToken string, userInfo *entity.UserCacheInfo) (err error) {
userInfoCache, err := json.Marshal(userInfo)
if err != nil {
@ -64,16 +45,54 @@ func (ar *authRepo) SetUserCacheInfo(ctx context.Context, accessToken string, us
return nil
}
// RemoveUserCacheInfo remove user cache info
func (ar *authRepo) RemoveUserCacheInfo(ctx context.Context, accessToken string) (err error) {
err = ar.data.Cache.Del(ctx, constant.UserTokenCacheKey+accessToken)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
return err
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return nil
}
func (ar *authRepo) GetCmsUserCacheInfo(ctx context.Context, accessToken string) (userInfo *entity.UserCacheInfo, err error) {
// SetUserStatus set user status
func (ar *authRepo) SetUserStatus(ctx context.Context, userID string, userInfo *entity.UserCacheInfo) (err error) {
userInfoCache, err := json.Marshal(userInfo)
if err != nil {
return err
}
err = ar.data.Cache.SetString(ctx, constant.UserStatusChangedCacheKey+userID,
string(userInfoCache), constant.UserStatusChangedCacheTime)
if err != nil {
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return nil
}
// GetUserStatus get user status
func (ar *authRepo) GetUserStatus(ctx context.Context, userID string) (userInfo *entity.UserCacheInfo, err error) {
userInfoCache, err := ar.data.Cache.GetString(ctx, constant.UserStatusChangedCacheKey+userID)
if err != nil {
return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
userInfo = &entity.UserCacheInfo{}
err = json.Unmarshal([]byte(userInfoCache), userInfo)
if err != nil {
return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return userInfo, nil
}
// RemoveUserStatus remove user status
func (ar *authRepo) RemoveUserStatus(ctx context.Context, userID string) (err error) {
err = ar.data.Cache.Del(ctx, constant.UserStatusChangedCacheKey+userID)
if err != nil {
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return nil
}
// GetBackyardUserCacheInfo get backyard user cache info
func (ar *authRepo) GetBackyardUserCacheInfo(ctx context.Context, accessToken string) (userInfo *entity.UserCacheInfo, err error) {
userInfoCache, err := ar.data.Cache.GetString(ctx, constant.AdminTokenCacheKey+accessToken)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
@ -87,7 +106,8 @@ func (ar *authRepo) GetCmsUserCacheInfo(ctx context.Context, accessToken string)
return userInfo, nil
}
func (ar *authRepo) SetCmsUserCacheInfo(ctx context.Context, accessToken string, userInfo *entity.UserCacheInfo) (err error) {
// SetBackyardUserCacheInfo set backyard user cache info
func (ar *authRepo) SetBackyardUserCacheInfo(ctx context.Context, accessToken string, userInfo *entity.UserCacheInfo) (err error) {
userInfoCache, err := json.Marshal(userInfo)
if err != nil {
return err
@ -96,25 +116,22 @@ func (ar *authRepo) SetCmsUserCacheInfo(ctx context.Context, accessToken string,
err = ar.data.Cache.SetString(ctx, constant.AdminTokenCacheKey+accessToken, string(userInfoCache),
constant.AdminTokenCacheTime)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
return err
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return nil
}
func (ar *authRepo) RemoveCmsUserCacheInfo(ctx context.Context, accessToken string) (err error) {
// RemoveBackyardUserCacheInfo remove backyard user cache info
func (ar *authRepo) RemoveBackyardUserCacheInfo(ctx context.Context, accessToken string) (err error) {
err = ar.data.Cache.Del(ctx, constant.AdminTokenCacheKey+accessToken)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
return err
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return nil
}
// NewAuthRepo new repository
func NewAuthRepo(
data *data.Data,
) auth.AuthRepo {
func NewAuthRepo(data *data.Data) auth.AuthRepo {
return &authRepo{
data: data,
}

View File

@ -37,7 +37,7 @@ func (cr *collectionGroupRepo) AddCollectionGroup(ctx context.Context, collectio
func (cr *collectionGroupRepo) AddCollectionDefaultGroup(ctx context.Context, userID string) (collectionGroup *entity.CollectionGroup, err error) {
defaultGroup := &entity.CollectionGroup{
Name: "default",
DefaultGroup: schema.CG_DEFAULT,
DefaultGroup: schema.CGDefault,
UserID: userID,
}
_, err = cr.data.DB.Insert(defaultGroup)
@ -60,7 +60,8 @@ func (cr *collectionGroupRepo) UpdateCollectionGroup(ctx context.Context, collec
// GetCollectionGroup get collection group one
func (cr *collectionGroupRepo) GetCollectionGroup(ctx context.Context, id string) (
collectionGroup *entity.CollectionGroup, exist bool, err error) {
collectionGroup *entity.CollectionGroup, exist bool, err error,
) {
collectionGroup = &entity.CollectionGroup{}
exist, err = cr.data.DB.ID(id).Get(collectionGroup)
if err != nil {
@ -84,9 +85,9 @@ func (cr *collectionGroupRepo) GetCollectionGroupPage(ctx context.Context, page,
return
}
func (cr *collectionGroupRepo) GetDefaultID(ctx context.Context, userId string) (collectionGroup *entity.CollectionGroup, has bool, err error) {
func (cr *collectionGroupRepo) GetDefaultID(ctx context.Context, userID string) (collectionGroup *entity.CollectionGroup, has bool, err error) {
collectionGroup = &entity.CollectionGroup{}
has, err = cr.data.DB.Where("user_id =? and default_group = ?", userId, schema.CG_DEFAULT).Get(collectionGroup)
has, err = cr.data.DB.Where("user_id =? and default_group = ?", userID, schema.CGDefault).Get(collectionGroup)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
return

View File

@ -74,9 +74,9 @@ func (cr *collectionRepo) GetCollectionList(ctx context.Context, collection *ent
}
// GetOneByObjectIDAndUser get one by object TagID and user
func (cr *collectionRepo) GetOneByObjectIDAndUser(ctx context.Context, userId string, objectId string) (collection *entity.Collection, exist bool, err error) {
func (cr *collectionRepo) GetOneByObjectIDAndUser(ctx context.Context, userID string, objectID string) (collection *entity.Collection, exist bool, err error) {
collection = &entity.Collection{}
exist, err = cr.data.DB.Where("user_id = ? and object_id = ?", userId, objectId).Get(collection)
exist, err = cr.data.DB.Where("user_id = ? and object_id = ?", userID, objectID).Get(collection)
if err != nil {
return nil, false, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
@ -84,9 +84,9 @@ func (cr *collectionRepo) GetOneByObjectIDAndUser(ctx context.Context, userId st
}
// SearchByObjectIDsAndUser search by object IDs and user
func (cr *collectionRepo) SearchByObjectIDsAndUser(ctx context.Context, userId string, objectIds []string) ([]*entity.Collection, error) {
func (cr *collectionRepo) SearchByObjectIDsAndUser(ctx context.Context, userID string, objectIDs []string) ([]*entity.Collection, error) {
collectionList := make([]*entity.Collection, 0)
err := cr.data.DB.Where("user_id = ?", userId).In("object_id", objectIds).Find(&collectionList)
err := cr.data.DB.Where("user_id = ?", userID).In("object_id", objectIDs).Find(&collectionList)
if err != nil {
return collectionList, err
}
@ -94,9 +94,9 @@ func (cr *collectionRepo) SearchByObjectIDsAndUser(ctx context.Context, userId s
}
// CountByObjectID count by object TagID
func (cr *collectionRepo) CountByObjectID(ctx context.Context, objectId string) (total int64, err error) {
func (cr *collectionRepo) CountByObjectID(ctx context.Context, objectID string) (total int64, err error) {
collection := &entity.Collection{}
total, err = cr.data.DB.Where("object_id = ?", objectId).Count(collection)
total, err = cr.data.DB.Where("object_id = ?", objectID).Count(collection)
if err != nil {
return 0, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
@ -105,7 +105,6 @@ func (cr *collectionRepo) CountByObjectID(ctx context.Context, objectId string)
// GetCollectionPage get collection page
func (cr *collectionRepo) GetCollectionPage(ctx context.Context, page, pageSize int, collection *entity.Collection) (collectionList []*entity.Collection, total int64, err error) {
collectionList = make([]*entity.Collection, 0)
session := cr.data.DB.NewSession()
@ -124,9 +123,9 @@ func (cr *collectionRepo) GetCollectionPage(ctx context.Context, page, pageSize
}
// SearchObjectCollected check object is collected or not
func (cr *collectionRepo) SearchObjectCollected(ctx context.Context, userId string, objectIds []string) (map[string]bool, error) {
func (cr *collectionRepo) SearchObjectCollected(ctx context.Context, userID string, objectIds []string) (map[string]bool, error) {
collectedMap := make(map[string]bool)
list, err := cr.SearchByObjectIDsAndUser(ctx, userId, objectIds)
list, err := cr.SearchByObjectIDsAndUser(ctx, userID, objectIds)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
return collectedMap, err
@ -148,7 +147,7 @@ func (cr *collectionRepo) SearchList(ctx context.Context, search *entity.Collect
search.Page = 0
}
if search.PageSize == 0 {
search.PageSize = constant.Default_PageSize
search.PageSize = constant.DefaultPageSize
}
offset := search.Page * search.PageSize
session := cr.data.DB.Where("")

View File

@ -69,7 +69,8 @@ func (cr *commentRepo) UpdateComment(ctx context.Context, comment *entity.Commen
// GetComment get comment one
func (cr *commentRepo) GetComment(ctx context.Context, commentID string) (
comment *entity.Comment, exist bool, err error) {
comment *entity.Comment, exist bool, err error,
) {
comment = &entity.Comment{}
exist, err = cr.data.DB.ID(commentID).Get(comment)
if err != nil {
@ -80,7 +81,8 @@ func (cr *commentRepo) GetComment(ctx context.Context, commentID string) (
// GetCommentPage get comment page
func (cr *commentRepo) GetCommentPage(ctx context.Context, commentQuery *comment.CommentQuery) (
commentList []*entity.Comment, total int64, err error) {
commentList []*entity.Comment, total int64, err error,
) {
commentList = make([]*entity.Comment, 0)
session := cr.data.DB.NewSession()

View File

@ -37,15 +37,13 @@ func (cr *CommonRepo) GetRootObjectID(objectID string) (rootObjectID string, err
exist, err = cr.data.DB.ID(objectID).Get(&answer)
if !exist {
err = errors.BadRequest(reason.ObjectNotFound)
} else {
objectID = answer.QuestionID
}
case "comment":
exist, err = cr.data.DB.ID(objectID).Get(&comment)
exist, _ = cr.data.DB.ID(objectID).Get(&comment)
if !exist {
err = errors.BadRequest(reason.ObjectNotFound)
} else {
objectID, err = cr.GetRootObjectID(comment.ObjectID)
_, err = cr.GetRootObjectID(comment.ObjectID)
}
default:
rootObjectID = objectID
@ -72,7 +70,7 @@ func (cr *CommonRepo) GetObjectIDMap(objectID string) (objectIDMap map[string]st
}
switch objectType {
case "answer":
exist, err = cr.data.DB.ID(objectID).Get(&answer)
exist, _ = cr.data.DB.ID(objectID).Get(&answer)
if !exist {
err = errors.BadRequest(reason.ObjectNotFound)
} else {
@ -80,7 +78,7 @@ func (cr *CommonRepo) GetObjectIDMap(objectID string) (objectIDMap map[string]st
ID = answer.ID
}
case "comment":
exist, err = cr.data.DB.ID(objectID).Get(&comment)
exist, _ = cr.data.DB.ID(objectID).Get(&comment)
if !exist {
err = errors.BadRequest(reason.ObjectNotFound)
} else {

View File

@ -65,10 +65,14 @@ func (cr *configRepo) Get(key string) (interface{}, error) {
// key string
func (cr *configRepo) GetString(key string) (string, error) {
value, err := cr.Get(key)
if value != nil {
return value.(string), err
if err != nil {
return "", err
}
return "", err
str, ok := value.(string)
if !ok {
return "", errors.InternalServer(reason.DatabaseError).WithMsg(fmt.Sprintf("config value is wrong type: %v", key))
}
return str, nil
}
// GetInt method for getting the config value to int64
@ -77,9 +81,8 @@ func (cr *configRepo) GetInt(key string) (int, error) {
value, err := cr.GetString(key)
if err != nil {
return 0, err
} else {
return converter.StringToInt(value), nil
}
return converter.StringToInt(value), nil
}
// GetArrayString method for getting the config value to string array
@ -96,31 +99,35 @@ func (cr *configRepo) GetArrayString(key string) ([]string, error) {
// GetConfigType method for getting the config type
func (cr *configRepo) GetConfigType(key string) (int, error) {
value, ok := Key2IDMapping[key]
if ok {
return value, nil
} else {
if !ok {
return 0, errors.InternalServer(reason.DatabaseError).WithMsg(fmt.Sprintf("no such config type: %v", key))
}
return value, nil
}
// GetConfigById get config key from config id
func (cr *configRepo) GetConfigById(id int, value any) (err error) {
var (
ok = true
key string
conf interface{}
)
key, ok = ID2KeyMapping[id]
// GetJsonConfigByIDAndSetToObject get config key from config id
func (cr *configRepo) GetJsonConfigByIDAndSetToObject(id int, object any) (err error) {
key, ok := ID2KeyMapping[id]
if !ok {
err = errors.InternalServer(reason.DatabaseError).WithMsg(fmt.Sprintf("no such config id: %v", id))
return
return errors.InternalServer(reason.DatabaseError).WithMsg(fmt.Sprintf("no such config id: %v", id))
}
conf, err = cr.Get(key)
value = json.Unmarshal([]byte(conf.(string)), value)
conf, err := cr.Get(key)
if err != nil {
return errors.InternalServer(reason.DatabaseError).WithError(err)
}
str, ok := conf.(string)
if !ok {
return errors.InternalServer(reason.DatabaseError).WithMsg(fmt.Sprintf("no such config id: %v", id))
}
err = json.Unmarshal([]byte(str), object)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithMsg(fmt.Sprintf("no such config id: %v", id))
}
return
}
// SetConfig set config
func (cr *configRepo) SetConfig(key, value string) (err error) {
id := Key2IDMapping[key]
_, err = cr.data.DB.ID(id).Update(&entity.Config{Value: value})

View File

@ -22,6 +22,7 @@ func NewEmailRepo(data *data.Data) export.EmailRepo {
}
}
// SetCode The email code is used to verify that the link in the message is out of date
func (e *emailRepo) SetCode(ctx context.Context, code, content string) error {
err := e.data.Cache.SetString(ctx, code, content, 10*time.Minute)
if err != nil {
@ -30,6 +31,7 @@ func (e *emailRepo) SetCode(ctx context.Context, code, content string) error {
return nil
}
// VerifyCode verify the code if out of date
func (e *emailRepo) VerifyCode(ctx context.Context, code string) (content string, err error) {
content, err = e.data.Cache.GetString(ctx, code)
if err != nil {

View File

@ -4,7 +4,6 @@ import (
"context"
"github.com/answerdev/answer/internal/base/data"
"github.com/answerdev/answer/internal/base/pager"
"github.com/answerdev/answer/internal/base/reason"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/service/meta"
@ -65,17 +64,7 @@ func (mr *metaRepo) GetMetaByObjectIdAndKey(ctx context.Context, objectID, key s
// GetMetaList get meta list all
func (mr *metaRepo) GetMetaList(ctx context.Context, meta *entity.Meta) (metaList []*entity.Meta, err error) {
metaList = make([]*entity.Meta, 0)
err = mr.data.DB.Find(metaList, meta)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return
}
// GetMetaPage get meta page
func (mr *metaRepo) GetMetaPage(ctx context.Context, page, pageSize int, meta *entity.Meta) (metaList []*entity.Meta, total int64, err error) {
metaList = make([]*entity.Meta, 0)
total, err = pager.Help(page, pageSize, metaList, meta, mr.data.DB.NewSession())
err = mr.data.DB.Find(&metaList, meta)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}

View File

@ -4,8 +4,8 @@ import (
"context"
"time"
"github.com/answerdev/answer/internal/base/constant"
"github.com/answerdev/answer/internal/base/data"
"github.com/answerdev/answer/internal/base/pager"
"github.com/answerdev/answer/internal/base/reason"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/schema"
@ -29,8 +29,7 @@ func NewNotificationRepo(data *data.Data) notficationcommon.NotificationRepo {
func (nr *notificationRepo) AddNotification(ctx context.Context, notification *entity.Notification) (err error) {
_, err = nr.data.DB.Insert(notification)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
return
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return
}
@ -40,8 +39,7 @@ func (nr *notificationRepo) UpdateNotificationContent(ctx context.Context, notif
notification.UpdatedAt = now
_, err = nr.data.DB.Where("id =?", notification.ID).Cols("content", "updated_at").Update(notification)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
return
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return
}
@ -51,8 +49,7 @@ func (nr *notificationRepo) ClearUnRead(ctx context.Context, userID string, noti
info.IsRead = schema.NotificationRead
_, err = nr.data.DB.Where("user_id =?", userID).And("type =?", notificationType).Cols("is_read").Update(info)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
return
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return
}
@ -62,8 +59,7 @@ func (nr *notificationRepo) ClearIDUnRead(ctx context.Context, userID string, id
info.IsRead = schema.NotificationRead
_, err = nr.data.DB.Where("user_id =?", userID).And("id =?", id).Cols("is_read").Update(info)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
return
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return
}
@ -88,32 +84,22 @@ func (nr *notificationRepo) GetByUserIdObjectIdTypeId(ctx context.Context, userI
return info, exist, nil
}
func (nr *notificationRepo) SearchList(ctx context.Context, search *schema.NotificationSearch) ([]*entity.Notification, int64, error) {
var count int64
var err error
func (nr *notificationRepo) GetNotificationPage(ctx context.Context, searchCond *schema.NotificationSearch) (
notificationList []*entity.Notification, total int64, err error) {
notificationList = make([]*entity.Notification, 0)
if searchCond.UserID == "" {
return notificationList, 0, nil
}
rows := make([]*entity.Notification, 0)
if search.UserID == "" {
return rows, 0, nil
session := nr.data.DB.NewSession()
session = session.Desc("updated_at")
cond := &entity.Notification{
UserID: searchCond.UserID,
Type: searchCond.Type,
}
if search.Page > 0 {
search.Page = search.Page - 1
} else {
search.Page = 0
}
if search.PageSize == 0 {
search.PageSize = constant.Default_PageSize
}
offset := search.Page * search.PageSize
session := nr.data.DB.Where("")
session = session.And("user_id = ?", search.UserID)
session = session.And("type = ?", search.Type)
session = session.OrderBy("updated_at desc")
session = session.Limit(search.PageSize, offset)
count, err = session.FindAndCount(&rows)
total, err = pager.Help(searchCond.Page, searchCond.PageSize, &notificationList, cond, session)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
return rows, count, err
}
return rows, count, nil
return
}

View File

@ -4,6 +4,7 @@ import (
"github.com/answerdev/answer/internal/base/data"
"github.com/answerdev/answer/internal/repo/activity"
"github.com/answerdev/answer/internal/repo/activity_common"
"github.com/answerdev/answer/internal/repo/answer"
"github.com/answerdev/answer/internal/repo/auth"
"github.com/answerdev/answer/internal/repo/captcha"
"github.com/answerdev/answer/internal/repo/collection"
@ -13,10 +14,13 @@ import (
"github.com/answerdev/answer/internal/repo/export"
"github.com/answerdev/answer/internal/repo/meta"
"github.com/answerdev/answer/internal/repo/notification"
"github.com/answerdev/answer/internal/repo/question"
"github.com/answerdev/answer/internal/repo/rank"
"github.com/answerdev/answer/internal/repo/reason"
"github.com/answerdev/answer/internal/repo/report"
"github.com/answerdev/answer/internal/repo/revision"
"github.com/answerdev/answer/internal/repo/search_common"
"github.com/answerdev/answer/internal/repo/site_info"
"github.com/answerdev/answer/internal/repo/tag"
"github.com/answerdev/answer/internal/repo/unique"
"github.com/answerdev/answer/internal/repo/user"
@ -40,24 +44,24 @@ var ProviderSetRepo = wire.NewSet(
user.NewUserRepo,
user.NewUserBackyardRepo,
rank.NewUserRankRepo,
NewQuestionRepo,
NewAnswerRepo,
NewActivityRepo,
question.NewQuestionRepo,
answer.NewAnswerRepo,
activity_common.NewActivityRepo,
activity.NewVoteRepo,
activity.NewFollowRepo,
activity.NewAnswerActivityRepo,
activity.NewQuestionActivityRepo,
activity.NewUserActiveActivityRepo,
tag.NewTagRepo,
tag.NewTagListRepo,
tag.NewTagRelRepo,
collection.NewCollectionRepo,
collection.NewCollectionGroupRepo,
auth.NewAuthRepo,
revision.NewRevisionRepo,
NewSearchRepo,
search_common.NewSearchRepo,
meta.NewMetaRepo,
export.NewEmailRepo,
reason.NewReasonRepo,
NewSiteInfo,
site_info.NewSiteInfo,
notification.NewNotificationRepo,
)

View File

@ -1,8 +1,11 @@
package repo
package question
import (
"context"
"strings"
"time"
"unicode"
"xorm.io/builder"
"github.com/answerdev/answer/internal/base/constant"
"github.com/answerdev/answer/internal/base/data"
@ -64,27 +67,27 @@ func (qr *questionRepo) UpdateQuestion(ctx context.Context, question *entity.Que
return
}
func (qr *questionRepo) UpdatePvCount(ctx context.Context, questionId string) (err error) {
func (qr *questionRepo) UpdatePvCount(ctx context.Context, questionID string) (err error) {
question := &entity.Question{}
_, err = qr.data.DB.Where("id =?", questionId).Incr("view_count", 1).Update(question)
_, err = qr.data.DB.Where("id =?", questionID).Incr("view_count", 1).Update(question)
if err != nil {
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return nil
}
func (qr *questionRepo) UpdateAnswerCount(ctx context.Context, questionId string, num int) (err error) {
func (qr *questionRepo) UpdateAnswerCount(ctx context.Context, questionID string, num int) (err error) {
question := &entity.Question{}
_, err = qr.data.DB.Where("id =?", questionId).Incr("answer_count", num).Update(question)
_, err = qr.data.DB.Where("id =?", questionID).Incr("answer_count", num).Update(question)
if err != nil {
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return nil
}
func (qr *questionRepo) UpdateCollectionCount(ctx context.Context, questionId string, num int) (err error) {
func (qr *questionRepo) UpdateCollectionCount(ctx context.Context, questionID string, num int) (err error) {
question := &entity.Question{}
_, err = qr.data.DB.Where("id =?", questionId).Incr("collection_count", num).Update(question)
_, err = qr.data.DB.Where("id =?", questionID).Incr("collection_count", num).Update(question)
if err != nil {
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
@ -119,7 +122,8 @@ func (qr *questionRepo) UpdateLastAnswer(ctx context.Context, question *entity.Q
// GetQuestion get question one
func (qr *questionRepo) GetQuestion(ctx context.Context, id string) (
question *entity.Question, exist bool, err error) {
question *entity.Question, exist bool, err error,
) {
question = &entity.Question{}
question.ID = id
exist, err = qr.data.DB.Where("id = ?", id).Get(question)
@ -179,7 +183,7 @@ func (qr *questionRepo) SearchList(ctx context.Context, search *schema.QuestionS
search.Page = 0
}
if search.PageSize == 0 {
search.PageSize = constant.Default_PageSize
search.PageSize = constant.DefaultPageSize
}
offset := search.Page * search.PageSize
session := qr.data.DB.Table("question")
@ -187,7 +191,7 @@ func (qr *questionRepo) SearchList(ctx context.Context, search *schema.QuestionS
if len(search.TagIDs) > 0 {
session = session.Join("LEFT", "tag_rel", "question.id = tag_rel.object_id")
session = session.And("tag_rel.tag_id =?", search.TagIDs[0])
//session = session.In("tag_rel.tag_id ", search.TagIDs)
// session = session.In("tag_rel.tag_id ", search.TagIDs)
session = session.And("tag_rel.status =?", entity.TagRelStatusAvailable)
}
@ -199,8 +203,8 @@ func (qr *questionRepo) SearchList(ctx context.Context, search *schema.QuestionS
// if search.Status > 0 {
// session = session.And("question.status = ?", search.Status)
// }
//switch
//newest, active,frequent,score,unanswered
// switch
// newest, active,frequent,score,unanswered
switch search.Order {
case "newest":
session = session.OrderBy("question.created_at desc")
@ -225,8 +229,16 @@ func (qr *questionRepo) SearchList(ctx context.Context, search *schema.QuestionS
}
func (qr *questionRepo) CmsSearchList(ctx context.Context, search *schema.CmsQuestionSearch) ([]*entity.Question, int64, error) {
var count int64
var err error
var (
count int64
err error
session = qr.data.DB.Table("question")
)
session.Where(builder.Eq{
"status": search.Status,
})
rows := make([]*entity.Question, 0)
if search.Page > 0 {
search.Page = search.Page - 1
@ -234,13 +246,42 @@ func (qr *questionRepo) CmsSearchList(ctx context.Context, search *schema.CmsQue
search.Page = 0
}
if search.PageSize == 0 {
search.PageSize = constant.Default_PageSize
search.PageSize = constant.DefaultPageSize
}
// search by question title like or question id
if len(search.Query) > 0 {
// check id search
var (
idSearch = false
id = ""
)
if strings.Contains(search.Query, "question:") {
idSearch = true
id = strings.TrimSpace(strings.TrimPrefix(search.Query, "question:"))
for _, r := range id {
if !unicode.IsDigit(r) {
idSearch = false
break
}
}
}
if idSearch {
session.And(builder.Eq{
"id": id,
})
} else {
session.And(builder.Like{
"title", search.Query,
})
}
}
offset := search.Page * search.PageSize
session := qr.data.DB.Table("question")
session = session.And("status =?", search.Status)
session = session.OrderBy("updated_at desc")
session = session.Limit(search.PageSize, offset)
session.OrderBy("updated_at desc").
Limit(search.PageSize, offset)
count, err = session.FindAndCount(&rows)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()

View File

@ -34,26 +34,28 @@ func NewUserRankRepo(data *data.Data, configRepo config.ConfigRepo) rank.UserRan
// session is need provider, it means this action must be success or failure
// if outer action is failed then this action is need rollback
func (ur *UserRankRepo) TriggerUserRank(ctx context.Context,
session *xorm.Session, userId string, deltaRank int, activityType int) (isReachStandard bool, err error) {
session *xorm.Session, userID string, deltaRank int, activityType int,
) (isReachStandard bool, err error) {
if deltaRank == 0 {
return false, nil
}
if deltaRank < 0 {
// if user rank is lower than 1 after this action, then user rank will be set to 1 only.
isReachMin, err := ur.checkUserMinRank(ctx, session, userId, activityType)
var isReachMin bool
isReachMin, err = ur.checkUserMinRank(ctx, session, userID, activityType)
if err != nil {
return false, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
if isReachMin {
_, err = session.Where(builder.Eq{"id": userId}).Update(&entity.User{Rank: 1})
_, err = session.Where(builder.Eq{"id": userID}).Update(&entity.User{Rank: 1})
if err != nil {
return false, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return false, nil
}
} else {
isReachStandard, err = ur.checkUserTodayRank(ctx, session, userId, activityType)
isReachStandard, err = ur.checkUserTodayRank(ctx, session, userID, activityType)
if err != nil {
return false, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
@ -61,7 +63,7 @@ func (ur *UserRankRepo) TriggerUserRank(ctx context.Context,
return isReachStandard, nil
}
}
_, err = session.Where(builder.Eq{"id": userId}).Incr("`rank`", deltaRank).Update(&entity.User{})
_, err = session.Where(builder.Eq{"id": userID}).Incr("`rank`", deltaRank).Update(&entity.User{})
if err != nil {
return false, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
@ -69,7 +71,8 @@ func (ur *UserRankRepo) TriggerUserRank(ctx context.Context,
}
func (ur *UserRankRepo) checkUserMinRank(ctx context.Context, session *xorm.Session, userID string, deltaRank int) (
isReachStandard bool, err error) {
isReachStandard bool, err error,
) {
bean := &entity.User{ID: userID}
_, err = session.Select("rank").Get(bean)
if err != nil {
@ -83,11 +86,13 @@ func (ur *UserRankRepo) checkUserMinRank(ctx context.Context, session *xorm.Sess
}
func (ur *UserRankRepo) checkUserTodayRank(ctx context.Context,
session *xorm.Session, userID string, activityType int) (isReachStandard bool, err error) {
session *xorm.Session, userID string, activityType int,
) (isReachStandard bool, err error) {
// exclude daily rank
exclude, err := ur.configRepo.GetArrayString("daily_rank_limit.exclude")
exclude, _ := ur.configRepo.GetArrayString("daily_rank_limit.exclude")
for _, item := range exclude {
excludeActivityType, err := ur.configRepo.GetInt(item)
var excludeActivityType int
excludeActivityType, err = ur.configRepo.GetInt(item)
if err != nil {
return false, err
}
@ -123,14 +128,15 @@ func (ur *UserRankRepo) checkUserTodayRank(ctx context.Context,
return true, nil
}
func (ur *UserRankRepo) UserRankPage(ctx context.Context, userId string, page, pageSize int) (
rankPage []*entity.Activity, total int64, err error) {
func (ur *UserRankRepo) UserRankPage(ctx context.Context, userID string, page, pageSize int) (
rankPage []*entity.Activity, total int64, err error,
) {
rankPage = make([]*entity.Activity, 0)
session := ur.data.DB.Where(builder.Eq{"has_rank": 1}.And(builder.Eq{"cancelled": 0}))
session.Desc("created_at")
cond := &entity.Activity{UserID: userId}
cond := &entity.Activity{UserID: userID}
total, err = pager.Help(page, pageSize, &rankPage, cond, session)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()

View File

@ -21,9 +21,9 @@ func NewReasonRepo(configRepo config.ConfigRepo) reason_common.ReasonRepo {
}
}
func (rr *reasonRepo) ListReasons(ctx context.Context, req schema.ReasonReq) (resp []schema.ReasonItem, err error) {
func (rr *reasonRepo) ListReasons(ctx context.Context, objectType, action string) (resp []schema.ReasonItem, err error) {
var (
reasonAction = fmt.Sprintf("%s.%s.reasons", req.ObjectType, req.Action)
reasonAction = fmt.Sprintf("%s.%s.reasons", objectType, action)
reasonKeys []string
cfgValue string
)

View File

@ -0,0 +1,86 @@
package repo_test
import (
"context"
"testing"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/repo/auth"
"github.com/stretchr/testify/assert"
)
var (
token = "token"
userID = "1"
)
func Test_authRepo_SetUserCacheInfo(t *testing.T) {
authRepo := auth.NewAuthRepo(testDataSource)
err := authRepo.SetUserCacheInfo(context.TODO(), token, &entity.UserCacheInfo{UserID: userID})
assert.NoError(t, err)
cacheInfo, err := authRepo.GetUserCacheInfo(context.TODO(), token)
assert.NoError(t, err)
assert.Equal(t, userID, cacheInfo.UserID)
}
func Test_authRepo_RemoveUserCacheInfo(t *testing.T) {
authRepo := auth.NewAuthRepo(testDataSource)
err := authRepo.SetUserCacheInfo(context.TODO(), token, &entity.UserCacheInfo{UserID: userID})
assert.NoError(t, err)
err = authRepo.RemoveUserCacheInfo(context.TODO(), token)
assert.NoError(t, err)
_, err = authRepo.GetUserCacheInfo(context.TODO(), token)
assert.Error(t, err)
}
func Test_authRepo_SetUserStatus(t *testing.T) {
authRepo := auth.NewAuthRepo(testDataSource)
err := authRepo.SetUserStatus(context.TODO(), userID, &entity.UserCacheInfo{UserID: userID})
assert.NoError(t, err)
cacheInfo, err := authRepo.GetUserStatus(context.TODO(), userID)
assert.NoError(t, err)
assert.Equal(t, userID, cacheInfo.UserID)
}
func Test_authRepo_RemoveUserStatus(t *testing.T) {
authRepo := auth.NewAuthRepo(testDataSource)
err := authRepo.SetUserStatus(context.TODO(), userID, &entity.UserCacheInfo{UserID: userID})
assert.NoError(t, err)
err = authRepo.RemoveUserStatus(context.TODO(), userID)
assert.NoError(t, err)
_, err = authRepo.GetUserStatus(context.TODO(), userID)
assert.Error(t, err)
}
func Test_authRepo_SetBackyardUserCacheInfo(t *testing.T) {
authRepo := auth.NewAuthRepo(testDataSource)
err := authRepo.SetBackyardUserCacheInfo(context.TODO(), token, &entity.UserCacheInfo{UserID: userID})
assert.NoError(t, err)
cacheInfo, err := authRepo.GetBackyardUserCacheInfo(context.TODO(), token)
assert.NoError(t, err)
assert.Equal(t, userID, cacheInfo.UserID)
}
func Test_authRepo_RemoveBackyardUserCacheInfo(t *testing.T) {
authRepo := auth.NewAuthRepo(testDataSource)
err := authRepo.SetBackyardUserCacheInfo(context.TODO(), token, &entity.UserCacheInfo{UserID: userID})
assert.NoError(t, err)
err = authRepo.RemoveBackyardUserCacheInfo(context.TODO(), token)
assert.NoError(t, err)
_, err = authRepo.GetBackyardUserCacheInfo(context.TODO(), token)
assert.Error(t, err)
}

View File

@ -0,0 +1,39 @@
package repo_test
import (
"context"
"testing"
"github.com/answerdev/answer/internal/repo/captcha"
"github.com/stretchr/testify/assert"
)
var (
ip = "127.0.0.1"
actionType = "actionType"
amount = 1
)
func Test_captchaRepo_DelActionType(t *testing.T) {
captchaRepo := captcha.NewCaptchaRepo(testDataSource)
err := captchaRepo.SetActionType(context.TODO(), ip, actionType, amount)
assert.NoError(t, err)
gotAmount, err := captchaRepo.GetActionType(context.TODO(), ip, actionType)
assert.NoError(t, err)
assert.Equal(t, amount, gotAmount)
err = captchaRepo.DelActionType(context.TODO(), ip, actionType)
assert.NoError(t, err)
}
func Test_captchaRepo_SetCaptcha(t *testing.T) {
captchaRepo := captcha.NewCaptchaRepo(testDataSource)
key, capt := "key", "1234"
err := captchaRepo.SetCaptcha(context.TODO(), key, capt)
assert.NoError(t, err)
gotCaptcha, err := captchaRepo.GetCaptcha(context.TODO(), key)
assert.NoError(t, err)
assert.Equal(t, capt, gotCaptcha)
}

View File

@ -0,0 +1,81 @@
package repo_test
import (
"context"
"testing"
"github.com/answerdev/answer/internal/base/pager"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/repo/comment"
"github.com/answerdev/answer/internal/repo/unique"
commentService "github.com/answerdev/answer/internal/service/comment"
"github.com/stretchr/testify/assert"
)
func buildCommentEntity() *entity.Comment {
return &entity.Comment{
UserID: "1",
ObjectID: "1",
QuestionID: "1",
VoteCount: 1,
Status: entity.CommentStatusAvailable,
OriginalText: "# title",
ParsedText: "<h1>Title</h1>",
}
}
func Test_commentRepo_AddComment(t *testing.T) {
uniqueIDRepo := unique.NewUniqueIDRepo(testDataSource)
commentRepo := comment.NewCommentRepo(testDataSource, uniqueIDRepo)
testCommentEntity := buildCommentEntity()
err := commentRepo.AddComment(context.TODO(), testCommentEntity)
assert.NoError(t, err)
err = commentRepo.RemoveComment(context.TODO(), testCommentEntity.ID)
assert.NoError(t, err)
return
}
func Test_commentRepo_GetCommentPage(t *testing.T) {
uniqueIDRepo := unique.NewUniqueIDRepo(testDataSource)
commentRepo := comment.NewCommentRepo(testDataSource, uniqueIDRepo)
testCommentEntity := buildCommentEntity()
err := commentRepo.AddComment(context.TODO(), testCommentEntity)
assert.NoError(t, err)
resp, total, err := commentRepo.GetCommentPage(context.TODO(), &commentService.CommentQuery{
PageCond: pager.PageCond{
Page: 1,
PageSize: 10,
},
})
assert.NoError(t, err)
assert.Equal(t, total, int64(1))
assert.Equal(t, resp[0].ID, testCommentEntity.ID)
err = commentRepo.RemoveComment(context.TODO(), testCommentEntity.ID)
assert.NoError(t, err)
return
}
func Test_commentRepo_UpdateComment(t *testing.T) {
uniqueIDRepo := unique.NewUniqueIDRepo(testDataSource)
commentRepo := comment.NewCommentRepo(testDataSource, uniqueIDRepo)
commonCommentRepo := comment.NewCommentCommonRepo(testDataSource, uniqueIDRepo)
testCommentEntity := buildCommentEntity()
err := commentRepo.AddComment(context.TODO(), testCommentEntity)
assert.NoError(t, err)
testCommentEntity.ParsedText = "test"
err = commentRepo.UpdateComment(context.TODO(), testCommentEntity)
assert.NoError(t, err)
newComment, exist, err := commonCommentRepo.GetComment(context.TODO(), testCommentEntity.ID)
assert.NoError(t, err)
assert.True(t, exist)
assert.Equal(t, testCommentEntity.ParsedText, newComment.ParsedText)
err = commentRepo.RemoveComment(context.TODO(), testCommentEntity.ID)
assert.NoError(t, err)
return
}

View File

@ -0,0 +1,62 @@
package repo_test
import (
"testing"
"github.com/answerdev/answer/internal/repo/config"
"github.com/answerdev/answer/internal/schema"
"github.com/stretchr/testify/assert"
)
func Test_configRepo_Get(t *testing.T) {
configRepo := config.NewConfigRepo(testDataSource)
_, err := configRepo.Get("email.config")
assert.NoError(t, err)
}
func Test_configRepo_GetArrayString(t *testing.T) {
configRepo := config.NewConfigRepo(testDataSource)
got, err := configRepo.GetArrayString("daily_rank_limit.exclude")
assert.NoError(t, err)
assert.Equal(t, 1, len(got))
assert.Equal(t, "answer.accepted", got[0])
}
func Test_configRepo_GetConfigById(t *testing.T) {
configRepo := config.NewConfigRepo(testDataSource)
closeInfo := &schema.GetReportTypeResp{}
err := configRepo.GetJsonConfigByIDAndSetToObject(74, closeInfo)
assert.NoError(t, err)
assert.Equal(t, "needs close", closeInfo.Name)
}
func Test_configRepo_GetConfigType(t *testing.T) {
configRepo := config.NewConfigRepo(testDataSource)
configType, err := configRepo.GetConfigType("answer.accepted")
assert.NoError(t, err)
assert.Equal(t, 1, configType)
}
func Test_configRepo_GetInt(t *testing.T) {
configRepo := config.NewConfigRepo(testDataSource)
got, err := configRepo.GetInt("answer.accepted")
assert.NoError(t, err)
assert.Equal(t, 15, got)
}
func Test_configRepo_GetString(t *testing.T) {
configRepo := config.NewConfigRepo(testDataSource)
_, err := configRepo.GetString("email.config")
assert.NoError(t, err)
}
func Test_configRepo_SetConfig(t *testing.T) {
configRepo := config.NewConfigRepo(testDataSource)
got, err := configRepo.GetString("email.config")
assert.NoError(t, err)
err = configRepo.SetConfig("email.config", got)
assert.NoError(t, err)
}

View File

@ -0,0 +1,20 @@
package repo_test
import (
"context"
"testing"
"github.com/answerdev/answer/internal/repo/export"
"github.com/stretchr/testify/assert"
)
func Test_emailRepo_VerifyCode(t *testing.T) {
emailRepo := export.NewEmailRepo(testDataSource)
code, content := "1111", "test"
err := emailRepo.SetCode(context.TODO(), code, content)
assert.NoError(t, err)
verifyContent, err := emailRepo.VerifyCode(context.TODO(), code)
assert.NoError(t, err)
assert.Equal(t, content, verifyContent)
}

View File

@ -0,0 +1,86 @@
package repo_test
import (
"context"
"testing"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/repo/meta"
"github.com/stretchr/testify/assert"
)
func buildMetaEntity() *entity.Meta {
return &entity.Meta{
ObjectID: "1",
Key: "1",
Value: "1",
}
}
func Test_metaRepo_GetMetaByObjectIdAndKey(t *testing.T) {
metaRepo := meta.NewMetaRepo(testDataSource)
metaEnt := buildMetaEntity()
err := metaRepo.AddMeta(context.TODO(), metaEnt)
assert.NoError(t, err)
gotMeta, exist, err := metaRepo.GetMetaByObjectIdAndKey(context.TODO(), metaEnt.ObjectID, metaEnt.Key)
assert.NoError(t, err)
assert.True(t, exist)
assert.Equal(t, metaEnt.ID, gotMeta.ID)
err = metaRepo.RemoveMeta(context.TODO(), metaEnt.ID)
assert.NoError(t, err)
}
func Test_metaRepo_GetMetaList(t *testing.T) {
metaRepo := meta.NewMetaRepo(testDataSource)
metaEnt := buildMetaEntity()
err := metaRepo.AddMeta(context.TODO(), metaEnt)
assert.NoError(t, err)
gotMetaList, err := metaRepo.GetMetaList(context.TODO(), metaEnt)
assert.NoError(t, err)
assert.Equal(t, len(gotMetaList), 1)
assert.Equal(t, gotMetaList[0].ID, metaEnt.ID)
err = metaRepo.RemoveMeta(context.TODO(), metaEnt.ID)
assert.NoError(t, err)
}
func Test_metaRepo_GetMetaPage(t *testing.T) {
metaRepo := meta.NewMetaRepo(testDataSource)
metaEnt := buildMetaEntity()
err := metaRepo.AddMeta(context.TODO(), metaEnt)
assert.NoError(t, err)
gotMetaList, err := metaRepo.GetMetaList(context.TODO(), metaEnt)
assert.NoError(t, err)
assert.Equal(t, len(gotMetaList), 1)
assert.Equal(t, gotMetaList[0].ID, metaEnt.ID)
err = metaRepo.RemoveMeta(context.TODO(), metaEnt.ID)
assert.NoError(t, err)
}
func Test_metaRepo_UpdateMeta(t *testing.T) {
metaRepo := meta.NewMetaRepo(testDataSource)
metaEnt := buildMetaEntity()
err := metaRepo.AddMeta(context.TODO(), metaEnt)
assert.NoError(t, err)
metaEnt.Value = "testing"
err = metaRepo.UpdateMeta(context.TODO(), metaEnt)
assert.NoError(t, err)
gotMeta, exist, err := metaRepo.GetMetaByObjectIdAndKey(context.TODO(), metaEnt.ObjectID, metaEnt.Key)
assert.NoError(t, err)
assert.True(t, exist)
assert.Equal(t, gotMeta.Value, metaEnt.Value)
err = metaRepo.RemoveMeta(context.TODO(), metaEnt.ID)
assert.NoError(t, err)
}

View File

@ -0,0 +1,104 @@
package repo_test
import (
"context"
"testing"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/repo/notification"
"github.com/answerdev/answer/internal/schema"
"github.com/stretchr/testify/assert"
)
func buildNotificationEntity() *entity.Notification {
return &entity.Notification{
UserID: "1",
ObjectID: "1",
Content: "1",
Type: schema.NotificationTypeInbox,
IsRead: schema.NotificationNotRead,
Status: schema.NotificationStatusNormal,
}
}
func Test_notificationRepo_ClearIDUnRead(t *testing.T) {
notificationRepo := notification.NewNotificationRepo(testDataSource)
ent := buildNotificationEntity()
err := notificationRepo.AddNotification(context.TODO(), ent)
assert.NoError(t, err)
err = notificationRepo.ClearIDUnRead(context.TODO(), ent.UserID, ent.ID)
assert.NoError(t, err)
got, exists, err := notificationRepo.GetById(context.TODO(), ent.ID)
assert.NoError(t, err)
assert.True(t, exists)
assert.Equal(t, schema.NotificationRead, got.IsRead)
}
func Test_notificationRepo_ClearUnRead(t *testing.T) {
notificationRepo := notification.NewNotificationRepo(testDataSource)
ent := buildNotificationEntity()
err := notificationRepo.AddNotification(context.TODO(), ent)
assert.NoError(t, err)
err = notificationRepo.ClearUnRead(context.TODO(), ent.UserID, ent.Type)
assert.NoError(t, err)
got, exists, err := notificationRepo.GetById(context.TODO(), ent.ID)
assert.NoError(t, err)
assert.True(t, exists)
assert.Equal(t, schema.NotificationRead, got.IsRead)
}
func Test_notificationRepo_GetById(t *testing.T) {
notificationRepo := notification.NewNotificationRepo(testDataSource)
ent := buildNotificationEntity()
err := notificationRepo.AddNotification(context.TODO(), ent)
assert.NoError(t, err)
got, exists, err := notificationRepo.GetById(context.TODO(), ent.ID)
assert.NoError(t, err)
assert.True(t, exists)
assert.Equal(t, got.ID, ent.ID)
}
func Test_notificationRepo_GetByUserIdObjectIdTypeId(t *testing.T) {
notificationRepo := notification.NewNotificationRepo(testDataSource)
ent := buildNotificationEntity()
err := notificationRepo.AddNotification(context.TODO(), ent)
assert.NoError(t, err)
got, exists, err := notificationRepo.GetByUserIdObjectIdTypeId(context.TODO(), ent.UserID, ent.ObjectID, ent.Type)
assert.NoError(t, err)
assert.True(t, exists)
assert.Equal(t, got.ObjectID, ent.ObjectID)
}
func Test_notificationRepo_GetNotificationPage(t *testing.T) {
notificationRepo := notification.NewNotificationRepo(testDataSource)
ent := buildNotificationEntity()
err := notificationRepo.AddNotification(context.TODO(), ent)
assert.NoError(t, err)
notificationPage, total, err := notificationRepo.GetNotificationPage(context.TODO(), &schema.NotificationSearch{UserID: userID})
assert.NoError(t, err)
assert.True(t, total > 0)
assert.Equal(t, notificationPage[0].UserID, ent.UserID)
}
func Test_notificationRepo_UpdateNotificationContent(t *testing.T) {
notificationRepo := notification.NewNotificationRepo(testDataSource)
ent := buildNotificationEntity()
err := notificationRepo.AddNotification(context.TODO(), ent)
assert.NoError(t, err)
ent.Content = "test"
err = notificationRepo.UpdateNotificationContent(context.TODO(), ent)
assert.NoError(t, err)
got, exists, err := notificationRepo.GetById(context.TODO(), ent.ID)
assert.NoError(t, err)
assert.True(t, exists)
assert.Equal(t, got.Content, ent.Content)
}

View File

@ -0,0 +1,18 @@
package repo_test
import (
"context"
"testing"
"github.com/answerdev/answer/internal/repo/config"
"github.com/answerdev/answer/internal/repo/reason"
"github.com/stretchr/testify/assert"
)
func Test_reasonRepo_ListReasons(t *testing.T) {
configRepo := config.NewConfigRepo(testDataSource)
reasonRepo := reason.NewReasonRepo(configRepo)
reasonItems, err := reasonRepo.ListReasons(context.TODO(), "question", "close")
assert.NoError(t, err)
assert.Equal(t, 4, len(reasonItems))
}

View File

@ -0,0 +1,172 @@
package repo_test
import (
"database/sql"
"fmt"
"os"
"testing"
"time"
"github.com/answerdev/answer/internal/base/data"
"github.com/answerdev/answer/internal/migrations"
"github.com/ory/dockertest/v3"
"github.com/ory/dockertest/v3/docker"
"github.com/segmentfault/pacman/cache"
"github.com/segmentfault/pacman/log"
"xorm.io/xorm"
"xorm.io/xorm/schemas"
)
var (
mysqlDBSetting = TestDBSetting{
Driver: string(schemas.MYSQL),
ImageName: "mariadb",
ImageVersion: "10.4.7",
ENV: []string{"MYSQL_ROOT_PASSWORD=root", "MYSQL_DATABASE=answer", "MYSQL_ROOT_HOST=%"},
PortID: "3306/tcp",
Connection: "root:root@(localhost:%s)/answer?parseTime=true", // port is not fixed, it will be got by port id
}
postgresDBSetting = TestDBSetting{
Driver: string(schemas.POSTGRES),
ImageName: "postgres",
ImageVersion: "14",
ENV: []string{"POSTGRES_USER=root", "POSTGRES_PASSWORD=root", "POSTGRES_DB=answer", "LISTEN_ADDRESSES='*'"},
PortID: "5432/tcp",
Connection: "host=localhost port=%s user=root password=root dbname=answer sslmode=disable",
}
sqlite3DBSetting = TestDBSetting{
Driver: string(schemas.SQLITE),
Connection: os.TempDir() + "answer-test-data.db",
}
dbSettingMapping = map[string]TestDBSetting{
mysqlDBSetting.Driver: mysqlDBSetting,
sqlite3DBSetting.Driver: sqlite3DBSetting,
postgresDBSetting.Driver: postgresDBSetting,
}
// after all test down will execute tearDown function to clean-up
tearDown func()
// testDataSource used for repo testing
testDataSource *data.Data
)
func TestMain(t *testing.M) {
dbSetting, ok := dbSettingMapping[os.Getenv("TEST_DB_DRIVER")]
if !ok {
dbSetting = dbSettingMapping[string(schemas.MYSQL)]
}
defer func() {
if tearDown != nil {
tearDown()
}
}()
if err := initTestDataSource(dbSetting); err != nil {
panic(err)
}
log.Info("init test database successfully")
if ret := t.Run(); ret != 0 {
panic(ret)
}
}
type TestDBSetting struct {
Driver string
ImageName string
ImageVersion string
ENV []string
PortID string
Connection string
}
func initTestDataSource(dbSetting TestDBSetting) error {
connection, imageCleanUp, err := initDatabaseImage(dbSetting)
if err != nil {
return err
}
dbSetting.Connection = connection
dbEngine, err := initDatabase(dbSetting)
if err != nil {
return err
}
newCache, err := initCache()
if err != nil {
return err
}
newData, dbCleanUp, err := data.NewData(dbEngine, newCache)
if err != nil {
return err
}
testDataSource = newData
tearDown = func() {
dbCleanUp()
log.Info("cleanup test database successfully")
imageCleanUp()
log.Info("cleanup test database image successfully")
}
return nil
}
func initDatabaseImage(dbSetting TestDBSetting) (connection string, cleanup func(), err error) {
// sqlite3 don't need to set up image
if dbSetting.Driver == string(schemas.SQLITE) {
return dbSetting.Connection, func() {
log.Info("remove database", dbSetting.Connection)
err = os.Remove(dbSetting.Connection)
if err != nil {
log.Error(err)
}
}, nil
}
pool, err := dockertest.NewPool("")
pool.MaxWait = time.Minute * 5
if err != nil {
return "", nil, fmt.Errorf("could not connect to docker: %s", err)
}
//resource, err := pool.Run(dbSetting.ImageName, dbSetting.ImageVersion, dbSetting.ENV)
resource, err := pool.RunWithOptions(&dockertest.RunOptions{
Repository: dbSetting.ImageName,
Tag: dbSetting.ImageVersion,
Env: dbSetting.ENV,
}, func(config *docker.HostConfig) {
config.AutoRemove = true
config.RestartPolicy = docker.RestartPolicy{Name: "no"}
})
if err != nil {
return "", nil, fmt.Errorf("could not pull resource: %s", err)
}
connection = fmt.Sprintf(dbSetting.Connection, resource.GetPort(dbSetting.PortID))
if err := pool.Retry(func() error {
db, err := sql.Open(dbSetting.Driver, connection)
if err != nil {
return err
}
return db.Ping()
}); err != nil {
return "", nil, fmt.Errorf("could not connect to database: %s", err)
}
return connection, func() { _ = pool.Purge(resource) }, nil
}
func initDatabase(dbSetting TestDBSetting) (dbEngine *xorm.Engine, err error) {
dataConf := &data.Database{Driver: dbSetting.Driver, Connection: dbSetting.Connection}
dbEngine, err = data.NewDB(true, dataConf)
if err != nil {
return nil, fmt.Errorf("connection to database failed: %s", err)
}
err = migrations.InitDB(dataConf)
if err != nil {
return nil, fmt.Errorf("migrations init database failed: %s", err)
}
return dbEngine, nil
}
func initCache() (newCache cache.Cache, err error) {
newCache, _, err = data.NewCache(&data.CacheConf{})
return newCache, err
}

View File

@ -0,0 +1,99 @@
package repo_test
import (
"context"
"encoding/json"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/repo/question"
"github.com/answerdev/answer/internal/repo/revision"
"github.com/answerdev/answer/internal/repo/unique"
"github.com/stretchr/testify/assert"
"testing"
)
var q = &entity.Question{
ID: "",
UserID: "1",
Title: "test",
OriginalText: "test",
ParsedText: "test",
Status: entity.QuestionStatusAvailable,
ViewCount: 0,
UniqueViewCount: 0,
VoteCount: 0,
AnswerCount: 0,
CollectionCount: 0,
FollowCount: 0,
AcceptedAnswerID: "",
LastAnswerID: "",
RevisionID: "0",
}
func getRev(objID, title, content string) *entity.Revision {
return &entity.Revision{
ID: "",
UserID: "1",
ObjectID: objID,
Title: title,
Content: content,
Log: "add rev",
}
}
func Test_revisionRepo_AddRevision(t *testing.T) {
var (
uniqueIDRepo = unique.NewUniqueIDRepo(testDataSource)
revisionRepo = revision.NewRevisionRepo(testDataSource, uniqueIDRepo)
questionRepo = question.NewQuestionRepo(testDataSource, uniqueIDRepo)
)
// create question
err := questionRepo.AddQuestion(context.TODO(), q)
assert.NoError(t, err)
assert.NotEqual(t, "", q.ID)
content, err := json.Marshal(q)
// auto update false
rev := getRev(q.ID, q.Title, string(content))
err = revisionRepo.AddRevision(context.TODO(), rev, false)
assert.NoError(t, err)
qr, _, _ := questionRepo.GetQuestion(context.TODO(), q.ID)
assert.NotEqual(t, rev.ID, qr.RevisionID)
// auto update false
rev = getRev(q.ID, q.Title, string(content))
err = revisionRepo.AddRevision(context.TODO(), rev, true)
assert.NoError(t, err)
qr, _, _ = questionRepo.GetQuestion(context.TODO(), q.ID)
assert.Equal(t, rev.ID, qr.RevisionID)
// recovery
t.Cleanup(func() {
err = questionRepo.RemoveQuestion(context.TODO(), q.ID)
assert.NoError(t, err)
})
}
func Test_revisionRepo_GetLastRevisionByObjectID(t *testing.T) {
var (
uniqueIDRepo = unique.NewUniqueIDRepo(testDataSource)
revisionRepo = revision.NewRevisionRepo(testDataSource, uniqueIDRepo)
)
Test_revisionRepo_AddRevision(t)
rev, exists, err := revisionRepo.GetLastRevisionByObjectID(context.TODO(), q.ID)
assert.NoError(t, err)
assert.True(t, exists)
assert.NotNil(t, rev)
}
func Test_revisionRepo_GetRevisionList(t *testing.T) {
var (
uniqueIDRepo = unique.NewUniqueIDRepo(testDataSource)
revisionRepo = revision.NewRevisionRepo(testDataSource, uniqueIDRepo)
)
Test_revisionRepo_AddRevision(t)
revs, err := revisionRepo.GetRevisionList(context.TODO(), &entity.Revision{ObjectID: q.ID})
assert.NoError(t, err)
assert.GreaterOrEqual(t, len(revs), 1)
}

View File

@ -0,0 +1,33 @@
package repo_test
import (
"context"
"testing"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/repo/site_info"
"github.com/stretchr/testify/assert"
)
func Test_siteInfoRepo_SaveByType(t *testing.T) {
siteInfoRepo := site_info.NewSiteInfo(testDataSource)
data := &entity.SiteInfo{Content: "site_info", Type: "test"}
err := siteInfoRepo.SaveByType(context.TODO(), data.Type, data)
assert.NoError(t, err)
got, exist, err := siteInfoRepo.GetByType(context.TODO(), data.Type)
assert.NoError(t, err)
assert.True(t, exist)
assert.Equal(t, data.Content, got.Content)
data.Content = "new site_info"
err = siteInfoRepo.SaveByType(context.TODO(), data.Type, data)
assert.NoError(t, err)
got, exist, err = siteInfoRepo.GetByType(context.TODO(), data.Type)
assert.NoError(t, err)
assert.True(t, exist)
assert.Equal(t, data.Content, got.Content)
}

View File

@ -0,0 +1,91 @@
package repo_test
import (
"context"
"sync"
"testing"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/repo/tag"
"github.com/stretchr/testify/assert"
)
var (
tagRelOnce sync.Once
testTagRelList = []*entity.TagRel{
{
ObjectID: "1",
TagID: "1",
Status: entity.TagRelStatusAvailable,
},
{
ObjectID: "2",
TagID: "2",
Status: entity.TagRelStatusAvailable,
},
}
)
func addTagRelList() {
tagRelRepo := tag.NewTagRelRepo(testDataSource)
err := tagRelRepo.AddTagRelList(context.TODO(), testTagRelList)
if err != nil {
panic(err)
}
}
func Test_tagListRepo_BatchGetObjectTagRelList(t *testing.T) {
tagRelOnce.Do(addTagRelList)
tagRelRepo := tag.NewTagRelRepo(testDataSource)
relList, err :=
tagRelRepo.BatchGetObjectTagRelList(context.TODO(), []string{testTagRelList[0].ObjectID, testTagRelList[1].ObjectID})
assert.NoError(t, err)
assert.Equal(t, 2, len(relList))
}
func Test_tagListRepo_CountTagRelByTagID(t *testing.T) {
tagRelOnce.Do(addTagRelList)
tagRelRepo := tag.NewTagRelRepo(testDataSource)
count, err := tagRelRepo.CountTagRelByTagID(context.TODO(), "1")
assert.NoError(t, err)
assert.Equal(t, int64(1), count)
}
func Test_tagListRepo_GetObjectTagRelList(t *testing.T) {
tagRelOnce.Do(addTagRelList)
tagRelRepo := tag.NewTagRelRepo(testDataSource)
relList, err :=
tagRelRepo.GetObjectTagRelList(context.TODO(), testTagRelList[0].ObjectID)
assert.NoError(t, err)
assert.Equal(t, 1, len(relList))
}
func Test_tagListRepo_GetObjectTagRelWithoutStatus(t *testing.T) {
tagRelOnce.Do(addTagRelList)
tagRelRepo := tag.NewTagRelRepo(testDataSource)
relList, err :=
tagRelRepo.BatchGetObjectTagRelList(context.TODO(), []string{testTagRelList[0].ObjectID, testTagRelList[1].ObjectID})
assert.NoError(t, err)
assert.Equal(t, 2, len(relList))
ids := []int64{relList[0].ID, relList[1].ID}
err = tagRelRepo.RemoveTagRelListByIDs(context.TODO(), ids)
assert.NoError(t, err)
count, err := tagRelRepo.CountTagRelByTagID(context.TODO(), "1")
assert.NoError(t, err)
assert.Equal(t, int64(0), count)
_, exist, err := tagRelRepo.GetObjectTagRelWithoutStatus(context.TODO(), relList[0].ObjectID, relList[0].TagID)
assert.NoError(t, err)
assert.True(t, exist)
err = tagRelRepo.EnableTagRelByIDs(context.TODO(), ids)
assert.NoError(t, err)
count, err = tagRelRepo.CountTagRelByTagID(context.TODO(), "1")
assert.NoError(t, err)
assert.Equal(t, int64(1), count)
}

View File

@ -0,0 +1,173 @@
package repo_test
import (
"context"
"fmt"
"sync"
"testing"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/repo/tag"
"github.com/answerdev/answer/internal/repo/unique"
"github.com/answerdev/answer/pkg/converter"
"github.com/stretchr/testify/assert"
)
var (
tagOnce sync.Once
testTagList = []*entity.Tag{
{
SlugName: "go",
DisplayName: "Golang",
OriginalText: "golang",
ParsedText: "<p>golang</p>",
Status: entity.TagStatusAvailable,
},
{
SlugName: "js",
DisplayName: "javascript",
OriginalText: "javascript",
ParsedText: "<p>javascript</p>",
Status: entity.TagStatusAvailable,
},
{
SlugName: "go2",
DisplayName: "Golang2",
OriginalText: "golang2",
ParsedText: "<p>golang2</p>",
Status: entity.TagStatusAvailable,
},
}
)
func addTagList() {
uniqueIDRepo := unique.NewUniqueIDRepo(testDataSource)
tagRepo := tag.NewTagRepo(testDataSource, uniqueIDRepo)
err := tagRepo.AddTagList(context.TODO(), testTagList)
if err != nil {
panic(err)
}
}
func Test_tagRepo_GetTagByID(t *testing.T) {
tagOnce.Do(addTagList)
tagRepo := tag.NewTagRepo(testDataSource, unique.NewUniqueIDRepo(testDataSource))
gotTag, exist, err := tagRepo.GetTagByID(context.TODO(), testTagList[0].ID)
assert.NoError(t, err)
assert.True(t, exist)
assert.Equal(t, testTagList[0].SlugName, gotTag.SlugName)
}
func Test_tagRepo_GetTagBySlugName(t *testing.T) {
tagOnce.Do(addTagList)
tagRepo := tag.NewTagRepo(testDataSource, unique.NewUniqueIDRepo(testDataSource))
gotTag, exist, err := tagRepo.GetTagBySlugName(context.TODO(), testTagList[0].SlugName)
assert.NoError(t, err)
assert.True(t, exist)
assert.Equal(t, testTagList[0].SlugName, gotTag.SlugName)
}
func Test_tagRepo_GetTagList(t *testing.T) {
tagOnce.Do(addTagList)
tagRepo := tag.NewTagRepo(testDataSource, unique.NewUniqueIDRepo(testDataSource))
gotTags, err := tagRepo.GetTagList(context.TODO(), &entity.Tag{ID: testTagList[0].ID})
assert.NoError(t, err)
assert.Equal(t, testTagList[0].SlugName, gotTags[0].SlugName)
}
func Test_tagRepo_GetTagListByIDs(t *testing.T) {
tagOnce.Do(addTagList)
tagRepo := tag.NewTagRepo(testDataSource, unique.NewUniqueIDRepo(testDataSource))
gotTags, err := tagRepo.GetTagListByIDs(context.TODO(), []string{testTagList[0].ID})
assert.NoError(t, err)
assert.Equal(t, testTagList[0].SlugName, gotTags[0].SlugName)
}
func Test_tagRepo_GetTagListByName(t *testing.T) {
tagOnce.Do(addTagList)
tagRepo := tag.NewTagRepo(testDataSource, unique.NewUniqueIDRepo(testDataSource))
gotTags, err := tagRepo.GetTagListByName(context.TODO(), testTagList[0].SlugName, 1)
assert.NoError(t, err)
assert.Equal(t, testTagList[0].SlugName, gotTags[0].SlugName)
}
func Test_tagRepo_GetTagListByNames(t *testing.T) {
tagOnce.Do(addTagList)
tagRepo := tag.NewTagRepo(testDataSource, unique.NewUniqueIDRepo(testDataSource))
gotTags, err := tagRepo.GetTagListByNames(context.TODO(), []string{testTagList[0].SlugName})
assert.NoError(t, err)
assert.Equal(t, testTagList[0].SlugName, gotTags[0].SlugName)
}
func Test_tagRepo_GetTagPage(t *testing.T) {
tagOnce.Do(addTagList)
tagRepo := tag.NewTagRepo(testDataSource, unique.NewUniqueIDRepo(testDataSource))
gotTags, _, err := tagRepo.GetTagPage(context.TODO(), 1, 1, &entity.Tag{SlugName: testTagList[0].SlugName}, "")
assert.NoError(t, err)
assert.Equal(t, testTagList[0].SlugName, gotTags[0].SlugName)
}
func Test_tagRepo_RemoveTag(t *testing.T) {
tagOnce.Do(addTagList)
uniqueIDRepo := unique.NewUniqueIDRepo(testDataSource)
tagRepo := tag.NewTagRepo(testDataSource, uniqueIDRepo)
err := tagRepo.RemoveTag(context.TODO(), testTagList[1].ID)
assert.NoError(t, err)
_, exist, err := tagRepo.GetTagBySlugName(context.TODO(), testTagList[1].SlugName)
assert.NoError(t, err)
assert.False(t, exist)
}
func Test_tagRepo_UpdateTag(t *testing.T) {
uniqueIDRepo := unique.NewUniqueIDRepo(testDataSource)
tagRepo := tag.NewTagRepo(testDataSource, uniqueIDRepo)
testTagList[0].DisplayName = "golang"
err := tagRepo.UpdateTag(context.TODO(), testTagList[0])
assert.NoError(t, err)
gotTag, exist, err := tagRepo.GetTagByID(context.TODO(), testTagList[0].ID)
assert.NoError(t, err)
assert.True(t, exist)
assert.Equal(t, testTagList[0].DisplayName, gotTag.DisplayName)
}
func Test_tagRepo_UpdateTagQuestionCount(t *testing.T) {
uniqueIDRepo := unique.NewUniqueIDRepo(testDataSource)
tagRepo := tag.NewTagRepo(testDataSource, uniqueIDRepo)
testTagList[0].DisplayName = "golang"
err := tagRepo.UpdateTagQuestionCount(context.TODO(), testTagList[0].ID, 100)
assert.NoError(t, err)
gotTag, exist, err := tagRepo.GetTagByID(context.TODO(), testTagList[0].ID)
assert.NoError(t, err)
assert.True(t, exist)
assert.Equal(t, 100, gotTag.QuestionCount)
}
func Test_tagRepo_UpdateTagSynonym(t *testing.T) {
uniqueIDRepo := unique.NewUniqueIDRepo(testDataSource)
tagRepo := tag.NewTagRepo(testDataSource, uniqueIDRepo)
testTagList[0].DisplayName = "golang"
err := tagRepo.UpdateTag(context.TODO(), testTagList[0])
assert.NoError(t, err)
err = tagRepo.UpdateTagSynonym(context.TODO(), []string{testTagList[2].SlugName},
converter.StringToInt64(testTagList[0].ID), testTagList[0].SlugName)
assert.NoError(t, err)
gotTag, exist, err := tagRepo.GetTagByID(context.TODO(), testTagList[2].ID)
assert.NoError(t, err)
assert.True(t, exist)
assert.Equal(t, testTagList[0].ID, fmt.Sprintf("%d", gotTag.MainTagID))
}

View File

@ -0,0 +1,53 @@
package repo_test
import (
"context"
"testing"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/repo/auth"
"github.com/answerdev/answer/internal/repo/user"
"github.com/stretchr/testify/assert"
)
func Test_userBackyardRepo_GetUserInfo(t *testing.T) {
userBackyardRepo := user.NewUserBackyardRepo(testDataSource, auth.NewAuthRepo(testDataSource))
got, exist, err := userBackyardRepo.GetUserInfo(context.TODO(), "1")
assert.NoError(t, err)
assert.True(t, exist)
assert.Equal(t, "1", got.ID)
}
func Test_userBackyardRepo_GetUserPage(t *testing.T) {
userBackyardRepo := user.NewUserBackyardRepo(testDataSource, auth.NewAuthRepo(testDataSource))
got, total, err := userBackyardRepo.GetUserPage(context.TODO(), 1, 1, &entity.User{Username: "admin"}, "")
assert.NoError(t, err)
assert.Equal(t, int64(1), total)
assert.Equal(t, "1", got[0].ID)
}
func Test_userBackyardRepo_UpdateUserStatus(t *testing.T) {
userBackyardRepo := user.NewUserBackyardRepo(testDataSource, auth.NewAuthRepo(testDataSource))
got, exist, err := userBackyardRepo.GetUserInfo(context.TODO(), "1")
assert.NoError(t, err)
assert.True(t, exist)
assert.Equal(t, entity.UserStatusAvailable, got.Status)
err = userBackyardRepo.UpdateUserStatus(context.TODO(), "1", entity.UserStatusSuspended, entity.EmailStatusAvailable,
"admin@admin.com")
assert.NoError(t, err)
got, exist, err = userBackyardRepo.GetUserInfo(context.TODO(), "1")
assert.NoError(t, err)
assert.True(t, exist)
assert.Equal(t, entity.UserStatusSuspended, got.Status)
err = userBackyardRepo.UpdateUserStatus(context.TODO(), "1", entity.UserStatusAvailable, entity.EmailStatusAvailable,
"admin@admin.com")
assert.NoError(t, err)
got, exist, err = userBackyardRepo.GetUserInfo(context.TODO(), "1")
assert.NoError(t, err)
assert.True(t, exist)
assert.Equal(t, entity.UserStatusAvailable, got.Status)
}

View File

@ -0,0 +1,121 @@
package repo_test
import (
"context"
"testing"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/repo/config"
"github.com/answerdev/answer/internal/repo/user"
"github.com/stretchr/testify/assert"
)
func Test_userRepo_AddUser(t *testing.T) {
userRepo := user.NewUserRepo(testDataSource, config.NewConfigRepo(testDataSource))
userInfo := &entity.User{
Username: "answer",
Pass: "answer",
EMail: "answer@example.com",
MailStatus: entity.EmailStatusAvailable,
Status: entity.UserStatusAvailable,
DisplayName: "answer",
IsAdmin: false,
}
err := userRepo.AddUser(context.TODO(), userInfo)
assert.NoError(t, err)
}
func Test_userRepo_BatchGetByID(t *testing.T) {
userRepo := user.NewUserRepo(testDataSource, config.NewConfigRepo(testDataSource))
got, err := userRepo.BatchGetByID(context.TODO(), []string{"1"})
assert.NoError(t, err)
assert.Equal(t, 1, len(got))
assert.Equal(t, "admin", got[0].Username)
}
func Test_userRepo_GetByEmail(t *testing.T) {
userRepo := user.NewUserRepo(testDataSource, config.NewConfigRepo(testDataSource))
got, exist, err := userRepo.GetByEmail(context.TODO(), "admin@admin.com")
assert.NoError(t, err)
assert.True(t, exist)
assert.Equal(t, "admin", got.Username)
}
func Test_userRepo_GetByUserID(t *testing.T) {
userRepo := user.NewUserRepo(testDataSource, config.NewConfigRepo(testDataSource))
got, exist, err := userRepo.GetByUserID(context.TODO(), "1")
assert.NoError(t, err)
assert.True(t, exist)
assert.Equal(t, "admin", got.Username)
}
func Test_userRepo_GetByUsername(t *testing.T) {
userRepo := user.NewUserRepo(testDataSource, config.NewConfigRepo(testDataSource))
got, exist, err := userRepo.GetByUsername(context.TODO(), "admin")
assert.NoError(t, err)
assert.True(t, exist)
assert.Equal(t, "admin", got.Username)
}
func Test_userRepo_IncreaseAnswerCount(t *testing.T) {
userRepo := user.NewUserRepo(testDataSource, config.NewConfigRepo(testDataSource))
err := userRepo.IncreaseAnswerCount(context.TODO(), "1", 1)
assert.NoError(t, err)
got, exist, err := userRepo.GetByUserID(context.TODO(), "1")
assert.NoError(t, err)
assert.True(t, exist)
assert.Equal(t, 1, got.AnswerCount)
}
func Test_userRepo_IncreaseQuestionCount(t *testing.T) {
userRepo := user.NewUserRepo(testDataSource, config.NewConfigRepo(testDataSource))
err := userRepo.IncreaseQuestionCount(context.TODO(), "1", 1)
assert.NoError(t, err)
got, exist, err := userRepo.GetByUserID(context.TODO(), "1")
assert.NoError(t, err)
assert.True(t, exist)
assert.Equal(t, 1, got.AnswerCount)
}
func Test_userRepo_UpdateEmail(t *testing.T) {
userRepo := user.NewUserRepo(testDataSource, config.NewConfigRepo(testDataSource))
err := userRepo.UpdateEmail(context.TODO(), "1", "admin@admin.com")
assert.NoError(t, err)
}
func Test_userRepo_UpdateEmailStatus(t *testing.T) {
userRepo := user.NewUserRepo(testDataSource, config.NewConfigRepo(testDataSource))
err := userRepo.UpdateEmailStatus(context.TODO(), "1", entity.EmailStatusToBeVerified)
assert.NoError(t, err)
}
func Test_userRepo_UpdateInfo(t *testing.T) {
userRepo := user.NewUserRepo(testDataSource, config.NewConfigRepo(testDataSource))
err := userRepo.UpdateInfo(context.TODO(), &entity.User{ID: "1", Bio: "test"})
assert.NoError(t, err)
got, exist, err := userRepo.GetByUserID(context.TODO(), "1")
assert.NoError(t, err)
assert.True(t, exist)
assert.Equal(t, "test", got.Bio)
}
func Test_userRepo_UpdateLastLoginDate(t *testing.T) {
userRepo := user.NewUserRepo(testDataSource, config.NewConfigRepo(testDataSource))
err := userRepo.UpdateLastLoginDate(context.TODO(), "1")
assert.NoError(t, err)
}
func Test_userRepo_UpdateNoticeStatus(t *testing.T) {
userRepo := user.NewUserRepo(testDataSource, config.NewConfigRepo(testDataSource))
err := userRepo.UpdateNoticeStatus(context.TODO(), "1", 1)
assert.NoError(t, err)
}
func Test_userRepo_UpdatePass(t *testing.T) {
userRepo := user.NewUserRepo(testDataSource, config.NewConfigRepo(testDataSource))
err := userRepo.UpdatePass(context.TODO(), "1", "admin")
assert.NoError(t, err)
}

View File

@ -86,7 +86,8 @@ func (ar *reportRepo) GetByID(ctx context.Context, id string) (report entity.Rep
func (ar *reportRepo) UpdateByID(
ctx context.Context,
id string,
handleData entity.Report) (err error) {
handleData entity.Report,
) (err error) {
_, err = ar.data.DB.ID(id).Update(&handleData)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()

View File

@ -81,7 +81,8 @@ func (rr *revisionRepo) UpdateObjectRevisionId(ctx context.Context, revision *en
// GetRevision get revision one
func (rr *revisionRepo) GetRevision(ctx context.Context, id string) (
revision *entity.Revision, exist bool, err error) {
revision *entity.Revision, exist bool, err error,
) {
revision = &entity.Revision{}
exist, err = rr.data.DB.ID(id).Get(revision)
if err != nil {
@ -92,9 +93,10 @@ func (rr *revisionRepo) GetRevision(ctx context.Context, id string) (
// GetLastRevisionByObjectID get object's last revision by object TagID
func (rr *revisionRepo) GetLastRevisionByObjectID(ctx context.Context, objectID string) (
revision *entity.Revision, exist bool, err error) {
revision *entity.Revision, exist bool, err error,
) {
revision = &entity.Revision{}
exist, err = rr.data.DB.Where("object_id = ?", objectID).OrderBy("create_time DESC").Get(revision)
exist, err = rr.data.DB.Where("object_id = ?", objectID).OrderBy("created_at DESC").Get(revision)
if err != nil {
return nil, false, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}

View File

@ -1,4 +1,4 @@
package repo
package search_common
import (
"context"
@ -21,7 +21,7 @@ import (
)
var (
q_fields = []string{
qFields = []string{
"`question`.`id`",
"`question`.`id` as `question_id`",
"`title`",
@ -34,7 +34,7 @@ var (
"`question`.`status` as `status`",
"`post_update_time`",
}
a_fields = []string{
aFields = []string{
"`answer`.`id` as `id`",
"`question_id`",
"`question`.`title` as `title`",
@ -67,11 +67,14 @@ func NewSearchRepo(data *data.Data, uniqueIDRepo unique.UniqueIDRepo, userCommon
// SearchContents search question and answer data
func (sr *searchRepo) SearchContents(ctx context.Context, words []string, tagID, userID string, votes int, page, size int, order string) (resp []schema.SearchResp, total int64, err error) {
if words = filterWords(words); len(words) == 0 {
return
}
var (
b *builder.Builder
ub *builder.Builder
qfs = q_fields
afs = a_fields
qfs = qFields
afs = aFields
argsQ = []interface{}{}
argsA = []interface{}{}
)
@ -141,11 +144,11 @@ func (sr *searchRepo) SearchContents(ctx context.Context, words []string, tagID,
b = b.Union("all", ub)
querySql, _, err := builder.MySQL().Select("*").From(b, "t").OrderBy(sr.parseOrder(ctx, order)).Limit(size, page-1).ToSQL()
querySQL, _, err := builder.MySQL().Select("*").From(b, "t").OrderBy(sr.parseOrder(ctx, order)).Limit(size, page-1).ToSQL()
if err != nil {
return
}
countSql, _, err := builder.MySQL().Select("count(*) total").From(b, "c").ToSQL()
countSQL, _, err := builder.MySQL().Select("count(*) total").From(b, "c").ToSQL()
if err != nil {
return
}
@ -153,11 +156,11 @@ func (sr *searchRepo) SearchContents(ctx context.Context, words []string, tagID,
queryArgs := []interface{}{}
countArgs := []interface{}{}
queryArgs = append(queryArgs, querySql)
queryArgs = append(queryArgs, querySQL)
queryArgs = append(queryArgs, argsQ...)
queryArgs = append(queryArgs, argsA...)
countArgs = append(countArgs, countSql)
countArgs = append(countArgs, countSQL)
countArgs = append(countArgs, argsQ...)
countArgs = append(countArgs, argsA...)
@ -181,8 +184,11 @@ func (sr *searchRepo) SearchContents(ctx context.Context, words []string, tagID,
// SearchQuestions search question data
func (sr *searchRepo) SearchQuestions(ctx context.Context, words []string, limitNoAccepted bool, answers, page, size int, order string) (resp []schema.SearchResp, total int64, err error) {
if words = filterWords(words); len(words) == 0 {
return
}
var (
qfs = q_fields
qfs = qFields
args = []interface{}{}
)
if order == "relevance" {
@ -223,18 +229,18 @@ func (sr *searchRepo) SearchQuestions(ctx context.Context, words []string, limit
queryArgs := []interface{}{}
countArgs := []interface{}{}
querySql, _, err := b.OrderBy(sr.parseOrder(ctx, order)).Limit(size, page-1).ToSQL()
querySQL, _, err := b.OrderBy(sr.parseOrder(ctx, order)).Limit(size, page-1).ToSQL()
if err != nil {
return
}
countSql, _, err := builder.MySQL().Select("count(*) total").From(b, "c").ToSQL()
countSQL, _, err := builder.MySQL().Select("count(*) total").From(b, "c").ToSQL()
if err != nil {
return
}
queryArgs = append(queryArgs, querySql)
queryArgs = append(queryArgs, querySQL)
queryArgs = append(queryArgs, args...)
countArgs = append(countArgs, countSql)
countArgs = append(countArgs, countSQL)
countArgs = append(countArgs, args...)
res, err := sr.data.DB.Query(queryArgs...)
@ -250,10 +256,6 @@ func (sr *searchRepo) SearchQuestions(ctx context.Context, words []string, limit
if len(tr) != 0 {
total = converter.StringToInt64(string(tr[0]["total"]))
}
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
return
}
resp, err = sr.parseResult(ctx, res)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
@ -263,8 +265,11 @@ func (sr *searchRepo) SearchQuestions(ctx context.Context, words []string, limit
// SearchAnswers search answer data
func (sr *searchRepo) SearchAnswers(ctx context.Context, words []string, limitAccepted bool, questionID string, page, size int, order string) (resp []schema.SearchResp, total int64, err error) {
if words = filterWords(words); len(words) == 0 {
return
}
var (
afs = a_fields
afs = aFields
args = []interface{}{}
)
if order == "relevance" {
@ -289,8 +294,8 @@ func (sr *searchRepo) SearchAnswers(ctx context.Context, words []string, limitAc
}
if limitAccepted {
b.Where(builder.Eq{"adopted": schema.Answer_Adopted_Enable})
args = append(args, schema.Answer_Adopted_Enable)
b.Where(builder.Eq{"adopted": schema.AnswerAdoptedEnable})
args = append(args, schema.AnswerAdoptedEnable)
}
if questionID != "" {
@ -301,18 +306,18 @@ func (sr *searchRepo) SearchAnswers(ctx context.Context, words []string, limitAc
queryArgs := []interface{}{}
countArgs := []interface{}{}
querySql, _, err := b.OrderBy(sr.parseOrder(ctx, order)).Limit(size, page-1).ToSQL()
querySQL, _, err := b.OrderBy(sr.parseOrder(ctx, order)).Limit(size, page-1).ToSQL()
if err != nil {
return
}
countSql, _, err := builder.MySQL().Select("count(*) total").From(b, "c").ToSQL()
countSQL, _, err := builder.MySQL().Select("count(*) total").From(b, "c").ToSQL()
if err != nil {
return
}
queryArgs = append(queryArgs, querySql)
queryArgs = append(queryArgs, querySQL)
queryArgs = append(queryArgs, args...)
countArgs = append(countArgs, countSql)
countArgs = append(countArgs, countSQL)
countArgs = append(countArgs, args...)
res, err := sr.data.DB.Query(queryArgs...)
@ -326,10 +331,6 @@ func (sr *searchRepo) SearchAnswers(ctx context.Context, words []string, limitAc
}
total = converter.StringToInt64(string(tr[0]["total"]))
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
return
}
resp, err = sr.parseResult(ctx, res)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
@ -438,7 +439,7 @@ func (sr *searchRepo) userBasicInfoFormat(ctx context.Context, dbinfo *entity.Us
Avatar: dbinfo.Avatar,
Website: dbinfo.Website,
Location: dbinfo.Location,
IpInfo: dbinfo.IPInfo,
IPInfo: dbinfo.IPInfo,
}
}
@ -451,24 +452,24 @@ func cutOutParsedText(parsedText string) string {
return parsedText
}
func addRelevanceField(search_fields, words, fields []string) (res []string, args []interface{}) {
var relevanceRes = []string{}
func addRelevanceField(searchFields, words, fields []string) (res []string, args []interface{}) {
relevanceRes := []string{}
args = []interface{}{}
for _, search_field := range search_fields {
for _, searchField := range searchFields {
var (
relevance = "(LENGTH(" + search_field + ") - LENGTH(%s))"
replacement = "REPLACE(%s, ?, '')"
replace_field = search_field
replaced string
argsField = []interface{}{}
relevance = "(LENGTH(" + searchField + ") - LENGTH(%s))"
replacement = "REPLACE(%s, ?, '')"
replaceField = searchField
replaced string
argsField = []interface{}{}
)
res = fields
for i, word := range words {
if i == 0 {
argsField = append(argsField, word)
replaced = fmt.Sprintf(replacement, replace_field)
replaced = fmt.Sprintf(replacement, replaceField)
} else {
argsField = append(argsField, word)
replaced = fmt.Sprintf(replacement, replaced)
@ -483,3 +484,12 @@ func addRelevanceField(search_fields, words, fields []string) (res []string, arg
res = append(res, "("+strings.Join(relevanceRes, " + ")+") as relevance")
return
}
func filterWords(words []string) (res []string) {
for _, word := range words {
if strings.TrimSpace(word) != "" {
res = append(res, word)
}
}
return
}

View File

@ -1,4 +1,4 @@
package repo
package site_info
import (
"context"
@ -27,7 +27,7 @@ func (sr *siteInfoRepo) SaveByType(ctx context.Context, siteType string, data *e
old = &entity.SiteInfo{}
exist bool
)
exist, err = sr.data.DB.Where(builder.Eq{"type": siteType}).Get(old)
exist, _ = sr.data.DB.Where(builder.Eq{"type": siteType}).Get(old)
if exist {
_, err = sr.data.DB.ID(old.ID).Update(data)
if err != nil {

View File

@ -10,20 +10,20 @@ import (
"github.com/segmentfault/pacman/errors"
)
// tagListRepo tagList repository
type tagListRepo struct {
// tagRelRepo tag rel repository
type tagRelRepo struct {
data *data.Data
}
// NewTagListRepo new repository
func NewTagListRepo(data *data.Data) tagcommon.TagRelRepo {
return &tagListRepo{
// NewTagRelRepo new repository
func NewTagRelRepo(data *data.Data) tagcommon.TagRelRepo {
return &tagRelRepo{
data: data,
}
}
// AddTagRelList add tag list
func (tr *tagListRepo) AddTagRelList(ctx context.Context, tagList []*entity.TagRel) (err error) {
func (tr *tagRelRepo) AddTagRelList(ctx context.Context, tagList []*entity.TagRel) (err error) {
_, err = tr.data.DB.Insert(tagList)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
@ -32,8 +32,8 @@ func (tr *tagListRepo) AddTagRelList(ctx context.Context, tagList []*entity.TagR
}
// RemoveTagRelListByObjectID delete tag list
func (tr *tagListRepo) RemoveTagRelListByObjectID(ctx context.Context, objectId string) (err error) {
_, err = tr.data.DB.Where("object_id = ?", objectId).Update(&entity.TagRel{Status: entity.TagRelStatusDeleted})
func (tr *tagRelRepo) RemoveTagRelListByObjectID(ctx context.Context, objectID string) (err error) {
_, err = tr.data.DB.Where("object_id = ?", objectID).Update(&entity.TagRel{Status: entity.TagRelStatusDeleted})
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
@ -41,7 +41,7 @@ func (tr *tagListRepo) RemoveTagRelListByObjectID(ctx context.Context, objectId
}
// RemoveTagRelListByIDs delete tag list
func (tr *tagListRepo) RemoveTagRelListByIDs(ctx context.Context, ids []int64) (err error) {
func (tr *tagRelRepo) RemoveTagRelListByIDs(ctx context.Context, ids []int64) (err error) {
_, err = tr.data.DB.In("id", ids).Update(&entity.TagRel{Status: entity.TagRelStatusDeleted})
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
@ -50,10 +50,11 @@ func (tr *tagListRepo) RemoveTagRelListByIDs(ctx context.Context, ids []int64) (
}
// GetObjectTagRelWithoutStatus get object tag relation no matter status
func (tr *tagListRepo) GetObjectTagRelWithoutStatus(ctx context.Context, objectId, tagID string) (
tagRel *entity.TagRel, exist bool, err error) {
func (tr *tagRelRepo) GetObjectTagRelWithoutStatus(ctx context.Context, objectID, tagID string) (
tagRel *entity.TagRel, exist bool, err error,
) {
tagRel = &entity.TagRel{}
session := tr.data.DB.Where("object_id = ?", objectId).And("tag_id = ?", tagID)
session := tr.data.DB.Where("object_id = ?", objectID).And("tag_id = ?", tagID)
exist, err = session.Get(tagRel)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
@ -62,7 +63,7 @@ func (tr *tagListRepo) GetObjectTagRelWithoutStatus(ctx context.Context, objectI
}
// EnableTagRelByIDs update tag status to available
func (tr *tagListRepo) EnableTagRelByIDs(ctx context.Context, ids []int64) (err error) {
func (tr *tagRelRepo) EnableTagRelByIDs(ctx context.Context, ids []int64) (err error) {
_, err = tr.data.DB.In("id", ids).Update(&entity.TagRel{Status: entity.TagRelStatusAvailable})
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
@ -71,9 +72,9 @@ func (tr *tagListRepo) EnableTagRelByIDs(ctx context.Context, ids []int64) (err
}
// GetObjectTagRelList get object tag relation list all
func (tr *tagListRepo) GetObjectTagRelList(ctx context.Context, objectId string) (tagListList []*entity.TagRel, err error) {
func (tr *tagRelRepo) GetObjectTagRelList(ctx context.Context, objectID string) (tagListList []*entity.TagRel, err error) {
tagListList = make([]*entity.TagRel, 0)
session := tr.data.DB.Where("object_id = ?", objectId)
session := tr.data.DB.Where("object_id = ?", objectID)
session.Where("status = ?", entity.TagRelStatusAvailable)
err = session.Find(&tagListList)
if err != nil {
@ -83,7 +84,7 @@ func (tr *tagListRepo) GetObjectTagRelList(ctx context.Context, objectId string)
}
// BatchGetObjectTagRelList get object tag relation list all
func (tr *tagListRepo) BatchGetObjectTagRelList(ctx context.Context, objectIds []string) (tagListList []*entity.TagRel, err error) {
func (tr *tagRelRepo) BatchGetObjectTagRelList(ctx context.Context, objectIds []string) (tagListList []*entity.TagRel, err error) {
tagListList = make([]*entity.TagRel, 0)
session := tr.data.DB.In("object_id", objectIds)
session.Where("status = ?", entity.TagRelStatusAvailable)
@ -95,7 +96,7 @@ func (tr *tagListRepo) BatchGetObjectTagRelList(ctx context.Context, objectIds [
}
// CountTagRelByTagID count tag relation
func (tr *tagListRepo) CountTagRelByTagID(ctx context.Context, tagID string) (count int64, err error) {
func (tr *tagRelRepo) CountTagRelByTagID(ctx context.Context, tagID string) (count int64, err error) {
count, err = tr.data.DB.Count(&entity.TagRel{TagID: tagID, Status: entity.AnswerStatusAvailable})
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()

View File

@ -2,7 +2,6 @@ package tag
import (
"context"
"fmt"
"github.com/answerdev/answer/internal/base/data"
"github.com/answerdev/answer/internal/base/pager"
@ -34,12 +33,11 @@ func NewTagRepo(
// AddTagList add tag
func (tr *tagRepo) AddTagList(ctx context.Context, tagList []*entity.Tag) (err error) {
for _, item := range tagList {
ID, err := tr.uniqueIDRepo.GenUniqueID(ctx, item.TableName())
item.ID, err = tr.uniqueIDRepo.GenUniqueIDStr(ctx, item.TableName())
if err != nil {
return err
}
item.RevisionID = "0"
item.ID = fmt.Sprintf("%d", ID)
}
_, err = tr.data.DB.Insert(tagList)
if err != nil {
@ -128,7 +126,8 @@ func (tr *tagRepo) UpdateTagQuestionCount(ctx context.Context, tagID string, que
// UpdateTagSynonym update synonym tag
func (tr *tagRepo) UpdateTagSynonym(ctx context.Context, tagSlugNameList []string, mainTagID int64,
mainTagSlugName string) (err error) {
mainTagSlugName string,
) (err error) {
bean := &entity.Tag{MainTagID: mainTagID, MainTagSlugName: mainTagSlugName}
session := tr.data.DB.In("slug_name", tagSlugNameList).MustCols("main_tag_id", "main_tag_slug_name")
_, err = session.Update(bean)
@ -140,7 +139,8 @@ func (tr *tagRepo) UpdateTagSynonym(ctx context.Context, tagSlugNameList []strin
// GetTagByID get tag one
func (tr *tagRepo) GetTagByID(ctx context.Context, tagID string) (
tag *entity.Tag, exist bool, err error) {
tag *entity.Tag, exist bool, err error,
) {
tag = &entity.Tag{}
session := tr.data.DB.Where(builder.Eq{"id": tagID})
session.Where(builder.Eq{"status": entity.TagStatusAvailable})
@ -164,7 +164,8 @@ func (tr *tagRepo) GetTagList(ctx context.Context, tag *entity.Tag) (tagList []*
// GetTagPage get tag page
func (tr *tagRepo) GetTagPage(ctx context.Context, page, pageSize int, tag *entity.Tag, queryCond string) (
tagList []*entity.Tag, total int64, err error) {
tagList []*entity.Tag, total int64, err error,
) {
tagList = make([]*entity.Tag, 0)
session := tr.data.DB.NewSession()

View File

@ -3,7 +3,6 @@ package unique
import (
"context"
"fmt"
"strconv"
"github.com/answerdev/answer/internal/base/constant"
"github.com/answerdev/answer/internal/base/data"
@ -25,21 +24,8 @@ func NewUniqueIDRepo(data *data.Data) unique.UniqueIDRepo {
}
}
// GenUniqueID generate unique id
// 1 + 00x(objectType) + 000000000000x(id)
func (ur *uniqueIDRepo) GenUniqueID(ctx context.Context, key string) (uniqueID int64, err error) {
idStr, err := ur.GenUniqueIDStr(ctx, key)
if err != nil {
return 0, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
uniqueID, err = strconv.ParseInt(idStr, 10, 64)
if err != nil {
return 0, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return uniqueID, nil
}
// GenUniqueIDStr generate unique id string
// 1 + 00x(objectType) + 000000000000x(id)
func (ur *uniqueIDRepo) GenUniqueIDStr(ctx context.Context, key string) (uniqueID string, err error) {
objectType := constant.ObjectTypeStrMapping[key]
bean := &entity.Uniqid{UniqidType: objectType}

View File

@ -3,13 +3,17 @@ package user
import (
"context"
"encoding/json"
"net/mail"
"strings"
"time"
"unicode"
"xorm.io/builder"
"github.com/answerdev/answer/internal/base/constant"
"github.com/answerdev/answer/internal/base/data"
"github.com/answerdev/answer/internal/base/pager"
"github.com/answerdev/answer/internal/base/reason"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/service/auth"
"github.com/answerdev/answer/internal/service/user_backyard"
"github.com/segmentfault/pacman/errors"
"github.com/segmentfault/pacman/log"
@ -17,19 +21,22 @@ import (
// userBackyardRepo user repository
type userBackyardRepo struct {
data *data.Data
data *data.Data
authRepo auth.AuthRepo
}
// NewUserBackyardRepo new repository
func NewUserBackyardRepo(data *data.Data) user_backyard.UserBackyardRepo {
func NewUserBackyardRepo(data *data.Data, authRepo auth.AuthRepo) user_backyard.UserBackyardRepo {
return &userBackyardRepo{
data: data,
data: data,
authRepo: authRepo,
}
}
// UpdateUserStatus update user status
func (ur *userBackyardRepo) UpdateUserStatus(ctx context.Context, userID string, userStatus, mailStatus int,
email string) (err error) {
email string,
) (err error) {
cond := &entity.User{Status: userStatus, MailStatus: mailStatus, EMail: email}
switch userStatus {
case entity.UserStatusSuspended:
@ -49,8 +56,7 @@ func (ur *userBackyardRepo) UpdateUserStatus(ctx context.Context, userID string,
}
t, _ := json.Marshal(userCacheInfo)
log.Infof("user change status: %s", string(t))
err = ur.data.Cache.SetString(ctx, constant.UserStatusChangedCacheKey+userID, string(t),
constant.UserStatusChangedCacheTime)
err = ur.authRepo.SetUserStatus(ctx, userID, userCacheInfo)
if err != nil {
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
@ -68,16 +74,51 @@ func (ur *userBackyardRepo) GetUserInfo(ctx context.Context, userID string) (use
}
// GetUserPage get user page
func (ur *userBackyardRepo) GetUserPage(ctx context.Context, page, pageSize int, user *entity.User) (users []*entity.User, total int64, err error) {
func (ur *userBackyardRepo) GetUserPage(ctx context.Context, page, pageSize int, user *entity.User, query string) (users []*entity.User, total int64, err error) {
users = make([]*entity.User, 0)
session := ur.data.DB.NewSession()
if user.Status == entity.UserStatusDeleted {
switch user.Status {
case entity.UserStatusDeleted:
session.Desc("deleted_at")
} else if user.Status == entity.UserStatusSuspended {
case entity.UserStatusSuspended:
session.Desc("suspended_at")
} else {
default:
session.Desc("created_at")
}
if len(query) > 0 {
if email, e := mail.ParseAddress(query); e == nil {
session.And(builder.Eq{"e_mail": email.Address})
} else {
var (
idSearch = false
id = ""
)
if strings.Contains(query, "user:") {
idSearch = true
id = strings.TrimSpace(strings.TrimPrefix(query, "user:"))
for _, r := range id {
if !unicode.IsDigit(r) {
idSearch = false
break
}
}
}
if idSearch {
session.And(builder.Eq{
"id": id,
})
} else {
session.And(builder.Or(
builder.Like{"username", query},
builder.Like{"display_name", query},
))
}
}
}
total, err = pager.Help(page, pageSize, &users, user, session)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()

View File

@ -2,7 +2,6 @@ package user
import (
"context"
"fmt"
"time"
"github.com/answerdev/answer/internal/base/data"
@ -86,13 +85,10 @@ func (ur *userRepo) UpdateNoticeStatus(ctx context.Context, userID string, notic
return nil
}
func (ur *userRepo) UpdatePass(ctx context.Context, Data *entity.User) error {
if Data.ID == "" {
return fmt.Errorf("input error")
}
_, err := ur.data.DB.Where("id = ?", Data.ID).Cols("pass").Update(Data)
func (ur *userRepo) UpdatePass(ctx context.Context, userID, pass string) error {
_, err := ur.data.DB.Where("id = ?", userID).Cols("pass").Update(&entity.User{Pass: pass})
if err != nil {
return err
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return nil
}

View File

@ -87,6 +87,7 @@ func (a *AnswerAPIRouter) RegisterUnAuthAnswerAPIRouter(r *gin.RouterGroup) {
r.GET("/comment", a.commentController.GetComment)
// user
r.GET("/user/info", a.userController.GetUserInfoByUserID)
r.GET("/user/status", a.userController.GetUserStatus)
r.GET("/user/action/record", a.userController.ActionRecord)
r.POST("/user/login/email", a.userController.UserEmailLogin)
@ -174,7 +175,6 @@ func (a *AnswerAPIRouter) RegisterAnswerAPIRouter(r *gin.RouterGroup) {
r.DELETE("/answer", a.answerController.RemoveAnswer)
// user
r.GET("/user/info", a.userController.GetUserInfoByUserID)
r.PUT("/user/password", a.userController.UserModifyPassWord)
r.PUT("/user/info", a.userController.UserUpdateInfo)
r.POST("/user/avatar/upload", a.userController.UploadUserAvatar)

View File

@ -9,44 +9,44 @@ type RemoveAnswerReq struct {
}
const (
Answer_Adopted_Failed = 1
Answer_Adopted_Enable = 2
AnswerAdoptedFailed = 1
AnswerAdoptedEnable = 2
)
type AnswerAddReq struct {
QuestionId string `json:"question_id" ` // question_id
QuestionID string `json:"question_id" ` // question_id
Content string `json:"content" ` // content
Html string `json:"html" ` // html
HTML string `json:"html" ` // html
UserID string `json:"-" ` // user_id
}
type AnswerUpdateReq struct {
ID string `json:"id"` // id
QuestionId string `json:"question_id" ` // question_id
QuestionID string `json:"question_id" ` // question_id
UserID string `json:"-" ` // user_id
Title string `json:"title" ` // title
Content string `json:"content"` // content
Html string `json:"html" ` // html
EditSummary string `validate:"omitempty" json:"edit_summary"` //edit_summary
HTML string `json:"html" ` // html
EditSummary string `validate:"omitempty" json:"edit_summary"` // edit_summary
}
type AnswerList struct {
QuestionId string `json:"question_id" form:"question_id"` // question_id
QuestionID string `json:"question_id" form:"question_id"` // question_id
Order string `json:"order" form:"order"` // 1 Default 2 time
Page int `json:"page" form:"page"` //Query number of pages
PageSize int `json:"page_size" form:"page_size"` //Search page size
Page int `json:"page" form:"page"` // Query number of pages
PageSize int `json:"page_size" form:"page_size"` // Search page size
LoginUserID string `json:"-" `
}
type AnswerInfo struct {
ID string `json:"id" xorm:"id"` // id
QuestionId string `json:"question_id" xorm:"question_id"` // question_id
QuestionID string `json:"question_id" xorm:"question_id"` // question_id
Content string `json:"content" xorm:"content"` // content
Html string `json:"html" xorm:"html"` // html
HTML string `json:"html" xorm:"html"` // html
CreateTime int64 `json:"create_time" xorm:"created"` // create_time
UpdateTime int64 `json:"update_time" xorm:"updated"` // update_time
Adopted int `json:"adopted"` // 1 Failed 2 Adopted
UserId string `json:"-" `
UserID string `json:"-" `
UserInfo *UserBasicInfo `json:"user_info,omitempty"`
UpdateUserInfo *UserBasicInfo `json:"update_user_info,omitempty"`
Collected bool `json:"collected"`
@ -60,12 +60,12 @@ type AnswerInfo struct {
type AdminAnswerInfo struct {
ID string `json:"id"`
QuestionId string `json:"question_id"`
QuestionID string `json:"question_id"`
Description string `json:"description"`
CreateTime int64 `json:"create_time"`
UpdateTime int64 `json:"update_time"`
Adopted int `json:"adopted"`
UserId string `json:"-" `
UserID string `json:"-" `
UserInfo *UserBasicInfo `json:"user_info"`
VoteCount int `json:"vote_count"`
QuestionInfo struct {

View File

@ -26,10 +26,8 @@ type GetUserPageReq struct {
Page int `validate:"omitempty,min=1" form:"page"`
// page size
PageSize int `validate:"omitempty,min=1" form:"page_size"`
// username
Username string `validate:"omitempty,gt=0,lte=50" form:"username"`
// email
EMail string `validate:"omitempty,gt=0,lte=100" form:"e_mail"`
Query string `validate:"omitempty,gt=0,lte=100" form:"query"`
// user status
Status string `validate:"omitempty,oneof=suspended deleted inactive" form:"status"`
}

View File

@ -3,8 +3,8 @@ package schema
import "time"
const (
CG_DEFAULT = 1
CG_DIY = 2
CGDefault = 1
CGDIY = 2
)
// CollectionSwitchReq switch collection request

View File

@ -2,7 +2,7 @@ package schema
const (
ForbiddenReasonTypeInactive = "inactive"
ForbiddenReasonTypeUrlExpired = "url_expired"
ForbiddenReasonTypeURLExpired = "url_expired"
ForbiddenReasonTypeUserSuspended = "suspended"
)

View File

@ -5,12 +5,11 @@ type RemoveQuestionReq struct {
// question id
ID string `validate:"required" comment:"question id" json:"id"`
UserID string `json:"-" ` // user_id
}
type CloseQuestionReq struct {
ID string `validate:"required" comment:"question id" json:"id"`
UserId string `json:"-" ` // user_id
UserID string `json:"-" ` // user_id
CloseType int `json:"close_type" ` // close_type
CloseMsg string `json:"close_msg" ` // close_type
}
@ -26,7 +25,7 @@ type QuestionAdd struct {
// content
Content string `validate:"required,gte=6,lte=65535" json:"content"`
// html
Html string `validate:"required,gte=6,lte=65535" json:"html"`
HTML string `validate:"required,gte=6,lte=65535" json:"html"`
// tags
Tags []*TagItem `validate:"required,dive" json:"tags"`
// user id
@ -41,7 +40,7 @@ type QuestionUpdate struct {
// content
Content string `validate:"required,gte=6,lte=65535" json:"content"`
// html
Html string `validate:"required,gte=6,lte=65535" json:"html"`
HTML string `validate:"required,gte=6,lte=65535" json:"html"`
// tags
Tags []*TagItem `validate:"required,dive" json:"tags"`
// edit summary
@ -65,7 +64,7 @@ type QuestionInfo struct {
ID string `json:"id" `
Title string `json:"title" xorm:"title"` // title
Content string `json:"content" xorm:"content"` // content
Html string `json:"html" xorm:"html"` // html
HTML string `json:"html" xorm:"html"` // html
Tags []*TagResp `json:"tags" ` // tags
ViewCount int `json:"view_count" xorm:"view_count"` // view_count
UniqueViewCount int `json:"unique_view_count" xorm:"unique_view_count"` // unique_view_count
@ -73,15 +72,15 @@ type QuestionInfo struct {
AnswerCount int `json:"answer_count" xorm:"answer_count"` // answer count
CollectionCount int `json:"collection_count" xorm:"collection_count"` // collection count
FollowCount int `json:"follow_count" xorm:"follow_count"` // follow count
AcceptedAnswerId string `json:"accepted_answer_id" ` // accepted_answer_id
LastAnswerId string `json:"last_answer_id" ` // last_answer_id
AcceptedAnswerID string `json:"accepted_answer_id" ` // accepted_answer_id
LastAnswerID string `json:"last_answer_id" ` // last_answer_id
CreateTime int64 `json:"create_time" ` // create_time
UpdateTime int64 `json:"-"` // update_time
PostUpdateTime int64 `json:"update_time"`
QuestionUpdateTime int64 `json:"edit_time"`
Status int `json:"status"`
Operation *Operation `json:"operation,omitempty"`
UserId string `json:"-" `
UserID string `json:"-" `
UserInfo *UserBasicInfo `json:"user_info"`
UpdateUserInfo *UserBasicInfo `json:"update_user_info,omitempty"`
LastAnsweredUserInfo *UserBasicInfo `json:"last_answered_user_info,omitempty"`
@ -108,10 +107,10 @@ type AdminQuestionInfo struct {
}
type Operation struct {
Operation_Type string `json:"operation_type"`
Operation_Description string `json:"operation_description"`
Operation_Msg string `json:"operation_msg"`
Operation_Time int64 `json:"operation_time"`
OperationType string `json:"operation_type"`
OperationDescription string `json:"operation_description"`
OperationMsg string `json:"operation_msg"`
OperationTime int64 `json:"operation_time"`
}
type GetCloseTypeResp struct {
@ -151,26 +150,27 @@ type UserQuestionInfo struct {
AnswerCount int `json:"answer_count"`
CollectionCount int `json:"collection_count"`
CreateTime int `json:"create_time"`
AcceptedAnswerId string `json:"accepted_answer_id"`
AcceptedAnswerID string `json:"accepted_answer_id"`
Status string `json:"status"`
}
type QuestionSearch struct {
Page int `json:"page" form:"page"` //Query number of pages
PageSize int `json:"page_size" form:"page_size"` //Search page size
Order string `json:"order" form:"order"` //Search order by
//Tags []string `json:"tags" form:"tags"` //Search tag
Page int `json:"page" form:"page"` // Query number of pages
PageSize int `json:"page_size" form:"page_size"` // Search page size
Order string `json:"order" form:"order"` // Search order by
// Tags []string `json:"tags" form:"tags"` // Search tag
Tag string `json:"tag" form:"tag"` //Search tag
TagIDs []string `json:"-" form:"-"` //Search tag
UserName string `json:"username" form:"username"` //Search username
TagIDs []string `json:"-" form:"-"` // Search tag
UserName string `json:"username" form:"username"` // Search username
UserID string `json:"-" form:"-"`
}
type CmsQuestionSearch struct {
Page int `json:"page" form:"page"` //Query number of pages
PageSize int `json:"page_size" form:"page_size"` //Search page size
Page int `json:"page" form:"page"` // Query number of pages
PageSize int `json:"page_size" form:"page_size"` // Search page size
Status int `json:"-" form:"-"`
StatusStr string `json:"status" form:"status"` //Status 1 Available 2 closed 10 UserDeleted
StatusStr string `json:"status" form:"status"` // Status 1 Available 2 closed 10 UserDeleted
Query string `validate:"omitempty,gt=0,lte=100" json:"query" form:"query" ` //Query string
}
type AdminSetQuestionStatusRequest struct {

View File

@ -107,7 +107,7 @@ func (tr *GetTagPageResp) GetExcerpt() {
}
type TagChange struct {
ObjectId string `json:"object_id"` // object_id
ObjectID string `json:"object_id"` // object_id
Tags []*TagItem `json:"tags"` // tags name
// user id
UserID string `json:"-"`

View File

@ -55,7 +55,7 @@ type GetUserResp struct {
// bio markdown
Bio string `json:"bio"`
// bio html
BioHtml string `json:"bio_html"`
BioHTML string `json:"bio_html"`
// website
Website string `json:"website"`
// location
@ -72,6 +72,7 @@ type GetUserResp struct {
func (r *GetUserResp) GetFromUserEntity(userInfo *entity.User) {
_ = copier.Copy(r, userInfo)
r.Avatar = FormatAvatarInfo(userInfo.Avatar)
r.CreatedAt = userInfo.CreatedAt.Unix()
r.LastLoginDate = userInfo.LastLoginDate.Unix()
statusShow, ok := UserStatusShow[userInfo.Status]
@ -80,6 +81,44 @@ func (r *GetUserResp) GetFromUserEntity(userInfo *entity.User) {
}
}
type GetUserToSetShowResp struct {
*GetUserResp
Avatar *AvatarInfo `json:"avatar"`
}
func (r *GetUserToSetShowResp) GetFromUserEntity(userInfo *entity.User) {
_ = copier.Copy(r, userInfo)
r.CreatedAt = userInfo.CreatedAt.Unix()
r.LastLoginDate = userInfo.LastLoginDate.Unix()
statusShow, ok := UserStatusShow[userInfo.Status]
if ok {
r.Status = statusShow
}
avatarInfo := &AvatarInfo{}
_ = json.Unmarshal([]byte(userInfo.Avatar), avatarInfo)
// if json.Unmarshal Error avatarInfo.Type is Empty
r.Avatar = avatarInfo
}
func FormatAvatarInfo(avatarJson string) string {
if avatarJson == "" {
return ""
}
AvatarInfo := &AvatarInfo{}
err := json.Unmarshal([]byte(avatarJson), AvatarInfo)
if err != nil {
return ""
}
switch AvatarInfo.Type {
case "gravatar":
return AvatarInfo.Gravatar
case "custom":
return AvatarInfo.Custom
default:
return ""
}
}
// GetUserStatusResp get user status info
type GetUserStatusResp struct {
// user status
@ -114,7 +153,7 @@ type GetOtherUserInfoByUsernameResp struct {
// bio markdown
Bio string `json:"bio"`
// bio html
BioHtml string `json:"bio_html"`
BioHTML string `json:"bio_html"`
// website
Website string `json:"website"`
// location
@ -129,6 +168,9 @@ type GetOtherUserInfoByUsernameResp struct {
func (r *GetOtherUserInfoByUsernameResp) GetFromUserEntity(userInfo *entity.User) {
_ = copier.Copy(r, userInfo)
Avatar := FormatAvatarInfo(userInfo.Avatar)
r.Avatar = Avatar
r.CreatedAt = userInfo.CreatedAt.Unix()
r.LastLoginDate = userInfo.LastLoginDate.Unix()
statusShow, ok := UserStatusShow[userInfo.Status]
@ -146,20 +188,18 @@ func (r *GetOtherUserInfoByUsernameResp) GetFromUserEntity(userInfo *entity.User
r.StatusMsg = statusMsgShow
}
}
}
const (
Mail_State_Pass = 1
Mail_State_Verifi = 2
MailStatePass = 1
MailStateVerifi = 2
Notice_Status_On = 1
Notice_Status_Off = 2
NoticeStatusOn = 1
NoticeStatusOff = 2
//ActionRecord ReportType
ActionRecord_Type_Login = "login"
ActionRecord_Type_Email = "e_mail"
ActionRecord_Type_Find_Pass = "find_pass"
ActionRecordTypeLogin = "login"
ActionRecordTypeEmail = "e_mail"
ActionRecordTypeFindPass = "find_pass"
)
var UserStatusShow = map[int]string{
@ -167,6 +207,7 @@ var UserStatusShow = map[int]string{
9: "forbidden",
10: "deleted",
}
var UserStatusShowMsg = map[int]string{
1: "",
9: "<strong>This user was suspended forever.</strong> This user doesnt meet a community guideline.",
@ -207,7 +248,7 @@ func (u *UserRegisterReq) Check() (errField *validator.ErrorField, err error) {
// UserModifyPassWordRequest
type UserModifyPassWordRequest struct {
UserId string `json:"-" ` // user_id
UserID string `json:"-" ` // user_id
OldPass string `json:"old_pass" ` // old password
Pass string `json:"pass" ` // password
}
@ -230,17 +271,23 @@ type UpdateInfoRequest struct {
// username
Username string `validate:"omitempty,gt=0,lte=30" json:"username"`
// avatar
Avatar string `validate:"omitempty,gt=0,lte=500" json:"avatar"`
Avatar AvatarInfo `json:"avatar"`
// bio
Bio string `validate:"omitempty,gt=0,lte=4096" json:"bio"`
// bio
BioHtml string `validate:"omitempty,gt=0,lte=4096" json:"bio_html"`
BioHTML string `validate:"omitempty,gt=0,lte=4096" json:"bio_html"`
// website
Website string `validate:"omitempty,gt=0,lte=500" json:"website"`
// location
Location string `validate:"omitempty,gt=0,lte=100" json:"location"`
// user id
UserId string `json:"-" `
UserID string `json:"-" `
}
type AvatarInfo struct {
Type string `validate:"omitempty,gt=0,lte=100" json:"type"`
Gravatar string `validate:"omitempty,gt=0,lte=200" json:"gravatar"`
Custom string `validate:"omitempty,gt=0,lte=200" json:"custom"`
}
func (u *UpdateInfoRequest) Check() (errField *validator.ErrorField, err error) {
@ -283,7 +330,7 @@ func (u *UserRePassWordRequest) Check() (errField *validator.ErrorField, err err
}
type UserNoticeSetRequest struct {
UserId string `json:"-" ` // user_id
UserID string `json:"-" ` // user_id
NoticeSwitch bool `json:"notice_switch" `
}
@ -294,7 +341,7 @@ type UserNoticeSetResp struct {
type ActionRecordReq struct {
// action
Action string `validate:"required,oneof=login e_mail find_pass" form:"action"`
Ip string `json:"-"`
IP string `json:"-"`
}
type ActionRecordResp struct {
@ -311,7 +358,7 @@ type UserBasicInfo struct {
Avatar string `json:"avatar" ` // avatar
Website string `json:"website" ` // website
Location string `json:"location" ` // location
IpInfo string `json:"ip_info"` // ip info
IPInfo string `json:"ip_info"` // ip info
Status string `json:"status"` // status
}

View File

@ -36,7 +36,7 @@ func NewCaptchaService(captchaRepo CaptchaRepo) *CaptchaService {
// ActionRecord action record
func (cs *CaptchaService) ActionRecord(ctx context.Context, req *schema.ActionRecordReq) (resp *schema.ActionRecordResp, err error) {
resp = &schema.ActionRecordResp{}
num, err := cs.captchaRepo.GetActionType(ctx, req.Ip, req.Action)
num, err := cs.captchaRepo.GetActionType(ctx, req.IP, req.Action)
if err != nil {
num = 0
}
@ -51,7 +51,8 @@ func (cs *CaptchaService) ActionRecord(ctx context.Context, req *schema.ActionRe
// ActionRecordVerifyCaptcha
// Verify that you need to enter a CAPTCHA, and that the CAPTCHA is correct
func (cs *CaptchaService) ActionRecordVerifyCaptcha(
ctx context.Context, actionType string, ip string, id string, VerifyValue string) bool {
ctx context.Context, actionType string, ip string, id string, VerifyValue string,
) bool {
num, cahceErr := cs.captchaRepo.GetActionType(ctx, ip, actionType)
if cahceErr != nil {
return true

View File

@ -14,9 +14,9 @@ type AnswerRepo interface {
GetAnswer(ctx context.Context, id string) (answer *entity.Answer, exist bool, err error)
GetAnswerList(ctx context.Context, answer *entity.Answer) (answerList []*entity.Answer, err error)
GetAnswerPage(ctx context.Context, page, pageSize int, answer *entity.Answer) (answerList []*entity.Answer, total int64, err error)
UpdateAdopted(ctx context.Context, id string, questionId string) error
UpdateAdopted(ctx context.Context, id string, questionID string) error
GetByID(ctx context.Context, id string) (*entity.Answer, bool, error)
GetByUserIdQuestionId(ctx context.Context, userId string, questionId string) (*entity.Answer, bool, error)
GetByUserIDQuestionID(ctx context.Context, userID string, questionID string) (*entity.Answer, bool, error)
SearchList(ctx context.Context, search *entity.AnswerSearch) ([]*entity.Answer, int64, error)
CmsSearchList(ctx context.Context, search *entity.CmsAnswerSearch) ([]*entity.Answer, int64, error)
UpdateAnswerStatus(ctx context.Context, answer *entity.Answer) (err error)
@ -33,8 +33,8 @@ func NewAnswerCommon(answerRepo AnswerRepo) *AnswerCommon {
}
}
func (as *AnswerCommon) SearchAnswered(ctx context.Context, userId, questionId string) (bool, error) {
_, has, err := as.answerRepo.GetByUserIdQuestionId(ctx, userId, questionId)
func (as *AnswerCommon) SearchAnswered(ctx context.Context, userID, questionID string) (bool, error) {
_, has, err := as.answerRepo.GetByUserIDQuestionID(ctx, userID, questionID)
if err != nil {
return has, err
}
@ -42,6 +42,9 @@ func (as *AnswerCommon) SearchAnswered(ctx context.Context, userId, questionId s
}
func (as *AnswerCommon) CmsSearchList(ctx context.Context, search *entity.CmsAnswerSearch) ([]*entity.Answer, int64, error) {
if search.Status == 0 {
search.Status = 1
}
return as.answerRepo.CmsSearchList(ctx, search)
}
@ -56,26 +59,26 @@ func (as *AnswerCommon) Search(ctx context.Context, search *entity.AnswerSearch)
func (as *AnswerCommon) ShowFormat(ctx context.Context, data *entity.Answer) *schema.AnswerInfo {
info := schema.AnswerInfo{}
info.ID = data.ID
info.QuestionId = data.QuestionID
info.QuestionID = data.QuestionID
info.Content = data.OriginalText
info.Html = data.ParsedText
info.HTML = data.ParsedText
info.Adopted = data.Adopted
info.VoteCount = data.VoteCount
info.CreateTime = data.CreatedAt.Unix()
info.UpdateTime = data.UpdatedAt.Unix()
info.UserId = data.UserID
info.UserID = data.UserID
return &info
}
func (as *AnswerCommon) AdminShowFormat(ctx context.Context, data *entity.Answer) *schema.AdminAnswerInfo {
info := schema.AdminAnswerInfo{}
info.ID = data.ID
info.QuestionId = data.QuestionID
info.QuestionID = data.QuestionID
info.Description = data.ParsedText
info.Adopted = data.Adopted
info.VoteCount = data.VoteCount
info.CreateTime = data.CreatedAt.Unix()
info.UpdateTime = data.UpdatedAt.Unix()
info.UserId = data.UserID
info.UserID = data.UserID
return &info
}

View File

@ -74,7 +74,7 @@ func (as *AnswerService) RemoveAnswer(ctx context.Context, id string) (err error
return nil
}
//user add question count
// user add question count
err = as.questionCommon.UpdateAnswerCount(ctx, answerInfo.QuestionID, -1)
if err != nil {
log.Error("IncreaseAnswerCount error", err.Error())
@ -97,7 +97,7 @@ func (as *AnswerService) RemoveAnswer(ctx context.Context, id string) (err error
}
func (as *AnswerService) Insert(ctx context.Context, req *schema.AnswerAddReq) (string, error) {
questionInfo, exist, err := as.questionRepo.GetQuestion(ctx, req.QuestionId)
questionInfo, exist, err := as.questionRepo.GetQuestion(ctx, req.QuestionID)
if err != nil {
return "", err
}
@ -108,24 +108,24 @@ func (as *AnswerService) Insert(ctx context.Context, req *schema.AnswerAddReq) (
insertData := new(entity.Answer)
insertData.UserID = req.UserID
insertData.OriginalText = req.Content
insertData.ParsedText = req.Html
insertData.Adopted = schema.Answer_Adopted_Failed
insertData.QuestionID = req.QuestionId
insertData.ParsedText = req.HTML
insertData.Adopted = schema.AnswerAdoptedFailed
insertData.QuestionID = req.QuestionID
insertData.RevisionID = "0"
insertData.Status = entity.AnswerStatusAvailable
insertData.UpdatedAt = now
if err := as.answerRepo.AddAnswer(ctx, insertData); err != nil {
if err = as.answerRepo.AddAnswer(ctx, insertData); err != nil {
return "", err
}
err = as.questionCommon.UpdateAnswerCount(ctx, req.QuestionId, 1)
err = as.questionCommon.UpdateAnswerCount(ctx, req.QuestionID, 1)
if err != nil {
log.Error("IncreaseAnswerCount error", err.Error())
}
err = as.questionCommon.UpdateLastAnswer(ctx, req.QuestionId, insertData.ID)
err = as.questionCommon.UpdateLastAnswer(ctx, req.QuestionID, insertData.ID)
if err != nil {
log.Error("UpdateLastAnswer error", err.Error())
}
err = as.questionCommon.UpdataPostTime(ctx, req.QuestionId)
err = as.questionCommon.UpdataPostTime(ctx, req.QuestionID)
if err != nil {
return insertData.ID, err
}
@ -140,8 +140,8 @@ func (as *AnswerService) Insert(ctx context.Context, req *schema.AnswerAddReq) (
ObjectID: insertData.ID,
Title: "",
}
InfoJson, _ := json.Marshal(insertData)
revisionDTO.Content = string(InfoJson)
infoJSON, _ := json.Marshal(insertData)
revisionDTO.Content = string(infoJSON)
err = as.revisionService.AddRevision(ctx, revisionDTO, true)
if err != nil {
return insertData.ID, err
@ -151,7 +151,7 @@ func (as *AnswerService) Insert(ctx context.Context, req *schema.AnswerAddReq) (
}
func (as *AnswerService) Update(ctx context.Context, req *schema.AnswerUpdateReq) (string, error) {
questionInfo, exist, err := as.questionRepo.GetQuestion(ctx, req.QuestionId)
questionInfo, exist, err := as.questionRepo.GetQuestion(ctx, req.QuestionID)
if err != nil {
return "", err
}
@ -161,15 +161,15 @@ func (as *AnswerService) Update(ctx context.Context, req *schema.AnswerUpdateReq
now := time.Now()
insertData := new(entity.Answer)
insertData.ID = req.ID
insertData.QuestionID = req.QuestionId
insertData.QuestionID = req.QuestionID
insertData.UserID = req.UserID
insertData.OriginalText = req.Content
insertData.ParsedText = req.Html
insertData.ParsedText = req.HTML
insertData.UpdatedAt = now
if err := as.answerRepo.UpdateAnswer(ctx, insertData, []string{"original_text", "parsed_text", "update_time"}); err != nil {
if err = as.answerRepo.UpdateAnswer(ctx, insertData, []string{"original_text", "parsed_text", "update_time"}); err != nil {
return "", err
}
err = as.questionCommon.UpdataPostTime(ctx, req.QuestionId)
err = as.questionCommon.UpdataPostTime(ctx, req.QuestionID)
if err != nil {
return insertData.ID, err
}
@ -179,8 +179,8 @@ func (as *AnswerService) Update(ctx context.Context, req *schema.AnswerUpdateReq
Title: "",
Log: req.EditSummary,
}
InfoJson, _ := json.Marshal(insertData)
revisionDTO.Content = string(InfoJson)
infoJSON, _ := json.Marshal(insertData)
revisionDTO.Content = string(infoJSON)
err = as.revisionService.AddRevision(ctx, revisionDTO, true)
if err != nil {
return insertData.ID, err
@ -228,7 +228,7 @@ func (as *AnswerService) UpdateAdopted(ctx context.Context, req *schema.AnswerAd
var oldAnswerInfo *entity.Answer
if len(questionInfo.AcceptedAnswerID) > 0 && questionInfo.AcceptedAnswerID != "0" {
oldAnswerInfo, exist, err = as.answerRepo.GetByID(ctx, questionInfo.AcceptedAnswerID)
oldAnswerInfo, _, err = as.answerRepo.GetByID(ctx, questionInfo.AcceptedAnswerID)
if err != nil {
return err
}
@ -249,8 +249,8 @@ func (as *AnswerService) UpdateAdopted(ctx context.Context, req *schema.AnswerAd
}
func (as *AnswerService) updateAnswerRank(ctx context.Context, userID string,
questionInfo *entity.Question, newAnswerInfo *entity.Answer, oldAnswerInfo *entity.Answer) {
questionInfo *entity.Question, newAnswerInfo *entity.Answer, oldAnswerInfo *entity.Answer,
) {
// if this question is already been answered, should cancel old answer rank
if oldAnswerInfo != nil {
err := as.answerActivityService.CancelAcceptAnswer(
@ -266,21 +266,20 @@ func (as *AnswerService) updateAnswerRank(ctx context.Context, userID string,
log.Error(err)
}
}
}
func (as *AnswerService) Get(ctx context.Context, answerID, loginUserId string) (*schema.AnswerInfo, *schema.QuestionInfo, bool, error) {
func (as *AnswerService) Get(ctx context.Context, answerID, loginUserID string) (*schema.AnswerInfo, *schema.QuestionInfo, bool, error) {
answerInfo, has, err := as.answerRepo.GetByID(ctx, answerID)
if err != nil {
return nil, nil, has, err
}
info := as.ShowFormat(ctx, answerInfo)
//todo questionFunc
questionInfo, err := as.questionCommon.Info(ctx, answerInfo.QuestionID, loginUserId)
// todo questionFunc
questionInfo, err := as.questionCommon.Info(ctx, answerInfo.QuestionID, loginUserID)
if err != nil {
return nil, nil, has, err
}
//todo UserFunc
// todo UserFunc
userinfo, has, err := as.userCommon.GetUserBasicInfoByID(ctx, answerInfo.UserID)
if err != nil {
return nil, nil, has, err
@ -290,13 +289,13 @@ func (as *AnswerService) Get(ctx context.Context, answerID, loginUserId string)
info.UpdateUserInfo = userinfo
}
if loginUserId == "" {
if loginUserID == "" {
return info, questionInfo, has, nil
}
info.VoteStatus = as.voteRepo.GetVoteStatus(ctx, answerID, loginUserId)
info.VoteStatus = as.voteRepo.GetVoteStatus(ctx, answerID, loginUserID)
CollectedMap, err := as.collectionCommon.SearchObjectCollected(ctx, loginUserId, []string{answerInfo.ID})
CollectedMap, err := as.collectionCommon.SearchObjectCollected(ctx, loginUserID, []string{answerInfo.ID})
if err != nil {
log.Error("CollectionFunc.SearchObjectCollected error", err)
}
@ -348,7 +347,7 @@ func (as *AnswerService) AdminSetAnswerStatus(ctx context.Context, answerID stri
func (as *AnswerService) SearchList(ctx context.Context, search *schema.AnswerList) ([]*schema.AnswerInfo, int64, error) {
list := make([]*schema.AnswerInfo, 0)
dbSearch := entity.AnswerSearch{}
dbSearch.QuestionID = search.QuestionId
dbSearch.QuestionID = search.QuestionID
dbSearch.Page = search.Page
dbSearch.PageSize = search.PageSize
dbSearch.Order = search.Order
@ -363,7 +362,7 @@ func (as *AnswerService) SearchList(ctx context.Context, search *schema.AnswerLi
return AnswerList, count, nil
}
func (as *AnswerService) SearchFormatInfo(ctx context.Context, dblist []*entity.Answer, loginUserId string) ([]*schema.AnswerInfo, error) {
func (as *AnswerService) SearchFormatInfo(ctx context.Context, dblist []*entity.Answer, loginUserID string) ([]*schema.AnswerInfo, error) {
list := make([]*schema.AnswerInfo, 0)
objectIds := make([]string, 0)
userIds := make([]string, 0)
@ -372,9 +371,9 @@ func (as *AnswerService) SearchFormatInfo(ctx context.Context, dblist []*entity.
list = append(list, item)
objectIds = append(objectIds, dbitem.ID)
userIds = append(userIds, dbitem.UserID)
if loginUserId != "" {
//item.VoteStatus = as.activityFunc.GetVoteStatus(ctx, item.TagID, loginUserId)
item.VoteStatus = as.voteRepo.GetVoteStatus(ctx, item.ID, loginUserId)
if loginUserID != "" {
// item.VoteStatus = as.activityFunc.GetVoteStatus(ctx, item.TagID, loginUserId)
item.VoteStatus = as.voteRepo.GetVoteStatus(ctx, item.ID, loginUserID)
}
}
userInfoMap, err := as.userCommon.BatchUserBasicInfoByID(ctx, userIds)
@ -382,18 +381,18 @@ func (as *AnswerService) SearchFormatInfo(ctx context.Context, dblist []*entity.
return list, err
}
for _, item := range list {
_, ok := userInfoMap[item.UserId]
_, ok := userInfoMap[item.UserID]
if ok {
item.UserInfo = userInfoMap[item.UserId]
item.UpdateUserInfo = userInfoMap[item.UserId]
item.UserInfo = userInfoMap[item.UserID]
item.UpdateUserInfo = userInfoMap[item.UserID]
}
}
if loginUserId == "" {
if loginUserID == "" {
return list, nil
}
CollectedMap, err := as.collectionCommon.SearchObjectCollected(ctx, loginUserId, objectIds)
CollectedMap, err := as.collectionCommon.SearchObjectCollected(ctx, loginUserID, objectIds)
if err != nil {
log.Error("CollectionFunc.SearchObjectCollected error", err)
}
@ -406,7 +405,7 @@ func (as *AnswerService) SearchFormatInfo(ctx context.Context, dblist []*entity.
}
for _, item := range list {
item.MemberActions = permission.GetAnswerPermission(loginUserId, item.UserId)
item.MemberActions = permission.GetAnswerPermission(loginUserID, item.UserID)
}
return list, nil

View File

@ -13,11 +13,12 @@ type AuthRepo interface {
GetUserCacheInfo(ctx context.Context, accessToken string) (userInfo *entity.UserCacheInfo, err error)
SetUserCacheInfo(ctx context.Context, accessToken string, userInfo *entity.UserCacheInfo) error
RemoveUserCacheInfo(ctx context.Context, accessToken string) (err error)
SetUserStatus(ctx context.Context, userID string, userInfo *entity.UserCacheInfo) (err error)
GetUserStatus(ctx context.Context, userID string) (userInfo *entity.UserCacheInfo, err error)
RemoveUserStatus(ctx context.Context, userID string) (err error)
GetCmsUserCacheInfo(ctx context.Context, accessToken string) (userInfo *entity.UserCacheInfo, err error)
SetCmsUserCacheInfo(ctx context.Context, accessToken string, userInfo *entity.UserCacheInfo) error
RemoveCmsUserCacheInfo(ctx context.Context, accessToken string) (err error)
GetBackyardUserCacheInfo(ctx context.Context, accessToken string) (userInfo *entity.UserCacheInfo, err error)
SetBackyardUserCacheInfo(ctx context.Context, accessToken string, userInfo *entity.UserCacheInfo) error
RemoveBackyardUserCacheInfo(ctx context.Context, accessToken string) (err error)
}
// AuthService kit service
@ -75,14 +76,14 @@ func (as *AuthService) RemoveUserCacheInfo(ctx context.Context, accessToken stri
//cms
func (as *AuthService) GetCmsUserCacheInfo(ctx context.Context, accessToken string) (userInfo *entity.UserCacheInfo, err error) {
return as.authRepo.GetCmsUserCacheInfo(ctx, accessToken)
return as.authRepo.GetBackyardUserCacheInfo(ctx, accessToken)
}
func (as *AuthService) SetCmsUserCacheInfo(ctx context.Context, accessToken string, userInfo *entity.UserCacheInfo) (err error) {
err = as.authRepo.SetCmsUserCacheInfo(ctx, accessToken, userInfo)
err = as.authRepo.SetBackyardUserCacheInfo(ctx, accessToken, userInfo)
return err
}
func (as *AuthService) RemoveCmsUserCacheInfo(ctx context.Context, accessToken string) (err error) {
return as.authRepo.RemoveCmsUserCacheInfo(ctx, accessToken)
return as.authRepo.RemoveBackyardUserCacheInfo(ctx, accessToken)
}

View File

@ -17,7 +17,7 @@ type CollectionGroupRepo interface {
UpdateCollectionGroup(ctx context.Context, collectionGroup *entity.CollectionGroup, cols []string) (err error)
GetCollectionGroup(ctx context.Context, id string) (collectionGroup *entity.CollectionGroup, exist bool, err error)
GetCollectionGroupPage(ctx context.Context, page, pageSize int, collectionGroup *entity.CollectionGroup) (collectionGroupList []*entity.CollectionGroup, total int64, err error)
GetDefaultID(ctx context.Context, userId string) (collectionGroup *entity.CollectionGroup, has bool, err error)
GetDefaultID(ctx context.Context, userID string) (collectionGroup *entity.CollectionGroup, has bool, err error)
}
// CollectionGroupService user service

View File

@ -23,7 +23,6 @@ func NewCollectionService(
collectionRepo collectioncommon.CollectionRepo,
collectionGroupRepo CollectionGroupRepo,
questionCommon *questioncommon.QuestionCommon,
) *CollectionService {
return &CollectionService{
collectionRepo: collectionRepo,
@ -31,6 +30,7 @@ func NewCollectionService(
questionCommon: questionCommon,
}
}
func (cs *CollectionService) CollectionSwitch(ctx context.Context, dto *schema.CollectionSwitchDTO) (resp *schema.CollectionSwitchResp, err error) {
resp = &schema.CollectionSwitchResp{}
dbData, has, err := cs.collectionRepo.GetOneByObjectIDAndUser(ctx, dto.UserID, dto.ObjectID)
@ -46,7 +46,8 @@ func (cs *CollectionService) CollectionSwitch(ctx context.Context, dto *schema.C
if err != nil {
log.Error("UpdateCollectionCount", err.Error())
}
count, err := cs.objectCollectionCount(ctx, dto.ObjectID)
var count int64
count, err = cs.objectCollectionCount(ctx, dto.ObjectID)
if err != nil {
return resp, err
}
@ -56,12 +57,17 @@ func (cs *CollectionService) CollectionSwitch(ctx context.Context, dto *schema.C
}
if dto.GroupID == "" || dto.GroupID == "0" {
defaultGroup, has, err := cs.collectionGroupRepo.GetDefaultID(ctx, dto.UserID)
var (
defaultGroup *entity.CollectionGroup
has bool
)
defaultGroup, has, err = cs.collectionGroupRepo.GetDefaultID(ctx, dto.UserID)
if err != nil {
return nil, err
}
if !has {
dbdefaultGroup, err := cs.collectionGroupRepo.AddCollectionDefaultGroup(ctx, dto.UserID)
var dbdefaultGroup *entity.CollectionGroup
dbdefaultGroup, err = cs.collectionGroupRepo.AddCollectionDefaultGroup(ctx, dto.UserID)
if err != nil {
return nil, err
}
@ -93,8 +99,8 @@ func (cs *CollectionService) CollectionSwitch(ctx context.Context, dto *schema.C
return
}
func (cs *CollectionService) objectCollectionCount(ctx context.Context, objectId string) (int64, error) {
count, err := cs.collectionRepo.CountByObjectID(ctx, objectId)
func (cs *CollectionService) objectCollectionCount(ctx context.Context, objectID string) (int64, error) {
count, err := cs.collectionRepo.CountByObjectID(ctx, objectID)
return count, err
}
@ -108,12 +114,16 @@ func (cs *CollectionService) add(ctx context.Context, collection *entity.Collect
}
if collection.UserCollectionGroupID == "" || collection.UserCollectionGroupID == "0" {
defaultGroup, has, err := cs.collectionGroupRepo.GetDefaultID(ctx, collection.UserID)
var (
defaultGroup *entity.CollectionGroup
has bool
)
defaultGroup, has, err = cs.collectionGroupRepo.GetDefaultID(ctx, collection.UserID)
if err != nil {
return err
}
if !has {
defaultGroup, err := cs.collectionGroupRepo.AddCollectionDefaultGroup(ctx, collection.UserID)
defaultGroup, err = cs.collectionGroupRepo.AddCollectionDefaultGroup(ctx, collection.UserID)
if err != nil {
return err
}

View File

@ -24,6 +24,7 @@ type CommentRepo interface {
AddComment(ctx context.Context, comment *entity.Comment) (err error)
RemoveComment(ctx context.Context, commentID string) (err error)
UpdateComment(ctx context.Context, comment *entity.Comment) (err error)
GetComment(ctx context.Context, commentID string) (comment *entity.Comment, exist bool, err error)
GetCommentPage(ctx context.Context, commentQuery *CommentQuery) (
comments []*entity.Comment, total int64, err error)
}

View File

@ -7,7 +7,7 @@ type ConfigRepo interface {
GetInt(key string) (int, error)
GetArrayString(key string) ([]string, error)
GetConfigType(key string) (int, error)
GetConfigById(id int, value any) (err error)
GetJsonConfigByIDAndSetToObject(id int, value any) (err error)
SetConfig(key, value string) (err error)
}

View File

@ -95,15 +95,15 @@ func (ns *NotificationService) ClearIDUnRead(ctx context.Context, userID string,
return nil
}
func (ns *NotificationService) GetList(ctx context.Context, search *schema.NotificationSearch) (
func (ns *NotificationService) GetNotificationPage(ctx context.Context, searchCond *schema.NotificationSearch) (
pageModel *pager.PageModel, err error) {
resp := make([]*schema.NotificationContent, 0)
searchType, ok := schema.NotificationType[search.TypeStr]
searchType, ok := schema.NotificationType[searchCond.TypeStr]
if !ok {
return pager.NewPageModel(0, resp), nil
}
search.Type = searchType
notifications, count, err := ns.notificationRepo.SearchList(ctx, search)
searchCond.Type = searchType
notifications, total, err := ns.notificationRepo.GetNotificationPage(ctx, searchCond)
if err != nil {
return nil, err
}
@ -123,5 +123,5 @@ func (ns *NotificationService) GetList(ctx context.Context, search *schema.Notif
}
resp = append(resp, item)
}
return pager.NewPageModel(count, resp), nil
return pager.NewPageModel(total, resp), nil
}

View File

@ -22,7 +22,7 @@ import (
type NotificationRepo interface {
AddNotification(ctx context.Context, notification *entity.Notification) (err error)
SearchList(ctx context.Context, search *schema.NotificationSearch) ([]*entity.Notification, int64, error)
GetNotificationPage(ctx context.Context, search *schema.NotificationSearch) ([]*entity.Notification, int64, error)
ClearUnRead(ctx context.Context, userID string, notificationType int) (err error)
ClearIDUnRead(ctx context.Context, userID string, id string) (err error)
GetByUserIdObjectIdTypeId(ctx context.Context, userID, objectID string, notificationType int) (*entity.Notification, bool, error)

View File

@ -31,9 +31,9 @@ type QuestionRepo interface {
SearchList(ctx context.Context, search *schema.QuestionSearch) ([]*entity.QuestionTag, int64, error)
UpdateQuestionStatus(ctx context.Context, question *entity.Question) (err error)
SearchByTitleLike(ctx context.Context, title string) (questionList []*entity.Question, err error)
UpdatePvCount(ctx context.Context, questionId string) (err error)
UpdateAnswerCount(ctx context.Context, questionId string, num int) (err error)
UpdateCollectionCount(ctx context.Context, questionId string, num int) (err error)
UpdatePvCount(ctx context.Context, questionID string) (err error)
UpdateAnswerCount(ctx context.Context, questionID string, num int) (err error)
UpdateCollectionCount(ctx context.Context, questionID string, num int) (err error)
UpdateAccepted(ctx context.Context, question *entity.Question) (err error)
UpdateLastAnswer(ctx context.Context, question *entity.Question) (err error)
FindByID(ctx context.Context, id []string) (questionList []*entity.Question, err error)
@ -64,7 +64,6 @@ func NewQuestionCommon(questionRepo QuestionRepo,
answerCommon *answercommon.AnswerCommon,
metaService *meta.MetaService,
configRepo config.ConfigRepo,
) *QuestionCommon {
return &QuestionCommon{
questionRepo: questionRepo,
@ -80,42 +79,44 @@ func NewQuestionCommon(questionRepo QuestionRepo,
}
}
func (qs *QuestionCommon) UpdataPv(ctx context.Context, questionId string) error {
return qs.questionRepo.UpdatePvCount(ctx, questionId)
}
func (qs *QuestionCommon) UpdateAnswerCount(ctx context.Context, questionId string, num int) error {
return qs.questionRepo.UpdateAnswerCount(ctx, questionId, num)
}
func (qs *QuestionCommon) UpdateCollectionCount(ctx context.Context, questionId string, num int) error {
return qs.questionRepo.UpdateCollectionCount(ctx, questionId, num)
func (qs *QuestionCommon) UpdataPv(ctx context.Context, questionID string) error {
return qs.questionRepo.UpdatePvCount(ctx, questionID)
}
func (qs *QuestionCommon) UpdateAccepted(ctx context.Context, questionId, AnswerId string) error {
func (qs *QuestionCommon) UpdateAnswerCount(ctx context.Context, questionID string, num int) error {
return qs.questionRepo.UpdateAnswerCount(ctx, questionID, num)
}
func (qs *QuestionCommon) UpdateCollectionCount(ctx context.Context, questionID string, num int) error {
return qs.questionRepo.UpdateCollectionCount(ctx, questionID, num)
}
func (qs *QuestionCommon) UpdateAccepted(ctx context.Context, questionID, AnswerID string) error {
question := &entity.Question{}
question.ID = questionId
question.AcceptedAnswerID = AnswerId
question.ID = questionID
question.AcceptedAnswerID = AnswerID
return qs.questionRepo.UpdateAccepted(ctx, question)
}
func (qs *QuestionCommon) UpdateLastAnswer(ctx context.Context, questionId, AnswerId string) error {
func (qs *QuestionCommon) UpdateLastAnswer(ctx context.Context, questionID, AnswerID string) error {
question := &entity.Question{}
question.ID = questionId
question.LastAnswerID = AnswerId
question.ID = questionID
question.LastAnswerID = AnswerID
return qs.questionRepo.UpdateLastAnswer(ctx, question)
}
func (qs *QuestionCommon) UpdataPostTime(ctx context.Context, questionId string) error {
func (qs *QuestionCommon) UpdataPostTime(ctx context.Context, questionID string) error {
questioninfo := &entity.Question{}
now := time.Now()
questioninfo.ID = questionId
questioninfo.ID = questionID
questioninfo.PostUpdateTime = now
return qs.questionRepo.UpdateQuestion(ctx, questioninfo, []string{"post_update_time"})
}
func (qs *QuestionCommon) FindInfoByID(ctx context.Context, questionIds []string, loginUserID string) (map[string]*schema.QuestionInfo, error) {
func (qs *QuestionCommon) FindInfoByID(ctx context.Context, questionIDs []string, loginUserID string) (map[string]*schema.QuestionInfo, error) {
list := make(map[string]*schema.QuestionInfo)
listAddTag := make([]*entity.QuestionTag, 0)
questionList, err := qs.questionRepo.FindByID(ctx, questionIds)
questionList, err := qs.questionRepo.FindByID(ctx, questionIDs)
if err != nil {
return list, err
}
@ -134,8 +135,8 @@ func (qs *QuestionCommon) FindInfoByID(ctx context.Context, questionIds []string
return list, nil
}
func (qs *QuestionCommon) Info(ctx context.Context, questionId string, loginUserID string) (showinfo *schema.QuestionInfo, err error) {
dbinfo, has, err := qs.questionRepo.GetQuestion(ctx, questionId)
func (qs *QuestionCommon) Info(ctx context.Context, questionID string, loginUserID string) (showinfo *schema.QuestionInfo, err error) {
dbinfo, has, err := qs.questionRepo.GetQuestion(ctx, questionID)
if err != nil {
return showinfo, err
}
@ -145,26 +146,27 @@ func (qs *QuestionCommon) Info(ctx context.Context, questionId string, loginUser
showinfo = qs.ShowFormat(ctx, dbinfo)
if showinfo.Status == 2 {
metainfo, err := qs.metaService.GetMetaByObjectIdAndKey(ctx, dbinfo.ID, entity.QuestionCloseReasonKey)
var metainfo *entity.Meta
metainfo, err = qs.metaService.GetMetaByObjectIdAndKey(ctx, dbinfo.ID, entity.QuestionCloseReasonKey)
if err != nil {
log.Error(err)
} else {
//metainfo.Value
// metainfo.Value
closemsg := &schema.CloseQuestionMeta{}
err := json.Unmarshal([]byte(metainfo.Value), closemsg)
err = json.Unmarshal([]byte(metainfo.Value), closemsg)
if err != nil {
log.Error("json.Unmarshal CloseQuestionMeta error", err.Error())
} else {
closeinfo := &schema.GetReportTypeResp{}
err = qs.configRepo.GetConfigById(closemsg.CloseType, closeinfo)
err = qs.configRepo.GetJsonConfigByIDAndSetToObject(closemsg.CloseType, closeinfo)
if err != nil {
log.Error("json.Unmarshal QuestionCloseJson error", err.Error())
} else {
operation := &schema.Operation{}
operation.Operation_Type = closeinfo.Name
operation.Operation_Description = closeinfo.Description
operation.Operation_Msg = closemsg.CloseMsg
operation.Operation_Time = metainfo.CreatedAt.Unix()
operation.OperationType = closeinfo.Name
operation.OperationDescription = closeinfo.Description
operation.OperationMsg = closemsg.CloseMsg
operation.OperationTime = metainfo.CreatedAt.Unix()
showinfo.Operation = operation
}
@ -173,7 +175,7 @@ func (qs *QuestionCommon) Info(ctx context.Context, questionId string, loginUser
}
}
tagmap, err := qs.tagCommon.GetObjectTag(ctx, questionId)
tagmap, err := qs.tagCommon.GetObjectTag(ctx, questionID)
if err != nil {
return showinfo, err
}
@ -193,10 +195,10 @@ func (qs *QuestionCommon) Info(ctx context.Context, questionId string, loginUser
return showinfo, nil
}
showinfo.VoteStatus = qs.voteRepo.GetVoteStatus(ctx, questionId, loginUserID)
showinfo.VoteStatus = qs.voteRepo.GetVoteStatus(ctx, questionID, loginUserID)
// // check is followed
isFollowed, _ := qs.followCommon.IsFollowed(loginUserID, questionId)
isFollowed, _ := qs.followCommon.IsFollowed(loginUserID, questionID)
showinfo.IsFollowed = isFollowed
has, err = qs.AnswerCommon.SearchAnswered(ctx, loginUserID, dbinfo.ID)
@ -205,7 +207,7 @@ func (qs *QuestionCommon) Info(ctx context.Context, questionId string, loginUser
}
showinfo.Answered = has
//login user Collected information
// login user Collected information
CollectedMap, err := qs.collectionCommon.SearchObjectCollected(ctx, loginUserID, []string{dbinfo.ID})
if err != nil {
@ -245,11 +247,11 @@ func (qs *QuestionCommon) ListFormat(ctx context.Context, questionList []*entity
if ok {
item.Tags = tagsMap[item.ID]
}
_, ok = userInfoMap[item.UserId]
_, ok = userInfoMap[item.UserID]
if ok {
item.UserInfo = userInfoMap[item.UserId]
item.UpdateUserInfo = userInfoMap[item.UserId]
item.LastAnsweredUserInfo = userInfoMap[item.UserId]
item.UserInfo = userInfoMap[item.UserID]
item.UpdateUserInfo = userInfoMap[item.UserID]
item.LastAnsweredUserInfo = userInfoMap[item.UserID]
}
}
@ -286,7 +288,7 @@ func (qs *QuestionCommon) RemoveQuestion(ctx context.Context, req *schema.Remove
return err
}
//user add question count
// user add question count
err = qs.userCommon.UpdateQuestionCount(ctx, questionInfo.UserID, -1)
if err != nil {
log.Error("user UpdateQuestionCount error", err.Error())
@ -332,7 +334,7 @@ func (as *QuestionCommon) RemoveAnswer(ctx context.Context, id string) (err erro
return nil
}
//user add question count
// user add question count
err = as.UpdateAnswerCount(ctx, answerinfo.QuestionID, -1)
if err != nil {
@ -356,21 +358,21 @@ func (qs *QuestionCommon) ShowFormat(ctx context.Context, data *entity.Question)
info.ID = data.ID
info.Title = data.Title
info.Content = data.OriginalText
info.Html = data.ParsedText
info.HTML = data.ParsedText
info.ViewCount = data.ViewCount
info.UniqueViewCount = data.UniqueViewCount
info.VoteCount = data.VoteCount
info.AnswerCount = data.AnswerCount
info.CollectionCount = data.CollectionCount
info.FollowCount = data.FollowCount
info.AcceptedAnswerId = data.AcceptedAnswerID
info.LastAnswerId = data.LastAnswerID
info.AcceptedAnswerID = data.AcceptedAnswerID
info.LastAnswerID = data.LastAnswerID
info.CreateTime = data.CreatedAt.Unix()
info.UpdateTime = data.UpdatedAt.Unix()
info.PostUpdateTime = data.PostUpdateTime.Unix()
info.QuestionUpdateTime = data.UpdatedAt.Unix()
info.Status = data.Status
info.UserId = data.UserID
info.UserID = data.UserID
info.Tags = make([]*schema.TagResp, 0)
return &info
}

Some files were not shown because too many files have changed in this diff Show More