diff --git a/internal/model/web/admin.go b/internal/model/web/admin.go index d0c3b46e..ec312f2c 100644 --- a/internal/model/web/admin.go +++ b/internal/model/web/admin.go @@ -5,5 +5,7 @@ package web type ChangeUserStatusReq struct { - *BaseInfo `json:"-"` + *BaseInfo `json:"-" binding:"-"` + ID int64 `json:"id" form:"id" binding:"required"` + Status int `json:"status" form:"status" binding:"required,oneof=1 2"` } diff --git a/internal/model/web/alipay.go b/internal/model/web/alipay.go index 9260725f..2063e88c 100644 --- a/internal/model/web/alipay.go +++ b/internal/model/web/alipay.go @@ -4,8 +4,14 @@ package web +import ( + "context" + + "github.com/smartwalle/alipay/v3" +) + type UserWalletBillsReq struct { - *BaseInfo `json:"-"` + *BaseInfo `json:"-" binding:"-"` } type UserWalletBillsResp struct { @@ -13,5 +19,8 @@ type UserWalletBillsResp struct { } type AlipayNotifyReq struct { - // TODO + Ctx context.Context + ID int64 + TradeNo string + TradeStatus alipay.TradeStatus } diff --git a/internal/servants/base/base.go b/internal/servants/base/base.go index eac975ed..301753f6 100644 --- a/internal/servants/base/base.go +++ b/internal/servants/base/base.go @@ -9,6 +9,7 @@ import ( "github.com/alimy/mir/v3" "github.com/gin-gonic/gin" + "github.com/go-redis/redis/v8" "github.com/rocboss/paopao-ce/internal/core" "github.com/rocboss/paopao-ce/pkg/xerror" ) @@ -17,6 +18,11 @@ type BaseServant struct { // TODO } +type DaoServant struct { + Redis *redis.Client + Ds core.DataService +} + type BaseBinding struct { // TODO } @@ -47,7 +53,7 @@ func BindAny(c *gin.Context, obj any) mir.Error { var errs xerror.ValidErrors err := c.ShouldBind(obj) if err != nil { - return mir.NewError(xerror.InvalidParams.Code(), xerror.InvalidParams.WithDetails(errs.Error())) + return mir.NewError(xerror.InvalidParams.StatusCode(), xerror.InvalidParams.WithDetails(errs.Error())) } // setup *core.User if needed if setter, ok := obj.(UserSetter); ok { @@ -65,7 +71,7 @@ func RenderAny(c *gin.Context, data any, err mir.Error) { Data: data, }) } else { - c.JSON(http.StatusInternalServerError, &JsonResp{ + c.JSON(xerror.HttpStatusCode(err), &JsonResp{ Code: err.StatusCode(), Msg: err.Error(), }) diff --git a/internal/servants/web/admin.go b/internal/servants/web/admin.go index d00f63c9..6a6dc2ec 100644 --- a/internal/servants/web/admin.go +++ b/internal/servants/web/admin.go @@ -5,10 +5,13 @@ package web import ( + "github.com/alimy/mir/v3" "github.com/gin-gonic/gin" api "github.com/rocboss/paopao-ce/auto/api/v1" + "github.com/rocboss/paopao-ce/internal/model/web" "github.com/rocboss/paopao-ce/internal/servants/base" "github.com/rocboss/paopao-ce/internal/servants/chain" + "github.com/rocboss/paopao-ce/pkg/xerror" ) var ( @@ -18,17 +21,15 @@ var ( ) type adminSrv struct { - base.BaseServant api.UnimplementedAdminServant + *base.DaoServant } type adminBinding struct { - base.BaseBinding *api.UnimplementedAdminBinding } type adminRender struct { - base.BaseRender *api.UnimplementedAdminRender } @@ -36,8 +37,23 @@ func (s *adminSrv) Chain() gin.HandlersChain { return gin.HandlersChain{chain.JWT(), chain.Admin()} } -func newAdminSrv() api.Admin { - return &adminSrv{} +func (s *adminSrv) ChangeUserStatus(req *web.ChangeUserStatusReq) mir.Error { + user, err := s.Ds.GetUserByID(req.ID) + if err != nil || user.Model == nil || user.ID <= 0 { + return _errNoExistUsername + } + // 执行更新 + user.Status = req.Status + if err := s.Ds.UpdateUser(user); err != nil { + return xerror.ServerError + } + return nil +} + +func newAdminSrv(s *base.DaoServant) api.Admin { + return &adminSrv{ + DaoServant: s, + } } func newAdminBinding() api.AdminBinding { diff --git a/internal/servants/web/alipay.go b/internal/servants/web/alipay.go index a6bc97f6..36fc6f60 100644 --- a/internal/servants/web/alipay.go +++ b/internal/servants/web/alipay.go @@ -5,10 +5,17 @@ package web import ( + "time" + + "github.com/alimy/mir/v3" "github.com/gin-gonic/gin" api "github.com/rocboss/paopao-ce/auto/api/v1" + "github.com/rocboss/paopao-ce/internal/model/web" "github.com/rocboss/paopao-ce/internal/servants/base" "github.com/rocboss/paopao-ce/internal/servants/chain" + "github.com/rocboss/paopao-ce/pkg/convert" + "github.com/sirupsen/logrus" + "github.com/smartwalle/alipay/v3" ) var ( @@ -21,48 +28,88 @@ var ( ) type alipayPubSrv struct { - base.BaseServant api.UnimplementedAlipayPubServant + *base.DaoServant } type alipayPubBinding struct { - base.BaseBinding *api.UnimplementedAlipayPubBinding + + alipayClient *alipay.Client } type alipayPubRender struct { - base.BaseRender *api.UnimplementedAlipayPubRender } type alipayPrivSrv struct { - base.BaseServant api.UnimplementedAlipayPrivServant } type alipayPrivBinding struct { - base.BaseBinding *api.UnimplementedAlipayPrivBinding } type alipayPrivRender struct { - base.BaseRender *api.UnimplementedAlipayPrivRender } +func (b *alipayPubBinding) BindAlipayNotify(c *gin.Context) (*web.AlipayNotifyReq, mir.Error) { + if err := c.Request.ParseForm(); err != nil { + logrus.Errorf("parse form err: %s", err) + return nil, _errRechargeNotifyError + } + noti, err := b.alipayClient.GetTradeNotification(c.Request) + if err != nil { + logrus.Errorf("alipayClient.GetTradeNotification err: %s form: %v", err, c.Request.Form) + return nil, _errRechargeNotifyError + } + return &web.AlipayNotifyReq{ + Ctx: c.Request.Context(), + ID: convert.StrTo(noti.OutTradeNo).MustInt64(), + TradeNo: noti.TradeNo, + TradeStatus: noti.TradeStatus, + }, nil +} + +func (s *alipayPubSrv) AlipayNotify(req *web.AlipayNotifyReq) mir.Error { + if req.TradeStatus == alipay.TradeStatusSuccess { + if ok, _ := s.Redis.SetNX(req.Ctx, "PaoPaoRecharge:"+req.TradeNo, 1, time.Second*5).Result(); ok { + recharge, err := s.Ds.GetRechargeByID(req.ID) + if err != nil { + logrus.Errorf("GetRechargeByID id:%d err: %s", req.ID, err) + return _errRechargeNotifyError + } + if recharge.TradeStatus != "TRADE_SUCCESS" { + // 标记为已付款 + err := s.Ds.HandleRechargeSuccess(recharge, req.TradeNo) + defer s.Redis.Del(req.Ctx, "PaoPaoRecharge:"+req.TradeNo) + if err != nil { + logrus.Errorf("HandleRechargeSuccess id:%d err: %s", req.ID, err) + return _errRechargeNotifyError + } + } + } + } + return nil +} + func (s *alipayPrivSrv) Chain() gin.HandlersChain { return gin.HandlersChain{chain.JWT()} } -func newAlipayPubSrv() api.AlipayPub { - return &alipayPubSrv{} +func newAlipayPubSrv(s *base.DaoServant) api.AlipayPub { + return &alipayPubSrv{ + DaoServant: s, + } } -func newAlipayPubBinding() api.AlipayPubBinding { +func newAlipayPubBinding(alipayClient *alipay.Client) api.AlipayPubBinding { return &alipayPubBinding{ UnimplementedAlipayPubBinding: &api.UnimplementedAlipayPubBinding{ BindAny: base.BindAny, }, + alipayClient: alipayClient, } } diff --git a/internal/servants/web/core.go b/internal/servants/web/core.go index 30bbe7e5..f4eef6e9 100644 --- a/internal/servants/web/core.go +++ b/internal/servants/web/core.go @@ -7,6 +7,7 @@ package web import ( "github.com/gin-gonic/gin" api "github.com/rocboss/paopao-ce/auto/api/v1" + "github.com/rocboss/paopao-ce/internal/core" "github.com/rocboss/paopao-ce/internal/servants/base" "github.com/rocboss/paopao-ce/internal/servants/chain" ) @@ -18,17 +19,18 @@ var ( ) type coreSrv struct { - base.BaseServant api.UnimplementedCoreServant + *base.DaoServant + + ts core.TweetSearchService + oss core.ObjectStorageService } type coreBinding struct { - base.BaseBinding *api.UnimplementedCoreBinding } type coreRender struct { - base.BaseRender *api.UnimplementedCoreRender } @@ -36,8 +38,12 @@ func (s *coreSrv) Chain() gin.HandlersChain { return gin.HandlersChain{chain.JWT()} } -func newCoreSrv() api.Core { - return &coreSrv{} +func newCoreSrv(s *base.DaoServant, ts core.TweetSearchService, oss core.ObjectStorageService) api.Core { + return &coreSrv{ + DaoServant: s, + ts: ts, + oss: oss, + } } func newCoreBinding() api.CoreBinding { diff --git a/internal/servants/web/web.go b/internal/servants/web/web.go index b735f5a7..22fc71a7 100644 --- a/internal/servants/web/web.go +++ b/internal/servants/web/web.go @@ -8,19 +8,32 @@ import ( "github.com/alimy/cfg" "github.com/gin-gonic/gin" api "github.com/rocboss/paopao-ce/auto/api/v1" + "github.com/rocboss/paopao-ce/internal/conf" + "github.com/rocboss/paopao-ce/internal/dao" + "github.com/rocboss/paopao-ce/internal/servants/base" + "github.com/sirupsen/logrus" + "github.com/smartwalle/alipay/v3" ) // RouteWeb register web route func RouteWeb(e *gin.Engine) { - api.RegisterAdminServant(e, newAdminSrv(), newAdminBinding(), newAdminRender()) - api.RegisterCoreServant(e, newCoreSrv(), newCoreBinding(), newCoreRender()) + ts := dao.TweetSearchService() + oss := dao.ObjectStorageService() + ds := &base.DaoServant{ + Redis: conf.Redis, + Ds: dao.DataService(), + } + // aways register servants + api.RegisterAdminServant(e, newAdminSrv(ds), newAdminBinding(), newAdminRender()) + api.RegisterCoreServant(e, newCoreSrv(ds, ts, oss), newCoreBinding(), newCoreRender()) api.RegisterLooseServant(e, newLooseSrv(), newLooseBinding(), newLooseRender()) api.RegisterPrivServant(e, newPrivSrv(), newPrivBinding(), newPrivRender()) api.RegisterPubServant(e, newPubSrv(), newPubBinding(), newPubRender()) - + // regster servants if needed by configure cfg.In(cfg.Actions{ "Alipay": func() { - api.RegisterAlipayPubServant(e, newAlipayPubSrv(), newAlipayPubBinding(), newAlipayPubRender()) + client := mustAlipayClient() + api.RegisterAlipayPubServant(e, newAlipayPubSrv(ds), newAlipayPubBinding(client), newAlipayPubRender()) api.RegisterAlipayPrivServant(e, newAlipayPrivSrv(), newAlipayPrivBinding(), newAlipayPrivRender()) }, "Followship": func() { @@ -31,3 +44,25 @@ func RouteWeb(e *gin.Engine) { }, }) } + +func mustAlipayClient() *alipay.Client { + s := conf.AlipaySetting + // 将 key 的验证调整到初始化阶段 + client, err := alipay.New(s.AppID, s.PrivateKey, s.InProduction) + if err != nil { + logrus.Fatalf("alipay.New err: %s", err) + } + // 加载应用公钥证书 + if err = client.LoadAppPublicCertFromFile(s.AppPublicCertFile); err != nil { + logrus.Fatalf("client.LoadAppPublicCertFromFile err: %s\n", err) + } + // 加载支付宝根证书 + if err = client.LoadAliPayRootCertFromFile(s.RootCertFile); err != nil { + logrus.Fatalf("client.LoadAliPayRootCertFromFile err: %s\n", err) + } + // 加载支付宝公钥证书 + if err = client.LoadAliPayPublicCertFromFile(s.PublicCertFile); err != nil { + logrus.Fatalf("client.LoadAliPayPublicCertFromFile err: %s\n", err) + } + return client +} diff --git a/pkg/xerror/xerror.go b/pkg/xerror/xerror.go index f614d18e..0fc7878c 100644 --- a/pkg/xerror/xerror.go +++ b/pkg/xerror/xerror.go @@ -8,9 +8,13 @@ import ( "fmt" "net/http" "strings" + + "github.com/alimy/mir/v3" ) var ( + _ mir.Error = (*Error)(nil) + codes = map[int]string{} ) @@ -51,10 +55,10 @@ func NewError(code int, msg string) *Error { } func (e *Error) Error() string { - return fmt.Sprintf("错误码: %d, 错误信息: %s", e.Code(), e.Msg()) + return fmt.Sprintf("错误码: %d, 错误信息: %s", e.StatusCode(), e.Msg()) } -func (e *Error) Code() int { +func (e *Error) StatusCode() int { return e.code } @@ -78,27 +82,26 @@ func (e *Error) WithDetails(details ...string) *Error { return &newError } -func (e *Error) StatusCode() int { - switch e.Code() { - case Success.Code(): +func HttpStatusCode(e mir.Error) int { + switch e.StatusCode() { + case Success.StatusCode(): return http.StatusOK - case ServerError.Code(): + case ServerError.StatusCode(): return http.StatusInternalServerError - case InvalidParams.Code(): + case InvalidParams.StatusCode(): return http.StatusBadRequest - case UnauthorizedAuthNotExist.Code(): + case UnauthorizedAuthNotExist.StatusCode(): fallthrough - case UnauthorizedAuthFailed.Code(): + case UnauthorizedAuthFailed.StatusCode(): fallthrough - case UnauthorizedTokenError.Code(): + case UnauthorizedTokenError.StatusCode(): fallthrough - case UnauthorizedTokenGenerate.Code(): + case UnauthorizedTokenGenerate.StatusCode(): fallthrough - case UnauthorizedTokenTimeout.Code(): + case UnauthorizedTokenTimeout.StatusCode(): return http.StatusUnauthorized - case TooManyRequests.Code(): + case TooManyRequests.StatusCode(): return http.StatusTooManyRequests } - return http.StatusInternalServerError }