From b4039aab811258f295c89636cb11dfb0b85b305e Mon Sep 17 00:00:00 2001 From: wucongxing8150 <815046773@qq.com> Date: Tue, 5 Mar 2024 14:50:01 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=BE=AE=E4=BF=A1=E5=85=AC?= =?UTF-8?q?=E4=BC=97=E5=B9=B3=E5=8F=B0=E5=AF=B9=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- api/.DS_Store | Bin 0 -> 6148 bytes api/controller/base.go | 1 + api/controller/v1/weChat.go | 79 +++++++++++++++++ api/requests/v1/WeChat.go | 12 +++ api/router/router.go | 9 ++ extend/weChat/Scheme/Scheme.go | 78 +++++++++++++++++ extend/weChat/WeChat.go | 1 + extend/weChat/base.go | 149 +++++++++++++++++++++++++++++++++ go.mod | 6 +- go.sum | 12 +-- utils/unserialize.go | 33 ++++++++ 12 files changed, 367 insertions(+), 16 deletions(-) create mode 100644 api/.DS_Store create mode 100644 api/controller/v1/weChat.go create mode 100644 api/requests/v1/WeChat.go create mode 100644 extend/weChat/Scheme/Scheme.go create mode 100644 extend/weChat/WeChat.go create mode 100644 extend/weChat/base.go create mode 100644 utils/unserialize.go diff --git a/.gitignore b/.gitignore index 0d992f9..3303680 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,5 @@ *.log # Dependency directories (remove the comment below to include it) -# vendor/ \ No newline at end of file +# vendor/ +.DS_Store/ \ No newline at end of file diff --git a/api/.DS_Store b/api/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..07dbdf2e5033a71cf0e748d830eb6a07794ee6b5 GIT binary patch literal 6148 zcmeHK-AcnS6i&A3GKSC#gjB$S+A2Q}J#soA(j!KQ7yEfFY$%q`s$fr@NBCtN9sg3=0 zz;AD|n1xJ5i|^l`CVAnypS;m*ZEd$jTXe;J@T3+&5f<~@4`;V%T`84Dl^#ad$z(Bh zcF$E-gh@7;>4GGlK+4@slErG_t9cgZx;8KY(Gi`o(_1cw?yxV9-NC9am#0T=Uk(Q+ zt5rwr?H`<7j-S()OuZR8Ik2r|$6^KVpxD*C2D2?(_p7$63S0b*dY8L;Pq z=x#O*w0dHI7^qU73EgVYlP#K1ZOO=CKE{$Ic^Q~StYFCmK< zAO`*!1H3f~Mm{Xco~=KYhi9#X_5ck9^GZ}eKwrBAfPwo+PX|q0q7HeU#nK>-f__~N PNEZQ32zA83FEH>0^an{* literal 0 HcmV?d00001 diff --git a/api/controller/base.go b/api/controller/base.go index f98df6d..4ac8b36 100644 --- a/api/controller/base.go +++ b/api/controller/base.go @@ -10,4 +10,5 @@ type Api struct { // version1 v1版本 type version1 struct { UserDoctor v1.UserDoctor // 角色数据 + WeChat v1.WeChat // 微信数据 } diff --git a/api/controller/v1/weChat.go b/api/controller/v1/weChat.go new file mode 100644 index 0000000..dafd55d --- /dev/null +++ b/api/controller/v1/weChat.go @@ -0,0 +1,79 @@ +package v1 + +import ( + "fmt" + "github.com/gin-gonic/gin" + v1 "hospital-open-api/api/requests/v1" + "hospital-open-api/api/responses" + "hospital-open-api/config" + "hospital-open-api/extend/weChat/Scheme" + "hospital-open-api/global" + "hospital-open-api/utils" + "regexp" + "strings" +) + +type WeChat struct{} + +// GetScheme 获取页面加密scheme码 +func (r *WeChat) GetScheme(c *gin.Context) { + WeChatRequest := v1.WeChatRequest{} + req := WeChatRequest.GetScheme + + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + // 验证path参数 + if strings.Count(req.Path, "/") != 3 || !strings.HasPrefix(req.Path, "/") || strings.HasSuffix(req.Path, "/") { + responses.FailWithMessage("path格式错误", c) + return + } + + // 验证query参数 + // 定义正则表达式,匹配 k1=v1&k2=v2 这种格式 + pattern := `^([a-zA-Z0-9_-]+=[a-zA-Z0-9_-]+&)*([a-zA-Z0-9_-]+=[a-zA-Z0-9_-]+)$` + reg := regexp.MustCompile(pattern) + res := reg.MatchString(req.Query) + if !res { + responses.FailWithMessage("query格式错误", c) + return + } + + // 判断环境 + envVersion := "trial" + if config.C.Env == "prod" { + envVersion = "release" + } + + fmt.Println(req.Query) + + // 构建请求数据 + JumpWxaData := &Scheme.GetSchemeJumpWxaRequest{ + Path: req.Path, + Query: req.Query, + EnvVersion: envVersion, + } + + GetSchemeRequest := &Scheme.GetSchemeRequest{ + JumpWxa: JumpWxaData, + ExpireType: 1, + ExpireTime: 0, + ExpireInterval: req.ExpireInterval, + } + + openLink, err := GetSchemeRequest.GetScheme(1) + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + responses.OkWithData(openLink, c) +} diff --git a/api/requests/v1/WeChat.go b/api/requests/v1/WeChat.go new file mode 100644 index 0000000..1167871 --- /dev/null +++ b/api/requests/v1/WeChat.go @@ -0,0 +1,12 @@ +package v1 + +type WeChatRequest struct { + GetScheme // 获取页面加密scheme码 +} + +// GetScheme 获取页面加密scheme码 +type GetScheme struct { + Path string `json:"path" form:"path" validate:"required" label:"页面路径"` + Query string `json:"query" form:"query" validate:"required" label:"参数"` + ExpireInterval int `json:"expire_interval" form:"expire_interval" validate:"required" label:"有效天数"` +} diff --git a/api/router/router.go b/api/router/router.go index 8f5f3fe..6af6676 100644 --- a/api/router/router.go +++ b/api/router/router.go @@ -80,8 +80,10 @@ func basicRouter(r *gin.Engine, api controller.Api) { func privateRouter(r *gin.Engine, api controller.Api) { v1Group := r.Group("/v1") { + // 用户 userGroup := v1Group.Group("/user") { + // 医生 doctorGroup := userGroup.Group("/doctor") { // 获取医生详情 @@ -91,5 +93,12 @@ func privateRouter(r *gin.Engine, api controller.Api) { doctorGroup.GET("/multi", api.V1.UserDoctor.GetMultiDoctor) } } + + // 微信 + wechatGroup := v1Group.Group("/wechat") + { + // 生成页面加密scheme码 + wechatGroup.POST("/scheme", api.V1.WeChat.GetScheme) + } } } diff --git a/extend/weChat/Scheme/Scheme.go b/extend/weChat/Scheme/Scheme.go new file mode 100644 index 0000000..26129a1 --- /dev/null +++ b/extend/weChat/Scheme/Scheme.go @@ -0,0 +1,78 @@ +package Scheme + +import ( + "encoding/json" + "errors" + "fmt" + "hospital-open-api/extend/weChat" + "strings" +) + +// GetSchemeJumpWxaRequest 跳转到的目标小程序信息 +type GetSchemeJumpWxaRequest struct { + Path string `json:"path"` + Query string `json:"query"` + // envVersion 要打开的小程序版本。正式版为 "release",体验版为 "trial",开发版为 "develop" + EnvVersion string `json:"env_version"` +} + +// GetSchemeRequest 请求参数 +// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/url-scheme/urlscheme.generate.html#请求参数 +type GetSchemeRequest struct { + JumpWxa *GetSchemeJumpWxaRequest `json:"jump_wxa"` + ExpireType int `json:"expire_type"` + ExpireTime int64 `json:"expire_time"` + ExpireInterval int `json:"expire_interval"` +} + +// GetSchemeResponse 获取加密scheme码返回数据 +type GetSchemeResponse struct { + OpenLink string `json:"openlink"` + + weChat.CommonError +} + +// GetScheme 获取加密scheme码 +func (r *GetSchemeRequest) GetScheme(userType int) (string, error) { + // 初始化配置 + weChatConfig := weChat.GetConfig(userType) + + // 获取token + accessToken, err := weChatConfig.GetAccessToken() + if err != nil { + return "", err + } + + url := fmt.Sprintf("%s?access_token=%s", "https://api.weixin.qq.com/wxa/generatescheme", accessToken) + + // 将请求数据转换为 JSON + requestBody, err := json.Marshal(r) + if err != nil { + return "", err + } + + // 去除json的转义 + requestBody = []byte(strings.Replace(string(requestBody), "\\u0026", "&", -1)) + + body, err := weChat.PostRequest(url, requestBody) + if err != nil { + return "", errors.New(err.Error()) + } + + var GetSchemeResponse GetSchemeResponse + err = json.Unmarshal(body, &GetSchemeResponse) + if err != nil { + // json解析失败 + return "", err + } + + if GetSchemeResponse.ErrCode != 0 || GetSchemeResponse.ErrMsg != "ok" { + return "", errors.New(GetSchemeResponse.ErrMsg) + } + + if GetSchemeResponse.OpenLink == "" { + return "", errors.New("失败") + } + + return GetSchemeResponse.OpenLink, nil +} diff --git a/extend/weChat/WeChat.go b/extend/weChat/WeChat.go new file mode 100644 index 0000000..1c0a0ab --- /dev/null +++ b/extend/weChat/WeChat.go @@ -0,0 +1 @@ +package weChat diff --git a/extend/weChat/base.go b/extend/weChat/base.go new file mode 100644 index 0000000..aac1d25 --- /dev/null +++ b/extend/weChat/base.go @@ -0,0 +1,149 @@ +package weChat + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "github.com/leeqvip/gophp" + "hospital-open-api/config" + "hospital-open-api/global" + "io" + "net/http" + "time" +) + +// CommonError 微信返回的通用错误 json +type CommonError struct { + ErrCode int64 `json:"errcode"` + ErrMsg string `json:"errmsg"` +} + +type GetAccessTokenResponse struct { + AccessToken string `json:"access_token"` + ExpiresIn int64 `json:"expires_in"` + + CommonError +} + +type WeChatConfig struct { + AppId string `json:"app_id"` + AppSecret string `json:"app_secret"` +} + +// GetConfig 初始化配置 +func GetConfig(userType int) *WeChatConfig { + weChatConfig := &WeChatConfig{} + if userType == 1 { + // 患者 + weChatConfig.AppId = config.C.Wechat.PatientAppId + weChatConfig.AppSecret = config.C.Wechat.PatientAppSecret + } else if userType == 2 { + // 医生/药师 + weChatConfig.AppId = config.C.Wechat.DoctorAppId + weChatConfig.AppSecret = config.C.Wechat.DoctorAppSecret + } + + return weChatConfig +} + +// GetAccessToken 获取access_token +func (c WeChatConfig) GetAccessToken() (string, error) { + // 检测缓存是否存在access_token + redisKey := "c:official_account.access_token." + c.AppId + "." + c.AppSecret + accessToken, _ := global.Redis.Get(context.Background(), redisKey).Result() + if accessToken != "" { + // 存在缓存,反序列化 + result, err := gophp.Unserialize([]byte(accessToken)) + if err != nil { + return "", err + } + + var ok bool + accessToken, ok = result.(string) + if !ok { + return "", errors.New("失败") + } + } else { + // 不存在缓存,重新获取 + var body []byte + + grantType := "client_credential" + + // 构建 URL + url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=%s&appid=%s&secret=%s", grantType, c.AppId, + c.AppSecret) + + body, err := GetRequest(url) + if err != nil { + return "", errors.New(err.Error()) + } + + var GetAccessTokenResponse GetAccessTokenResponse + err = json.Unmarshal(body, &GetAccessTokenResponse) + if err != nil { + return "", errors.New(err.Error()) + } + + if GetAccessTokenResponse.ErrCode != 0 { + return "", errors.New(GetAccessTokenResponse.ErrMsg) + } + + accessToken = GetAccessTokenResponse.AccessToken + expiresIn := GetAccessTokenResponse.ExpiresIn + + // 序列化 + redisValue, _ := gophp.Serialize(accessToken) + + // 增加缓存 + global.Redis.Set(context.Background(), redisKey, string(redisValue), + time.Duration(expiresIn-1500)*time.Second) + } + + if accessToken == "" { + return "", errors.New("失败") + } + + return accessToken, nil +} + +// GetRequest 发送 GET 请求并返回响应内容和错误(如果有) +func GetRequest(url string) ([]byte, error) { + // 发送 GET 请求 + resp, err := http.Get(url) + if err != nil { + return nil, err + } + defer func(Body io.ReadCloser) { + _ = Body.Close() + }(resp.Body) + + // 读取响应内容 + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return body, nil +} + +// PostRequest 统一请求 +func PostRequest(url string, requestBody []byte) ([]byte, error) { + // 发起 POST 请求 + resp, err := http.Post(url, "application/json", bytes.NewBuffer(requestBody)) + if err != nil { + return nil, err + } + + defer func(Body io.ReadCloser) { + _ = Body.Close() + }(resp.Body) + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return body, nil +} diff --git a/go.mod b/go.mod index 4ce2fd0..7efaa86 100644 --- a/go.mod +++ b/go.mod @@ -15,9 +15,7 @@ require ( github.com/go-playground/universal-translator v0.18.1 github.com/go-playground/validator/v10 v10.15.1 github.com/go-redis/redis/v8 v8.11.5 - github.com/golang-jwt/jwt/v5 v5.0.0 - github.com/google/uuid v1.3.1 - github.com/mojocn/base64Captcha v1.3.5 + github.com/leeqvip/gophp v1.1.1 github.com/sirupsen/logrus v1.9.3 github.com/spf13/viper v1.16.0 github.com/wechatpay-apiv3/wechatpay-go v0.2.17 @@ -50,7 +48,6 @@ require ( github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/goccy/go-json v0.10.2 // indirect - github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect @@ -73,7 +70,6 @@ require ( github.com/ugorji/go/codec v1.2.11 // indirect golang.org/x/arch v0.3.0 // indirect golang.org/x/crypto v0.10.0 // indirect - golang.org/x/image v0.0.0-20190802002840-cff245a6509b // indirect golang.org/x/net v0.11.0 // indirect golang.org/x/sys v0.9.0 // indirect golang.org/x/text v0.10.0 // indirect diff --git a/go.sum b/go.sum index 91f9f62..f82cdae 100644 --- a/go.sum +++ b/go.sum @@ -145,10 +145,6 @@ github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= -github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -204,8 +200,6 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= @@ -237,6 +231,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/leeqvip/gophp v1.1.1 h1:+HNBayDVEKnaON0+qj/goDjlZ4Bos7hrdy6/Cb5v5M8= +github.com/leeqvip/gophp v1.1.1/go.mod h1:DRoO5E9Sk+t4/3LGPCH4QZ/arcASXk9VsqdeTXLgYC4= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= @@ -252,8 +248,6 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mojocn/base64Captcha v1.3.5 h1:Qeilr7Ta6eDtG4S+tQuZ5+hO+QHbiGAJdi4PfoagaA0= -github.com/mojocn/base64Captcha v1.3.5/go.mod h1:/tTTXn4WTpX9CfrmipqRytCpJ27Uw3G6I7NcP2WwcmY= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= @@ -346,8 +340,6 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= diff --git a/utils/unserialize.go b/utils/unserialize.go new file mode 100644 index 0000000..9f8cb61 --- /dev/null +++ b/utils/unserialize.go @@ -0,0 +1,33 @@ +package utils + +import ( + "errors" + "regexp" + "strconv" +) + +// Unserialize 反序列化 PHP 序列化的字符串 +func Unserialize(data string) (string, error) { + // 使用正则表达式找到字符串值 + re := regexp.MustCompile(`s:(\d+):"(.*?)";`) + match := re.FindStringSubmatch(data) + if len(match) != 3 { + return "", errors.New("invalid input format") + } + + // 解析字符串长度 + length, err := strconv.Atoi(match[1]) + if err != nil { + return "", err + } + + // 解析字符串值 + value := match[2] + + // 检查字符串长度是否与值长度匹配 + if len(value) != length { + return "", errors.New("string length does not match") + } + + return value, nil +}