From 08b67dba70021fc61904e9ddc4697644e987c18e Mon Sep 17 00:00:00 2001 From: zzw <1464003642@qq.com> Date: Tue, 22 Sep 2026 16:47:38 +0800 Subject: [PATCH] feat: ota --- bff/bff.go | 3 +- bff/internal/dialer/nacos.go | 7 +- bff/internal/request/upload.go | 75 ++++ go.mod | 17 +- go.sum | 32 +- pkg/oss/oss.go | 174 +++++++++ rpc/chore/chore.pb | Bin 16373 -> 16748 bytes rpc/chore/chore.proto | 25 ++ rpc/chore/pb/chore.pb.go | 353 +++++++++++++----- rpc/chore/pb/chore_grpc.pb.go | 80 ++++ rpc/equipment/equipment.pb | Bin 18068 -> 18579 bytes rpc/equipment/equipment.proto | 11 + rpc/equipment/pb/equipment.pb.go | 116 ++++-- rpc/equipment/pb/equipment_grpc.pb.go | 40 +- services/chore/chore.go | 4 + services/chore/choreclient/chore.go | 19 + .../internal/logic/internalUploadLogic.go | 56 +++ services/chore/internal/logic/uploadLogic.go | 49 +++ services/chore/internal/server/choreServer.go | 12 + services/chore/validator/upload.go | 17 + .../equipment/equipmentclient/equipment.go | 9 +- services/equipment/internal/dao/version.go | 30 +- .../internal/logic/apiVersionLogic.go | 58 +++ .../internal/server/equipmentServer.go | 7 +- services/equipment/validator/version.go | 11 + services/sale/run.toml | 6 +- 26 files changed, 1033 insertions(+), 178 deletions(-) create mode 100644 bff/internal/request/upload.go create mode 100644 pkg/oss/oss.go create mode 100644 services/chore/internal/logic/internalUploadLogic.go create mode 100644 services/chore/internal/logic/uploadLogic.go create mode 100644 services/chore/validator/upload.go create mode 100644 services/equipment/internal/logic/apiVersionLogic.go diff --git a/bff/bff.go b/bff/bff.go index d0c6dfd..6165a89 100644 --- a/bff/bff.go +++ b/bff/bff.go @@ -56,7 +56,8 @@ func main() { gw := gateway.MustNewServer(c.GatewayConf, gateway.WithDialer(dialer.Nacos), - + + gateway.WithMiddleware(request.FormUploadMiddleware), // 权鉴+穿透 gateway.WithMiddleware(request.UserReadMiddleware), gateway.WithMiddleware(response.Wrap), diff --git a/bff/internal/dialer/nacos.go b/bff/internal/dialer/nacos.go index 9c5bdd4..fa30859 100644 --- a/bff/internal/dialer/nacos.go +++ b/bff/internal/dialer/nacos.go @@ -26,7 +26,12 @@ func Nacos(conf zrpc.RpcClientConf) zrpc.Client { return zrpc.MustNewClient(cliConf, zrpc.WithDialOption(grpc.WithTransportCredentials(insecure.NewCredentials())), zrpc.WithDialOption(grpc.WithUnaryInterceptor(request.UserClientInterceptor)), - zrpc.WithDialOption(grpc.WithDefaultCallOptions(grpc.WaitForReady(true))), + zrpc.WithDialOption(grpc.WithDefaultCallOptions( + grpc.WaitForReady(true), + // 上传文件经 base64 后可达 ~170MB+,需大于默认 4MB + grpc.MaxCallRecvMsgSize(256<<20), + grpc.MaxCallSendMsgSize(256<<20), + )), zrpc.WithDialOption(grpc.WithConnectParams(grpc.ConnectParams{ MinConnectTimeout: 2 * time.Second, })), diff --git a/bff/internal/request/upload.go b/bff/internal/request/upload.go new file mode 100644 index 0000000..9ad41ec --- /dev/null +++ b/bff/internal/request/upload.go @@ -0,0 +1,75 @@ +package request + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "io" + "lone-services/bff/internal/response" + "lone-services/pkg/oss" + "net/http" + "path/filepath" + "strings" +) + +const ( + ossUploadAPIPath = "/api/v3/chore/oss/upload" + formFileField = "file" + maxUploadMemory = oss.MaxUploadBytes +) + +func FormUploadMiddleware(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost || r.URL.Path != ossUploadAPIPath { + next(w, r) + return + } + + ct := r.Header.Get("Content-Type") + ctrl := &response.BaseController{} + if !strings.HasPrefix(strings.ToLower(ct), "multipart/form-data") { + ctrl.Error(w, response.Define(response.ErrorMissingParams, "请使用 multipart/form-data 上传,字段名为 file")) + return + } + + if err := r.ParseMultipartForm(int64(maxUploadMemory)); err != nil { + ctrl.Error(w, response.Define(response.ErrorMissingParams, "解析上传文件失败")) + return + } + file, header, err := r.FormFile(formFileField) + if err != nil { + ctrl.Error(w, response.Define(response.ErrorMissingParams, "请上传文件,字段名为 file")) + return + } + defer file.Close() + + content, err := io.ReadAll(io.LimitReader(file, int64(maxUploadMemory)+1)) + if err != nil || len(content) == 0 { + ctrl.Error(w, response.Define(response.ErrorMissingParams, "文件内容不能为空")) + return + } + if len(content) > maxUploadMemory { + ctrl.Error(w, response.Define(response.ErrorMissingParams, "文件过大,最大 128MB")) + return + } + + filename := filepath.Base(header.Filename) + if filename == "." || filename == "" { + filename = "file" + } + + payload, err := json.Marshal(map[string]string{ + "filename": filename, + "content": base64.StdEncoding.EncodeToString(content), + }) + if err != nil { + ctrl.Fail(w) + return + } + + r.Body = io.NopCloser(bytes.NewReader(payload)) + r.ContentLength = int64(len(payload)) + r.Header.Set("Content-Type", "application/json") + next(w, r) + } +} diff --git a/go.mod b/go.mod index 8b5686f..c2c7969 100644 --- a/go.mod +++ b/go.mod @@ -10,10 +10,11 @@ require ( github.com/alibabacloud-go/sts-20150401/v2 v2.1.0 github.com/alibabacloud-go/tea v1.5.3 github.com/alibabacloud-go/tea-utils/v2 v2.0.9 + github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible github.com/fsnotify/fsnotify v1.10.1 - github.com/gin-gonic/gin v1.12.0 github.com/go-playground/validator/v10 v10.30.3 github.com/golang-jwt/jwt/v4 v4.5.2 + github.com/google/uuid v1.6.0 github.com/json-iterator/go v1.1.12 github.com/mssola/user_agent v0.6.0 github.com/redis/go-redis/v9 v9.21.0 @@ -45,13 +46,9 @@ require ( github.com/aliyun/credentials-go v1.4.13 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bytedance/gopkg v0.1.3 // indirect - github.com/bytedance/sonic v1.15.0 // indirect - github.com/bytedance/sonic/loader v0.5.0 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/clbanning/mxj/v2 v2.7.0 // indirect - github.com/cloudwego/base64x v0.1.6 // indirect github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect @@ -63,7 +60,6 @@ require ( github.com/fullstorydev/grpcurl v1.9.3 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.13 // indirect - github.com/gin-contrib/sse v1.1.0 // indirect github.com/go-jose/go-jose/v4 v4.1.4 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -74,14 +70,11 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-sql-driver/mysql v1.10.0 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect - github.com/goccy/go-json v0.10.5 // indirect - github.com/goccy/go-yaml v1.19.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/cel-go v0.25.0 // indirect github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect github.com/grafana/pyroscope-go v1.3.0 // indirect github.com/grafana/pyroscope-go/godeltaprof v0.1.10 // indirect @@ -110,8 +103,6 @@ require ( github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.66.1 // indirect github.com/prometheus/procfs v0.16.1 // indirect - github.com/quic-go/qpack v0.6.0 // indirect - github.com/quic-go/quic-go v0.59.0 // indirect github.com/richardlehane/mscfb v1.0.7 // indirect github.com/richardlehane/msoleps v1.0.6 // indirect github.com/sagikazarmark/locafero v0.11.0 // indirect @@ -126,15 +117,12 @@ require ( github.com/tiendc/go-deepcopy v1.7.2 // indirect github.com/titanous/json5 v1.0.0 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect - github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.3.1 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xuri/efp v0.0.1 // indirect github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 // indirect go.etcd.io/etcd/api/v3 v3.5.21 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.21 // indirect go.etcd.io/etcd/client/v3 v3.5.21 // indirect - go.mongodb.org/mongo-driver/v2 v2.8.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/otel v1.44.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect @@ -152,7 +140,6 @@ require ( go.uber.org/multierr v1.9.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/arch v0.22.0 // indirect golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect golang.org/x/net v0.56.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect diff --git a/go.sum b/go.sum index 8d6846d..6f8a2eb 100644 --- a/go.sum +++ b/go.sum @@ -67,6 +67,8 @@ github.com/alibabacloud-go/tea-xml v1.1.2 h1:oLxa7JUXm2EDFzMg+7oRsYc+kutgCVwm+bZ github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= github.com/alicebob/miniredis/v2 v2.38.0 h1:nZAzCR+Lj+Vxk4ZXzm2NuKq2O33RXj1XxJ2e2uP9jiw= github.com/alicebob/miniredis/v2 v2.38.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM= +github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g= +github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0= github.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmPrib8NVePL3fxM= @@ -85,12 +87,6 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= -github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= -github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= -github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE= -github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k= -github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= -github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -101,8 +97,6 @@ github.com/clbanning/mxj/v2 v2.5.6/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= -github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik= github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4= @@ -135,10 +129,6 @@ github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sa github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= -github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= -github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= -github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8= -github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc= github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -169,10 +159,6 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= -github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= -github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= @@ -300,10 +286,6 @@ github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9Z github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= -github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= -github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= -github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= -github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= github.com/redis/go-redis/v9 v9.21.0 h1:FPBE4hhbAke+TLmcY3WkpbDffJEomdqPn3HYiqAtL9E= github.com/redis/go-redis/v9 v9.21.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA= github.com/richardlehane/mscfb v1.0.7 h1:oeoiM0WE79vHwE8RpIYYvIAc8ajTH2mb6UZm55/+EB0= @@ -350,8 +332,6 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= @@ -363,10 +343,6 @@ github.com/titanous/json5 v1.0.0/go.mod h1:7JH1M8/LHKc6cyP5o5g3CSaRj+mBrIimTxzpv github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= -github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= -github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= -github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8= @@ -391,8 +367,6 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.21 h1:lPBu71Y7osQmzlflM9OfeIV2JlmpBjqBNlLtcoB go.etcd.io/etcd/client/pkg/v3 v3.5.21/go.mod h1:BgqT/IXPjK9NkeSDjbzwsHySX3yIle2+ndz28nVsjUs= go.etcd.io/etcd/client/v3 v3.5.21 h1:T6b1Ow6fNjOLOtM0xSoKNQt1ASPCLWrF9XMHcH9pEyY= go.etcd.io/etcd/client/v3 v3.5.21/go.mod h1:mFYy67IOqmbRf/kRUvsHixzo3iG+1OF2W2+jVIQRAnU= -go.mongodb.org/mongo-driver/v2 v2.8.0 h1:CxWDGQYY8QQwNjAl/aq2sfWakdnWZynnqJ9F4DhHbP8= -go.mongodb.org/mongo-driver/v2 v2.8.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= @@ -433,8 +407,6 @@ go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI= -golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/pkg/oss/oss.go b/pkg/oss/oss.go new file mode 100644 index 0000000..1beb3bc --- /dev/null +++ b/pkg/oss/oss.go @@ -0,0 +1,174 @@ +package oss + +import ( + "bytes" + "fmt" + "path" + "regexp" + "strings" + "time" + + "lone-services/pkg/utils" + + alioss "github.com/aliyun/aliyun-oss-go-sdk/oss" +) + +const MaxUploadBytes = 128 << 20 // 128MB + +var unsafeFileChars = regexp.MustCompile(`[^\w.\-\p{Han}]+`) + +type Config struct { + AccessKeyId string + AccessKeySecret string + BucketName string + Endpoint string + Host string + Dir string // 固定前缀,如 test/chore/files/ +} + +type Result struct { + URL string `json:"url"` + Path string `json:"path"` // object key,如 test/chore/files/20260403/a.zip + Host string `json:"host"` +} + +type UploadInput struct { + Filename string + Content []byte + ContentType string +} + +func LoadConfig() Config { + cfg := Config{ + AccessKeyId: utils.GetConfigString("oss.accessKeyId"), + AccessKeySecret: utils.GetConfigString("oss.accessKeySecret"), + BucketName: utils.GetConfigString("oss.bucketName"), + Endpoint: utils.GetConfigString("oss.endpoint"), + Host: utils.GetConfigString("oss.host"), + Dir: utils.GetConfigString("oss.dir"), + } + if cfg.BucketName == utils.StringEmpty { + cfg.BucketName = "lone-images" + } + if cfg.Endpoint == utils.StringEmpty { + cfg.Endpoint = "https://oss-accelerate.aliyuncs.com" + } + if cfg.Host == utils.StringEmpty { + cfg.Host = "https://images.ailuowan.com" + } + if cfg.Dir == utils.StringEmpty { + cfg.Dir = "test/chore/files/" + } + if !strings.HasPrefix(cfg.Host, "http") { + cfg.Host = "https://" + cfg.Host + } + return cfg +} + +func (c Config) Validate() error { + if c.AccessKeyId == utils.StringEmpty || c.AccessKeySecret == utils.StringEmpty { + return fmt.Errorf("oss access key missing") + } + return nil +} + +func Upload(in UploadInput) (*Result, error) { + cfg := LoadConfig() + if err := cfg.Validate(); err != nil { + return nil, err + } + if len(in.Content) == 0 { + return nil, fmt.Errorf("empty file content") + } + if len(in.Content) > MaxUploadBytes { + return nil, fmt.Errorf("file too large, max %d bytes", MaxUploadBytes) + } + + objectKey := buildObjectKey(cfg.Dir, in.Filename) + endpoint := normalizeEndpoint(cfg.Endpoint, cfg.BucketName) + + client, err := alioss.New(endpoint, cfg.AccessKeyId, cfg.AccessKeySecret) + if err != nil { + return nil, fmt.Errorf("create oss client: %w", err) + } + bucket, err := client.Bucket(cfg.BucketName) + if err != nil { + return nil, fmt.Errorf("get oss bucket: %w", err) + } + + opts := make([]alioss.Option, 0, 1) + if ct := strings.TrimSpace(in.ContentType); ct != "" { + opts = append(opts, alioss.ContentType(ct)) + } + if err = bucket.PutObject(objectKey, bytes.NewReader(in.Content), opts...); err != nil { + return nil, fmt.Errorf("put object: %w", err) + } + + host := strings.TrimRight(cfg.Host, "/") + return &Result{ + URL: host + "/" + strings.TrimLeft(objectKey, "/"), + Path: objectKey, + Host: host, + }, nil +} + +func normalizeEndpoint(endpoint, bucket string) string { + ep := strings.TrimSpace(endpoint) + ep = strings.TrimPrefix(ep, "https://") + ep = strings.TrimPrefix(ep, "http://") + ep = strings.TrimSuffix(ep, "/") + prefix := bucket + "." + if bucket != "" && strings.HasPrefix(ep, prefix) { + ep = strings.TrimPrefix(ep, prefix) + } + return ep +} + +// buildObjectKey: {oss.dir}{YYYYMMDD}/{filename} +func buildObjectKey(configDir, filename string) string { + base := strings.Trim(sanitizeDir(configDir), "/") + if base != "" { + base += "/" + } + dateDir := time.Now().In(utils.AsiaShanghai).Format(utils.YMD) + name := sanitizeFilename(filename) + return base + dateDir + "/" + name +} + +func sanitizeDir(dir string) string { + dir = strings.TrimSpace(dir) + dir = strings.ReplaceAll(dir, "\\", "/") + dir = strings.Trim(dir, "/") + if dir == "" { + return "" + } + segs := strings.Split(dir, "/") + clean := make([]string, 0, len(segs)) + for _, s := range segs { + s = strings.TrimSpace(s) + if s == "" || s == "." || s == ".." { + continue + } + clean = append(clean, s) + } + return strings.Join(clean, "/") +} + +func sanitizeFilename(filename string) string { + name := path.Base(strings.ReplaceAll(filename, "\\", "/")) + name = strings.TrimSpace(name) + name = unsafeFileChars.ReplaceAllString(name, "_") + name = strings.Trim(name, "._") + if name == "" || name == "." || name == ".." { + return "file" + } + if len(name) > 200 { + ext := path.Ext(name) + base := strings.TrimSuffix(name, ext) + if len(base) > 180 { + base = base[:180] + } + name = base + ext + } + return name +} diff --git a/rpc/chore/chore.pb b/rpc/chore/chore.pb index 4657ddb21eee567aaaa6dc04811fffaf92368da7..c4213ff72fa9a17cffab8deae4364186f0966f83 100644 GIT binary patch delta 389 zcmexb|E7s?gOe%K3W?1wrWY9N9l1C|3v%)kQ-V?pg`~JR(lT>W^AdAYB^VVLH8_Ld zd?5)g_T>D$lGMBs2_~QvPY{f&<2+RhFW#ku^NHBw44rXr_HdkV1+QKv0+$u^UfQt>{J|O|U z93>5$X diff --git a/rpc/chore/chore.proto b/rpc/chore/chore.proto index 131e684..df8b78a 100644 --- a/rpc/chore/chore.proto +++ b/rpc/chore/chore.proto @@ -11,6 +11,15 @@ service Chore { body: "*" }; } + // 对外:服务端直传 OSS + rpc Upload(UploadReq) returns (Response) { + option (google.api.http) = { + post: "/api/v3/chore/oss/upload" + body: "*" + }; + } + // 对内:服务间直传 OSS(无 HTTP 暴露) + rpc InternalUpload(InternalUploadReq) returns (InternalUploadData); rpc Test(TestReq) returns (Response) { option (google.api.http) = { post: "/api/v3/chore/test" @@ -120,6 +129,22 @@ message PolicyReq { string key = 1; } +message UploadReq { + string filename = 1; + bytes content = 2; +} + +message InternalUploadReq { + string filename = 1; + bytes content = 2; +} + +message InternalUploadData { + string url = 1; + string path = 2; + string host = 3; +} + message TestReq { int32 id = 1; repeated int64 ids = 2; diff --git a/rpc/chore/pb/chore.pb.go b/rpc/chore/pb/chore.pb.go index 5425d6b..1a880e4 100644 --- a/rpc/chore/pb/chore.pb.go +++ b/rpc/chore/pb/chore.pb.go @@ -394,6 +394,170 @@ func (x *PolicyReq) GetKey() string { return "" } +type UploadReq struct { + state protoimpl.MessageState `protogen:"open.v1"` + Filename string `protobuf:"bytes,1,opt,name=filename,proto3" json:"filename,omitempty"` // 由 BFF 从 form-data 文件名填入 + Content []byte `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"` // 由 BFF 从 form-data 文件内容填入 + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UploadReq) Reset() { + *x = UploadReq{} + mi := &file_chore_chore_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UploadReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UploadReq) ProtoMessage() {} + +func (x *UploadReq) ProtoReflect() protoreflect.Message { + mi := &file_chore_chore_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UploadReq.ProtoReflect.Descriptor instead. +func (*UploadReq) Descriptor() ([]byte, []int) { + return file_chore_chore_proto_rawDescGZIP(), []int{7} +} + +func (x *UploadReq) GetFilename() string { + if x != nil { + return x.Filename + } + return "" +} + +func (x *UploadReq) GetContent() []byte { + if x != nil { + return x.Content + } + return nil +} + +type InternalUploadReq struct { + state protoimpl.MessageState `protogen:"open.v1"` + Filename string `protobuf:"bytes,1,opt,name=filename,proto3" json:"filename,omitempty"` + Content []byte `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InternalUploadReq) Reset() { + *x = InternalUploadReq{} + mi := &file_chore_chore_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InternalUploadReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InternalUploadReq) ProtoMessage() {} + +func (x *InternalUploadReq) ProtoReflect() protoreflect.Message { + mi := &file_chore_chore_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InternalUploadReq.ProtoReflect.Descriptor instead. +func (*InternalUploadReq) Descriptor() ([]byte, []int) { + return file_chore_chore_proto_rawDescGZIP(), []int{8} +} + +func (x *InternalUploadReq) GetFilename() string { + if x != nil { + return x.Filename + } + return "" +} + +func (x *InternalUploadReq) GetContent() []byte { + if x != nil { + return x.Content + } + return nil +} + +type InternalUploadData struct { + state protoimpl.MessageState `protogen:"open.v1"` + Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` // 完整访问地址 + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` // OSS object key + Host string `protobuf:"bytes,3,opt,name=host,proto3" json:"host,omitempty"` // CDN/自定义域名 + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InternalUploadData) Reset() { + *x = InternalUploadData{} + mi := &file_chore_chore_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InternalUploadData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InternalUploadData) ProtoMessage() {} + +func (x *InternalUploadData) ProtoReflect() protoreflect.Message { + mi := &file_chore_chore_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InternalUploadData.ProtoReflect.Descriptor instead. +func (*InternalUploadData) Descriptor() ([]byte, []int) { + return file_chore_chore_proto_rawDescGZIP(), []int{9} +} + +func (x *InternalUploadData) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *InternalUploadData) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *InternalUploadData) GetHost() string { + if x != nil { + return x.Host + } + return "" +} + type TestReq struct { state protoimpl.MessageState `protogen:"open.v1"` Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` @@ -406,7 +570,7 @@ type TestReq struct { func (x *TestReq) Reset() { *x = TestReq{} - mi := &file_chore_chore_proto_msgTypes[7] + mi := &file_chore_chore_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -418,7 +582,7 @@ func (x *TestReq) String() string { func (*TestReq) ProtoMessage() {} func (x *TestReq) ProtoReflect() protoreflect.Message { - mi := &file_chore_chore_proto_msgTypes[7] + mi := &file_chore_chore_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -431,7 +595,7 @@ func (x *TestReq) ProtoReflect() protoreflect.Message { // Deprecated: Use TestReq.ProtoReflect.Descriptor instead. func (*TestReq) Descriptor() ([]byte, []int) { - return file_chore_chore_proto_rawDescGZIP(), []int{7} + return file_chore_chore_proto_rawDescGZIP(), []int{10} } func (x *TestReq) GetId() int32 { @@ -471,7 +635,7 @@ type DecryptMobileReq struct { func (x *DecryptMobileReq) Reset() { *x = DecryptMobileReq{} - mi := &file_chore_chore_proto_msgTypes[8] + mi := &file_chore_chore_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -483,7 +647,7 @@ func (x *DecryptMobileReq) String() string { func (*DecryptMobileReq) ProtoMessage() {} func (x *DecryptMobileReq) ProtoReflect() protoreflect.Message { - mi := &file_chore_chore_proto_msgTypes[8] + mi := &file_chore_chore_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -496,7 +660,7 @@ func (x *DecryptMobileReq) ProtoReflect() protoreflect.Message { // Deprecated: Use DecryptMobileReq.ProtoReflect.Descriptor instead. func (*DecryptMobileReq) Descriptor() ([]byte, []int) { - return file_chore_chore_proto_rawDescGZIP(), []int{8} + return file_chore_chore_proto_rawDescGZIP(), []int{11} } func (x *DecryptMobileReq) GetOriginMobile() string { @@ -515,7 +679,7 @@ type RegionsReq struct { func (x *RegionsReq) Reset() { *x = RegionsReq{} - mi := &file_chore_chore_proto_msgTypes[9] + mi := &file_chore_chore_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -527,7 +691,7 @@ func (x *RegionsReq) String() string { func (*RegionsReq) ProtoMessage() {} func (x *RegionsReq) ProtoReflect() protoreflect.Message { - mi := &file_chore_chore_proto_msgTypes[9] + mi := &file_chore_chore_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -540,7 +704,7 @@ func (x *RegionsReq) ProtoReflect() protoreflect.Message { // Deprecated: Use RegionsReq.ProtoReflect.Descriptor instead. func (*RegionsReq) Descriptor() ([]byte, []int) { - return file_chore_chore_proto_rawDescGZIP(), []int{9} + return file_chore_chore_proto_rawDescGZIP(), []int{12} } func (x *RegionsReq) GetParentId() int32 { @@ -559,7 +723,7 @@ type RegionsByIdsReq struct { func (x *RegionsByIdsReq) Reset() { *x = RegionsByIdsReq{} - mi := &file_chore_chore_proto_msgTypes[10] + mi := &file_chore_chore_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -571,7 +735,7 @@ func (x *RegionsByIdsReq) String() string { func (*RegionsByIdsReq) ProtoMessage() {} func (x *RegionsByIdsReq) ProtoReflect() protoreflect.Message { - mi := &file_chore_chore_proto_msgTypes[10] + mi := &file_chore_chore_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -584,7 +748,7 @@ func (x *RegionsByIdsReq) ProtoReflect() protoreflect.Message { // Deprecated: Use RegionsByIdsReq.ProtoReflect.Descriptor instead. func (*RegionsByIdsReq) Descriptor() ([]byte, []int) { - return file_chore_chore_proto_rawDescGZIP(), []int{10} + return file_chore_chore_proto_rawDescGZIP(), []int{13} } func (x *RegionsByIdsReq) GetIds() []int64 { @@ -604,7 +768,7 @@ type RegionItem struct { func (x *RegionItem) Reset() { *x = RegionItem{} - mi := &file_chore_chore_proto_msgTypes[11] + mi := &file_chore_chore_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -616,7 +780,7 @@ func (x *RegionItem) String() string { func (*RegionItem) ProtoMessage() {} func (x *RegionItem) ProtoReflect() protoreflect.Message { - mi := &file_chore_chore_proto_msgTypes[11] + mi := &file_chore_chore_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -629,7 +793,7 @@ func (x *RegionItem) ProtoReflect() protoreflect.Message { // Deprecated: Use RegionItem.ProtoReflect.Descriptor instead. func (*RegionItem) Descriptor() ([]byte, []int) { - return file_chore_chore_proto_rawDescGZIP(), []int{11} + return file_chore_chore_proto_rawDescGZIP(), []int{14} } func (x *RegionItem) GetId() int64 { @@ -655,7 +819,7 @@ type RegionsByIdsData struct { func (x *RegionsByIdsData) Reset() { *x = RegionsByIdsData{} - mi := &file_chore_chore_proto_msgTypes[12] + mi := &file_chore_chore_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -667,7 +831,7 @@ func (x *RegionsByIdsData) String() string { func (*RegionsByIdsData) ProtoMessage() {} func (x *RegionsByIdsData) ProtoReflect() protoreflect.Message { - mi := &file_chore_chore_proto_msgTypes[12] + mi := &file_chore_chore_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -680,7 +844,7 @@ func (x *RegionsByIdsData) ProtoReflect() protoreflect.Message { // Deprecated: Use RegionsByIdsData.ProtoReflect.Descriptor instead. func (*RegionsByIdsData) Descriptor() ([]byte, []int) { - return file_chore_chore_proto_rawDescGZIP(), []int{12} + return file_chore_chore_proto_rawDescGZIP(), []int{15} } func (x *RegionsByIdsData) GetItems() []*RegionItem { @@ -700,7 +864,7 @@ type SendSmsReq struct { func (x *SendSmsReq) Reset() { *x = SendSmsReq{} - mi := &file_chore_chore_proto_msgTypes[13] + mi := &file_chore_chore_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -712,7 +876,7 @@ func (x *SendSmsReq) String() string { func (*SendSmsReq) ProtoMessage() {} func (x *SendSmsReq) ProtoReflect() protoreflect.Message { - mi := &file_chore_chore_proto_msgTypes[13] + mi := &file_chore_chore_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -725,7 +889,7 @@ func (x *SendSmsReq) ProtoReflect() protoreflect.Message { // Deprecated: Use SendSmsReq.ProtoReflect.Descriptor instead. func (*SendSmsReq) Descriptor() ([]byte, []int) { - return file_chore_chore_proto_rawDescGZIP(), []int{13} + return file_chore_chore_proto_rawDescGZIP(), []int{16} } func (x *SendSmsReq) GetMobile() string { @@ -755,7 +919,7 @@ type InternalActionApplyLogCreateReq struct { func (x *InternalActionApplyLogCreateReq) Reset() { *x = InternalActionApplyLogCreateReq{} - mi := &file_chore_chore_proto_msgTypes[14] + mi := &file_chore_chore_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -767,7 +931,7 @@ func (x *InternalActionApplyLogCreateReq) String() string { func (*InternalActionApplyLogCreateReq) ProtoMessage() {} func (x *InternalActionApplyLogCreateReq) ProtoReflect() protoreflect.Message { - mi := &file_chore_chore_proto_msgTypes[14] + mi := &file_chore_chore_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -780,7 +944,7 @@ func (x *InternalActionApplyLogCreateReq) ProtoReflect() protoreflect.Message { // Deprecated: Use InternalActionApplyLogCreateReq.ProtoReflect.Descriptor instead. func (*InternalActionApplyLogCreateReq) Descriptor() ([]byte, []int) { - return file_chore_chore_proto_rawDescGZIP(), []int{14} + return file_chore_chore_proto_rawDescGZIP(), []int{17} } func (x *InternalActionApplyLogCreateReq) GetAdminId() int64 { @@ -827,7 +991,7 @@ type InternalActionApplyLogCreateData struct { func (x *InternalActionApplyLogCreateData) Reset() { *x = InternalActionApplyLogCreateData{} - mi := &file_chore_chore_proto_msgTypes[15] + mi := &file_chore_chore_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -839,7 +1003,7 @@ func (x *InternalActionApplyLogCreateData) String() string { func (*InternalActionApplyLogCreateData) ProtoMessage() {} func (x *InternalActionApplyLogCreateData) ProtoReflect() protoreflect.Message { - mi := &file_chore_chore_proto_msgTypes[15] + mi := &file_chore_chore_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -852,7 +1016,7 @@ func (x *InternalActionApplyLogCreateData) ProtoReflect() protoreflect.Message { // Deprecated: Use InternalActionApplyLogCreateData.ProtoReflect.Descriptor instead. func (*InternalActionApplyLogCreateData) Descriptor() ([]byte, []int) { - return file_chore_chore_proto_rawDescGZIP(), []int{15} + return file_chore_chore_proto_rawDescGZIP(), []int{18} } func (x *InternalActionApplyLogCreateData) GetId() int64 { @@ -875,7 +1039,7 @@ type SendPasswordReq struct { func (x *SendPasswordReq) Reset() { *x = SendPasswordReq{} - mi := &file_chore_chore_proto_msgTypes[16] + mi := &file_chore_chore_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -887,7 +1051,7 @@ func (x *SendPasswordReq) String() string { func (*SendPasswordReq) ProtoMessage() {} func (x *SendPasswordReq) ProtoReflect() protoreflect.Message { - mi := &file_chore_chore_proto_msgTypes[16] + mi := &file_chore_chore_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -900,7 +1064,7 @@ func (x *SendPasswordReq) ProtoReflect() protoreflect.Message { // Deprecated: Use SendPasswordReq.ProtoReflect.Descriptor instead. func (*SendPasswordReq) Descriptor() ([]byte, []int) { - return file_chore_chore_proto_rawDescGZIP(), []int{16} + return file_chore_chore_proto_rawDescGZIP(), []int{19} } func (x *SendPasswordReq) GetMobile() string { @@ -947,7 +1111,7 @@ type SendPasswordResp struct { func (x *SendPasswordResp) Reset() { *x = SendPasswordResp{} - mi := &file_chore_chore_proto_msgTypes[17] + mi := &file_chore_chore_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -959,7 +1123,7 @@ func (x *SendPasswordResp) String() string { func (*SendPasswordResp) ProtoMessage() {} func (x *SendPasswordResp) ProtoReflect() protoreflect.Message { - mi := &file_chore_chore_proto_msgTypes[17] + mi := &file_chore_chore_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -972,7 +1136,7 @@ func (x *SendPasswordResp) ProtoReflect() protoreflect.Message { // Deprecated: Use SendPasswordResp.ProtoReflect.Descriptor instead. func (*SendPasswordResp) Descriptor() ([]byte, []int) { - return file_chore_chore_proto_rawDescGZIP(), []int{17} + return file_chore_chore_proto_rawDescGZIP(), []int{20} } func (x *SendPasswordResp) GetCode() int32 { @@ -1009,7 +1173,17 @@ const file_chore_chore_proto_rawDesc = "" + "\x04type\x18\x01 \x01(\x05R\x04type\x12\x18\n" + "\acontent\x18\x02 \x01(\tR\acontent\"\x1d\n" + "\tPolicyReq\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\"W\n" + + "\x03key\x18\x01 \x01(\tR\x03key\"A\n" + + "\tUploadReq\x12\x1a\n" + + "\bfilename\x18\x01 \x01(\tR\bfilename\x12\x18\n" + + "\acontent\x18\x02 \x01(\fR\acontent\"I\n" + + "\x11InternalUploadReq\x12\x1a\n" + + "\bfilename\x18\x01 \x01(\tR\bfilename\x12\x18\n" + + "\acontent\x18\x02 \x01(\fR\acontent\"N\n" + + "\x12InternalUploadData\x12\x10\n" + + "\x03url\x18\x01 \x01(\tR\x03url\x12\x12\n" + + "\x04path\x18\x02 \x01(\tR\x04path\x12\x12\n" + + "\x04host\x18\x03 \x01(\tR\x04host\"W\n" + "\aTestReq\x12\x0e\n" + "\x02id\x18\x01 \x01(\x05R\x02id\x12\x10\n" + "\x03ids\x18\x02 \x03(\x03R\x03ids\x12\x16\n" + @@ -1047,9 +1221,11 @@ const file_chore_chore_proto_rawDesc = "" + "\x02ip\x18\x04 \x01(\tR\x02ip\x12!\n" + "\fbrowser_name\x18\x05 \x01(\tR\vbrowserName\"&\n" + "\x10SendPasswordResp\x12\x12\n" + - "\x04code\x18\x01 \x01(\x05R\x04code2\x9b\v\n" + + "\x04code\x18\x01 \x01(\x05R\x04code2\xb4\f\n" + "\x05Chore\x12P\n" + - "\x06Policy\x12\x10.chore.PolicyReq\x1a\x0f.chore.Response\"#\x82\xd3\xe4\x93\x02\x1d:\x01*\x12\x18/api/v3/chore/oss/policy\x12F\n" + + "\x06Policy\x12\x10.chore.PolicyReq\x1a\x0f.chore.Response\"#\x82\xd3\xe4\x93\x02\x1d:\x01*\x12\x18/api/v3/chore/oss/policy\x12P\n" + + "\x06Upload\x12\x10.chore.UploadReq\x1a\x0f.chore.Response\"#\x82\xd3\xe4\x93\x02\x1d:\x01*\"\x18/api/v3/chore/oss/upload\x12E\n" + + "\x0eInternalUpload\x12\x18.chore.InternalUploadReq\x1a\x19.chore.InternalUploadData\x12F\n" + "\x04Test\x12\x0e.chore.TestReq\x1a\x0f.chore.Response\"\x1d\x82\xd3\xe4\x93\x02\x17:\x01*\"\x12/api/v3/chore/test\x12_\n" + "\rDecryptMobile\x12\x17.chore.DecryptMobileReq\x1a\x0f.chore.Response\"$\x82\xd3\xe4\x93\x02\x1e\x12\x1c/api/v3/chore/decrypt/mobile\x12L\n" + "\aRegions\x12\x11.chore.RegionsReq\x1a\x0f.chore.Response\"\x1d\x82\xd3\xe4\x93\x02\x17\x12\x15/api/v3/chore/regions\x12J\n" + @@ -1083,7 +1259,7 @@ func file_chore_chore_proto_rawDescGZIP() []byte { return file_chore_chore_proto_rawDescData } -var file_chore_chore_proto_msgTypes = make([]protoimpl.MessageInfo, 18) +var file_chore_chore_proto_msgTypes = make([]protoimpl.MessageInfo, 21) var file_chore_chore_proto_goTypes = []any{ (*Response)(nil), // 0: chore.Response (*EmptyReq)(nil), // 1: chore.EmptyReq @@ -1092,56 +1268,63 @@ var file_chore_chore_proto_goTypes = []any{ (*EditReq)(nil), // 4: chore.EditReq (*SystemEditReq)(nil), // 5: chore.SystemEditReq (*PolicyReq)(nil), // 6: chore.PolicyReq - (*TestReq)(nil), // 7: chore.TestReq - (*DecryptMobileReq)(nil), // 8: chore.DecryptMobileReq - (*RegionsReq)(nil), // 9: chore.RegionsReq - (*RegionsByIdsReq)(nil), // 10: chore.RegionsByIdsReq - (*RegionItem)(nil), // 11: chore.RegionItem - (*RegionsByIdsData)(nil), // 12: chore.RegionsByIdsData - (*SendSmsReq)(nil), // 13: chore.SendSmsReq - (*InternalActionApplyLogCreateReq)(nil), // 14: chore.InternalActionApplyLogCreateReq - (*InternalActionApplyLogCreateData)(nil), // 15: chore.InternalActionApplyLogCreateData - (*SendPasswordReq)(nil), // 16: chore.SendPasswordReq - (*SendPasswordResp)(nil), // 17: chore.SendPasswordResp + (*UploadReq)(nil), // 7: chore.UploadReq + (*InternalUploadReq)(nil), // 8: chore.InternalUploadReq + (*InternalUploadData)(nil), // 9: chore.InternalUploadData + (*TestReq)(nil), // 10: chore.TestReq + (*DecryptMobileReq)(nil), // 11: chore.DecryptMobileReq + (*RegionsReq)(nil), // 12: chore.RegionsReq + (*RegionsByIdsReq)(nil), // 13: chore.RegionsByIdsReq + (*RegionItem)(nil), // 14: chore.RegionItem + (*RegionsByIdsData)(nil), // 15: chore.RegionsByIdsData + (*SendSmsReq)(nil), // 16: chore.SendSmsReq + (*InternalActionApplyLogCreateReq)(nil), // 17: chore.InternalActionApplyLogCreateReq + (*InternalActionApplyLogCreateData)(nil), // 18: chore.InternalActionApplyLogCreateData + (*SendPasswordReq)(nil), // 19: chore.SendPasswordReq + (*SendPasswordResp)(nil), // 20: chore.SendPasswordResp } var file_chore_chore_proto_depIdxs = []int32{ - 11, // 0: chore.RegionsByIdsData.items:type_name -> chore.RegionItem + 14, // 0: chore.RegionsByIdsData.items:type_name -> chore.RegionItem 6, // 1: chore.Chore.Policy:input_type -> chore.PolicyReq - 7, // 2: chore.Chore.Test:input_type -> chore.TestReq - 8, // 3: chore.Chore.DecryptMobile:input_type -> chore.DecryptMobileReq - 9, // 4: chore.Chore.Regions:input_type -> chore.RegionsReq - 13, // 5: chore.Chore.SendSms:input_type -> chore.SendSmsReq - 1, // 6: chore.Chore.AnswerItems:input_type -> chore.EmptyReq - 3, // 7: chore.Chore.AnswerStatus:input_type -> chore.StatusReq - 2, // 8: chore.Chore.AnswerInfo:input_type -> chore.IdReq - 4, // 9: chore.Chore.AnswerCreate:input_type -> chore.EditReq - 4, // 10: chore.Chore.AnswerEdit:input_type -> chore.EditReq - 5, // 11: chore.Chore.SystemEdit:input_type -> chore.SystemEditReq - 3, // 12: chore.Chore.SystemStatus:input_type -> chore.StatusReq - 2, // 13: chore.Chore.SystemInfo:input_type -> chore.IdReq - 1, // 14: chore.Chore.SystemItems:input_type -> chore.EmptyReq - 10, // 15: chore.Chore.RegionsByIds:input_type -> chore.RegionsByIdsReq - 14, // 16: chore.Chore.InternalActionApplyLogCreate:input_type -> chore.InternalActionApplyLogCreateReq - 16, // 17: chore.Chore.SendPassword:input_type -> chore.SendPasswordReq - 0, // 18: chore.Chore.Policy:output_type -> chore.Response - 0, // 19: chore.Chore.Test:output_type -> chore.Response - 0, // 20: chore.Chore.DecryptMobile:output_type -> chore.Response - 0, // 21: chore.Chore.Regions:output_type -> chore.Response - 0, // 22: chore.Chore.SendSms:output_type -> chore.Response - 0, // 23: chore.Chore.AnswerItems:output_type -> chore.Response - 0, // 24: chore.Chore.AnswerStatus:output_type -> chore.Response - 0, // 25: chore.Chore.AnswerInfo:output_type -> chore.Response - 0, // 26: chore.Chore.AnswerCreate:output_type -> chore.Response - 0, // 27: chore.Chore.AnswerEdit:output_type -> chore.Response - 0, // 28: chore.Chore.SystemEdit:output_type -> chore.Response - 0, // 29: chore.Chore.SystemStatus:output_type -> chore.Response - 0, // 30: chore.Chore.SystemInfo:output_type -> chore.Response - 0, // 31: chore.Chore.SystemItems:output_type -> chore.Response - 12, // 32: chore.Chore.RegionsByIds:output_type -> chore.RegionsByIdsData - 15, // 33: chore.Chore.InternalActionApplyLogCreate:output_type -> chore.InternalActionApplyLogCreateData - 17, // 34: chore.Chore.SendPassword:output_type -> chore.SendPasswordResp - 18, // [18:35] is the sub-list for method output_type - 1, // [1:18] is the sub-list for method input_type + 7, // 2: chore.Chore.Upload:input_type -> chore.UploadReq + 8, // 3: chore.Chore.InternalUpload:input_type -> chore.InternalUploadReq + 10, // 4: chore.Chore.Test:input_type -> chore.TestReq + 11, // 5: chore.Chore.DecryptMobile:input_type -> chore.DecryptMobileReq + 12, // 6: chore.Chore.Regions:input_type -> chore.RegionsReq + 16, // 7: chore.Chore.SendSms:input_type -> chore.SendSmsReq + 1, // 8: chore.Chore.AnswerItems:input_type -> chore.EmptyReq + 3, // 9: chore.Chore.AnswerStatus:input_type -> chore.StatusReq + 2, // 10: chore.Chore.AnswerInfo:input_type -> chore.IdReq + 4, // 11: chore.Chore.AnswerCreate:input_type -> chore.EditReq + 4, // 12: chore.Chore.AnswerEdit:input_type -> chore.EditReq + 5, // 13: chore.Chore.SystemEdit:input_type -> chore.SystemEditReq + 3, // 14: chore.Chore.SystemStatus:input_type -> chore.StatusReq + 2, // 15: chore.Chore.SystemInfo:input_type -> chore.IdReq + 1, // 16: chore.Chore.SystemItems:input_type -> chore.EmptyReq + 13, // 17: chore.Chore.RegionsByIds:input_type -> chore.RegionsByIdsReq + 17, // 18: chore.Chore.InternalActionApplyLogCreate:input_type -> chore.InternalActionApplyLogCreateReq + 19, // 19: chore.Chore.SendPassword:input_type -> chore.SendPasswordReq + 0, // 20: chore.Chore.Policy:output_type -> chore.Response + 0, // 21: chore.Chore.Upload:output_type -> chore.Response + 9, // 22: chore.Chore.InternalUpload:output_type -> chore.InternalUploadData + 0, // 23: chore.Chore.Test:output_type -> chore.Response + 0, // 24: chore.Chore.DecryptMobile:output_type -> chore.Response + 0, // 25: chore.Chore.Regions:output_type -> chore.Response + 0, // 26: chore.Chore.SendSms:output_type -> chore.Response + 0, // 27: chore.Chore.AnswerItems:output_type -> chore.Response + 0, // 28: chore.Chore.AnswerStatus:output_type -> chore.Response + 0, // 29: chore.Chore.AnswerInfo:output_type -> chore.Response + 0, // 30: chore.Chore.AnswerCreate:output_type -> chore.Response + 0, // 31: chore.Chore.AnswerEdit:output_type -> chore.Response + 0, // 32: chore.Chore.SystemEdit:output_type -> chore.Response + 0, // 33: chore.Chore.SystemStatus:output_type -> chore.Response + 0, // 34: chore.Chore.SystemInfo:output_type -> chore.Response + 0, // 35: chore.Chore.SystemItems:output_type -> chore.Response + 15, // 36: chore.Chore.RegionsByIds:output_type -> chore.RegionsByIdsData + 18, // 37: chore.Chore.InternalActionApplyLogCreate:output_type -> chore.InternalActionApplyLogCreateData + 20, // 38: chore.Chore.SendPassword:output_type -> chore.SendPasswordResp + 20, // [20:39] is the sub-list for method output_type + 1, // [1:20] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name @@ -1158,7 +1341,7 @@ func file_chore_chore_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_chore_chore_proto_rawDesc), len(file_chore_chore_proto_rawDesc)), NumEnums: 0, - NumMessages: 18, + NumMessages: 21, NumExtensions: 0, NumServices: 1, }, diff --git a/rpc/chore/pb/chore_grpc.pb.go b/rpc/chore/pb/chore_grpc.pb.go index cad2e6b..84ed14f 100644 --- a/rpc/chore/pb/chore_grpc.pb.go +++ b/rpc/chore/pb/chore_grpc.pb.go @@ -20,6 +20,8 @@ const _ = grpc.SupportPackageIsVersion9 const ( Chore_Policy_FullMethodName = "/chore.Chore/Policy" + Chore_Upload_FullMethodName = "/chore.Chore/Upload" + Chore_InternalUpload_FullMethodName = "/chore.Chore/InternalUpload" Chore_Test_FullMethodName = "/chore.Chore/Test" Chore_DecryptMobile_FullMethodName = "/chore.Chore/DecryptMobile" Chore_Regions_FullMethodName = "/chore.Chore/Regions" @@ -43,6 +45,10 @@ const ( // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type ChoreClient interface { Policy(ctx context.Context, in *PolicyReq, opts ...grpc.CallOption) (*Response, error) + // 对外:服务端直传 OSS + Upload(ctx context.Context, in *UploadReq, opts ...grpc.CallOption) (*Response, error) + // 对内:服务间直传 OSS(无 HTTP 暴露) + InternalUpload(ctx context.Context, in *InternalUploadReq, opts ...grpc.CallOption) (*InternalUploadData, error) Test(ctx context.Context, in *TestReq, opts ...grpc.CallOption) (*Response, error) DecryptMobile(ctx context.Context, in *DecryptMobileReq, opts ...grpc.CallOption) (*Response, error) Regions(ctx context.Context, in *RegionsReq, opts ...grpc.CallOption) (*Response, error) @@ -79,6 +85,26 @@ func (c *choreClient) Policy(ctx context.Context, in *PolicyReq, opts ...grpc.Ca return out, nil } +func (c *choreClient) Upload(ctx context.Context, in *UploadReq, opts ...grpc.CallOption) (*Response, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Response) + err := c.cc.Invoke(ctx, Chore_Upload_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *choreClient) InternalUpload(ctx context.Context, in *InternalUploadReq, opts ...grpc.CallOption) (*InternalUploadData, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(InternalUploadData) + err := c.cc.Invoke(ctx, Chore_InternalUpload_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *choreClient) Test(ctx context.Context, in *TestReq, opts ...grpc.CallOption) (*Response, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Response) @@ -244,6 +270,10 @@ func (c *choreClient) SendPassword(ctx context.Context, in *SendPasswordReq, opt // for forward compatibility. type ChoreServer interface { Policy(context.Context, *PolicyReq) (*Response, error) + // 对外:服务端直传 OSS + Upload(context.Context, *UploadReq) (*Response, error) + // 对内:服务间直传 OSS(无 HTTP 暴露) + InternalUpload(context.Context, *InternalUploadReq) (*InternalUploadData, error) Test(context.Context, *TestReq) (*Response, error) DecryptMobile(context.Context, *DecryptMobileReq) (*Response, error) Regions(context.Context, *RegionsReq) (*Response, error) @@ -273,6 +303,12 @@ type UnimplementedChoreServer struct{} func (UnimplementedChoreServer) Policy(context.Context, *PolicyReq) (*Response, error) { return nil, status.Error(codes.Unimplemented, "method Policy not implemented") } +func (UnimplementedChoreServer) Upload(context.Context, *UploadReq) (*Response, error) { + return nil, status.Error(codes.Unimplemented, "method Upload not implemented") +} +func (UnimplementedChoreServer) InternalUpload(context.Context, *InternalUploadReq) (*InternalUploadData, error) { + return nil, status.Error(codes.Unimplemented, "method InternalUpload not implemented") +} func (UnimplementedChoreServer) Test(context.Context, *TestReq) (*Response, error) { return nil, status.Error(codes.Unimplemented, "method Test not implemented") } @@ -360,6 +396,42 @@ func _Chore_Policy_Handler(srv interface{}, ctx context.Context, dec func(interf return interceptor(ctx, in, info, handler) } +func _Chore_Upload_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UploadReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ChoreServer).Upload(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Chore_Upload_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ChoreServer).Upload(ctx, req.(*UploadReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Chore_InternalUpload_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InternalUploadReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ChoreServer).InternalUpload(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Chore_InternalUpload_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ChoreServer).InternalUpload(ctx, req.(*InternalUploadReq)) + } + return interceptor(ctx, in, info, handler) +} + func _Chore_Test_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(TestReq) if err := dec(in); err != nil { @@ -659,6 +731,14 @@ var Chore_ServiceDesc = grpc.ServiceDesc{ MethodName: "Policy", Handler: _Chore_Policy_Handler, }, + { + MethodName: "Upload", + Handler: _Chore_Upload_Handler, + }, + { + MethodName: "InternalUpload", + Handler: _Chore_InternalUpload_Handler, + }, { MethodName: "Test", Handler: _Chore_Test_Handler, diff --git a/rpc/equipment/equipment.pb b/rpc/equipment/equipment.pb index 23e1c8db2ae38c2df312b37a2895012ea22dbf44..5415bf1c29389b74969dba8638b3516b65a6ae87 100644 GIT binary patch delta 715 zcmbQz%Q$%=k8o36o!aHAlmnQ~wzS8bPE~u243FFJz*ZRGOxj zl3JWxlvz-cU!+%N2oi};%gjlQM+nCkrN$FzI(~@-OvZ7}r-rYI3ed7L0pcLvHeKjdXd4 z;zf*{LM(p%ey$QN?28z=h1kMegFM|LC7w@i)J&FvsSy$4_i^=e5Alc(2y%7vjBs_4 zXqluH2QxBGOMP><)-%TXZbqQ}0^NR6O^iaaK&BA8UO|#xazTL;ODqM0l{*7fdjb_BqUZt>m!NxUUh3w<`U)&e=hZepHO}JFRC44JbS%gW zODzILdr)d&X=-takPsJ3No7H*1fv3@25S(QJ$a3Z6thu>;N~DZZ4OnTXf7^#B+9#k{6TjXk<_J)s$m} z@Uk>BfI5I`{QUh~!Rmp!!(4+r-6G*SK58b*EMgQ9;`ed&a}V)|4+wH~^Nau+4>6=b zD{k@?Ep>>In}2CNXJlvNc)>2v?YB8kmz#-cE7Rs`eGNu5A4nQ%vM{Yz-RxqT#l>v2 jT43`Rdu equipment.ItemsRequest @@ -1562,29 +1620,31 @@ var file_equipment_equipment_proto_depIdxs = []int32{ 17, // 18: equipment.Equipment.ApiMaskUsedItems:input_type -> equipment.ApiMaskUsedItemsRequest 16, // 19: equipment.Equipment.ApiMaskStoreUse:input_type -> equipment.ApiMaskUseRequest 17, // 20: equipment.Equipment.ApiMaskStoreUseItems:input_type -> equipment.ApiMaskUsedItemsRequest - 0, // 21: equipment.Equipment.Items:output_type -> equipment.Response - 0, // 22: equipment.Equipment.Info:output_type -> equipment.Response - 0, // 23: equipment.Equipment.Create:output_type -> equipment.Response - 0, // 24: equipment.Equipment.Edit:output_type -> equipment.Response - 0, // 25: equipment.Equipment.Status:output_type -> equipment.Response - 0, // 26: equipment.Equipment.MaskItems:output_type -> equipment.Response - 0, // 27: equipment.Equipment.BeansItems:output_type -> equipment.Response - 0, // 28: equipment.Equipment.BindExpiry:output_type -> equipment.Response - 0, // 29: equipment.Equipment.ApiMaskAdd:output_type -> equipment.Response - 0, // 30: equipment.Equipment.ApiBindItems:output_type -> equipment.Response - 0, // 31: equipment.Equipment.ApiUnBind:output_type -> equipment.Response - 0, // 32: equipment.Equipment.ApiBindAdd:output_type -> equipment.Response - 0, // 33: equipment.Equipment.ApiBindExpiry:output_type -> equipment.Response - 0, // 34: equipment.Equipment.ApiBindExpiryGet:output_type -> equipment.Response - 0, // 35: equipment.Equipment.ApiBeansAdd:output_type -> equipment.Response - 0, // 36: equipment.Equipment.ApiBeansLast:output_type -> equipment.Response - 0, // 37: equipment.Equipment.ApiBeansItems:output_type -> equipment.Response - 0, // 38: equipment.Equipment.ApiMaskUse:output_type -> equipment.Response - 0, // 39: equipment.Equipment.ApiMaskUsedItems:output_type -> equipment.Response - 0, // 40: equipment.Equipment.ApiMaskStoreUse:output_type -> equipment.Response - 0, // 41: equipment.Equipment.ApiMaskStoreUseItems:output_type -> equipment.Response - 21, // [21:42] is the sub-list for method output_type - 0, // [0:21] is the sub-list for method input_type + 19, // 21: equipment.Equipment.ApiVersion:input_type -> equipment.ApiVersionRequest + 0, // 22: equipment.Equipment.Items:output_type -> equipment.Response + 0, // 23: equipment.Equipment.Info:output_type -> equipment.Response + 0, // 24: equipment.Equipment.Create:output_type -> equipment.Response + 0, // 25: equipment.Equipment.Edit:output_type -> equipment.Response + 0, // 26: equipment.Equipment.Status:output_type -> equipment.Response + 0, // 27: equipment.Equipment.MaskItems:output_type -> equipment.Response + 0, // 28: equipment.Equipment.BeansItems:output_type -> equipment.Response + 0, // 29: equipment.Equipment.BindExpiry:output_type -> equipment.Response + 0, // 30: equipment.Equipment.ApiMaskAdd:output_type -> equipment.Response + 0, // 31: equipment.Equipment.ApiBindItems:output_type -> equipment.Response + 0, // 32: equipment.Equipment.ApiUnBind:output_type -> equipment.Response + 0, // 33: equipment.Equipment.ApiBindAdd:output_type -> equipment.Response + 0, // 34: equipment.Equipment.ApiBindExpiry:output_type -> equipment.Response + 0, // 35: equipment.Equipment.ApiBindExpiryGet:output_type -> equipment.Response + 0, // 36: equipment.Equipment.ApiBeansAdd:output_type -> equipment.Response + 0, // 37: equipment.Equipment.ApiBeansLast:output_type -> equipment.Response + 0, // 38: equipment.Equipment.ApiBeansItems:output_type -> equipment.Response + 0, // 39: equipment.Equipment.ApiMaskUse:output_type -> equipment.Response + 0, // 40: equipment.Equipment.ApiMaskUsedItems:output_type -> equipment.Response + 0, // 41: equipment.Equipment.ApiMaskStoreUse:output_type -> equipment.Response + 0, // 42: equipment.Equipment.ApiMaskStoreUseItems:output_type -> equipment.Response + 0, // 43: equipment.Equipment.ApiVersion:output_type -> equipment.Response + 22, // [22:44] is the sub-list for method output_type + 0, // [0:22] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name @@ -1601,7 +1661,7 @@ func file_equipment_equipment_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_equipment_equipment_proto_rawDesc), len(file_equipment_equipment_proto_rawDesc)), NumEnums: 0, - NumMessages: 19, + NumMessages: 20, NumExtensions: 0, NumServices: 1, }, diff --git a/rpc/equipment/pb/equipment_grpc.pb.go b/rpc/equipment/pb/equipment_grpc.pb.go index 0cfe4c5..20cba85 100644 --- a/rpc/equipment/pb/equipment_grpc.pb.go +++ b/rpc/equipment/pb/equipment_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.2 -// - protoc v4.25.8 +// - protoc v3.19.4 // source: equipment/equipment.proto package equipment @@ -40,6 +40,7 @@ const ( Equipment_ApiMaskUsedItems_FullMethodName = "/equipment.Equipment/ApiMaskUsedItems" Equipment_ApiMaskStoreUse_FullMethodName = "/equipment.Equipment/ApiMaskStoreUse" Equipment_ApiMaskStoreUseItems_FullMethodName = "/equipment.Equipment/ApiMaskStoreUseItems" + Equipment_ApiVersion_FullMethodName = "/equipment.Equipment/ApiVersion" ) // EquipmentClient is the client API for Equipment service. @@ -68,6 +69,7 @@ type EquipmentClient interface { ApiMaskUsedItems(ctx context.Context, in *ApiMaskUsedItemsRequest, opts ...grpc.CallOption) (*Response, error) ApiMaskStoreUse(ctx context.Context, in *ApiMaskUseRequest, opts ...grpc.CallOption) (*Response, error) ApiMaskStoreUseItems(ctx context.Context, in *ApiMaskUsedItemsRequest, opts ...grpc.CallOption) (*Response, error) + ApiVersion(ctx context.Context, in *ApiVersionRequest, opts ...grpc.CallOption) (*Response, error) } type equipmentClient struct { @@ -288,6 +290,16 @@ func (c *equipmentClient) ApiMaskStoreUseItems(ctx context.Context, in *ApiMaskU return out, nil } +func (c *equipmentClient) ApiVersion(ctx context.Context, in *ApiVersionRequest, opts ...grpc.CallOption) (*Response, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Response) + err := c.cc.Invoke(ctx, Equipment_ApiVersion_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // EquipmentServer is the server API for Equipment service. // All implementations must embed UnimplementedEquipmentServer // for forward compatibility. @@ -314,6 +326,7 @@ type EquipmentServer interface { ApiMaskUsedItems(context.Context, *ApiMaskUsedItemsRequest) (*Response, error) ApiMaskStoreUse(context.Context, *ApiMaskUseRequest) (*Response, error) ApiMaskStoreUseItems(context.Context, *ApiMaskUsedItemsRequest) (*Response, error) + ApiVersion(context.Context, *ApiVersionRequest) (*Response, error) mustEmbedUnimplementedEquipmentServer() } @@ -387,6 +400,9 @@ func (UnimplementedEquipmentServer) ApiMaskStoreUse(context.Context, *ApiMaskUse func (UnimplementedEquipmentServer) ApiMaskStoreUseItems(context.Context, *ApiMaskUsedItemsRequest) (*Response, error) { return nil, status.Error(codes.Unimplemented, "method ApiMaskStoreUseItems not implemented") } +func (UnimplementedEquipmentServer) ApiVersion(context.Context, *ApiVersionRequest) (*Response, error) { + return nil, status.Error(codes.Unimplemented, "method ApiVersion not implemented") +} func (UnimplementedEquipmentServer) mustEmbedUnimplementedEquipmentServer() {} func (UnimplementedEquipmentServer) testEmbeddedByValue() {} @@ -786,6 +802,24 @@ func _Equipment_ApiMaskStoreUseItems_Handler(srv interface{}, ctx context.Contex return interceptor(ctx, in, info, handler) } +func _Equipment_ApiVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ApiVersionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EquipmentServer).ApiVersion(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Equipment_ApiVersion_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EquipmentServer).ApiVersion(ctx, req.(*ApiVersionRequest)) + } + return interceptor(ctx, in, info, handler) +} + // Equipment_ServiceDesc is the grpc.ServiceDesc for Equipment service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -877,6 +911,10 @@ var Equipment_ServiceDesc = grpc.ServiceDesc{ MethodName: "ApiMaskStoreUseItems", Handler: _Equipment_ApiMaskStoreUseItems_Handler, }, + { + MethodName: "ApiVersion", + Handler: _Equipment_ApiVersion_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "equipment/equipment.proto", diff --git a/services/chore/chore.go b/services/chore/chore.go index 3240076..0d98363 100644 --- a/services/chore/chore.go +++ b/services/chore/chore.go @@ -148,6 +148,10 @@ func main() { }) defer s.Stop() + s.AddOptions( + grpc.MaxRecvMsgSize(256<<20), + grpc.MaxSendMsgSize(256<<20), + ) s.AddUnaryInterceptors(validate.UnaryServerInterceptor(validate.MustNew())) logx.Infof("Starting rpc server at %s...", listenOn) diff --git a/services/chore/choreclient/chore.go b/services/chore/choreclient/chore.go index bae15b1..b8a7ac4 100644 --- a/services/chore/choreclient/chore.go +++ b/services/chore/choreclient/chore.go @@ -20,6 +20,8 @@ type ( IdReq = chore.IdReq InternalActionApplyLogCreateData = chore.InternalActionApplyLogCreateData InternalActionApplyLogCreateReq = chore.InternalActionApplyLogCreateReq + InternalUploadData = chore.InternalUploadData + InternalUploadReq = chore.InternalUploadReq PolicyReq = chore.PolicyReq RegionItem = chore.RegionItem RegionsByIdsData = chore.RegionsByIdsData @@ -32,9 +34,14 @@ type ( StatusReq = chore.StatusReq SystemEditReq = chore.SystemEditReq TestReq = chore.TestReq + UploadReq = chore.UploadReq Chore interface { Policy(ctx context.Context, in *PolicyReq, opts ...grpc.CallOption) (*Response, error) + // 对外:服务端直传 OSS + Upload(ctx context.Context, in *UploadReq, opts ...grpc.CallOption) (*Response, error) + // 对内:服务间直传 OSS(无 HTTP 暴露) + InternalUpload(ctx context.Context, in *InternalUploadReq, opts ...grpc.CallOption) (*InternalUploadData, error) Test(ctx context.Context, in *TestReq, opts ...grpc.CallOption) (*Response, error) DecryptMobile(ctx context.Context, in *DecryptMobileReq, opts ...grpc.CallOption) (*Response, error) Regions(ctx context.Context, in *RegionsReq, opts ...grpc.CallOption) (*Response, error) @@ -69,6 +76,18 @@ func (m *defaultChore) Policy(ctx context.Context, in *PolicyReq, opts ...grpc.C return client.Policy(ctx, in, opts...) } +// 对外:服务端直传 OSS +func (m *defaultChore) Upload(ctx context.Context, in *UploadReq, opts ...grpc.CallOption) (*Response, error) { + client := chore.NewChoreClient(m.cli.Conn()) + return client.Upload(ctx, in, opts...) +} + +// 对内:服务间直传 OSS(无 HTTP 暴露) +func (m *defaultChore) InternalUpload(ctx context.Context, in *InternalUploadReq, opts ...grpc.CallOption) (*InternalUploadData, error) { + client := chore.NewChoreClient(m.cli.Conn()) + return client.InternalUpload(ctx, in, opts...) +} + func (m *defaultChore) Test(ctx context.Context, in *TestReq, opts ...grpc.CallOption) (*Response, error) { client := chore.NewChoreClient(m.cli.Conn()) return client.Test(ctx, in, opts...) diff --git a/services/chore/internal/logic/internalUploadLogic.go b/services/chore/internal/logic/internalUploadLogic.go new file mode 100644 index 0000000..de35493 --- /dev/null +++ b/services/chore/internal/logic/internalUploadLogic.go @@ -0,0 +1,56 @@ +package logic + +import ( + "context" + "strings" + + "lone-services/pkg/oss" + "lone-services/pkg/utils" + "lone-services/pkg/validate" + chore "lone-services/rpc/chore/pb" + "lone-services/services/chore/internal/svc" + "lone-services/services/chore/validator" + + "github.com/zeromicro/go-zero/core/logx" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type InternalUploadLogic struct { + ctx context.Context + svcCtx *svc.ServiceContext + logx.Logger +} + +func NewInternalUploadLogic(ctx context.Context, svcCtx *svc.ServiceContext) *InternalUploadLogic { + return &InternalUploadLogic{ + ctx: ctx, + svcCtx: svcCtx, + Logger: logx.WithContext(ctx), + } +} + +// InternalUpload 对内:服务间直传 OSS(无 HTTP 暴露) +func (l *InternalUploadLogic) InternalUpload(in *chore.InternalUploadReq) (*chore.InternalUploadData, error) { + var req validator.UploadValidator + if msg := validate.ValidateFromProto(in, &req); msg != utils.StringEmpty { + return nil, status.Error(codes.InvalidArgument, msg) + } + + res, err := oss.Upload(oss.UploadInput{ + Filename: req.Filename, + Content: req.Content, + }) + if err != nil { + l.Errorf("internal oss upload: %v", err) + if strings.Contains(err.Error(), "empty file") || strings.Contains(err.Error(), "too large") { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + return nil, status.Error(codes.Internal, utils.Fail.Msg) + } + return &chore.InternalUploadData{ + Url: res.URL, + Path: res.Path, + Host: res.Host, + }, nil +} diff --git a/services/chore/internal/logic/uploadLogic.go b/services/chore/internal/logic/uploadLogic.go new file mode 100644 index 0000000..cbfcdba --- /dev/null +++ b/services/chore/internal/logic/uploadLogic.go @@ -0,0 +1,49 @@ +package logic + +import ( + "context" + "strings" + + "lone-services/pkg/oss" + "lone-services/pkg/utils" + "lone-services/pkg/validate" + chore "lone-services/rpc/chore/pb" + "lone-services/services/chore/internal/svc" + "lone-services/services/chore/validator" + + "github.com/zeromicro/go-zero/core/logx" +) + +type UploadLogic struct { + ctx context.Context + svcCtx *svc.ServiceContext + logx.Logger +} + +func NewUploadLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UploadLogic { + return &UploadLogic{ + ctx: ctx, + svcCtx: svcCtx, + Logger: logx.WithContext(ctx), + } +} + +func (l *UploadLogic) Upload(in *chore.UploadReq) (*chore.Response, error) { + var req validator.UploadValidator + if msg := validate.ValidateFromProto(in, &req); msg != utils.StringEmpty { + return outResponse(utils.ErrorParams, msg), nil + } + + res, err := oss.Upload(oss.UploadInput{ + Filename: req.Filename, + Content: req.Content, + }) + if err != nil { + l.Errorf("oss upload: %v", err) + if strings.Contains(err.Error(), "empty file") || strings.Contains(err.Error(), "too large") { + return outResponse(utils.ErrorParams, err.Error()), nil + } + return failResponse(utils.Fail), nil + } + return okResponse(res), nil +} diff --git a/services/chore/internal/server/choreServer.go b/services/chore/internal/server/choreServer.go index 3ded417..381b1d4 100644 --- a/services/chore/internal/server/choreServer.go +++ b/services/chore/internal/server/choreServer.go @@ -28,6 +28,18 @@ func (s *ChoreServer) Policy(ctx context.Context, in *chore.PolicyReq) (*chore.R return l.Policy(in) } +// 对外:服务端直传 OSS +func (s *ChoreServer) Upload(ctx context.Context, in *chore.UploadReq) (*chore.Response, error) { + l := logic.NewUploadLogic(ctx, s.svcCtx) + return l.Upload(in) +} + +// 对内:服务间直传 OSS(无 HTTP 暴露) +func (s *ChoreServer) InternalUpload(ctx context.Context, in *chore.InternalUploadReq) (*chore.InternalUploadData, error) { + l := logic.NewInternalUploadLogic(ctx, s.svcCtx) + return l.InternalUpload(in) +} + func (s *ChoreServer) Test(ctx context.Context, in *chore.TestReq) (*chore.Response, error) { l := logic.NewTestLogic(ctx, s.svcCtx) return l.Test(in) diff --git a/services/chore/validator/upload.go b/services/chore/validator/upload.go new file mode 100644 index 0000000..0d95915 --- /dev/null +++ b/services/chore/validator/upload.go @@ -0,0 +1,17 @@ +package validator + +import "lone-services/pkg/validate" + +type UploadValidator struct { + Filename string `validate:"required,max=255"` + Content []byte `validate:"required,min=1"` +} + +func (p UploadValidator) GetMessage() validate.ValidatorMessages { + return validate.ValidatorMessages{ + "Filename.required": "文件名不能为空", + "Filename.max": "文件名过长", + "Content.required": "文件内容不能为空", + "Content.min": "文件内容不能为空", + } +} diff --git a/services/equipment/equipmentclient/equipment.go b/services/equipment/equipmentclient/equipment.go index 9079f59..2658ebf 100644 --- a/services/equipment/equipmentclient/equipment.go +++ b/services/equipment/equipmentclient/equipment.go @@ -1,5 +1,5 @@ // Code generated by goctl. DO NOT EDIT. -// goctl 1.10.2 +// goctl 1.10.1 // Source: equipment.proto package equipmentClient @@ -23,6 +23,7 @@ type ( ApiMaskUseRequest = equipment.ApiMaskUseRequest ApiMaskUsedItemsRequest = equipment.ApiMaskUsedItemsRequest ApiUnBindRequest = equipment.ApiUnBindRequest + ApiVersionRequest = equipment.ApiVersionRequest BeansItemsRequest = equipment.BeansItemsRequest BindExpiryRequest = equipment.BindExpiryRequest CreateRequest = equipment.CreateRequest @@ -57,6 +58,7 @@ type ( ApiMaskUsedItems(ctx context.Context, in *ApiMaskUsedItemsRequest, opts ...grpc.CallOption) (*Response, error) ApiMaskStoreUse(ctx context.Context, in *ApiMaskUseRequest, opts ...grpc.CallOption) (*Response, error) ApiMaskStoreUseItems(ctx context.Context, in *ApiMaskUsedItemsRequest, opts ...grpc.CallOption) (*Response, error) + ApiVersion(ctx context.Context, in *ApiVersionRequest, opts ...grpc.CallOption) (*Response, error) } defaultEquipment struct { @@ -175,3 +177,8 @@ func (m *defaultEquipment) ApiMaskStoreUseItems(ctx context.Context, in *ApiMask client := equipment.NewEquipmentClient(m.cli.Conn()) return client.ApiMaskStoreUseItems(ctx, in, opts...) } + +func (m *defaultEquipment) ApiVersion(ctx context.Context, in *ApiVersionRequest, opts ...grpc.CallOption) (*Response, error) { + client := equipment.NewEquipmentClient(m.cli.Conn()) + return client.ApiVersion(ctx, in, opts...) +} diff --git a/services/equipment/internal/dao/version.go b/services/equipment/internal/dao/version.go index cf37c93..e57e49f 100644 --- a/services/equipment/internal/dao/version.go +++ b/services/equipment/internal/dao/version.go @@ -1,19 +1,23 @@ package dao +import "lone-services/pkg/utils" + type Version struct { - Id int64 `gorm:"column:id;type:int(11);primary_key;AUTO_INCREMENT" json:"id"` - HardwareVersion string `gorm:"column:hardware_version;type:varchar(32);default:'';NOT NULL" json:"hardware_version"` - Version string `gorm:"column:version;type:varchar(32);default:'';NOT NULL" json:"version"` - FirmwareSrc string `gorm:"column:firmware_src;type:varchar(255);default:'';NOT NULL" json:"firmware_src"` - SoftSrc string `gorm:"column:soft_src;type:varchar(255);default:'';NOT NULL" json:"soft_src"` - AddSrc string `gorm:"column:add_src;type:varchar(255);default:'';NOT NULL" json:"add_src"` - SoftConfig string `gorm:"column:soft_config;type:text" json:"soft_config"` - Describe string `gorm:"column:describe;type:varchar(255);default:'';NOT NULL" json:"describe"` - Status uint8 `gorm:"column:status;type:tinyint(1);default:1;NOT NULL" json:"status"` - Reason string `gorm:"column:reason;type:varchar(32);default:'';NOT NULL" json:"reason"` - Type uint8 `gorm:"column:type;type:tinyint(1);default:0;NOT NULL" json:"type"` - AdminId int64 `gorm:"column:admin_id;type:int(11);default:0;NOT NULL" json:"admin_id"` - AdminName string `gorm:"column:admin_name;type:varchar(32);default:'';NOT NULL" json:"admin_name"` + Id int64 `gorm:"column:id;type:int(11);primary_key;AUTO_INCREMENT" json:"id"` + HardwareVersion string `gorm:"column:hardware_version;type:varchar(32);default:'';NOT NULL" json:"hardware_version"` + Version string `gorm:"column:version;type:varchar(32);default:'';NOT NULL" json:"version"` + FirmwareSrc string `gorm:"column:firmware_src;type:varchar(255);default:'';NOT NULL" json:"firmware_src"` + SoftSrc string `gorm:"column:soft_src;type:varchar(255);default:'';NOT NULL" json:"soft_src"` + AddSrc string `gorm:"column:add_src;type:varchar(255);default:'';NOT NULL" json:"add_src"` + SoftConfig string `gorm:"column:soft_config;type:text" json:"soft_config"` + Describe string `gorm:"column:describe;type:varchar(255);default:'';NOT NULL" json:"describe"` + Status uint8 `gorm:"column:status;type:tinyint(1);default:1;NOT NULL" json:"status"` + Reason string `gorm:"column:reason;type:varchar(32);default:'';NOT NULL" json:"reason"` + Type uint8 `gorm:"column:type;type:tinyint(1);default:0;NOT NULL" json:"type"` + AdminId int64 `gorm:"column:admin_id;type:int(11);default:0;NOT NULL" json:"admin_id"` + AdminName string `gorm:"column:admin_name;type:varchar(32);default:'';NOT NULL" json:"admin_name"` + CreatedAt utils.CustomTime `gorm:"column:created_at;type:datetime" json:"created_at"` + UpdatedAt utils.CustomTime `gorm:"column:updated_at;type:datetime" json:"updated_at"` } type VersionInfo struct { diff --git a/services/equipment/internal/logic/apiVersionLogic.go b/services/equipment/internal/logic/apiVersionLogic.go new file mode 100644 index 0000000..ede50cf --- /dev/null +++ b/services/equipment/internal/logic/apiVersionLogic.go @@ -0,0 +1,58 @@ +package logic + +import ( + "context" + "lone-services/pkg/modelbase" + "lone-services/pkg/utils" + "lone-services/services/equipment/internal/dao" + "lone-services/services/equipment/internal/model" + "lone-services/services/equipment/validator" + "strconv" + + equipment "lone-services/rpc/equipment/pb" + "lone-services/services/equipment/internal/svc" + + "github.com/zeromicro/go-zero/core/logx" +) + +type ApiVersionLogic struct { + ctx context.Context + svcCtx *svc.ServiceContext + logx.Logger + BaseLogic +} + +func NewApiVersionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ApiVersionLogic { + return &ApiVersionLogic{ + ctx: ctx, + svcCtx: svcCtx, + Logger: logx.WithContext(ctx), + } +} + +func (l *ApiVersionLogic) ApiVersion(in *equipment.ApiVersionRequest) (*equipment.Response, error) { + var v validator.ApiVersionValidator + if fail := l.checkParams(in, &v); fail != nil { + return fail, nil + } + + w := modelbase.Params{ + Eq: map[string]string{ + "status": utils.StringStatusOk, + "type": strconv.Itoa(int(in.Type)), + }, + Order: "id desc", + } + if in.Version != utils.StringEmpty { + w.Eq["hardware_version"] = in.Version + } + + var info []dao.Version + err := model.VersionModel{}.Init().Items(w, &info) + if err != nil { + l.Logger.Error(err) + return l.fail(utils.Fail) + } + + return l.ok(info) +} diff --git a/services/equipment/internal/server/equipmentServer.go b/services/equipment/internal/server/equipmentServer.go index d813a5d..8cfe27e 100644 --- a/services/equipment/internal/server/equipmentServer.go +++ b/services/equipment/internal/server/equipmentServer.go @@ -1,5 +1,5 @@ // Code generated by goctl. DO NOT EDIT. -// goctl 1.10.2 +// goctl 1.10.1 // Source: equipment.proto package server @@ -128,3 +128,8 @@ func (s *EquipmentServer) ApiMaskStoreUseItems(ctx context.Context, in *equipmen l := logic.NewApiMaskStoreUseItemsLogic(ctx, s.svcCtx) return l.ApiMaskStoreUseItems(in) } + +func (s *EquipmentServer) ApiVersion(ctx context.Context, in *equipment.ApiVersionRequest) (*equipment.Response, error) { + l := logic.NewApiVersionLogic(ctx, s.svcCtx) + return l.ApiVersion(in) +} diff --git a/services/equipment/validator/version.go b/services/equipment/validator/version.go index 4fda8bc..b01cf04 100644 --- a/services/equipment/validator/version.go +++ b/services/equipment/validator/version.go @@ -51,3 +51,14 @@ type VersionItemsValidator struct { Size int `form:"size" json:"size"` Type uint8 `form:"type" json:"type"` } + +type ApiVersionValidator struct { + Type int32 `form:"type" json:"type" validate:"required"` + Version string `form:"version" json:"version"` +} + +func (p ApiVersionValidator) GetMessage() validate.ValidatorMessages { + return validate.ValidatorMessages{ + "Type.required": "Type 必传", + } +} diff --git a/services/sale/run.toml b/services/sale/run.toml index 26ee240..6f83ec6 100644 --- a/services/sale/run.toml +++ b/services/sale/run.toml @@ -2,7 +2,7 @@ login_out_time=43200 #api接口超时时间分 login_refresh_out_time=83200 name = "sale-service" - listenOn = "0.0.0.0:10700" + listenOn = "0.0.0.0:10300" mode = "dev" [log] path = "logs" @@ -53,5 +53,7 @@ [encrypt] data_key = "u2t9T3luZtoRfhBstkFN6TiIMW38BA8a" + [services] - chore = "chore-service" \ No newline at end of file + chore = "chore-service" + user = "user-service" \ No newline at end of file