From e2ba6924b862f4088d192ba563c4aafb18c33b88 Mon Sep 17 00:00:00 2001 From: lianghuanjie Date: Thu, 5 Dec 2024 20:51:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 26 +++ Dockerfile | 31 +++ Makefile | 65 ++++++ README.md | 21 ++ doc/api/nova-task.api | 46 +++++ doc/swagger/nova-task.json | 190 ++++++++++++++++++ docker-compose.yml | 13 ++ etc/novatask.yaml | 13 ++ go.mod | 53 +++++ go.sum | 137 +++++++++++++ goctl.yaml | 159 +++++++++++++++ internal/config/config.go | 40 ++++ internal/handler/routes.go | 34 ++++ .../handler/task/get_task_list_handler.go | 29 +++ .../handler/task/get_task_reward_handler.go | 29 +++ internal/logic/task/get_task_list_logic.go | 53 +++++ internal/logic/task/get_task_reward_logic.go | 31 +++ internal/model/nh_task_model.go | 45 +++++ internal/model/nh_task_model_gen.go | 97 +++++++++ internal/model/vars.go | 5 + internal/pkg/encrypt/jwt/jwt.go | 106 ++++++++++ internal/pkg/encrypt/md5/md5.go | 31 +++ internal/pkg/encrypt/sha/sha.go | 30 +++ internal/pkg/errs/error.go | 40 ++++ internal/pkg/errs/errors.go | 56 ++++++ internal/pkg/errs/http_respone.go | 64 ++++++ internal/pkg/errs/reason.go | 33 +++ internal/svc/service_context.go | 18 ++ internal/types/types.go | 33 +++ novatask.go | 32 +++ 30 files changed, 1560 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 README.md create mode 100644 doc/api/nova-task.api create mode 100644 doc/swagger/nova-task.json create mode 100644 docker-compose.yml create mode 100644 etc/novatask.yaml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 goctl.yaml create mode 100644 internal/config/config.go create mode 100644 internal/handler/routes.go create mode 100644 internal/handler/task/get_task_list_handler.go create mode 100644 internal/handler/task/get_task_reward_handler.go create mode 100644 internal/logic/task/get_task_list_logic.go create mode 100644 internal/logic/task/get_task_reward_logic.go create mode 100755 internal/model/nh_task_model.go create mode 100755 internal/model/nh_task_model_gen.go create mode 100644 internal/model/vars.go create mode 100644 internal/pkg/encrypt/jwt/jwt.go create mode 100644 internal/pkg/encrypt/md5/md5.go create mode 100644 internal/pkg/encrypt/sha/sha.go create mode 100644 internal/pkg/errs/error.go create mode 100644 internal/pkg/errs/errors.go create mode 100644 internal/pkg/errs/http_respone.go create mode 100644 internal/pkg/errs/reason.go create mode 100644 internal/svc/service_context.go create mode 100644 internal/types/types.go create mode 100644 novatask.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e2741c5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# ---> Go +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# local config +*_local.yaml + +# Dependency directories (remove the comment below to include it) +vendor/ + +builder/ +logs/ +static/ +bin/ +cert/ +.idea/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f4e07fc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,31 @@ +FROM golang:1.23.4-alpine AS builder + +LABEL stage=gobuilder + +ENV CGO_ENABLED=0 +ENV GOPROXY=https://goproxy.cn,direct +RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories + +RUN apk update --no-cache && apk add --no-cache tzdata + +WORKDIR /build + +ADD go.mod . +ADD go.sum . +RUN go mod download +COPY . . +COPY ./etc /app/etc +RUN go build -ldflags="-s -w" -o /app/novatask novatask.go + + +FROM scratch + +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt +COPY --from=builder /usr/share/zoneinfo/UTC /usr/share/zoneinfo/UTC +ENV TZ=UTC + +WORKDIR /app +COPY --from=builder /app/novatask /app/novatask +COPY --from=builder /app/etc /app/etc + +CMD ["./novatask", "-f", "etc/novatask.yaml"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..51f0895 --- /dev/null +++ b/Makefile @@ -0,0 +1,65 @@ +host ?= 192.168.2.108:3306 +user ?= huangjie +pwd ?= jMDqPQM^a6hsAR +table ?= +cache ?= +database ?= "nova_home" + +.PHONY: db +# 链接数据库生成模型代码 +db: + goctl model mysql datasource -url="${user}:${pwd}@tcp(${host})/${database}" -table="${table}" --dir internal/model --strict --style go_zero ${cache} + +.PHONY: init +# 初始化开发环境,安装依赖工具 +init: + go install github.com/zeromicro/go-zero/tools/goctl@latest + goctl env check --install --verbose --force + go install github.com/zeromicro/goctl-swagger@latest + +.PHONY: api-format +# 格式化api文件 +api-format: + goctl api format --dir doc/api/ + goctl api plugin -plugin goctl-swagger="swagger -filename nova-task.json" -api doc/api/nova-task.api -dir doc/swagger + +.PHONY: api +# 根据api文件生成代码 +api: + make api-format + goctl api go -api doc/api/nova-task.api --dir . --style go_zero + +.PHONY: build +# 编译二进制可执行文件 +build: + mkdir -p bin/ && go build -ldflags="-s -w" -o ./bin/ ./... + +.PHONY: img +# 构建Docker镜像 +img: + docker build --platform=amd64 -t nova-task:latest . + docker tag `docker images -aq -f "reference=nova-task:latest"` harbor.phantom-u3d002.com/nova/nova-task:latest + +.PHONY: push +push: + docker push harbor.phantom-u3d002.com/nova/nova-task:latest + + +# 帮助信息 +help: + @echo '' + @echo 'Usage:' + @echo ' make [target]' + @echo '' + @echo 'Targets:' + @awk '/^[a-zA-Z\-\0-9]+:/ { \ + helpMessage = match(lastLine, /^# (.*)/); \ + if (helpMessage) { \ + helpCommand = substr($$1, 0, index($$1, ":")-1); \ + helpMessage = substr(lastLine, RSTART + 2, RLENGTH); \ + printf "\033[36m%-22s\033[0m %s\n", helpCommand,helpMessage; \ + } \ + } \ + { lastLine = $$0 }' $(MAKEFILE_LIST) + +.DEFAULT_GOAL := help \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..dee9534 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# nova task 服务端 + +## 快速开始 +1. 基于go-zero框架开发 +2. golang 1.23.0+ +3. 安装依赖工具: make init + +## 组件依赖 +1. MySQL 8.0+ +2. Docker 27.1+ + +## 配置文件 [novatask.yaml](etc/saas.yaml) +1. 修改Mysql中的数据库地址以及账号密码 +2. 其他配置项根据注释说明和实际需求自行修改 + +## 正式服部署 +1. 将[docker-compose.yml](docker-compose.yml)文件拷贝到服务器上,并修改需要部署的镜像版本以及根据配置的端口修改端口映射 +2. 在同一目录下创建文件夹etc, 并将[novatask.yaml](etc/saas.yaml)文件拷贝到etc目录下,并根据需求修改配置文件 +3. 拉取镜像:``docker compose pull`` +4. 起服:``docker compose up -d`` +5. 配置http反向代理,将``/api/task``前缀的路由代理到映射出来的端口上 \ No newline at end of file diff --git a/doc/api/nova-task.api b/doc/api/nova-task.api new file mode 100644 index 0000000..d470976 --- /dev/null +++ b/doc/api/nova-task.api @@ -0,0 +1,46 @@ +syntax = "v1" + +@server ( + prefix: /api/task/v1 + group: task + jwt: Auth +) +service novatask { + @doc "获取任务列表" + @handler GetTaskList + get /tasks (GetTaskListReq) returns (GetTaskListResp) + + @doc "领取任务奖励" + @handler GetTaskReward + get /reward/:id (GetTaskRewardReq) returns (GetTaskRewardResp) +} + +type GetTaskListReq { + CommunityId uint `form:"community_id"` +} + +type Task { + Id uint `json:"id"` + Title string `json:"title"` + SubTitle string `json:"sub_title"` + Description string `json:"description"` + Points int `json:"points"` + ButtonText string `json:"button_text"` + Type int8 `json:"type"` + StartAt string `json:"start_at"` + EndAt string `json:"end_at"` + Status int8 `json:"status"` +} + +type GetTaskListResp { + Tasks []Task `json:"tasks"` +} + +type GetTaskRewardReq { + ID uint `path:"id"` +} + +type GetTaskRewardResp { + Points int `json:"points"` +} + diff --git a/doc/swagger/nova-task.json b/doc/swagger/nova-task.json new file mode 100644 index 0000000..b3ded41 --- /dev/null +++ b/doc/swagger/nova-task.json @@ -0,0 +1,190 @@ +{ + "swagger": "2.0", + "info": { + "title": "", + "version": "" + }, + "schemes": [ + "http", + "https" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/api/task/v1/reward/{id}": { + "get": { + "summary": "领取任务奖励", + "operationId": "GetTaskReward", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/GetTaskRewardResp" + } + } + }, + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "task" + ], + "security": [ + { + "apiKey": [] + } + ] + } + }, + "/api/task/v1/tasks": { + "get": { + "summary": "获取任务列表", + "operationId": "GetTaskList", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/GetTaskListResp" + } + } + }, + "parameters": [ + { + "name": "community_id", + "in": "query", + "required": true, + "type": "integer", + "format": "uint32" + } + ], + "tags": [ + "task" + ], + "consumes": [ + "multipart/form-data" + ], + "security": [ + { + "apiKey": [] + } + ] + } + } + }, + "definitions": { + "GetTaskListReq": { + "type": "object", + "properties": { + "community_id": { + "type": "integer", + "format": "uint32" + } + }, + "title": "GetTaskListReq", + "required": [ + "community_id" + ] + }, + "GetTaskListResp": { + "type": "object", + "properties": { + "tasks": { + "type": "array", + "items": { + "$ref": "#/definitions/Task" + } + } + }, + "title": "GetTaskListResp", + "required": [ + "tasks" + ] + }, + "GetTaskRewardReq": { + "type": "object", + "title": "GetTaskRewardReq" + }, + "GetTaskRewardResp": { + "type": "object", + "properties": { + "points": { + "type": "integer", + "format": "int32" + } + }, + "title": "GetTaskRewardResp", + "required": [ + "points" + ] + }, + "Task": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "uint32" + }, + "title": { + "type": "string" + }, + "sub_title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "points": { + "type": "integer", + "format": "int32" + }, + "button_text": { + "type": "string" + }, + "type": { + "type": "integer", + "format": "int8" + }, + "start_at": { + "type": "string" + }, + "end_at": { + "type": "string" + }, + "status": { + "type": "integer", + "format": "int8" + } + }, + "title": "Task", + "required": [ + "id", + "title", + "sub_title", + "description", + "points", + "button_text", + "type", + "start_at", + "end_at", + "status" + ] + } + }, + "securityDefinitions": { + "apiKey": { + "type": "apiKey", + "description": "Enter JWT Bearer token **_only_**", + "name": "Authorization", + "in": "header" + } + } +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..69a2aba --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +services: + saas-server: + image: harbor.phantom-u3d002.com/nova/nova-task:latest + container_name: nova-task + privileged: true + restart: always + ports: + - "8888:8888" + volumes: + - ./logs:/app/logs + - ./etc:/app/etc + environment: + TZ: UTC \ No newline at end of file diff --git a/etc/novatask.yaml b/etc/novatask.yaml new file mode 100644 index 0000000..8dba4c0 --- /dev/null +++ b/etc/novatask.yaml @@ -0,0 +1,13 @@ +Name: novatask +Host: 0.0.0.0 +Port: 8888 + +Auth: # js-sdk鉴权相关配置 + AccessSecret: "ac2d27613e131be6286c0eb1713929dd" # 鉴权token密钥 + AccessExpire: 168h # 鉴权token过期时间 + +MySql: # mysql相关配置 + Addr: "127.0.0.1:3306" # mysql地址 + User: "root" # mysql用户 + Password: "123456" # mysql密码 + Database: "nova_home" # 数据库名 \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a51e0e5 --- /dev/null +++ b/go.mod @@ -0,0 +1,53 @@ +module nova_task + +go 1.23.4 + +require ( + github.com/golang-jwt/jwt/v4 v4.5.1 + github.com/spf13/cast v1.7.0 + github.com/zeromicro/go-zero v1.7.4 +) + +require ( + filippo.io/edwards25519 v1.1.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/fatih/color v1.18.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-sql-driver/mysql v1.8.1 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/openzipkin/zipkin-go v0.4.3 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/prometheus/client_golang v1.20.5 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + go.opentelemetry.io/otel v1.24.0 // indirect + go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 // indirect + go.opentelemetry.io/otel/exporters/zipkin v1.24.0 // indirect + go.opentelemetry.io/otel/metric v1.24.0 // indirect + go.opentelemetry.io/otel/sdk v1.24.0 // indirect + go.opentelemetry.io/otel/trace v1.24.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect + go.uber.org/automaxprocs v1.6.0 // indirect + golang.org/x/net v0.31.0 // indirect + golang.org/x/sys v0.27.0 // indirect + golang.org/x/text v0.20.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/grpc v1.65.0 // indirect + google.golang.org/protobuf v1.35.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..8c0c3c3 --- /dev/null +++ b/go.sum @@ -0,0 +1,137 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= +github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= +github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= +github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/zeromicro/go-zero v1.7.4 h1:lyIUsqbpVRzM4NmXu5pRM3XrdRdUuWOkQmHiNmJF0VU= +github.com/zeromicro/go-zero v1.7.4/go.mod h1:jmv4hTdUBkDn6kxgI+WrKQw0q6LKxDElGPMfCLOeeEY= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4= +go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 h1:Mw5xcxMwlqoJd97vwPxA8isEaIoxsta9/Q51+TTJLGE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0/go.mod h1:CQNu9bj7o7mC6U7+CA/schKEYakYXWr79ucDHTMGhCM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 h1:Xw8U6u2f8DK2XAkGRFV7BBLENgnTGX9i4rQRxJf+/vs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0/go.mod h1:6KW1Fm6R/s6Z3PGXwSJN2K4eT6wQB3vXX6CVnYX9NmM= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 h1:s0PHtIkN+3xrbDOpt2M8OTG92cWqUESvzh2MxiR5xY8= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0/go.mod h1:hZlFbDbRt++MMPCCfSJfmhkGIWnX1h3XjkfxZUjLrIA= +go.opentelemetry.io/otel/exporters/zipkin v1.24.0 h1:3evrL5poBuh1KF51D9gO/S+N/1msnm4DaBqs/rpXUqY= +go.opentelemetry.io/otel/exporters/zipkin v1.24.0/go.mod h1:0EHgD8R0+8yRhUYJOGR8Hfg2dpiJQxDOszd5smVO9wM= +go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= +go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d h1:kHjw/5UfflP/L5EbledDrcG4C2597RtymmGRZvHiCuY= +google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY= +gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= diff --git a/goctl.yaml b/goctl.yaml new file mode 100644 index 0000000..8da963d --- /dev/null +++ b/goctl.yaml @@ -0,0 +1,159 @@ +model: + types_map: + bigint: + null_type: sql.NullInt64 + type: int64 + unsigned_type: uint64 + dec: + null_type: decimal.NullDecimal + pkg: github.com/shopspring/decimal + type: decimal.Decimal + decimal: + null_type: decimal.NullDecimal + pkg: github.com/shopspring/decimal + type: decimal.Decimal + double: + null_type: sql.NullFloat64 + type: float64 + float: + null_type: sql.NullFloat64 + type: float64 + float4: + null_type: sql.NullFloat64 + type: float64 + float8: + null_type: sql.NullFloat64 + type: float64 + int: + null_type: sql.NullInt64 + type: int + unsigned_type: uint + int1: + null_type: sql.NullInt64 + type: int64 + unsigned_type: uint64 + int2: + null_type: sql.NullInt64 + type: int64 + unsigned_type: uint64 + int3: + null_type: sql.NullInt64 + type: int64 + unsigned_type: uint64 + int4: + null_type: sql.NullInt64 + type: int64 + unsigned_type: uint64 + int8: + null_type: sql.NullInt64 + type: int64 + unsigned_type: uint64 + integer: + null_type: sql.NullInt64 + type: int + unsigned_type: uint + mediumint: + null_type: sql.NullInt64 + type: int64 + unsigned_type: uint64 + middleint: + null_type: sql.NullInt64 + type: int64 + unsigned_type: uint64 + smallint: + null_type: sql.NullInt64 + type: int64 + unsigned_type: uint64 + tinyint: + null_type: sql.NullInt16 + type: int8 + unsigned_type: uint8 + date: + null_type: sql.NullTime + type: time.Time + datetime: + null_type: sql.NullTime + type: time.Time + timestamp: + null_type: sql.NullTime + type: time.Time + time: + null_type: sql.NullString + type: string + year: + null_type: sql.NullInt64 + type: int64 + unsigned_type: uint64 + bit: + null_type: sql.NullByte + type: byte + unsigned_type: byte + bool: + null_type: sql.NullBool + type: bool + boolean: + null_type: sql.NullBool + type: bool + char: + null_type: sql.NullString + type: string + varchar: + null_type: sql.NullString + type: string + nvarchar: + null_type: sql.NullString + type: string + nchar: + null_type: sql.NullString + type: string + character: + null_type: sql.NullString + type: string + longvarchar: + null_type: sql.NullString + type: string + linestring: + null_type: sql.NullString + type: string + multilinestring: + null_type: sql.NullString + type: string + binary: + null_type: "[]byte" + type: "[]byte" + varbinary: + null_type: "[]byte" + type: "[]byte" + tinytext: + null_type: sql.NullString + type: string + text: + null_type: sql.NullString + type: string + mediumtext: + null_type: sql.NullString + type: string + longtext: + null_type: sql.NullString + type: string + enum: + null_type: sql.NullString + type: string + set: + null_type: sql.NullString + type: string + json: + null_type: sql.NullString + type: string + blob: + null_type: "[]byte" + type: "[]byte" + longblob: + null_type: "[]byte" + type: "[]byte" + mediumblob: + null_type: "[]byte" + type: "[]byte" + tinyblob: + null_type: "[]byte" + type: "[]byte" \ No newline at end of file diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..1dbed85 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,40 @@ +package config + +import ( + "fmt" + "github.com/zeromicro/go-zero/core/stores/sqlx" + "github.com/zeromicro/go-zero/rest" + "net/url" +) + +type Config struct { + rest.RestConf + MySql MySqlConf + Auth struct { + AccessSecret string + AccessExpire int64 + } +} + +// MySqlConf mysql配置 +type MySqlConf struct { + Addr string + User string + Password string + Database string + Loc string `json:",default=Local"` + Log string `json:",default=disableStmt,options=allow|disable|disableStmt"` +} + +func (m MySqlConf) Dsn() string { + return fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=%s", m.User, m.Password, m.Addr, m.Database, url.QueryEscape(m.Loc)) +} + +func (m MySqlConf) Conn(opts ...sqlx.SqlOption) sqlx.SqlConn { + if m.Log == "disable" { + sqlx.DisableLog() + } else if m.Log == "disableStmt" { + sqlx.DisableStmtLog() + } + return sqlx.NewMysql(m.Dsn(), opts...) +} diff --git a/internal/handler/routes.go b/internal/handler/routes.go new file mode 100644 index 0000000..f82d3d8 --- /dev/null +++ b/internal/handler/routes.go @@ -0,0 +1,34 @@ +// Code generated by goctl. DO NOT EDIT. +// goctl 1.7.3 + +package handler + +import ( + "net/http" + + task "nova_task/internal/handler/task" + "nova_task/internal/svc" + + "github.com/zeromicro/go-zero/rest" +) + +func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { + server.AddRoutes( + []rest.Route{ + { + // 领取任务奖励 + Method: http.MethodGet, + Path: "/reward/:id", + Handler: task.GetTaskRewardHandler(serverCtx), + }, + { + // 获取任务列表 + Method: http.MethodGet, + Path: "/tasks", + Handler: task.GetTaskListHandler(serverCtx), + }, + }, + rest.WithJwt(serverCtx.Config.Auth.AccessSecret), + rest.WithPrefix("/api/task/v1"), + ) +} diff --git a/internal/handler/task/get_task_list_handler.go b/internal/handler/task/get_task_list_handler.go new file mode 100644 index 0000000..d6c4c12 --- /dev/null +++ b/internal/handler/task/get_task_list_handler.go @@ -0,0 +1,29 @@ +package task + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "nova_task/internal/logic/task" + "nova_task/internal/svc" + "nova_task/internal/types" +) + +// 获取任务列表 +func GetTaskListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.GetTaskListReq + if err := httpx.Parse(r, &req); err != nil { + httpx.ErrorCtx(r.Context(), w, err) + return + } + + l := task.NewGetTaskListLogic(r.Context(), svcCtx) + resp, err := l.GetTaskList(&req) + if err != nil { + httpx.ErrorCtx(r.Context(), w, err) + } else { + httpx.OkJsonCtx(r.Context(), w, resp) + } + } +} diff --git a/internal/handler/task/get_task_reward_handler.go b/internal/handler/task/get_task_reward_handler.go new file mode 100644 index 0000000..8f16190 --- /dev/null +++ b/internal/handler/task/get_task_reward_handler.go @@ -0,0 +1,29 @@ +package task + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "nova_task/internal/logic/task" + "nova_task/internal/svc" + "nova_task/internal/types" +) + +// 领取任务奖励 +func GetTaskRewardHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.GetTaskRewardReq + if err := httpx.Parse(r, &req); err != nil { + httpx.ErrorCtx(r.Context(), w, err) + return + } + + l := task.NewGetTaskRewardLogic(r.Context(), svcCtx) + resp, err := l.GetTaskReward(&req) + if err != nil { + httpx.ErrorCtx(r.Context(), w, err) + } else { + httpx.OkJsonCtx(r.Context(), w, resp) + } + } +} diff --git a/internal/logic/task/get_task_list_logic.go b/internal/logic/task/get_task_list_logic.go new file mode 100644 index 0000000..ccac48d --- /dev/null +++ b/internal/logic/task/get_task_list_logic.go @@ -0,0 +1,53 @@ +package task + +import ( + "context" + "nova_task/internal/pkg/errs" + "time" + + "nova_task/internal/svc" + "nova_task/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type GetTaskListLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +// NewGetTaskListLogic 获取任务列表 +func NewGetTaskListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetTaskListLogic { + return &GetTaskListLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *GetTaskListLogic) GetTaskList(req *types.GetTaskListReq) (*types.GetTaskListResp, error) { + tasks, err := l.svcCtx.TaskModel.FindTasksByCommunity(l.ctx, req.CommunityId) + if err != nil { + l.Errorw("get task list failed", logx.Field("err", err), logx.Field("communityId", req.CommunityId)) + return nil, errs.InternalServer(errs.ErrDatabaseOperate, err) + } + + resp := &types.GetTaskListResp{} + for _, t := range tasks { + resp.Tasks = append(resp.Tasks, types.Task{ + Id: t.Id, + Title: t.Title, + SubTitle: t.SubTitle, + Description: t.Description, + Points: t.Points, + ButtonText: t.ButtonText, + Type: t.Type, + StartAt: t.StartAt.Time.Format(time.DateTime), + EndAt: t.EndAt.Time.Format(time.DateTime), + Status: t.Status, + }) + } + + return resp, nil +} diff --git a/internal/logic/task/get_task_reward_logic.go b/internal/logic/task/get_task_reward_logic.go new file mode 100644 index 0000000..1a484da --- /dev/null +++ b/internal/logic/task/get_task_reward_logic.go @@ -0,0 +1,31 @@ +package task + +import ( + "context" + + "nova_task/internal/svc" + "nova_task/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type GetTaskRewardLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +// 领取任务奖励 +func NewGetTaskRewardLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetTaskRewardLogic { + return &GetTaskRewardLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *GetTaskRewardLogic) GetTaskReward(req *types.GetTaskRewardReq) (resp *types.GetTaskRewardResp, err error) { + // todo: add your logic here and delete this line + + return +} diff --git a/internal/model/nh_task_model.go b/internal/model/nh_task_model.go new file mode 100755 index 0000000..ef30533 --- /dev/null +++ b/internal/model/nh_task_model.go @@ -0,0 +1,45 @@ +package model + +import ( + "context" + "errors" + "fmt" + "github.com/zeromicro/go-zero/core/stores/sqlx" +) + +var _ NhTaskModel = (*customNhTaskModel)(nil) + +type ( + // NhTaskModel is an interface to be customized, add more methods here, + // and implement the added methods in customNhTaskModel. + NhTaskModel interface { + nhTaskModel + withSession(session sqlx.Session) NhTaskModel + FindTasksByCommunity(ctx context.Context, communityId uint) ([]*NhTask, error) + } + + customNhTaskModel struct { + *defaultNhTaskModel + } +) + +func (m *customNhTaskModel) FindTasksByCommunity(ctx context.Context, communityId uint) ([]*NhTask, error) { + query := fmt.Sprintf("select %s from %s where community_id = ?", nhTaskRows, m.table) + var tasks []*NhTask + err := m.conn.QueryRowsCtx(ctx, &tasks, query, communityId) + if err != nil && !errors.Is(err, sqlx.ErrNotFound) { + return nil, err + } + return tasks, nil +} + +// NewNhTaskModel returns a model for the database table. +func NewNhTaskModel(conn sqlx.SqlConn) NhTaskModel { + return &customNhTaskModel{ + defaultNhTaskModel: newNhTaskModel(conn), + } +} + +func (m *customNhTaskModel) withSession(session sqlx.Session) NhTaskModel { + return NewNhTaskModel(sqlx.NewSqlConnFromSession(session)) +} diff --git a/internal/model/nh_task_model_gen.go b/internal/model/nh_task_model_gen.go new file mode 100755 index 0000000..acf48a3 --- /dev/null +++ b/internal/model/nh_task_model_gen.go @@ -0,0 +1,97 @@ +// Code generated by goctl. DO NOT EDIT. +// versions: +// goctl version: 1.7.3 + +package model + +import ( + "context" + "database/sql" + "fmt" + "strings" + "time" + + "github.com/zeromicro/go-zero/core/stores/builder" + "github.com/zeromicro/go-zero/core/stores/sqlx" + "github.com/zeromicro/go-zero/core/stringx" +) + +var ( + nhTaskFieldNames = builder.RawFieldNames(&NhTask{}) + nhTaskRows = strings.Join(nhTaskFieldNames, ",") + nhTaskRowsExpectAutoSet = strings.Join(stringx.Remove(nhTaskFieldNames, "`id`", "`create_at`", "`create_time`", "`created_at`", "`update_at`", "`update_time`", "`updated_at`"), ",") + nhTaskRowsWithPlaceHolder = strings.Join(stringx.Remove(nhTaskFieldNames, "`id`", "`create_at`", "`create_time`", "`created_at`", "`update_at`", "`update_time`", "`updated_at`"), "=?,") + "=?" +) + +type ( + nhTaskModel interface { + Insert(ctx context.Context, data *NhTask) (sql.Result, error) + FindOne(ctx context.Context, id uint) (*NhTask, error) + Update(ctx context.Context, data *NhTask) error + Delete(ctx context.Context, id uint) error + } + + defaultNhTaskModel struct { + conn sqlx.SqlConn + table string + } + + NhTask struct { + Id uint `db:"id"` + CommunityId uint `db:"community_id"` // 合作社区ID,0=官方平台 + Title string `db:"title"` // 大标题 + SubTitle string `db:"sub_title"` // 子标题 + Description string `db:"description"` // 描述 + Points int `db:"points"` // 积分数量 + ButtonText string `db:"button_text"` // 按钮上的文字 + Type int8 `db:"type"` // 0=一次性任务,1=每天任务 + Status int8 `db:"status"` // 0=不启用,1=启用 + StartAt sql.NullTime `db:"start_at"` // 开始时间 + EndAt sql.NullTime `db:"end_at"` // 结束时间 + CreatedAt time.Time `db:"created_at"` // 创建时间 + UpdatedAt time.Time `db:"updated_at"` // 修改时间 + } +) + +func newNhTaskModel(conn sqlx.SqlConn) *defaultNhTaskModel { + return &defaultNhTaskModel{ + conn: conn, + table: "`nh_task`", + } +} + +func (m *defaultNhTaskModel) Delete(ctx context.Context, id uint) error { + query := fmt.Sprintf("delete from %s where `id` = ?", m.table) + _, err := m.conn.ExecCtx(ctx, query, id) + return err +} + +func (m *defaultNhTaskModel) FindOne(ctx context.Context, id uint) (*NhTask, error) { + query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", nhTaskRows, m.table) + var resp NhTask + err := m.conn.QueryRowCtx(ctx, &resp, query, id) + switch err { + case nil: + return &resp, nil + case sqlx.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *defaultNhTaskModel) Insert(ctx context.Context, data *NhTask) (sql.Result, error) { + query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, nhTaskRowsExpectAutoSet) + ret, err := m.conn.ExecCtx(ctx, query, data.CommunityId, data.Title, data.SubTitle, data.Description, data.Points, data.ButtonText, data.Type, data.Status, data.StartAt, data.EndAt) + return ret, err +} + +func (m *defaultNhTaskModel) Update(ctx context.Context, data *NhTask) error { + query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, nhTaskRowsWithPlaceHolder) + _, err := m.conn.ExecCtx(ctx, query, data.CommunityId, data.Title, data.SubTitle, data.Description, data.Points, data.ButtonText, data.Type, data.Status, data.StartAt, data.EndAt, data.Id) + return err +} + +func (m *defaultNhTaskModel) tableName() string { + return m.table +} diff --git a/internal/model/vars.go b/internal/model/vars.go new file mode 100644 index 0000000..69ca814 --- /dev/null +++ b/internal/model/vars.go @@ -0,0 +1,5 @@ +package model + +import "github.com/zeromicro/go-zero/core/stores/sqlx" + +var ErrNotFound = sqlx.ErrNotFound diff --git a/internal/pkg/encrypt/jwt/jwt.go b/internal/pkg/encrypt/jwt/jwt.go new file mode 100644 index 0000000..9b7739b --- /dev/null +++ b/internal/pkg/encrypt/jwt/jwt.go @@ -0,0 +1,106 @@ +package jwt + +import ( + "errors" + "fmt" + jwt "github.com/golang-jwt/jwt/v4" + "github.com/spf13/cast" + "github.com/zeromicro/go-zero/rest/token" + "net/http" + "time" +) + +type Config struct { + AccessSecret string `json:",default=ac2d27613e131be6286c0eb17139293d"` + AccessExpire time.Duration `json:",default=24h"` +} + +type TokenBuilder struct { + config Config +} + +func NewTokenBuilder(config Config) *TokenBuilder { + return &TokenBuilder{config: config} +} + +// GenerateToken 生成token +func (b *TokenBuilder) GenerateToken(kvs map[string]any) (string, int64, error) { + // 创建一个新的 Token + tok := jwt.New(jwt.SigningMethodHS256) + // 设置 Token 的声明(Payload) + claims := tok.Claims.(jwt.MapClaims) + for k, v := range kvs { + claims[k] = v + } + expiredAt := time.Now().Add(b.config.AccessExpire).Unix() + claims["exp"] = expiredAt // 设置过期时间 + + // 使用密钥签名 Token + tkStr, err := tok.SignedString([]byte(b.config.AccessSecret)) + return tkStr, expiredAt, err +} + +func (b *TokenBuilder) ParseUidFromToken(tokenStr string) (string, string, string, int64, error) { + r := &http.Request{Header: http.Header{}} + r.Header.Set("Authorization", fmt.Sprintf("Bearer %s", tokenStr)) + parser := token.NewTokenParser() + tok, err := parser.ParseToken(r, b.config.AccessSecret, "") + if err != nil { + return "", "", "", 0, err + } + if !tok.Valid { + return "", "", "", 0, errors.New("token is invalid") + } + var appid, userId, tgId string + var expiredAt int64 + if claims, ok := tok.Claims.(jwt.MapClaims); ok { + if uid, ok := claims["uid"]; ok { + userId = cast.ToString(uid) + } + if aid, ok := claims["app_id"]; ok { + appid = cast.ToString(aid) + } + if tid, ok := claims["tg_id"]; ok { + tgId = cast.ToString(tid) + } + if exp, ok := claims["exp"]; ok { + expiredAt = cast.ToInt64(exp) + } + } + return appid, userId, tgId, expiredAt, nil +} + +//// ParseUid 解析出uid +//func (b *TokenBuilder) ParseUid(r *http.Request) (string, error) { +// parser := token.NewTokenParser() +// tok, err := parser.ParseToken(r, b.config.AccessSecret, "") +// if err != nil { +// return "", err +// } +// if !tok.Valid { +// return "", errors.New("token is invalid") +// } +// if claims, ok := tok.Claims.(jwt.MapClaims); ok { +// if uid, ok := claims["uid"]; ok { +// return cast.ToString(uid), nil +// } +// } +// return "", errors.New("token not exist uid") +//} + +//func ParseUid(r *http.Request, accessSecret string) (string, error) { +// parser := token.NewTokenParser() +// tok, err := parser.ParseToken(r, accessSecret, "") +// if err != nil { +// return "", err +// } +// if !tok.Valid { +// return "", errors.New("token is invalid") +// } +// if claims, ok := tok.Claims.(jwt.MapClaims); ok { +// if uid, ok := claims["uid"]; ok { +// return cast.ToString(uid), nil +// } +// } +// return "", errors.New("token not exist uid") +//} diff --git a/internal/pkg/encrypt/md5/md5.go b/internal/pkg/encrypt/md5/md5.go new file mode 100644 index 0000000..506198c --- /dev/null +++ b/internal/pkg/encrypt/md5/md5.go @@ -0,0 +1,31 @@ +package md5 + +import ( + "crypto/md5" + "encoding/hex" + "fmt" +) + +func Md5(src []byte) string { + has := md5.Sum(src) + md5Str := hex.EncodeToString(has[:]) + return md5Str +} + +func Md5str(src string) string { + return Md5([]byte(src)) +} + +func Md516(src string) string { + data := []byte(src) + has := md5.Sum(data) + md5Str := hex.EncodeToString(has[:]) + return md5Str[8:24] +} + +func Md516Upper(src string) string { + data := []byte(src) + has := md5.Sum(data) + md5Str := fmt.Sprintf("%X", has) + return md5Str[8:24] +} diff --git a/internal/pkg/encrypt/sha/sha.go b/internal/pkg/encrypt/sha/sha.go new file mode 100644 index 0000000..7c5b6c2 --- /dev/null +++ b/internal/pkg/encrypt/sha/sha.go @@ -0,0 +1,30 @@ +package sha + +import ( + "crypto/hmac" + "crypto/sha1" + "crypto/sha256" + "encoding/base64" + "encoding/hex" +) + +func Sha1(src string) string { + h := sha1.New() + h.Write([]byte(src)) + sh := hex.EncodeToString(h.Sum(nil)) + return sh +} + +func Sha256(src string) string { + m := sha256.New() + m.Write([]byte(src)) + res := hex.EncodeToString(m.Sum(nil)) + return res +} + +func HmacSha256Base64(src, secret string) string { + h := hmac.New(sha256.New, []byte(secret)) + h.Write([]byte(src)) + sign := base64.StdEncoding.EncodeToString(h.Sum(nil)) + return sign +} diff --git a/internal/pkg/errs/error.go b/internal/pkg/errs/error.go new file mode 100644 index 0000000..c3197c8 --- /dev/null +++ b/internal/pkg/errs/error.go @@ -0,0 +1,40 @@ +package errs + +import ( + "fmt" + "github.com/spf13/cast" +) + +type err struct { + code int + reason Reason + msg string +} + +func New(code int, reason Reason, message any) error { + return err{ + code: code, + reason: reason, + msg: cast.ToString(message), + } +} + +// Error error +func (e err) Error() string { + return fmt.Sprintf("code=%d msg=%s", e.code, e.msg) +} + +// Code return code +func (e err) Code() int { + return e.code +} + +// Reason return reason +func (e err) Reason() Reason { + return e.reason +} + +// Message return message +func (e err) Message() string { + return e.msg +} diff --git a/internal/pkg/errs/errors.go b/internal/pkg/errs/errors.go new file mode 100644 index 0000000..169c930 --- /dev/null +++ b/internal/pkg/errs/errors.go @@ -0,0 +1,56 @@ +package errs + +import ( + "net/http" +) + +func Success() error { + return New(http.StatusOK, ErrSucceed, "success") +} + +func NotFound(reason Reason, v any) error { + return New(http.StatusNotFound, reason, v) +} + +func BadRequest(reason Reason, v any) error { + return New(http.StatusBadRequest, reason, v) +} + +func InternalServer(reason Reason, v any) error { + return New(http.StatusInternalServerError, reason, v) +} + +// Unauthorized new Unauthorized error that is mapped to a 401 response. +func Unauthorized(reason Reason, v any) error { + return New(http.StatusUnauthorized, reason, v) +} + +// Forbidden new Forbidden error that is mapped to a 403 response. +func Forbidden(reason Reason, v any) error { + return New(http.StatusForbidden, reason, v) +} + +// Conflict new Conflict error that is mapped to a 409 response. +func Conflict(reason Reason, v any) error { + return New(http.StatusConflict, reason, v) +} + +// ServiceUnavailable new ServiceUnavailable error that is mapped to an HTTP 503 response. +func ServiceUnavailable(reason Reason, v any) error { + return New(http.StatusServiceUnavailable, reason, v) +} + +// GatewayTimeout new GatewayTimeout error that is mapped to an HTTP 504 response. +func GatewayTimeout(reason Reason, v any) error { + return New(http.StatusGatewayTimeout, reason, v) +} + +// BadGateway new BadGateway error that is mapped to an HTTP 504 response. +func BadGateway(reason Reason, v any) error { + return New(http.StatusBadGateway, reason, v) +} + +// ClientClosed new ClientClosed error that is mapped to an HTTP 499 response. +func ClientClosed(reason Reason, v any) error { + return New(499, reason, v) +} diff --git a/internal/pkg/errs/http_respone.go b/internal/pkg/errs/http_respone.go new file mode 100644 index 0000000..0f1ed37 --- /dev/null +++ b/internal/pkg/errs/http_respone.go @@ -0,0 +1,64 @@ +package errs + +import ( + "context" + "github.com/zeromicro/go-zero/rest/httpx" + "net/http" + "os" + "strings" +) + +var debug bool + +func init() { + if strings.ToLower(os.Getenv("VANS_API_DEBUG")) == "on" { + debug = true + } + httpx.SetErrorHandlerCtx(ErrorHandleCtx) + httpx.SetErrorHandler(ErrorHandle) +} + +func SetDebug(d bool) { + debug = d +} + +func ErrorHandle(err error) (int, any) { + return ErrorHandleCtx(context.Background(), err) +} + +func ErrorHandleCtx(ctx context.Context, err error) (int, any) { + code := http.StatusBadRequest + reason := ErrInternalServer + var msg string + if ec, ok := err.(interface{ Code() int }); ok { + code = ec.Code() + } + if ec, ok := err.(interface{ Message() string }); ok { + msg = ec.Message() + } else { + msg = err.Error() + } + if ec, ok := err.(interface{ Reason() Reason }); ok { + reason = ec.Reason() + } + + var errMsg string + if reason < ErrUnknownLogicError && reason != ErrSucceed { + errMsg = msg + msg = "system error" + } + + body := map[string]any{ + "code": reason, + "message": msg, + } + if errMsg != "" && debug { + body["err"] = errMsg + } + if ec, ok := err.(interface{ Metadata() any }); ok { + if md := ec.Metadata(); md != nil { + body["metadata"] = md + } + } + return code, body +} diff --git a/internal/pkg/errs/reason.go b/internal/pkg/errs/reason.go new file mode 100644 index 0000000..c5a7b19 --- /dev/null +++ b/internal/pkg/errs/reason.go @@ -0,0 +1,33 @@ +package errs + +type Reason int + +const ( + // ======= 系统错误:0~999 ======= + ErrUnknownReason Reason = 0 // 未知错误 + ErrSucceed Reason = 200 // 成功 + ErrOverload Reason = 403 // 请求超载 + + // ======= 服务器内部错误:1000~9999 ======= + ErrInternalServer Reason = 1000 // 未知的服务器内部错误 + ErrDatabaseOperate Reason = 1001 // 数据库错误 + ErrRedisOperate Reason = 1002 // redis错误 + ErrEncodePassword Reason = 1003 // 密码加密错误 + ErrGenerateUUid Reason = 1004 // 生成uuid错误 + ErrGenerateToken Reason = 1005 // 生成token错误 + ErrGetExchangeRate Reason = 1005 // 获取汇率错误 + + // ======= 业务层错误:10000~99999 ======= + ErrUnknownLogicError Reason = 10000 // 未知的业务错误 + ErrInvalidParam Reason = 10001 // 无效参数错误 + ErrInvalidSignature Reason = 10002 // 无效签名错误 + ErrInvalidToken Reason = 10003 // 无效的token + ErrInvoiceHasPaid Reason = 10004 // 订单已支付 + ErrInvalidAppId Reason = 10005 // 应用id无效 + ErrUserNotHasInviter Reason = 10006 // 用户没有邀请人 + + // ========= admin 业务相关错误码: 30000~39999 ========= + ErrUnknownAdminError Reason = 30000 // 未知的admin错误 + ErrInvalidPassword Reason = 30001 // 无效密码 + ErrInvalidAccount Reason = 30002 // 无效账号 +) diff --git a/internal/svc/service_context.go b/internal/svc/service_context.go new file mode 100644 index 0000000..a9e2ffd --- /dev/null +++ b/internal/svc/service_context.go @@ -0,0 +1,18 @@ +package svc + +import ( + "nova_task/internal/config" + "nova_task/internal/model" +) + +type ServiceContext struct { + Config config.Config + TaskModel model.NhTaskModel +} + +func NewServiceContext(c config.Config) *ServiceContext { + return &ServiceContext{ + Config: c, + TaskModel: model.NewNhTaskModel(c.MySql.Conn()), + } +} diff --git a/internal/types/types.go b/internal/types/types.go new file mode 100644 index 0000000..7a24bf1 --- /dev/null +++ b/internal/types/types.go @@ -0,0 +1,33 @@ +// Code generated by goctl. DO NOT EDIT. +// goctl 1.7.3 + +package types + +type GetTaskListReq struct { + CommunityId uint `form:"community_id"` +} + +type GetTaskListResp struct { + Tasks []Task `json:"tasks"` +} + +type GetTaskRewardReq struct { + ID uint `path:"id"` +} + +type GetTaskRewardResp struct { + Points int `json:"points"` +} + +type Task struct { + Id uint `json:"id"` + Title string `json:"title"` + SubTitle string `json:"sub_title"` + Description string `json:"description"` + Points int `json:"points"` + ButtonText string `json:"button_text"` + Type int8 `json:"type"` + StartAt string `json:"start_at"` + EndAt string `json:"end_at"` + Status int8 `json:"status"` +} diff --git a/novatask.go b/novatask.go new file mode 100644 index 0000000..30949a6 --- /dev/null +++ b/novatask.go @@ -0,0 +1,32 @@ +package main + +import ( + "flag" + "fmt" + + "nova_task/internal/config" + "nova_task/internal/handler" + "nova_task/internal/svc" + + "github.com/zeromicro/go-zero/core/conf" + "github.com/zeromicro/go-zero/rest" +) + +var configFile = flag.String("f", "etc/novatask.yaml", "the config file") + +func main() { + flag.Parse() + + var c config.Config + conf.MustLoad(*configFile, &c) + c.MustSetUp() + + server := rest.MustNewServer(c.RestConf) + defer server.Stop() + + ctx := svc.NewServiceContext(c) + handler.RegisterHandlers(server, ctx) + + fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port) + server.Start() +}