123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729 |
- // Copyright 2019 github.com. All rights reserved.
- // Use of this source code is governed by github.com.
- package v1
- import (
- "fmt"
- "net/http"
- "property-household-gateway/errors"
- param_v1 "property-household-gateway/param/v1"
- "property-household-gateway/pb"
- "property-household-gateway/pb/v1"
- "property-household-gateway/utils"
- "strings"
- "time"
- "git.getensh.com/common/gopkgs/logger"
- "git.getensh.com/common/gopkgs/tasker/httptasker"
- "git.getensh.com/common/gopkgs/util"
- "github.com/dgrijalva/jwt-go"
- "property-household-gateway/parser"
- "git.getensh.com/common/gopkgs/jwtwrapper"
- "github.com/gin-gonic/gin"
- "go.uber.org/zap"
- )
- func TokenTest() {
- subject := map[string]interface{}{
- "nick_name": "",
- "open_id": "",
- "real_name": "",
- "phone": "",
- }
- str, _ := json.MarshalToString(subject)
- token, _ := jwtwrapper.GenToken(fmt.Sprintf("%d", 1), parser.Conf.Jwt.Issuer, str,
- time.Duration(parser.Conf.Jwt.Seconds)*time.Second)
- fmt.Printf("token:%s\n", token)
- }
- // 登录
- // @Summary 登录
- // @Description 登录
- // @Tags 用户
- // @Accept json
- // @Produce json
- // @Param body body v1.LoginBody true "登录信息"
- // @Success 200 {object} v1.LoginResponse
- // @Failure 500 {object} base.HTTPError
- // @Router /api/v1/user/login [post]
- func (c *Controller) Login(ctx *gin.Context) {
- // 解析参数
- req := ¶m_v1.LoginRequest{}
- parseParamTask := func() error {
- err := util.ShouldBind(ctx, &req.Header, nil, nil, &req.LoginBody)
- if err != nil {
- logger.Error("func",
- zap.String("call", "util.ShouldBind"),
- zap.String("error", err.Error()))
- return errors.ParamsError
- }
- return nil
- }
- // 业务处理
- handleServiceTask := func() error {
- // 响应数据
- resp := param_v1.LoginResponse{}
- rpcReq := &v1.LoginRequest{
- Code: req.Code,
- PhoneEncrypt: req.PhoneEncrypt,
- PhoneIv: req.PhoneIv,
- UserEncrypt: req.UserEncrypt,
- UserIv: req.UserIv,
- }
- rpcRsp, err := pb.Household.Login(ctx, rpcReq)
- if err != nil {
- s, _ := json.MarshalToString(req)
- logger.Error("func",
- zap.String("call", "pb.Household.Login"),
- zap.String("params", s),
- zap.String("error", err.Error()))
- return errors.ErrorTransForm(err)
- }
- subject := map[string]interface{}{
- "nick_name": rpcRsp.NickName,
- "open_id": rpcRsp.OpenId,
- "real_name": rpcRsp.RealName,
- "phone": rpcRsp.Phone,
- "openim_id": rpcRsp.OpenimId,
- "user_icon": rpcRsp.Avatar,
- "id_number": rpcRsp.IdNumber,
- }
- str, _ := json.MarshalToString(subject)
- // 生成token
- token, err := jwtwrapper.GenToken(fmt.Sprintf("%d", rpcRsp.Uid), parser.Conf.Jwt.Issuer, str,
- time.Duration(parser.Conf.Jwt.Seconds)*time.Second)
- if err != nil {
- logger.Error("func",
- zap.String("call", "util.GenJwtToken"),
- zap.String("args", fmt.Sprintf("%d", rpcRsp.Uid)),
- zap.String("error", err.Error()))
- return errors.SystemError
- }
- resp.Data.Uid = rpcRsp.Uid
- resp.Data.Token = token
- resp.Data.User = rpcRsp.RealName
- resp.Data.OpenId = rpcRsp.OpenId
- resp.Data.Avatar = rpcRsp.Avatar
- resp.Data.NickName = rpcRsp.NickName
- resp.Data.OpenimId = rpcRsp.OpenimId
- resp.Data.Phone = rpcRsp.Phone
- if resp.Data.User == "" {
- resp.Data.User = rpcRsp.Phone
- }
- ctx.JSON(http.StatusOK, resp)
- return nil
- }
- // 执行任务
- httptasker.Exec(ctx, parseParamTask, handleServiceTask)
- }
- //
- // @Summary 获取openim token
- // @Description 获取openim_token
- // @Tags openim
- // @Accept json
- // @Produce json
- // @Param token header string true "token"
- // @Param openim_id query string true "openim_id"
- // @Success 200 {object} v1.OpenImTokenResponse
- // @Failure 500 {object} base.HTTPError
- // @Router /api/v1/user/openim_token [get]
- func (c *Controller) OpenImToken(ctx *gin.Context) {
- // 解析参数
- req := ¶m_v1.OpenImTokenRequest{}
- parseParamTask := func() error {
- err := util.ShouldBind(ctx, &req.Header, nil, &req.OpenImTokenQuery, nil)
- if err != nil {
- logger.Error("func",
- zap.String("call", "util.ShouldBind"),
- zap.String("error", err.Error()))
- return errors.ParamsError
- }
- return nil
- }
- // 业务处理
- handleServiceTask := func() error {
- // 响应数据
- resp := param_v1.OpenImTokenResponse{}
- rpcReq := &v1.OpenImTokenRequest{
- Platform: 5,
- OpenimId: req.OpenimId,
- }
- rpcRsp, err := pb.Thirdparty.OpenImToken(ctx, rpcReq)
- if err != nil {
- s, _ := json.MarshalToString(req)
- logger.Error("func",
- zap.String("call", "pb.Thirdparty.OpenImToken"),
- zap.String("params", s),
- zap.String("error", err.Error()))
- return errors.ErrorTransForm(err)
- }
- resp.Data = *rpcRsp
- ctx.JSON(http.StatusOK, resp)
- return nil
- }
- // 执行任务
- httptasker.Exec(ctx, parseParamTask, handleServiceTask)
- }
- //
- // @Summary 批量获取openim 用户信息
- // @Description 批量获取openim_用户信息
- // @Tags openim
- // @Accept json
- // @Produce json
- // @Param token header string true "token"
- // @Param openim_ids query string true "用户openim_id,以逗号分割"
- // @Success 200 {object} v1.OpenImUsersInfoResponse
- // @Failure 500 {object} base.HTTPError
- // @Router /api/v1/user/openim_users_info [get]
- func (c *Controller) OpenImUsersInfo(ctx *gin.Context) {
- // 解析参数
- req := ¶m_v1.OpenImUsersInfoRequest{}
- parseParamTask := func() error {
- err := util.ShouldBind(ctx, &req.Header, nil, &req.OpenImUsersInfoQuery, nil)
- if err != nil {
- logger.Error("func",
- zap.String("call", "util.ShouldBind"),
- zap.String("error", err.Error()))
- return errors.ParamsError
- }
- return nil
- }
- // 业务处理
- handleServiceTask := func() error {
- // 响应数据
- resp := param_v1.OpenImUsersInfoResponse{}
- array := strings.Split(req.OpenimIds, ",")
- rpcReq := &v1.OpenImUserInfoRequest{
- PlatformId: 5,
- OpenimIds: array,
- }
- rpcRsp, err := pb.Thirdparty.OpenImUserInfo(ctx, rpcReq)
- if err != nil {
- s, _ := json.MarshalToString(req)
- logger.Error("func",
- zap.String("call", "pb.Thirdparty.OpenImUserInfo"),
- zap.String("params", s),
- zap.String("error", err.Error()))
- return errors.ErrorTransForm(err)
- }
- if len(rpcRsp.List) == 0 {
- rpcRsp.List = make([]*v1.OpenImUserData, 0)
- }
- resp.Data = *rpcRsp
- ctx.JSON(http.StatusOK, resp)
- return nil
- }
- // 执行任务
- httptasker.Exec(ctx, parseParamTask, handleServiceTask)
- }
- // token
- // @Summary 刷新token
- // @Description 刷新token
- // @Tags 用户
- // @Accept json
- // @Produce json
- // @Param token header string true "token"
- // @Success 200 {object} v1.TokenResponse
- // @Failure 500 {object} base.HTTPError
- // @Router /api/v1/token_refresh [put]
- func (c *Controller) TokenRefresh(ctx *gin.Context) {
- // 解析参数
- req := ¶m_v1.TokenRequest{}
- parseParamTask := func() error {
- err := util.ShouldBind(ctx, &req.Header, nil, nil, nil)
- if err != nil {
- logger.Error("func",
- zap.String("call", "util.ShouldBind"),
- zap.String("error", err.Error()))
- return errors.ParamsError
- }
- return nil
- }
- // 业务处理
- handleServiceTask := func() error {
- tokenObj, err := jwtwrapper.ParseToken(req.Token)
- if tokenObj == nil {
- return errors.TokenFailedError
- }
- if err != nil {
- switch err.(*jwt.ValidationError).Errors {
- case jwt.ValidationErrorExpired:
- if tokenObj == nil {
- return errors.TokenFailedError
- }
- if time.Now().Unix()-tokenObj.ExpiresAt > 600 {
- return errors.TokenExpiredError
- }
- default:
- return errors.TokenFailedError
- }
- }
- uid := tokenObj.Id
- subject := tokenObj.Subject
- //remberPass := gjson.GetBytes([]byte(subject), "rember_password").Bool()
- // 生成token
- token, err := jwtwrapper.GenToken(uid, parser.Conf.Jwt.Issuer, subject,
- time.Duration(parser.Conf.Jwt.Seconds)*time.Second)
- if err != nil {
- logger.Error("func",
- zap.String("call", "util.GenJwtToken"),
- zap.String("args", fmt.Sprintf("%s", uid)),
- zap.String("error", err.Error()))
- return errors.SystemError
- }
- /*
- refreshTokenTime := time.Duration(24*60*60) * time.Second
- if remberPass {
- refreshTokenTime = time.Duration(7*24*60*60) * time.Second
- }
- refreshToken, err := jwtwrapper.GenToken(uid, parser.Conf.Jwt.Issuer, subject,
- refreshTokenTime)*/
- if err != nil {
- logger.Error("func",
- zap.String("call", "util.GenJwtToken"),
- zap.String("args", fmt.Sprintf("%s", uid)),
- zap.String("error", err.Error()))
- return errors.SystemError
- }
- resp := param_v1.TokenResponse{}
- resp.Data = token
- resp.RefreshToken = token
- ctx.JSON(http.StatusOK, resp)
- return nil
- }
- // 执行任务
- httptasker.Exec(ctx, parseParamTask, handleServiceTask)
- }
- //
- // @Summary 实名认证
- // @Description 实名认证
- // @Tags 用户
- // @Accept json
- // @Produce json
- // @Param token header string true "token"
- // @Param body body v1.RealNameAuthBody true "信息"
- // @Success 200 {object} v1.RealNameAuthResponse
- // @Failure 500 {object} base.HTTPError
- // @Router /api/v1/user/real_name_auth [put]
- func (c *Controller) RealNameAuth(ctx *gin.Context) {
- // 解析参数
- req := ¶m_v1.RealNameAuthRequest{}
- parseParamTask := func() error {
- err := util.ShouldBind(ctx, &req.Header, nil, nil, &req.RealNameAuthBody)
- if err != nil {
- logger.Error("func",
- zap.String("call", "util.ShouldBind"),
- zap.String("error", err.Error()))
- return errors.ParamsError
- }
- return nil
- }
- // 业务处理
- handleServiceTask := func() error {
- tokenInfo, err := utils.GetJwtTokenInfo(ctx)
- if err != nil {
- return err
- }
- // 响应数据
- resp := param_v1.RealNameAuthResponse{}
- rpcReq := &v1.RealNameAuthRequest{
- Uid: tokenInfo.Uid,
- IdNumber: req.IdNumber,
- IdType: req.IdType,
- RealName: req.RealName,
- }
- _, err = pb.Household.RealNameAuth(ctx, rpcReq)
- if err != nil {
- s, _ := json.MarshalToString(req)
- logger.Error("func",
- zap.String("call", "pb.Household.RealNameAuth"),
- zap.String("params", s),
- zap.String("error", err.Error()))
- return errors.ErrorTransForm(err)
- }
- subject := map[string]interface{}{
- "nick_name": tokenInfo.NickName,
- "open_id": tokenInfo.OpenId,
- "real_name": tokenInfo.RealName,
- "phone": tokenInfo.Phone,
- "openim_id": tokenInfo.OpenimId,
- "user_icon": tokenInfo.UserIcon,
- "id_number": tokenInfo.IdNumber,
- }
- if req.RealName != "" && req.RealName != tokenInfo.RealName {
- subject["real_name"] = req.RealName
- }
- if req.IdNumber != "" && req.IdNumber != tokenInfo.IdNumber {
- subject["id_number"] = req.IdNumber
- }
- str, _ := json.MarshalToString(subject)
- // 生成token
- token, err := jwtwrapper.GenToken(fmt.Sprintf("%d", tokenInfo.Uid), parser.Conf.Jwt.Issuer, str,
- time.Duration(parser.Conf.Jwt.Seconds)*time.Second)
- if err != nil {
- logger.Error("func",
- zap.String("call", "util.GenJwtToken"),
- zap.String("args", fmt.Sprintf("%d", tokenInfo.Uid)),
- zap.String("error", err.Error()))
- return errors.SystemError
- }
- resp.Data.Token = token
- ctx.JSON(http.StatusOK, resp)
- return nil
- }
- // 执行任务
- httptasker.Exec(ctx, parseParamTask, handleServiceTask)
- }
- //
- // @Summary 修改昵称
- // @Description 修改昵称
- // @Tags 用户
- // @Accept json
- // @Produce json
- // @Param token header string true "token"
- // @Param body body v1.UserUpdateNickNameBody true "信息"
- // @Success 200 {object} v1.UserUpdateNickNameResponse
- // @Failure 500 {object} base.HTTPError
- // @Router /api/v1/user/nick_name [put]
- func (c *Controller) UserUpdateNickName(ctx *gin.Context) {
- // 解析参数
- req := ¶m_v1.UserUpdateNickNameRequest{}
- parseParamTask := func() error {
- err := util.ShouldBind(ctx, &req.Header, nil, nil, &req.UserUpdateNickNameBody)
- if err != nil {
- logger.Error("func",
- zap.String("call", "util.ShouldBind"),
- zap.String("error", err.Error()))
- return errors.ParamsError
- }
- return nil
- }
- // 业务处理
- handleServiceTask := func() error {
- tokenInfo, err := utils.GetJwtTokenInfo(ctx)
- if err != nil {
- return err
- }
- // 响应数据
- resp := param_v1.UserUpdateNickNameResponse{}
- rpcReq := &v1.UserUpdateNickNameRequest{
- Uid: tokenInfo.Uid,
- NickName: req.NickName,
- }
- _, err = pb.Household.UserUpdateNickName(ctx, rpcReq)
- if err != nil {
- s, _ := json.MarshalToString(req)
- logger.Error("func",
- zap.String("call", "pb.Household.UserUpdateNickName"),
- zap.String("params", s),
- zap.String("error", err.Error()))
- return errors.ErrorTransForm(err)
- }
- ctx.JSON(http.StatusOK, resp)
- return nil
- }
- // 执行任务
- httptasker.Exec(ctx, parseParamTask, handleServiceTask)
- }
- //
- // @Summary 获取用户信息
- // @Description 获取用户信息
- // @Tags 用户
- // @Accept json
- // @Produce json
- // @Param token header string true "token"
- // @Success 200 {object} v1.UserInfoResponse
- // @Failure 500 {object} base.HTTPError
- // @Router /api/v1/user/info [get]
- func (c *Controller) UserInfo(ctx *gin.Context) {
- // 解析参数
- req := ¶m_v1.UserInfoRequest{}
- parseParamTask := func() error {
- err := util.ShouldBind(ctx, &req.Header, nil, nil, nil)
- if err != nil {
- logger.Error("func",
- zap.String("call", "util.ShouldBind"),
- zap.String("error", err.Error()))
- return errors.ParamsError
- }
- return nil
- }
- // 业务处理
- handleServiceTask := func() error {
- tokenInfo, err := utils.GetJwtTokenInfo(ctx)
- if err != nil {
- return err
- }
- // 响应数据
- resp := param_v1.UserInfoResponse{}
- rpcReq := &v1.UserInfoRequest{
- Uid: tokenInfo.Uid,
- }
- rpcRsp, err := pb.Household.UserInfo(ctx, rpcReq)
- if err != nil {
- s, _ := json.MarshalToString(req)
- logger.Error("func",
- zap.String("call", "pb.Household.UserInfo"),
- zap.String("params", s),
- zap.String("error", err.Error()))
- return errors.ErrorTransForm(err)
- }
- resp.Data = *rpcRsp
- ctx.JSON(http.StatusOK, resp)
- return nil
- }
- // 执行任务
- httptasker.Exec(ctx, parseParamTask, handleServiceTask)
- }
- //
- // @Summary 用户的房屋
- // @Description 用户的房屋
- // @Tags 用户
- // @Accept json
- // @Produce json
- // @Param token header string true "token"
- // @Param for_rent query bool false "true:返回可发布租房的房屋 false:返回用户的所有房屋"
- // @Success 200 {object} v1.HouseholdHousesResponse
- // @Failure 500 {object} base.HTTPError
- // @Router /api/v1/user/house [get]
- func (c *Controller) HouseholdHouses(ctx *gin.Context) {
- // 解析参数
- req := ¶m_v1.HouseholdHousesRequest{}
- parseParamTask := func() error {
- err := util.ShouldBind(ctx, &req.Header, nil, &req.HouseholdHousesQuery, nil)
- if err != nil {
- logger.Error("func",
- zap.String("call", "util.ShouldBind"),
- zap.String("error", err.Error()))
- return errors.ParamsError
- }
- return nil
- }
- // 业务处理
- handleServiceTask := func() error {
- tokenInfo, err := utils.GetJwtTokenInfo(ctx)
- if err != nil {
- return err
- }
- // 响应数据
- resp := param_v1.HouseholdHousesResponse{}
- rpcReq := &v1.HouseholdHousesRequest{
- Uid: tokenInfo.Uid,
- ForRent: req.ForRent,
- //BaseConf:
- //SpecialConf
- }
- rpcRsp, err := pb.Household.HouseholdHouses(ctx, rpcReq)
- if err != nil {
- s, _ := json.MarshalToString(req)
- logger.Error("func",
- zap.String("call", "pb.Household.HouseholdHouses"),
- zap.String("params", s),
- zap.String("error", err.Error()))
- return errors.ErrorTransForm(err)
- }
- if rpcRsp.List == nil {
- rpcRsp.List = make([]*v1.HouseholdHouseInfo, 0)
- }
- resp.Data = *rpcRsp
- ctx.JSON(http.StatusOK, resp)
- return nil
- }
- // 执行任务
- httptasker.Exec(ctx, parseParamTask, handleServiceTask)
- }
- //
- // @Summary 用户房屋下的车位(仅业主可查看)
- // @Description 用户房屋下的车位(仅业主可查看)
- // @Tags 用户
- // @Accept json
- // @Produce json
- // @Param token header string true "token"
- // @Param house_id query int true "房屋id"
- // @Param garden_id query int true "小区id"
- // @Param user_type query int true "住户类型1 业主 2 家人 3 租户"
- // @Success 200 {object} v1.HouseholdSpaceResponse
- // @Failure 500 {object} base.HTTPError
- // @Router /api/v1/user/space [get]
- func (c *Controller) HouseholdSpace(ctx *gin.Context) {
- // 解析参数
- req := ¶m_v1.HouseholdSpaceRequest{}
- parseParamTask := func() error {
- err := util.ShouldBind(ctx, &req.Header, nil, &req.HouseholdSpaceQuery, nil)
- if err != nil {
- logger.Error("func",
- zap.String("call", "util.ShouldBind"),
- zap.String("error", err.Error()))
- return errors.ParamsError
- }
- return nil
- }
- // 业务处理
- handleServiceTask := func() error {
- //tokenInfo, err := utils.GetJwtTokenInfo(ctx)
- //if err != nil {
- // return err
- //}
- // 响应数据
- resp := param_v1.HouseholdSpaceResponse{}
- rpcReq := &v1.ParkSpaceListRequest{
- GardenId: req.GardenId,
- PageSize: -1,
- Page: -1,
- HouseId: req.HouseId,
- }
- // 非业主不能查看
- if req.UserType != 1 {
- resp.Data = v1.ParkSpaceListReply{}
- resp.Data.List = make([]*v1.ParkSpaceItem, 0)
- ctx.JSON(http.StatusOK, resp)
- return nil
- }
- rpcRsp, err := pb.Garden.ParkSpaceList(ctx, rpcReq)
- if err != nil {
- s, _ := json.MarshalToString(req)
- logger.Error("func",
- zap.String("call", "pb.Garden.ParkSpaceList"),
- zap.String("params", s),
- zap.String("error", err.Error()))
- return errors.ErrorTransForm(err)
- }
- if rpcRsp.List == nil {
- rpcRsp.List = make([]*v1.ParkSpaceItem, 0)
- }
- resp.Data = *rpcRsp
- ctx.JSON(http.StatusOK, resp)
- return nil
- }
- // 执行任务
- httptasker.Exec(ctx, parseParamTask, handleServiceTask)
- }
- //
- // @Summary 用户小区下的车辆
- // @Description 用户小区下的车辆
- // @Tags 用户
- // @Accept json
- // @Produce json
- // @Param token header string true "token"
- // @Param garden_id query int true "小区id"
- // @Success 200 {object} v1.HouseholdVehicleResponse
- // @Failure 500 {object} base.HTTPError
- // @Router /api/v1/user/vehicle [get]
- func (c *Controller) HouseholdVehicle(ctx *gin.Context) {
- // 解析参数
- req := ¶m_v1.HouseholdVehicleRequest{}
- parseParamTask := func() error {
- err := util.ShouldBind(ctx, &req.Header, nil, &req.HouseholdVehicleQuery, nil)
- if err != nil {
- logger.Error("func",
- zap.String("call", "util.ShouldBind"),
- zap.String("error", err.Error()))
- return errors.ParamsError
- }
- return nil
- }
- // 业务处理
- handleServiceTask := func() error {
- tokenInfo, err := utils.GetJwtTokenInfo(ctx)
- if err != nil {
- return err
- }
- //响应数据
- resp := param_v1.HouseholdVehicleResponse{}
- rpcReq := &v1.VehicleListRequest{
- GardenId: req.GardenId,
- PageSize: -1,
- Page: -1,
- HouseholdUid: tokenInfo.Uid,
- }
- rpcRsp, err := pb.Garden.VehicleList(ctx, rpcReq)
- if err != nil {
- s, _ := json.MarshalToString(req)
- logger.Error("func",
- zap.String("call", "pb.Garden.VehicleList"),
- zap.String("params", s),
- zap.String("error", err.Error()))
- return errors.ErrorTransForm(err)
- }
- if rpcRsp.List == nil {
- rpcRsp.List = make([]*v1.VehicleItem, 0)
- }
- resp.Data = *rpcRsp
- ctx.JSON(http.StatusOK, resp)
- return nil
- }
- // 执行任务
- httptasker.Exec(ctx, parseParamTask, handleServiceTask)
- }
|