package hdl import ( "applet/app/cfg" "applet/app/db" "applet/app/e" alipay "applet/app/lib/gopay" "applet/app/md" "applet/app/svc" "applet/app/svc/sys_cfg" "applet/app/utils" "applet/app/utils/cache" "code.fnuoos.com/EggPlanet/egg_models.git/src/implement" "code.fnuoos.com/EggPlanet/egg_models.git/src/model" rule2 "code.fnuoos.com/EggPlanet/egg_system_rules.git" "code.fnuoos.com/EggPlanet/egg_system_rules.git/enum" enum2 "code.fnuoos.com/EggPlanet/egg_system_rules.git/enum" md2 "code.fnuoos.com/EggPlanet/egg_system_rules.git/md" "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule" md3 "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule/egg_energy/md" "code.fnuoos.com/go_rely_warehouse/zyos_go_mq.git/rabbit" "errors" "fmt" "github.com/gin-gonic/gin" "github.com/go-pay/gopay" alipay2 "github.com/go-pay/gopay/alipay" "github.com/jinzhu/copier" "time" ) // GetAmountFlow // @Summary 蛋蛋星球-钱包-余额明细(获取) // @Tags 钱包 // @Description 余额明细(获取) // @Accept json // @Produce json // @param Authorization header string true "验证参数Bearer和token空格拼接" // @Param limit query string true "每页大小" // @Param page query string true "页数" // @Param startAt query string false "开始时间" // @Param endAt query string false "结束时间" // @Param direction query string false "流水方向(1.收入 2.支出 0.全部)" // @Success 200 {object} md.GetAmountFlowResp "具体数据" // @Failure 400 {object} md.Response "具体错误" // @Router /api/v1/wallet/amountFlow [GET] func GetAmountFlow(c *gin.Context) { pageStr := c.DefaultQuery("page", "1") limitStr := c.DefaultQuery("limit", "10") startAt := c.Query("startAt") endAt := c.Query("endAt") directionStr := c.Query("direction") val, exists := c.Get("user") if !exists { e.OutErr(c, e.ERR_USER_CHECK_ERR, nil) return } user, ok := val.(*model.User) if !ok { e.OutErr(c, e.ERR_USER_CHECK_ERR, nil) return } direction := 0 switch directionStr { case "1": direction = 1 case "2": direction = 2 } page := utils.StrToInt(pageStr) limit := utils.StrToInt(limitStr) flowDb := implement.NewUserWalletFlowDb(db.Db) flows, total, err := flowDb.UserWalletFlowFindByCoinAndUser(page, limit, user.Id, startAt, endAt, direction, false, 0, 0) if err != nil { e.OutErr(c, e.ERR_DB_ORM, err.Error()) return } list := make([]md.WalletFlowNode, 0, len(flows)) for _, flow := range flows { temp := md.WalletFlowNode{ Id: flow.Id, Uid: flow.Uid, Direction: flow.Direction, Amount: flow.Amount, BeforeAmount: flow.BeforeAmount, AfterAmount: flow.AfterAmount, SysFee: flow.SysFee, OrdId: flow.OrdId, Title: flow.Title, Kind: flow.Kind, State: flow.State, Memo: flow.Memo, CreateTime: flow.CreateAt, UpdateTime: flow.UpdateAt, } list = append(list, temp) } resp := md.GetAmountFlowResp{ List: list, Paginate: md.Paginate{ Limit: limit, Page: page, Total: total, }, } e.OutSuc(c, resp, nil) } // WithdrawGetAmount // @Summary 蛋蛋星球-钱包-提现余额(获取) // @Tags 钱包 // @Description 提现余额(获取) // @Accept json // @Produce json // @param Authorization header string true "验证参数Bearer和token空格拼接" // @Success 200 {object} md.WithdrawGetAmountResp "具体数据" // @Failure 400 {object} md.Response "具体错误" // @Router /api/v1/wallet/withdraw/index [GET] func WithdrawGetAmount(c *gin.Context) { val, exists := c.Get("user") if !exists { e.OutErr(c, e.ERR_USER_CHECK_ERR, nil) return } user, ok := val.(*model.User) if !ok { e.OutErr(c, e.ERR_USER_CHECK_ERR, nil) return } walletDb := implement.NewUserWalletDb(db.Db) wallet, err := walletDb.GetUserVirtualWallet(user.Id) if err != nil { e.OutErr(c, e.ERR_DB_ORM, err.Error()) return } resp := md.WithdrawGetAmountResp{ Amount: wallet.Amount, } e.OutSuc(c, resp, nil) } // WithdrawApply // @Summary 蛋蛋星球-钱包-发起提现 // @Tags 钱包 // @Description 发起提现 // @Accept json // @Produce json // @param Authorization header string true "验证参数Bearer和token空格拼接" // @Param req body md.WithdrawApplyReq true "具体参数" // @Success 200 {string} "success" // @Failure 400 {object} md.Response "具体错误" // @Router /api/v1/wallet/withdraw/apply [POST] func WithdrawApply(c *gin.Context) { var req md.WithdrawApplyReq err := c.ShouldBindJSON(&req) if err != nil { err = svc.HandleValidateErr(err) err1 := err.(e.E) e.OutErr(c, err1.Code, err1.Error()) return } user := svc.GetUser(c) var userId, openId string var kind int //sysCfgDb := implement.NewSysCfgDb(db.Db, cache.GetPool().Get()) //sysCfgMap := sysCfgDb.SysCfgFindWithDb(enum.AlipayAppId, enum.WxAppId) if req.Kind == enum.FinWithdrawApplyWithdrawKindForAli.String() { alipayUserInfoDb := implement.NewAlipayUserInfoDb(db.Db) aliInfo, err := alipayUserInfoDb.GetAlipayUserInfo(user.Id) if err != nil { e.OutErr(c, e.ERR, err.Error()) return } if aliInfo == nil { e.OutErr(c, e.ERR, "支付宝用户信息未授权") return } //appId = sysCfgMap[enum.AlipayAppId] userId = aliInfo.UserId openId = aliInfo.OpenId kind = int(enum.FinWithdrawApplyWithdrawKindForAli) } else if req.Kind == enum.FinWithdrawApplyWithdrawKindForWx.String() { wxUserInfoDb := implement.NewWxUserInfoDb(db.Db) wxInfo, err := wxUserInfoDb.GetWxUserInfo(user.Id) if err != nil { e.OutErr(c, e.ERR, err.Error()) return } if wxInfo == nil { e.OutErr(c, e.ERR, "微信用户信息未授权") return } //appId = sysCfgMap[enum.WxAppId] userId = wxInfo.UserId openId = wxInfo.OpenId kind = int(enum.FinWithdrawApplyWithdrawKindForWx) } else { e.OutErr(c, e.ERR, "未知的提现类型") return } //1、判断是否可以提现 err, realAmount, fee, isAuto, isFirst := svc.CheckWithdraw(c, req.Amount) if err != nil { e.OutErr(c, e.ERR, err.Error()) return } // 2、加锁 防止并发提取 mutexKey := fmt.Sprintf("egg_app_withdraw_apply:%s", utils.Int64ToStr(user.Id)) withdrawAvailable, err := cache.Do("SET", mutexKey, 1, "EX", 5, "NX") if err != nil { e.OutErr(c, e.ERR, err) return } if withdrawAvailable != "OK" { e.OutErr(c, e.ERR, e.NewErr(400000, "请求过于频繁,请稍后再试")) return } // 开启事务 session := db.Db.NewSession() defer session.Close() err = session.Begin() if err != nil { session.Rollback() e.OutErr(c, e.ERR_DB_ORM, err) return } //3、处理用户余额 dealUserWalletReq := md2.DealUserWalletReq{ Direction: "sub", Kind: int(enum.UserWithdrawApply), Title: enum.UserWithdrawApply.String(), Uid: user.Id, Amount: utils.StrToFloat64(req.Amount), } rule2.Init(cfg.RedisAddr) err = rule.DealUserWallet(session, dealUserWalletReq) if err != nil { e.OutErr(c, e.ERR_DB_ORM, err.Error()) session.Rollback() return } //4、新增提现记录 now := time.Now() id := utils.StrToInt64(utils.OrderUUID(int(user.Id))) finWithdrawApplyDb := implement.NewFinWithdrawApplyDb(db.Db) finWithdrawApply := &model.FinWithdrawApply{ Id: id, Uid: user.Id, AdmId: 0, Amount: req.Amount, RealAmount: realAmount, Fee: fee, Type: func(isAuto bool) int { if isAuto { return 2 } return 1 }(isAuto), WithdrawAccount: userId, WithdrawName: openId, Reason: 0, PaymentDate: "", State: 0, WithdrawKind: kind, IsFirst: func(isFirst bool) int { if isFirst { return 1 } return 0 }(isFirst), Memo: "", UpdateAt: now.Format("2006-01-02 15:04:05"), CreateAt: now.Format("2006-01-02 15:04:05"), } insertAffected, err := finWithdrawApplyDb.FinWithdrawApplyInsertOneBySession(session, finWithdrawApply) if err != nil { session.Rollback() e.OutErr(c, e.ERR_DB_ORM, err) return } if insertAffected <= 0 { session.Rollback() e.OutErr(c, e.ERR_DB_ORM, "生成提现单失败") return } err = session.Begin() if err != nil { session.Rollback() e.OutErr(c, e.ERR_DB_ORM, err) return } err = session.Commit() if err != nil { _ = session.Rollback() e.OutErr(c, e.ERR_DB_ORM, err) return } //5、推入mq if isAuto { //更改提现单记录状态 finWithdrawApply.State = int(enum.FinWithdrawApplyStateForIng) updateAffected, err1 := finWithdrawApplyDb.UpdateFinWithdrawApply(finWithdrawApply, "state") if err1 != nil { e.OutErr(c, e.ERR_DB_ORM, err1.Error()) return } if updateAffected <= 0 { e.OutErr(c, e.ERR_DB_ORM, "更新提现单状态失败") return } ch, err1 := rabbit.Cfg.Pool.GetChannel() if err1 != nil { e.OutErr(c, e.ERR_INIT_RABBITMQ, err1.Error()) return } defer ch.Release() var data md3.EggFinWithdrawApplyData err = copier.Copy(&data, &finWithdrawApply) if err != nil { e.OutErr(c, e.ERR, err.Error()) return } ch.Publish(md3.EggAppExchange, data, md3.EggFinWithdrawApply) } e.OutSuc(c, "success", nil) } // GetWithdrawCondition // @Summary 蛋蛋星球-钱包-提现条件(获取) // @Tags 钱包 // @Description 提现条件(获取) // @Accept json // @Produce json // @param Authorization header string true "验证参数Bearer和token空格拼接" // @Success 200 {object} md.GetWithdrawConditionResp "具体数据" // @Failure 400 {object} md.Response "具体错误" // @Router /api/v1/wallet/withdraw/condition [GET] func GetWithdrawCondition(c *gin.Context) { user := svc.GetUser(c) settingDb := implement.NewFinWithdrawSettingDb(db.Db) setting, err := settingDb.FinWithdrawSettingGetOne() if err != nil { e.OutErr(c, e.ERR_DB_ORM, err) return } //1. 判断是否为第一次提现 isFirst := false has, err := db.Db.Where("uid = ?", user.Id).Get(model.FinWithdrawApply{}) if !has { //第一次提现 isFirst = true } resp := svc.GetWithdrawCondition(user, setting, isFirst) alipayUserInfoDb := implement.NewAlipayUserInfoDb(db.Db) alipayInfo, err := alipayUserInfoDb.GetAlipayUserInfo(user.Id) if err != nil { e.OutErr(c, e.ERR_DB_ORM, err.Error()) return } if alipayInfo != nil { resp.IsBindAlipay = true } userInfoDb := implement.NewWxUserInfoDb(db.Db) wxUserInfo, err := userInfoDb.GetWxUserInfo(user.Id) if err != nil { e.OutErr(c, e.ERR_DB_ORM, err.Error()) return } if wxUserInfo != nil { resp.IsBindWx = true } e.OutSuc(c, resp, nil) } // LaunchBindAlipayAccount // @Summary 蛋蛋星球-钱包-发起绑定支付宝获得URL // @Tags 钱包 // @Description 发起绑定支付宝获得URL // @Accept json // @Produce json // @param Authorization header string true "验证参数Bearer和token空格拼接" // @Success 200 {string} "Url" // @Failure 400 {object} md.Response "具体错误" // @Router /api/v1/wallet/withdraw/launchBindAlipay [GET] func LaunchBindAlipayAccount(c *gin.Context) { client, err := alipay.InitAlipay(nil) if err != nil { e.OutErr(c, e.ERR, err.Error()) return } appId := client.AppId scope := "auth_user" sysCfgDb := sys_cfg.NewSysCfgDb(db.Db) sysCfgs, err := sysCfgDb.SysCfgGetAll() if err != nil { e.OutErr(c, e.ERR_DB_ORM, err.Error()) return } if sysCfgs == nil { e.OutErr(c, e.ERR_CFG_CACHE, nil) return } cfgMap := make(map[string]string, len(*sysCfgs)) for _, cfg := range *sysCfgs { cfgMap[cfg.Key] = cfg.Val } targetId := utils.UUIDHexString() alipayPrivateKey := cfgMap[enum2.AlipayPrivateKey] pid := cfgMap[enum2.AlipayPid] bm := make(gopay.BodyMap) bm.Set("apiname", "com.alipay.account.auth") bm.Set("app_id", appId) bm.Set("app_name", "mc") bm.Set("auth_type", "AUTHACCOUNT") bm.Set("biz_type", "openservice") bm.Set("pid", pid) bm.Set("product_id", "APP_FAST_LOGIN") bm.Set("scope", scope) bm.Set("sign_type", "RSA2") bm.Set("method", "alipay.open.auth.sdk.code.get") bm.Set("target_id", targetId) privateKey, err := utils.StringToPrivateKey(alipayPrivateKey) if err != nil { e.OutErr(c, e.ERR, err.Error()) return } sign, err := alipay2.GetRsaSign(bm, alipay2.RSA2, privateKey) if err != nil { e.OutErr(c, e.ERR, err.Error()) return } resUrl := fmt.Sprintf("apiname=com.alipay.account.auth&app_id=%s&app_name=mc&"+ "auth_type=AUTHACCOUNT&biz_type=openservice&method=alipay.open.auth.sdk.code.get"+ "&pid=%s&product_id=APP_FAST_LOGIN&scope=%s&sign_type=RSA2&"+ "target_id=%s&sign=%s", appId, pid, scope, targetId, sign) e.OutSuc(c, resUrl, nil) } // BindAlipayAccount // @Summary 蛋蛋星球-钱包-绑定支付宝 // @Tags 钱包 // @Description 绑定支付宝 // @Accept json // @Produce json // @param Authorization header string true "验证参数Bearer和token空格拼接" // @Param req body md.BindAlipayAccountReq true "具体参数" // @Success 200 {string} "success" // @Failure 400 {object} md.Response "具体错误" // @Router /api/v1/wallet/withdraw/bindAlipay [POST] func BindAlipayAccount(c *gin.Context) { var req md.BindAlipayAccountReq err := c.ShouldBindJSON(&req) if err != nil { err = svc.HandleValidateErr(err) err1 := err.(e.E) e.OutErr(c, err1.Code, err1.Error()) return } user := svc.GetUser(c) var alipayStruct *alipay.InitAlipayStruct client, err := alipay.InitAlipay(alipayStruct) if err != nil { e.OutErr(c, e.ERR, err.Error()) return } bm := make(gopay.BodyMap) bm = bm.Set("grant_type", "authorization_code") bm = bm.Set("code", req.AuthCode) systemOauthToken, err := client.SystemOauthToken(c, bm) if err != nil { e.OutErr(c, e.ERR, err.Error()) return } info, err := client.UserInfoShare(c, systemOauthToken.Response.AccessToken) if err != nil { e.OutErr(c, e.ERR, err.Error()) return } infoDb := implement.NewAlipayUserInfoDb(db.Db) userInfo, err := infoDb.GetAlipayUserInfo(user.Id) if err != nil { e.OutErr(c, e.ERR, err.Error()) return } now := time.Now() if userInfo == nil { m := model.AlipayUserInfo{ Uid: user.Id, UserId: info.Response.UserId, OpenId: info.Response.OpenId, AppId: client.AppId, UserName: info.Response.NickName, Ext: "", CreateAt: now.Format("2006-01-02 15:04:05"), UpdateAt: now.Format("2006-01-02 15:04:05"), } _, err = infoDb.AlipayUserInfoInsert(&m) if err != nil { e.OutErr(c, e.ERR_DB_ORM, err.Error()) return } } else { cols := []string{"open_id", "app_id", "user_id", "user_name"} m := model.AlipayUserInfo{ Id: userInfo.Id, Uid: userInfo.Uid, UserId: info.Response.UserId, OpenId: info.Response.OpenId, AppId: client.AppId, UserName: info.Response.NickName, Ext: "", CreateAt: userInfo.CreateAt, UpdateAt: now.Format("2006-01-02 15:04:05"), } _, err := infoDb.UpdateAlipayUserInfo(&m, cols...) if err != nil { e.OutErr(c, e.ERR_DB_ORM, err.Error()) return } } e.OutSuc(c, "success", nil) } // BindWxPayAccount // @Summary 蛋蛋星球-钱包-绑定微信支付 // @Tags 钱包 // @Description 绑定微信支付 // @Accept json // @Produce json // @param Authorization header string true "验证参数Bearer和token空格拼接" // @Param req body md.BindWxPayAccountReq true "具体参数" // @Success 200 {string} "success" // @Failure 400 {object} md.Response "具体错误" // @Router /api/v1/wallet/withdraw/bindWxPay [POST] func BindWxPayAccount(c *gin.Context) { var req md.BindWxPayAccountReq err := c.ShouldBindJSON(&req) if err != nil { err = svc.HandleValidateErr(err) err1 := err.(e.E) e.OutErr(c, err1.Code, err1.Error()) return } user := svc.GetUser(c) wxUserInfoDb := implement.NewWxUserInfoDb(db.Db) wxUserInfo, err := wxUserInfoDb.GetWxUserInfo(user.Id) if err != nil { e.OutErr(c, e.ERR_DB_ORM, err.Error()) return } if wxUserInfo != nil { e.OutErr(c, e.ERR, errors.New("用户已绑定过微信").Error()) return } now := time.Now() newWxUserInfo := model.WxUserInfo{ Uid: user.Id, UserId: req.UserId, OpenId: req.OpenId, AppId: req.AppId, Ext: req.Ext, CreateAt: now.Format("2006-01-02 15:04:05"), UpdateAt: now.Format("2006-01-02 15:04:05"), } affected, err := wxUserInfoDb.WxUserInfoInsert(&newWxUserInfo) if err != nil { e.OutErr(c, e.ERR_DB_ORM, err.Error()) return } if affected <= 0 { e.OutErr(c, e.ERR, errors.New("绑定失败")) } e.OutSuc(c, "success", nil) }