蛋蛋星球-客户端
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 

580 lignes
16 KiB

  1. package hdl
  2. import (
  3. "applet/app/cfg"
  4. "applet/app/db"
  5. "applet/app/e"
  6. alipay "applet/app/lib/gopay"
  7. "applet/app/md"
  8. "applet/app/svc"
  9. "applet/app/utils"
  10. "applet/app/utils/cache"
  11. "code.fnuoos.com/EggPlanet/egg_models.git/src/implement"
  12. "code.fnuoos.com/EggPlanet/egg_models.git/src/model"
  13. rule2 "code.fnuoos.com/EggPlanet/egg_system_rules.git"
  14. "code.fnuoos.com/EggPlanet/egg_system_rules.git/enum"
  15. enum2 "code.fnuoos.com/EggPlanet/egg_system_rules.git/enum"
  16. md2 "code.fnuoos.com/EggPlanet/egg_system_rules.git/md"
  17. "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule"
  18. md3 "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule/egg_energy/md"
  19. "code.fnuoos.com/go_rely_warehouse/zyos_go_mq.git/rabbit"
  20. "errors"
  21. "fmt"
  22. "github.com/gin-gonic/gin"
  23. "github.com/go-pay/gopay"
  24. alipay2 "github.com/go-pay/gopay/alipay"
  25. "github.com/jinzhu/copier"
  26. "time"
  27. )
  28. // GetAmountFlow
  29. // @Summary 蛋蛋星球-钱包-余额明细(获取)
  30. // @Tags 钱包
  31. // @Description 余额明细(获取)
  32. // @Accept json
  33. // @Produce json
  34. // @param Authorization header string true "验证参数Bearer和token空格拼接"
  35. // @Param limit query string true "每页大小"
  36. // @Param page query string true "页数"
  37. // @Param startAt query string false "开始时间"
  38. // @Param endAt query string false "结束时间"
  39. // @Param direction query string false "流水方向(1.收入 2.支出 0.全部)"
  40. // @Success 200 {object} md.GetAmountFlowResp "具体数据"
  41. // @Failure 400 {object} md.Response "具体错误"
  42. // @Router /api/v1/wallet/amountFlow [GET]
  43. func GetAmountFlow(c *gin.Context) {
  44. pageStr := c.DefaultQuery("page", "1")
  45. limitStr := c.DefaultQuery("limit", "10")
  46. startAt := c.Query("startAt")
  47. endAt := c.Query("endAt")
  48. directionStr := c.Query("direction")
  49. val, exists := c.Get("user")
  50. if !exists {
  51. e.OutErr(c, e.ERR_USER_CHECK_ERR, nil)
  52. return
  53. }
  54. user, ok := val.(*model.User)
  55. if !ok {
  56. e.OutErr(c, e.ERR_USER_CHECK_ERR, nil)
  57. return
  58. }
  59. direction := 0
  60. switch directionStr {
  61. case "1":
  62. direction = 1
  63. case "2":
  64. direction = 2
  65. }
  66. page := utils.StrToInt(pageStr)
  67. limit := utils.StrToInt(limitStr)
  68. flowDb := implement.NewUserWalletFlowDb(db.Db)
  69. flows, total, err := flowDb.UserWalletFlowFindByCoinAndUser(page, limit, user.Id, startAt, endAt, direction, false, 0, 0)
  70. if err != nil {
  71. e.OutErr(c, e.ERR_DB_ORM, err.Error())
  72. return
  73. }
  74. list := make([]md.WalletFlowNode, 0, len(flows))
  75. for _, flow := range flows {
  76. temp := md.WalletFlowNode{
  77. Id: flow.Id,
  78. Uid: flow.Uid,
  79. Direction: flow.Direction,
  80. Amount: flow.Amount,
  81. BeforeAmount: flow.BeforeAmount,
  82. AfterAmount: flow.AfterAmount,
  83. SysFee: flow.SysFee,
  84. OrdId: flow.OrdId,
  85. Title: flow.Title,
  86. Kind: flow.Kind,
  87. State: flow.State,
  88. Memo: flow.Memo,
  89. CreateTime: flow.CreateAt,
  90. UpdateTime: flow.UpdateAt,
  91. }
  92. list = append(list, temp)
  93. }
  94. resp := md.GetAmountFlowResp{
  95. List: list,
  96. Paginate: md.Paginate{
  97. Limit: limit,
  98. Page: page,
  99. Total: total,
  100. },
  101. }
  102. e.OutSuc(c, resp, nil)
  103. }
  104. // WithdrawGetAmount
  105. // @Summary 蛋蛋星球-钱包-提现余额(获取)
  106. // @Tags 钱包
  107. // @Description 提现余额(获取)
  108. // @Accept json
  109. // @Produce json
  110. // @param Authorization header string true "验证参数Bearer和token空格拼接"
  111. // @Success 200 {object} md.WithdrawGetAmountResp "具体数据"
  112. // @Failure 400 {object} md.Response "具体错误"
  113. // @Router /api/v1/wallet/withdraw/index [GET]
  114. func WithdrawGetAmount(c *gin.Context) {
  115. val, exists := c.Get("user")
  116. if !exists {
  117. e.OutErr(c, e.ERR_USER_CHECK_ERR, nil)
  118. return
  119. }
  120. user, ok := val.(*model.User)
  121. if !ok {
  122. e.OutErr(c, e.ERR_USER_CHECK_ERR, nil)
  123. return
  124. }
  125. walletDb := implement.NewUserWalletDb(db.Db)
  126. wallet, err := walletDb.GetUserVirtualWallet(user.Id)
  127. if err != nil {
  128. e.OutErr(c, e.ERR_DB_ORM, err.Error())
  129. return
  130. }
  131. resp := md.WithdrawGetAmountResp{
  132. Amount: wallet.Amount,
  133. }
  134. e.OutSuc(c, resp, nil)
  135. }
  136. // WithdrawApply
  137. // @Summary 蛋蛋星球-钱包-发起提现
  138. // @Tags 钱包
  139. // @Description 发起提现
  140. // @Accept json
  141. // @Produce json
  142. // @param Authorization header string true "验证参数Bearer和token空格拼接"
  143. // @Param req body md.WithdrawApplyReq true "具体参数"
  144. // @Success 200 {string} "success"
  145. // @Failure 400 {object} md.Response "具体错误"
  146. // @Router /api/v1/wallet/withdraw/apply [POST]
  147. func WithdrawApply(c *gin.Context) {
  148. var req md.WithdrawApplyReq
  149. err := c.ShouldBindJSON(&req)
  150. if err != nil {
  151. err = svc.HandleValidateErr(err)
  152. err1 := err.(e.E)
  153. e.OutErr(c, err1.Code, err1.Error())
  154. return
  155. }
  156. user := svc.GetUser(c)
  157. var userId, openId string
  158. var kind int
  159. //sysCfgDb := implement.NewSysCfgDb(db.Db, cache.GetPool().Get())
  160. //sysCfgMap := sysCfgDb.SysCfgFindWithDb(enum.AlipayAppId, enum.WxAppId)
  161. if req.Kind == enum.FinWithdrawApplyWithdrawKindForAli.String() {
  162. alipayUserInfoDb := implement.NewAlipayUserInfoDb(db.Db)
  163. aliInfo, err := alipayUserInfoDb.GetAlipayUserInfo(user.Id)
  164. if err != nil {
  165. e.OutErr(c, e.ERR, err.Error())
  166. return
  167. }
  168. if aliInfo == nil {
  169. e.OutErr(c, e.ERR, "支付宝用户信息未授权")
  170. return
  171. }
  172. //appId = sysCfgMap[enum.AlipayAppId]
  173. userId = aliInfo.UserId
  174. openId = aliInfo.OpenId
  175. kind = int(enum.FinWithdrawApplyWithdrawKindForAli)
  176. } else if req.Kind == enum.FinWithdrawApplyWithdrawKindForWx.String() {
  177. wxUserInfoDb := implement.NewWxUserInfoDb(db.Db)
  178. wxInfo, err := wxUserInfoDb.GetWxUserInfo(user.Id)
  179. if err != nil {
  180. e.OutErr(c, e.ERR, err.Error())
  181. return
  182. }
  183. if wxInfo == nil {
  184. e.OutErr(c, e.ERR, "微信用户信息未授权")
  185. return
  186. }
  187. //appId = sysCfgMap[enum.WxAppId]
  188. userId = wxInfo.UserId
  189. openId = wxInfo.OpenId
  190. kind = int(enum.FinWithdrawApplyWithdrawKindForWx)
  191. } else {
  192. e.OutErr(c, e.ERR, "未知的提现类型")
  193. return
  194. }
  195. //1、判断是否可以提现
  196. err, realAmount, fee, isAuto, isFirst := svc.CheckWithdraw(c, req.Amount)
  197. if err != nil {
  198. e.OutErr(c, e.ERR, err.Error())
  199. return
  200. }
  201. // 2、加锁 防止并发提取
  202. mutexKey := fmt.Sprintf("egg_app_withdraw_apply:%s", utils.Int64ToStr(user.Id))
  203. withdrawAvailable, err := cache.Do("SET", mutexKey, 1, "EX", 5, "NX")
  204. if err != nil {
  205. e.OutErr(c, e.ERR, err)
  206. return
  207. }
  208. if withdrawAvailable != "OK" {
  209. e.OutErr(c, e.ERR, e.NewErr(400000, "请求过于频繁,请稍后再试"))
  210. return
  211. }
  212. // 开启事务
  213. session := db.Db.NewSession()
  214. defer session.Close()
  215. err = session.Begin()
  216. if err != nil {
  217. session.Rollback()
  218. e.OutErr(c, e.ERR_DB_ORM, err)
  219. return
  220. }
  221. //3、处理用户余额
  222. dealUserWalletReq := md2.DealUserWalletReq{
  223. Direction: "sub",
  224. Kind: int(enum.UserWithdrawApply),
  225. Title: enum.UserWithdrawApply.String(),
  226. Uid: user.Id,
  227. Amount: utils.StrToFloat64(req.Amount),
  228. }
  229. rule2.Init(cfg.RedisAddr)
  230. err = rule.DealUserWallet(session, dealUserWalletReq)
  231. if err != nil {
  232. e.OutErr(c, e.ERR_DB_ORM, err.Error())
  233. session.Rollback()
  234. return
  235. }
  236. //4、新增提现记录
  237. now := time.Now()
  238. finWithdrawApplyDb := implement.NewFinWithdrawApplyDb(db.Db)
  239. finWithdrawApply := &model.FinWithdrawApply{
  240. Uid: user.Id,
  241. AdmId: 0,
  242. Amount: req.Amount,
  243. RealAmount: realAmount,
  244. Fee: fee,
  245. Type: func(isAuto bool) int {
  246. if isAuto {
  247. return 2
  248. }
  249. return 1
  250. }(isAuto),
  251. WithdrawAccount: userId,
  252. WithdrawName: openId,
  253. Reason: 0,
  254. PaymentDate: "",
  255. State: 0,
  256. WithdrawKind: kind,
  257. IsFirst: func(isFirst bool) int {
  258. if isFirst {
  259. return 1
  260. }
  261. return 0
  262. }(isFirst),
  263. Memo: "",
  264. UpdateAt: now.Format("2006-01-02 15:04:05"),
  265. CreateAt: now.Format("2006-01-02 15:04:05"),
  266. }
  267. insertAffected, err := finWithdrawApplyDb.FinWithdrawApplyInsertOneBySession(session, finWithdrawApply)
  268. if err != nil {
  269. session.Rollback()
  270. e.OutErr(c, e.ERR_DB_ORM, err)
  271. return
  272. }
  273. if insertAffected <= 0 {
  274. session.Rollback()
  275. e.OutErr(c, e.ERR_DB_ORM, "生成提现单失败")
  276. return
  277. }
  278. err = session.Begin()
  279. if err != nil {
  280. session.Rollback()
  281. e.OutErr(c, e.ERR_DB_ORM, err)
  282. return
  283. }
  284. err = session.Commit()
  285. if err != nil {
  286. _ = session.Rollback()
  287. e.OutErr(c, e.ERR_DB_ORM, err)
  288. return
  289. }
  290. //5、推入mq
  291. if isAuto {
  292. //更改提现单记录状态
  293. finWithdrawApply.State = int(enum.FinWithdrawApplyStateForIng)
  294. updateAffected, err1 := finWithdrawApplyDb.UpdateFinWithdrawApply(finWithdrawApply, "state")
  295. if err1 != nil {
  296. e.OutErr(c, e.ERR_DB_ORM, err1.Error())
  297. return
  298. }
  299. if updateAffected <= 0 {
  300. e.OutErr(c, e.ERR_DB_ORM, "更新提现单状态失败")
  301. return
  302. }
  303. ch, err1 := rabbit.Cfg.Pool.GetChannel()
  304. if err1 != nil {
  305. e.OutErr(c, e.ERR_INIT_RABBITMQ, err1.Error())
  306. return
  307. }
  308. defer ch.Release()
  309. var data md3.EggFinWithdrawApplyData
  310. err = copier.Copy(&data, &finWithdrawApply)
  311. if err != nil {
  312. e.OutErr(c, e.ERR, err.Error())
  313. return
  314. }
  315. ch.Publish(md3.EggAppExchange, utils.SerializeStr(data), md3.EggFinWithdrawApply)
  316. }
  317. e.OutSuc(c, "success", nil)
  318. }
  319. // GetWithdrawCondition
  320. // @Summary 蛋蛋星球-钱包-提现条件(获取)
  321. // @Tags 钱包
  322. // @Description 提现条件(获取)
  323. // @Accept json
  324. // @Produce json
  325. // @param Authorization header string true "验证参数Bearer和token空格拼接"
  326. // @Success 200 {object} md.GetWithdrawConditionResp "具体数据"
  327. // @Failure 400 {object} md.Response "具体错误"
  328. // @Router /api/v1/wallet/withdraw/condition [GET]
  329. func GetWithdrawCondition(c *gin.Context) {
  330. user := svc.GetUser(c)
  331. settingDb := implement.NewFinWithdrawSettingDb(db.Db)
  332. setting, err := settingDb.FinWithdrawSettingGetOne()
  333. if err != nil {
  334. e.OutErr(c, e.ERR_DB_ORM, err)
  335. return
  336. }
  337. //1. 判断是否为第一次提现
  338. isFirst := false
  339. has, err := db.Db.Where("uid = ?", user.Id).
  340. Get(model.FinWithdrawApply{})
  341. if !has { //第一次提现
  342. isFirst = true
  343. }
  344. resp := svc.GetWithdrawCondition(user, setting, isFirst)
  345. alipayUserInfoDb := implement.NewAlipayUserInfoDb(db.Db)
  346. alipayInfo, err := alipayUserInfoDb.GetAlipayUserInfo(user.Id)
  347. if err != nil {
  348. e.OutErr(c, e.ERR_DB_ORM, err.Error())
  349. return
  350. }
  351. if alipayInfo != nil {
  352. resp.IsBindAlipay = true
  353. }
  354. userInfoDb := implement.NewWxUserInfoDb(db.Db)
  355. wxUserInfo, err := userInfoDb.GetWxUserInfo(user.Id)
  356. if err != nil {
  357. e.OutErr(c, e.ERR_DB_ORM, err.Error())
  358. return
  359. }
  360. if wxUserInfo != nil {
  361. resp.IsBindWx = true
  362. }
  363. e.OutSuc(c, resp, nil)
  364. }
  365. // LaunchBindAlipayAccount
  366. // @Summary 蛋蛋星球-钱包-发起绑定支付宝获得URL
  367. // @Tags 钱包
  368. // @Description 发起绑定支付宝获得URL
  369. // @Accept json
  370. // @Produce json
  371. // @param Authorization header string true "验证参数Bearer和token空格拼接"
  372. // @Success 200 {string} "Url"
  373. // @Failure 400 {object} md.Response "具体错误"
  374. // @Router /api/v1/wallet/withdraw/launchBindAlipay [GET]
  375. func LaunchBindAlipayAccount(c *gin.Context) {
  376. client, err := alipay.InitAlipay(nil)
  377. if err != nil {
  378. e.OutErr(c, e.ERR, err.Error())
  379. return
  380. }
  381. appId := client.AppId
  382. scope := "auth_user"
  383. redisConn := cache.GetPool().Get()
  384. sysCfgDb := implement.NewSysCfgDb(db.Db, redisConn)
  385. sysCfgs, err := sysCfgDb.SysCfgGetAll()
  386. if err != nil {
  387. e.OutErr(c, e.ERR_DB_ORM, err.Error())
  388. return
  389. }
  390. if sysCfgs == nil {
  391. e.OutErr(c, e.ERR_CFG_CACHE, nil)
  392. return
  393. }
  394. cfgMap := make(map[string]string, len(*sysCfgs))
  395. for _, cfg := range *sysCfgs {
  396. cfgMap[cfg.Key] = cfg.Val
  397. }
  398. targetId := utils.UUIDHexString()
  399. alipayPrivateKey := cfgMap[enum2.AlipayPrivateKey]
  400. pid := cfgMap[enum2.AlipayPid]
  401. bm := make(gopay.BodyMap)
  402. bm.Set("apiname", "com.alipay.account.auth")
  403. bm.Set("app_id", appId)
  404. bm.Set("app_name", "mc")
  405. bm.Set("auth_type", "AUTHACCOUNT")
  406. bm.Set("biz_type", "openservice")
  407. bm.Set("pid", pid)
  408. bm.Set("product_id", "APP_FAST_LOGIN")
  409. bm.Set("scope", scope)
  410. bm.Set("sign_type", "RSA2")
  411. bm.Set("method", "alipay.open.auth.sdk.code.get")
  412. bm.Set("target_id", targetId)
  413. privateKey, err := utils.StringToPrivateKey(alipayPrivateKey)
  414. if err != nil {
  415. e.OutErr(c, e.ERR, err.Error())
  416. return
  417. }
  418. sign, err := alipay2.GetRsaSign(bm, alipay2.RSA2, privateKey)
  419. if err != nil {
  420. e.OutErr(c, e.ERR, err.Error())
  421. return
  422. }
  423. resUrl := fmt.Sprintf("apiname=com.alipay.account.auth&app_id=%s&app_name=mc&"+
  424. "auth_type=AUTHACCOUNT&biz_type=openservice&method=alipay.open.auth.sdk.code.get"+
  425. "&pid=%s&product_id=APP_FAST_LOGIN&scope=%s&sign_type=RSA2&"+
  426. "target_id=%s&sign=%s", appId, pid, scope, targetId, sign)
  427. e.OutSuc(c, resUrl, nil)
  428. }
  429. // BindAlipayAccount
  430. // @Summary 蛋蛋星球-钱包-绑定支付宝
  431. // @Tags 钱包
  432. // @Description 绑定支付宝
  433. // @Accept json
  434. // @Produce json
  435. // @param Authorization header string true "验证参数Bearer和token空格拼接"
  436. // @Param req body md.BindAlipayAccountReq true "具体参数"
  437. // @Success 200 {string} "success"
  438. // @Failure 400 {object} md.Response "具体错误"
  439. // @Router /api/v1/wallet/withdraw/bindAlipay [POST]
  440. func BindAlipayAccount(c *gin.Context) {
  441. var req md.BindAlipayAccountReq
  442. err := c.ShouldBindJSON(&req)
  443. if err != nil {
  444. err = svc.HandleValidateErr(err)
  445. err1 := err.(e.E)
  446. e.OutErr(c, err1.Code, err1.Error())
  447. return
  448. }
  449. user := svc.GetUser(c)
  450. var alipayStruct *alipay.InitAlipayStruct
  451. client, err := alipay.InitAlipay(alipayStruct)
  452. if err != nil {
  453. e.OutErr(c, e.ERR, err.Error())
  454. return
  455. }
  456. bm := make(gopay.BodyMap)
  457. bm = bm.Set("grant_type", "authorization_code")
  458. bm = bm.Set("code", req.AuthCode)
  459. systemOauthToken, err := client.SystemOauthToken(c, bm)
  460. if err != nil {
  461. e.OutErr(c, e.ERR, err.Error())
  462. return
  463. }
  464. info, err := client.UserInfoShare(c, systemOauthToken.Response.AccessToken)
  465. if err != nil {
  466. e.OutErr(c, e.ERR, err.Error())
  467. return
  468. }
  469. now := time.Now()
  470. m := model.AlipayUserInfo{
  471. Uid: user.Id,
  472. UserId: info.Response.UserId,
  473. OpenId: info.Response.OpenId,
  474. AppId: client.AppId,
  475. UserName: info.Response.NickName,
  476. Ext: "",
  477. CreateAt: now.Format("2006-01-02 15:04:05"),
  478. UpdateAt: now.Format("2006-01-02 15:04:05"),
  479. }
  480. infoDb := implement.NewAlipayUserInfoDb(db.Db)
  481. _, err = infoDb.AlipayUserInfoInsert(&m)
  482. if err != nil {
  483. e.OutErr(c, e.ERR_DB_ORM, err.Error())
  484. return
  485. }
  486. e.OutSuc(c, "success", nil)
  487. }
  488. // BindWxPayAccount
  489. // @Summary 蛋蛋星球-钱包-绑定微信支付
  490. // @Tags 钱包
  491. // @Description 绑定微信支付
  492. // @Accept json
  493. // @Produce json
  494. // @param Authorization header string true "验证参数Bearer和token空格拼接"
  495. // @Param req body md.BindWxPayAccountReq true "具体参数"
  496. // @Success 200 {string} "success"
  497. // @Failure 400 {object} md.Response "具体错误"
  498. // @Router /api/v1/wallet/withdraw/bindWxPay [POST]
  499. func BindWxPayAccount(c *gin.Context) {
  500. var req md.BindWxPayAccountReq
  501. err := c.ShouldBindJSON(&req)
  502. if err != nil {
  503. err = svc.HandleValidateErr(err)
  504. err1 := err.(e.E)
  505. e.OutErr(c, err1.Code, err1.Error())
  506. return
  507. }
  508. user := svc.GetUser(c)
  509. wxUserInfoDb := implement.NewWxUserInfoDb(db.Db)
  510. wxUserInfo, err := wxUserInfoDb.GetWxUserInfo(user.Id)
  511. if err != nil {
  512. e.OutErr(c, e.ERR_DB_ORM, err.Error())
  513. return
  514. }
  515. if wxUserInfo != nil {
  516. e.OutErr(c, e.ERR, errors.New("用户已绑定过微信").Error())
  517. return
  518. }
  519. now := time.Now()
  520. newWxUserInfo := model.WxUserInfo{
  521. Uid: user.Id,
  522. UserId: req.UserId,
  523. OpenId: req.OpenId,
  524. AppId: req.AppId,
  525. Ext: req.Ext,
  526. CreateAt: now.Format("2006-01-02 15:04:05"),
  527. UpdateAt: now.Format("2006-01-02 15:04:05"),
  528. }
  529. affected, err := wxUserInfoDb.WxUserInfoInsert(&newWxUserInfo)
  530. if err != nil {
  531. e.OutErr(c, e.ERR_DB_ORM, err.Error())
  532. return
  533. }
  534. if affected <= 0 {
  535. e.OutErr(c, e.ERR, errors.New("绑定失败"))
  536. }
  537. e.OutSuc(c, "success", nil)
  538. }