蛋蛋星球-客户端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

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