From 7ea7b36776d1e6b05d25df6985fb3da1f2da3627 Mon Sep 17 00:00:00 2001 From: Leufolium Date: Sat, 23 Dec 2023 22:03:53 +0800 Subject: [PATCH] by Robin at 20231223; contact session --- api/consts/predicate.go | 10 +- api/errcode/errcode.go | 8 + .../proto/contact_customer_service_api.go | 16 +- .../proto/contact_customer_service_op.go | 42 ++--- .../proto/contact_customer_service_vo_op.go | 6 +- .../proto/not_null_def_api.go | 15 +- .../proto/not_null_def_op.go | 28 +-- .../contact_customer_service_session_api.go | 50 ++++++ .../contact_customer_service_session_op.go | 68 +++++++ .../proto/not_null_def_api.go | 22 +++ .../proto/not_null_def_op.go | 22 +++ .../contact_customer_service_api.go | 10 +- .../controller/contact_customer_service_op.go | 26 +-- .../contact_customer_service_session_api.go | 42 +++++ .../contact_customer_service_session_op.go | 57 ++++++ app/mix/controller/init.go | 18 +- app/mix/dao/mongo.go | 69 ++++++- app/mix/dao/mongo_idseq.go | 29 ++- app/mix/service/apiservice.go | 52 +++++- .../service/logic/contact_customer_service.go | 12 +- .../logic/contact_customer_service_session.go | 72 ++++++++ .../service/opservice_business_validation.go | 58 +++++- app/mix/service/service.go | 168 ++++++++++++------ codecreate/codecreate.go | 28 +-- codecreate/consts/consts.go | 2 +- codecreate/resource/EntityDefine.xlsx | Bin 39037 -> 40118 bytes dbstruct/contact_customer_service.go | 17 +- dbstruct/contact_customer_service_session.go | 12 ++ dbstruct/idSeq.go | 4 + 29 files changed, 765 insertions(+), 198 deletions(-) create mode 100644 api/proto/contact_customer_service_session/proto/contact_customer_service_session_api.go create mode 100644 api/proto/contact_customer_service_session/proto/contact_customer_service_session_op.go create mode 100644 api/proto/contact_customer_service_session/proto/not_null_def_api.go create mode 100644 api/proto/contact_customer_service_session/proto/not_null_def_op.go create mode 100644 app/mix/controller/contact_customer_service_session_api.go create mode 100644 app/mix/controller/contact_customer_service_session_op.go create mode 100644 app/mix/service/logic/contact_customer_service_session.go create mode 100644 dbstruct/contact_customer_service_session.go diff --git a/api/consts/predicate.go b/api/consts/predicate.go index 57b6cf61..58366d5c 100644 --- a/api/consts/predicate.go +++ b/api/consts/predicate.go @@ -13,8 +13,8 @@ const ( Ignore = 2 //不感兴趣 IsIgnored = 3 //被不感兴趣 Friend = 4 //好友 - UnLockWeixinTo = 5 //主语已向宾语解锁微信 - AbleToAccessWeixinOf = 6 //主语可从宾语处查看微信 + UnLockWeixinTo = 5 //主语已向宾语解锁微信-已弃用 + AbleToAccessWeixinOf = 6 //主语可从宾语处查看微信-已弃用 ) // call_history谓词 @@ -22,3 +22,9 @@ const ( Like = 0 //喜欢 TakeNoInterest = 1 //无感 ) + +// contact_customer_service谓词 +const ( + ContactCustomerService_FromUser = 0 + ContactCustomerService_FromSupportor = 1 +) diff --git a/api/errcode/errcode.go b/api/errcode/errcode.go index 6e363c59..1cd304ff 100644 --- a/api/errcode/errcode.go +++ b/api/errcode/errcode.go @@ -120,6 +120,9 @@ var ErrCodeMsgMap = map[ErrCode]string{ ErrCodeTextAuditTaskSrvFail: "文字审核任务服务错误", ErrCodeTextAuditTaskNotExist: "文字审核任务不存在", ErrCodeTextAuditTaskManuallyPassFail: "文字审核人工通过失败", + + ErrCodeContactCustomerServiceSessionSrvFail: "联系客服对话表服务错误", + ErrCodeContactCustomerServiceSessionNotExist: "联系客服对话表不存在", } const ( @@ -286,6 +289,11 @@ const ( ErrCodeTextAuditTaskNotExist ErrCode = -23002 // 文字审核任务不存在 ErrCodeTextAuditTaskManuallyPassFail ErrCode = -23003 // 文字审核人工通过失败 + // ContactCustomerServiceSession: 24xxx + ErrCodeContactCustomerServiceSessionSrvOk ErrCode = ErrCodeOk + ErrCodeContactCustomerServiceSessionSrvFail ErrCode = -24001 // 联系客服对话表服务错误 + ErrCodeContactCustomerServiceSessionNotExist ErrCode = -24002 // 联系客服对话表不存在 + // Media: 60xxx ErrCodeMediaSrvOk ErrCode = ErrCodeOk ErrCodeMediaSrvFail ErrCode = -60001 // 媒体服务错误 diff --git a/api/proto/contact_customer_service/proto/contact_customer_service_api.go b/api/proto/contact_customer_service/proto/contact_customer_service_api.go index 9730aabd..7ecf9fe8 100644 --- a/api/proto/contact_customer_service/proto/contact_customer_service_api.go +++ b/api/proto/contact_customer_service/proto/contact_customer_service_api.go @@ -19,21 +19,21 @@ type ApiCreateResp struct { Data *ApiCreateData `json:"data"` } -// api 列表-单mid查询 -type ApiListByMidReq struct { +// api 列表-单session_id查询 +type ApiListBySessionIdReq struct { base.BaseRequest - Mid *int64 `json:"mid"` - Offset int `json:"offset"` - Limit int `json:"limit"` + SessionId *int64 `json:"session_id"` + Offset int `json:"offset"` + Limit int `json:"limit"` } -type ApiListByMidData struct { +type ApiListBySessionIdData struct { List []*dbstruct.ContactCustomerService `json:"list"` Offset int `json:"offset"` More int `json:"more"` } -type ApiListByMidResp struct { +type ApiListBySessionIdResp struct { base.BaseResponse - Data *ApiListByMidData `json:"data"` + Data *ApiListBySessionIdData `json:"data"` } diff --git a/api/proto/contact_customer_service/proto/contact_customer_service_op.go b/api/proto/contact_customer_service/proto/contact_customer_service_op.go index bf7dff35..cb72a2c4 100644 --- a/api/proto/contact_customer_service/proto/contact_customer_service_op.go +++ b/api/proto/contact_customer_service/proto/contact_customer_service_op.go @@ -19,20 +19,6 @@ type OpCreateResp struct { Data *OpCreateData `json:"data"` } -// op 删除 -type OpDeleteReq struct { - base.BaseRequest - Id *int64 `json:"id"` -} - -type OpDeleteData struct { -} - -type OpDeleteResp struct { - base.BaseResponse - Data *OpDeleteData `json:"data"` -} - // op 更新 type OpUpdateByIdsReq struct { base.BaseRequest @@ -48,35 +34,35 @@ type OpUpdateByIdsResp struct { Data *OpUpdateByIdsData `json:"data"` } -// op 列表 -type OpListReq struct { +// op 查询所有未读 +type OpListUnreadReq struct { base.BaseRequest } -type OpListData struct { - List []*ContactCustomerServiceOpVO `json:"list"` +type OpListUnreadData struct { + List []*ContactCustomerServiceOpUnreadVO `json:"list"` } -type OpListResp struct { +type OpListUnreaResp struct { base.BaseResponse - Data *OpListData `json:"data"` + Data *OpListUnreadData `json:"data"` } -// api 列表-单mid查询 -type OpListByMidReq struct { +// api 列表-单sessionId查询 +type OpListBySessionIdReq struct { base.BaseRequest - Mid *int64 `json:"mid"` - Offset int `json:"offset"` - Limit int `json:"limit"` + SessionId *int64 `json:"session_id"` + Offset int `json:"offset"` + Limit int `json:"limit"` } -type OpListByMidData struct { +type OpListBySessionIdData struct { List []*dbstruct.ContactCustomerService `json:"list"` Offset int `json:"offset"` More int `json:"more"` } -type OpListByMidResp struct { +type OpListBySessionIdResp struct { base.BaseResponse - Data *OpListByMidData `json:"data"` + Data *OpListBySessionIdData `json:"data"` } diff --git a/api/proto/contact_customer_service/proto/contact_customer_service_vo_op.go b/api/proto/contact_customer_service/proto/contact_customer_service_vo_op.go index e1576c21..d2313f2f 100644 --- a/api/proto/contact_customer_service/proto/contact_customer_service_vo_op.go +++ b/api/proto/contact_customer_service/proto/contact_customer_service_vo_op.go @@ -2,7 +2,7 @@ package proto import "service/dbstruct" -type ContactCustomerServiceOpVO struct { - Mid int64 `json:"mid"` // 待回复用户mid - List []*dbstruct.ContactCustomerService `json:"contents"` // 待回复用户内容 +type ContactCustomerServiceOpUnreadVO struct { + SessionId int64 `json:"session_id"` // 待回复用户mid + List []*dbstruct.ContactCustomerService `json:"contents"` // 待回复用户内容 } diff --git a/api/proto/contact_customer_service/proto/not_null_def_api.go b/api/proto/contact_customer_service/proto/not_null_def_api.go index 66d91c98..fde6413b 100644 --- a/api/proto/contact_customer_service/proto/not_null_def_api.go +++ b/api/proto/contact_customer_service/proto/not_null_def_api.go @@ -6,19 +6,18 @@ import ( // api 创建 func (p *ApiCreateReq) ProvideNotNullValue() (params []*validator.JsonParam) { - params = make([]*validator.JsonParam, 4) + params = make([]*validator.JsonParam, 0) - params[0] = validator.NewInt64PtrParam("请填写主语mid!", p.SubMid) - params[1] = validator.NewInt64PtrParam("请填写宾语mid!", p.ObjMid) - params[2] = validator.NewInt64PtrParam("请填写谓词!", p.Predicate) - params[3] = validator.NewStringPtrParam("请填写聊天信息!", p.Message) + params = append(params, validator.NewInt64PtrParam("请填写对话ID!", p.SessionId)) + params = append(params, validator.NewInt64PtrParam("请填写谓词!", p.Predicate)) + params = append(params, validator.NewStringPtrParam("请填写聊天信息!", p.Message)) return } // api 列表 -func (p *ApiListByMidReq) ProvideNotNullValue() (params []*validator.JsonParam) { - params = make([]*validator.JsonParam, 1) - params[0] = validator.NewInt64PtrParam("mid should not be null", p.Mid) +func (p *ApiListBySessionIdReq) ProvideNotNullValue() (params []*validator.JsonParam) { + params = make([]*validator.JsonParam, 0) + params = append(params, validator.NewInt64PtrParam("session_id should not be null", p.SessionId)) return params } diff --git a/api/proto/contact_customer_service/proto/not_null_def_op.go b/api/proto/contact_customer_service/proto/not_null_def_op.go index a9460492..31e6f26e 100644 --- a/api/proto/contact_customer_service/proto/not_null_def_op.go +++ b/api/proto/contact_customer_service/proto/not_null_def_op.go @@ -6,35 +6,25 @@ import ( // op 创建 func (p *OpCreateReq) ProvideNotNullValue() (params []*validator.JsonParam) { - params = make([]*validator.JsonParam, 4) + params = make([]*validator.JsonParam, 0) - params[0] = validator.NewInt64PtrParam("请填写主语mid!", p.SubMid) - params[1] = validator.NewInt64PtrParam("请填写宾语mid!", p.ObjMid) - params[2] = validator.NewInt64PtrParam("请填写谓词!", p.Predicate) - params[3] = validator.NewStringPtrParam("请填写聊天信息!", p.Message) + params = append(params, validator.NewInt64PtrParam("请填写对话ID!", p.SessionId)) + params = append(params, validator.NewInt64PtrParam("请填写谓词!", p.Predicate)) + params = append(params, validator.NewStringPtrParam("请填写聊天信息!", p.Message)) return } // op 按id更新 func (p *OpUpdateByIdsReq) ProvideNotNullValue() (params []*validator.JsonParam) { - params = make([]*validator.JsonParam, 1) - params[0] = validator.NewInt64SliceParam("欲更新的联系客服Ids为空!", p.Ids) + params = make([]*validator.JsonParam, 0) + params = append(params, validator.NewInt64SliceParam("欲更新的联系客服Ids为空!", p.Ids)) return params } -// op 删除 -func (p *OpDeleteReq) ProvideNotNullValue() (params []*validator.JsonParam) { - params = make([]*validator.JsonParam, 1) - - params[0] = validator.NewInt64PtrParam("请填写欲删除的聊天记录Id!", p.Id) - - return -} - // op 列表 -func (p *OpListByMidReq) ProvideNotNullValue() (params []*validator.JsonParam) { - params = make([]*validator.JsonParam, 1) - params[0] = validator.NewInt64PtrParam("mid should not be null", p.Mid) +func (p *OpListBySessionIdReq) ProvideNotNullValue() (params []*validator.JsonParam) { + params = make([]*validator.JsonParam, 0) + params = append(params, validator.NewInt64PtrParam("session_id should not be null", p.SessionId)) return params } diff --git a/api/proto/contact_customer_service_session/proto/contact_customer_service_session_api.go b/api/proto/contact_customer_service_session/proto/contact_customer_service_session_api.go new file mode 100644 index 00000000..89899fa4 --- /dev/null +++ b/api/proto/contact_customer_service_session/proto/contact_customer_service_session_api.go @@ -0,0 +1,50 @@ +package proto + +import ( + "service/api/base" + "service/dbstruct" +) + +// op 创建 +type ApiCreateReq struct { + base.BaseRequest + *dbstruct.ContactCustomerServiceSession +} + +type ApiCreateData struct { + SessionId int64 `json:"session_id"` +} + +type ApiCreateResp struct { + base.BaseResponse + Data *ApiCreateData `json:"data"` +} + +// op 更新 +type ApiUpdateReq struct { + base.BaseRequest + *dbstruct.ContactCustomerServiceSession +} + +type ApiUpdateData struct { +} + +type ApiUpdateResp struct { + base.BaseResponse + Data *ApiUpdateData `json:"data"` +} + +// op 列表 +type ApiListByMidReq struct { + base.BaseRequest + Mid *int64 `json:"mid"` +} + +type ApiListByMidData struct { + Session *dbstruct.ContactCustomerServiceSession `json:"session"` +} + +type ApiListResp struct { + base.BaseResponse + Data *ApiListByMidData `json:"data"` +} diff --git a/api/proto/contact_customer_service_session/proto/contact_customer_service_session_op.go b/api/proto/contact_customer_service_session/proto/contact_customer_service_session_op.go new file mode 100644 index 00000000..6afdb92d --- /dev/null +++ b/api/proto/contact_customer_service_session/proto/contact_customer_service_session_op.go @@ -0,0 +1,68 @@ +package proto + +import ( + "service/api/base" + "service/dbstruct" +) + +// op 创建 +type OpCreateReq struct { + base.BaseRequest + *dbstruct.ContactCustomerServiceSession +} + +type OpCreateData struct { + SessionId int64 `json:"session_id"` +} + +type OpCreateResp struct { + base.BaseResponse + Data *OpCreateData `json:"data"` +} + +// op 更新 +type OpUpdateReq struct { + base.BaseRequest + *dbstruct.ContactCustomerServiceSession +} + +type OpUpdateData struct { +} + +type OpUpdateResp struct { + base.BaseResponse + Data *OpUpdateData `json:"data"` +} + +// op 列表 +type OpListByMidReq struct { + base.BaseRequest + Mid *int64 `json:"mid"` +} + +type OpListByMidData struct { + Session *dbstruct.ContactCustomerServiceSession `json:"session"` +} + +type OpListByMidResp struct { + base.BaseResponse + Data *OpListByMidData `json:"data"` +} + +// op 列出所有对话 +type OpListReq struct { + base.BaseRequest + Offset int `json:"offset"` + Limit int `json:"limit"` +} + +type OpListData struct { + List []*dbstruct.ContactCustomerServiceSession `json:"data"` + Offset int `json:"offset"` + More int `json:"more"` +} + +type OpListResp struct { + base.BaseResponse + Data *OpListData `json:"data"` +} diff --git a/api/proto/contact_customer_service_session/proto/not_null_def_api.go b/api/proto/contact_customer_service_session/proto/not_null_def_api.go new file mode 100644 index 00000000..f830ffa2 --- /dev/null +++ b/api/proto/contact_customer_service_session/proto/not_null_def_api.go @@ -0,0 +1,22 @@ +package proto + +import ( + "service/library/validator" +) + +// api 创建 +func (p *ApiCreateReq) ProvideNotNullValue() (params []*validator.JsonParam) { + params = make([]*validator.JsonParam, 0) + + params = append(params, validator.NewInt64PtrParam("请填写主语mid!", p.SubMid)) + params = append(params, validator.NewInt64PtrParam("请填写宾语mid!", p.ObjMid)) + + return +} + +// api 列表-mid单查 +func (p *ApiListByMidReq) ProvideNotNullValue() (params []*validator.JsonParam) { + params = make([]*validator.JsonParam, 0) + params = append(params, validator.NewInt64PtrParam("mid should not be null", p.Mid)) + return params +} diff --git a/api/proto/contact_customer_service_session/proto/not_null_def_op.go b/api/proto/contact_customer_service_session/proto/not_null_def_op.go new file mode 100644 index 00000000..b7a47650 --- /dev/null +++ b/api/proto/contact_customer_service_session/proto/not_null_def_op.go @@ -0,0 +1,22 @@ +package proto + +import ( + "service/library/validator" +) + +// op 创建 +func (p *OpCreateReq) ProvideNotNullValue() (params []*validator.JsonParam) { + params = make([]*validator.JsonParam, 0) + + params = append(params, validator.NewInt64PtrParam("请填写主语mid!", p.SubMid)) + params = append(params, validator.NewInt64PtrParam("请填写宾语mid!", p.ObjMid)) + + return +} + +// op 列表-mid单查 +func (p *OpListByMidReq) ProvideNotNullValue() (params []*validator.JsonParam) { + params = make([]*validator.JsonParam, 0) + params = append(params, validator.NewInt64PtrParam("mid should not be null", p.Mid)) + return params +} diff --git a/app/mix/controller/contact_customer_service_api.go b/app/mix/controller/contact_customer_service_api.go index b326a8a7..972f00ed 100644 --- a/app/mix/controller/contact_customer_service_api.go +++ b/app/mix/controller/contact_customer_service_api.go @@ -23,22 +23,22 @@ func ApiCreateContactCustomerService(ctx *gin.Context) { ReplyOk(ctx, nil) } -func ApiGetContactCustomerServiceListByMid(ctx *gin.Context) { - req := ctx.MustGet("client_req").(*contact_customer_serviceproto.ApiListByMidReq) +func ApiGetContactCustomerServiceListBySessionId(ctx *gin.Context) { + req := ctx.MustGet("client_req").(*contact_customer_serviceproto.ApiListBySessionIdReq) //设置默认页长 if req.Limit == 0 { req.Limit = consts.DefaultPageSize } - list, ec := service.DefaultService.ApiGetContactCustomerServiceListByMid(ctx, req) + list, ec := service.DefaultService.ApiGetContactCustomerServiceListBySessionId(ctx, req) if ec != errcode.ErrCodeContactCustomerServiceSrvOk { - logger.Error("OpGetContactCustomerServiceListByMid fail, req: %v, ec: %v", util.ToJson(req), ec) + logger.Error("OpGetContactCustomerServiceListBySessionId fail, req: %v, ec: %v", util.ToJson(req), ec) ReplyErrCodeMsg(ctx, ec) return } - data := &contact_customer_serviceproto.ApiListByMidData{ + data := &contact_customer_serviceproto.ApiListBySessionIdData{ List: list, Offset: req.Offset + len(list), } diff --git a/app/mix/controller/contact_customer_service_op.go b/app/mix/controller/contact_customer_service_op.go index 5eb180ae..fdc90dfc 100644 --- a/app/mix/controller/contact_customer_service_op.go +++ b/app/mix/controller/contact_customer_service_op.go @@ -35,34 +35,22 @@ func OpUpdateContactCustomerServiceByIds(ctx *gin.Context) { ReplyOk(ctx, nil) } -func OpDeleteContactCustomerService(ctx *gin.Context) { - req := ctx.MustGet("client_req").(*contact_customer_serviceproto.OpDeleteReq) - ec := service.DefaultService.OpDeleteContactCustomerService(ctx, util.DerefInt64(req.Id)) - if ec != errcode.ErrCodeContactCustomerServiceSrvOk { - logger.Error("OpDeleteContactCustomerService fail, req: %v, ec: %v", util.ToJson(req), ec) - ReplyErrCodeMsg(ctx, ec) - return - } - - ReplyOk(ctx, nil) -} - -func OpGetContactCustomerServiceListByMid(ctx *gin.Context) { - req := ctx.MustGet("client_req").(*contact_customer_serviceproto.OpListByMidReq) +func OpGetContactCustomerServiceListBySessionId(ctx *gin.Context) { + req := ctx.MustGet("client_req").(*contact_customer_serviceproto.OpListBySessionIdReq) //设置默认页长 if req.Limit == 0 { req.Limit = consts.DefaultPageSize } - list, ec := service.DefaultService.OpGetContactCustomerServiceListByMid(ctx, req) + list, ec := service.DefaultService.OpGetContactCustomerServiceListBySessionId(ctx, req) if ec != errcode.ErrCodeContactCustomerServiceSrvOk { - logger.Error("OpGetContactCustomerServiceListByMid fail, req: %v, ec: %v", util.ToJson(req), ec) + logger.Error("OpGetContactCustomerServiceListBySessionId fail, req: %v, ec: %v", util.ToJson(req), ec) ReplyErrCodeMsg(ctx, ec) return } - data := &contact_customer_serviceproto.OpListByMidData{ + data := &contact_customer_serviceproto.OpListBySessionIdData{ List: list, Offset: req.Offset + len(list), } @@ -73,7 +61,7 @@ func OpGetContactCustomerServiceListByMid(ctx *gin.Context) { } func OpGetContactCustomerServiceListUnreadGroupByMid(ctx *gin.Context) { - req := ctx.MustGet("client_req").(*contact_customer_serviceproto.OpListReq) + req := ctx.MustGet("client_req").(*contact_customer_serviceproto.OpListUnreadReq) list, ec := service.DefaultService.OpGetContactCustomerServiceListUnreadGroupByMid(ctx, req) if ec != errcode.ErrCodeContactCustomerServiceSrvOk { @@ -82,7 +70,7 @@ func OpGetContactCustomerServiceListUnreadGroupByMid(ctx *gin.Context) { return } - data := &contact_customer_serviceproto.OpListData{ + data := &contact_customer_serviceproto.OpListUnreadData{ List: list, } ReplyOk(ctx, data) diff --git a/app/mix/controller/contact_customer_service_session_api.go b/app/mix/controller/contact_customer_service_session_api.go new file mode 100644 index 00000000..4eebc656 --- /dev/null +++ b/app/mix/controller/contact_customer_service_session_api.go @@ -0,0 +1,42 @@ +package controller + +import ( + "service/api/errcode" + contact_customer_service_sessionproto "service/api/proto/contact_customer_service_session/proto" + "service/app/mix/service" + "service/bizcommon/util" + "service/library/logger" + + "github.com/gin-gonic/gin" +) + +func ApiCreateContactCustomerServiceSession(ctx *gin.Context) { + req := ctx.MustGet("client_req").(*contact_customer_service_sessionproto.ApiCreateReq) + ec := service.DefaultService.ApiCreateContactCustomerServiceSession(ctx, req) + if ec != errcode.ErrCodeContactCustomerServiceSessionSrvOk { + logger.Error("ApiCreateContactCustomerServiceSession fail, req: %v, ec: %v", util.ToJson(req), ec) + ReplyErrorMsg(ctx, "server error") + return + } + + data := &contact_customer_service_sessionproto.ApiCreateData{ + SessionId: util.DerefInt64(req.Id), + } + + ReplyOk(ctx, data) +} + +func ApiGetContactCustomerServiceSessionListByMid(ctx *gin.Context) { + req := ctx.MustGet("client_req").(*contact_customer_service_sessionproto.ApiListByMidReq) + session, ec := service.DefaultService.ApiGetContactCustomerServiceSessionListByMid(ctx, req) + if ec != errcode.ErrCodeContactCustomerServiceSessionSrvOk { + logger.Error("ApiGetContactCustomerServiceSessionListByMid fail, req: %v, ec: %v", util.ToJson(req), ec) + ReplyErrCodeMsg(ctx, ec) + return + } + + data := &contact_customer_service_sessionproto.ApiListByMidData{ + Session: session, + } + ReplyOk(ctx, data) +} diff --git a/app/mix/controller/contact_customer_service_session_op.go b/app/mix/controller/contact_customer_service_session_op.go new file mode 100644 index 00000000..3dbde2cc --- /dev/null +++ b/app/mix/controller/contact_customer_service_session_op.go @@ -0,0 +1,57 @@ +package controller + +import ( + "service/api/errcode" + contact_customer_service_sessionproto "service/api/proto/contact_customer_service_session/proto" + "service/app/mix/service" + "service/bizcommon/util" + "service/library/logger" + + "github.com/gin-gonic/gin" +) + +func OpCreateContactCustomerServiceSession(ctx *gin.Context) { + req := ctx.MustGet("client_req").(*contact_customer_service_sessionproto.OpCreateReq) + ec := service.DefaultService.OpCreateContactCustomerServiceSession(ctx, req) + if ec != errcode.ErrCodeContactCustomerServiceSessionSrvOk { + logger.Error("OpCreateContactCustomerServiceSession fail, req: %v, ec: %v", util.ToJson(req), ec) + ReplyErrorMsg(ctx, "server error") + return + } + + data := &contact_customer_service_sessionproto.OpCreateData{ + SessionId: util.DerefInt64(req.Id), + } + + ReplyOk(ctx, data) +} + +func OpGetContactCustomerServiceSessionListByMid(ctx *gin.Context) { + req := ctx.MustGet("client_req").(*contact_customer_service_sessionproto.OpListByMidReq) + session, ec := service.DefaultService.OpGetContactCustomerServiceSessionListByMid(ctx, req) + if ec != errcode.ErrCodeContactCustomerServiceSessionSrvOk { + logger.Error("OpGetContactCustomerServiceSessionListByMid fail, req: %v, ec: %v", util.ToJson(req), ec) + ReplyErrCodeMsg(ctx, ec) + return + } + + data := &contact_customer_service_sessionproto.OpListByMidData{ + Session: session, + } + ReplyOk(ctx, data) +} + +func OpGetContactCustomerServiceSessionList(ctx *gin.Context) { + req := ctx.MustGet("client_req").(*contact_customer_service_sessionproto.OpListReq) + list, ec := service.DefaultService.OpGetContactCustomerServiceSessionList(ctx, req) + if ec != errcode.ErrCodeContactCustomerServiceSessionSrvOk { + logger.Error("OpGetContactCustomerServiceSessionList fail, req: %v, ec: %v", util.ToJson(req), ec) + ReplyErrCodeMsg(ctx, ec) + return + } + + data := &contact_customer_service_sessionproto.OpListData{ + List: list, + } + ReplyOk(ctx, data) +} diff --git a/app/mix/controller/init.go b/app/mix/controller/init.go index ca915bb5..f1a25648 100644 --- a/app/mix/controller/init.go +++ b/app/mix/controller/init.go @@ -22,6 +22,7 @@ import ( accountrelationproto "service/api/proto/accountrelation/proto" callhistoryproto "service/api/proto/callhistory/proto" contact_customer_serviceproto "service/api/proto/contact_customer_service/proto" + contact_customer_service_sessionproto "service/api/proto/contact_customer_service_session/proto" feedbackproto "service/api/proto/feedback/proto" footprintproto "service/api/proto/footprint/proto" loginproto "service/api/proto/login/proto" @@ -136,7 +137,12 @@ func Init(r *gin.Engine) { // 联系客服 apiContactCustomerServiceGroup := r.Group("/api/contact_customer_service", PrepareToC()) apiContactCustomerServiceGroup.POST("create", middleware.JSONParamValidator(contact_customer_serviceproto.ApiCreateReq{}), ApiCreateContactCustomerService) - apiContactCustomerServiceGroup.POST("list_by_mid", middleware.JSONParamValidator(contact_customer_serviceproto.ApiListByMidReq{}), ApiGetContactCustomerServiceListByMid) + apiContactCustomerServiceGroup.POST("list_by_mid", middleware.JSONParamValidator(contact_customer_serviceproto.ApiListBySessionIdReq{}), ApiGetContactCustomerServiceListBySessionId) + + // 联系客服对话表 + apiContactCustomerServiceSessionGroup := r.Group("/api/contact_customer_service_session", PrepareToC()) + apiContactCustomerServiceSessionGroup.POST("create", middleware.JSONParamValidator(contact_customer_service_sessionproto.ApiCreateReq{}), ApiCreateContactCustomerServiceSession) + apiContactCustomerServiceSessionGroup.POST("list_by_mid", middleware.JSONParamValidator(contact_customer_service_sessionproto.ApiListByMidReq{}), ApiGetContactCustomerServiceSessionListByMid) // 主播标签 apiStreamerTagGroup := r.Group("/api/streamer_tag", PrepareOp()) @@ -302,8 +308,14 @@ func Init(r *gin.Engine) { opContactCustomerServiceGroup.POST("create", middleware.JSONParamValidator(contact_customer_serviceproto.OpCreateReq{}), OpCreateContactCustomerService) opContactCustomerServiceGroup.POST("update_by_ids", middleware.JSONParamValidator(contact_customer_serviceproto.OpUpdateByIdsReq{}), OpUpdateContactCustomerServiceByIds) //opContactCustomerServiceGroup.POST("delete", middleware.JSONParamValidator(contact_customer_serviceproto.OpDeleteReq{}), OpDeleteContactCustomerService) - opContactCustomerServiceGroup.POST("list_by_mid", middleware.JSONParamValidator(contact_customer_serviceproto.OpListByMidReq{}), OpGetContactCustomerServiceListByMid) - opContactCustomerServiceGroup.POST("list_unread_group_by_mid", middleware.JSONParamValidator(contact_customer_serviceproto.OpListReq{}), OpGetContactCustomerServiceListUnreadGroupByMid) + opContactCustomerServiceGroup.POST("list_by_session", middleware.JSONParamValidator(contact_customer_serviceproto.OpListBySessionIdReq{}), OpGetContactCustomerServiceListBySessionId) + opContactCustomerServiceGroup.POST("list_unread_group_by_session_id", middleware.JSONParamValidator(contact_customer_serviceproto.OpListUnreadReq{}), OpGetContactCustomerServiceListUnreadGroupByMid) + + // 联系客服对话表 + opContactCustomerServiceSessionGroup := r.Group("/op/contact_customer_service_session", PrepareOp()) + opContactCustomerServiceSessionGroup.POST("create", middleware.JSONParamValidator(contact_customer_service_sessionproto.OpCreateReq{}), OpCreateContactCustomerServiceSession) + opContactCustomerServiceSessionGroup.POST("list_by_mid", middleware.JSONParamValidator(contact_customer_service_sessionproto.OpListByMidReq{}), OpGetContactCustomerServiceSessionListByMid) + opContactCustomerServiceSessionGroup.POST("list", middleware.JSONParamValidator(contact_customer_service_sessionproto.OpListReq{}), OpGetContactCustomerServiceSessionList) // 图片审核任务 opImageAuditTaskGroup := r.Group("/op/image_audit_task", PrepareOp()) diff --git a/app/mix/dao/mongo.go b/app/mix/dao/mongo.go index ab4adad8..4d61054a 100644 --- a/app/mix/dao/mongo.go +++ b/app/mix/dao/mongo.go @@ -17,6 +17,7 @@ import ( accountrelationproto "service/api/proto/accountrelation/proto" callhistoryproto "service/api/proto/callhistory/proto" contact_customer_service_proto "service/api/proto/contact_customer_service/proto" + contact_customer_service_sessionproto "service/api/proto/contact_customer_service_session/proto" feedbackproto "service/api/proto/feedback/proto" footprintproto "service/api/proto/footprint/proto" imageaudittaskproto "service/api/proto/imageaudittask/proto" @@ -134,6 +135,9 @@ const ( DBTextAudit = "text_audit" COLTextAudit = "text_audit" COLTextAuditTask = "text_audit_task" + + DBContactCustomerServiceSession = "contact_customer_service_session" + COLContactCustomerServiceSession = "contact_customer_service_session" ) // 商品表 @@ -305,6 +309,11 @@ func (m *Mongo) getColTextAuditTask() *qmgo.Collection { return m.clientMix.Database(DBTextAudit).Collection(COLTextAuditTask) } +// 联系客服对话表表 +func (m *Mongo) getColContactCustomerServiceSession() *qmgo.Collection { + return m.clientMix.Database(DBContactCustomerServiceSession).Collection(COLContactCustomerServiceSession) +} + // 商品相关 func (m *Mongo) CreateProduct(ctx *gin.Context, product *dbstruct.Product) error { col := m.getColProduct() @@ -2103,12 +2112,13 @@ func (m *Mongo) DeleteContactCustomerService(ctx *gin.Context, id int64) error { return err } -func (m *Mongo) GetContactCustomerServiceListUnread(ctx *gin.Context, req *contact_customer_service_proto.OpListReq) ([]*dbstruct.ContactCustomerService, error) { +func (m *Mongo) GetUnreadContactCustomerServiceList(ctx *gin.Context, req *contact_customer_service_proto.OpListUnreadReq) ([]*dbstruct.ContactCustomerService, error) { list := make([]*dbstruct.ContactCustomerService, 0) col := m.getColContactCustomerService() query := qmgo.M{ - "is_read": consts.ContactCustomerService_NotRead, - "del_flag": 0, + "is_read": consts.ContactCustomerService_NotRead, + "predicate": consts.ContactCustomerService_FromUser, + "del_flag": 0, } err := col.Find(ctx, query).Sort("-ct").All(&list) if err == qmgo.ErrNoSuchDocuments { @@ -2118,12 +2128,12 @@ func (m *Mongo) GetContactCustomerServiceListUnread(ctx *gin.Context, req *conta return list, err } -func (m *Mongo) GetContactCustomerServiceListByMid(ctx *gin.Context, req *contact_customer_service_proto.OpListByMidReq) ([]*dbstruct.ContactCustomerService, error) { +func (m *Mongo) GetContactCustomerServiceListBySessionId(ctx *gin.Context, req *contact_customer_service_proto.OpListBySessionIdReq) ([]*dbstruct.ContactCustomerService, error) { list := make([]*dbstruct.ContactCustomerService, 0) col := m.getColContactCustomerService() query := qmgo.M{ - "sub_mid": util.DerefInt64(req.Mid), - "del_flag": 0, + "session_id": util.DerefInt64(req.SessionId), + "del_flag": 0, } err := col.Find(ctx, query).Sort("-ct").Skip(int64(req.Offset)).Limit(int64(req.Limit)).All(&list) if err == qmgo.ErrNoSuchDocuments { @@ -2508,3 +2518,50 @@ func (m *Mongo) UpdateTextAuditTaskByIds(ctx *gin.Context, textaudittask *dbstru _, err := col.UpdateAll(ctx, filter, up) return err } + +// 联系客服对话表相关 +func (m *Mongo) CreateContactCustomerServiceSession(ctx *gin.Context, contact_customer_service_session *dbstruct.ContactCustomerServiceSession) error { + col := m.getColContactCustomerServiceSession() + _, err := col.InsertOne(ctx, contact_customer_service_session) + return err +} + +func (m *Mongo) UpdateContactCustomerServiceSession(ctx *gin.Context, contact_customer_service_session *dbstruct.ContactCustomerServiceSession) error { + col := m.getColContactCustomerServiceSession() + set := util.EntityToM(contact_customer_service_session) + set["ut"] = time.Now().Unix() + up := qmgo.M{ + "$set": set, + } + err := col.UpdateId(ctx, contact_customer_service_session.Id, up) + return err +} + +func (m *Mongo) GetContactCustomerServiceSessionListByMid(ctx *gin.Context, req *contact_customer_service_sessionproto.OpListByMidReq) (*dbstruct.ContactCustomerServiceSession, error) { + session := &dbstruct.ContactCustomerServiceSession{} + col := m.getColContactCustomerServiceSession() + query := qmgo.M{ + "sub_mid": util.DerefInt64(req.Mid), + "del_flag": 0, + } + err := col.Find(ctx, query).One(&session) + if err == qmgo.ErrNoSuchDocuments { + err = nil + return nil, err + } + return session, err +} + +func (m *Mongo) GetContactCustomerServiceSessionList(ctx *gin.Context, req *contact_customer_service_sessionproto.OpListReq) ([]*dbstruct.ContactCustomerServiceSession, error) { + list := make([]*dbstruct.ContactCustomerServiceSession, 0) + col := m.getColContactCustomerServiceSession() + query := qmgo.M{ + "del_flag": 0, + } + err := col.Find(ctx, query).Sort("-ct").Skip(int64(req.Offset)).Limit(int64(req.Limit)).All(&list) + if err == qmgo.ErrNoSuchDocuments { + err = nil + return nil, err + } + return list, err +} diff --git a/app/mix/dao/mongo_idseq.go b/app/mix/dao/mongo_idseq.go index 8d06b760..08dfac76 100644 --- a/app/mix/dao/mongo_idseq.go +++ b/app/mix/dao/mongo_idseq.go @@ -39,6 +39,9 @@ const ( DBContactCustomerServiceIdSeq = "contact_customer_service_id_seq" COLContactCustomerServiceIdSeq = "contact_customer_service_id_seq" + + DBContactCustomerServiceSessionIdSeq = "contact_customer_service_session_id_seq" + COLContactCustomerServiceSessionIdSeq = "contact_customer_service_session_id_seq" ) // UserIdSeq序列表 @@ -91,6 +94,11 @@ func (m *Mongo) getColContactCustomerServiceIdSeq() *qmgo.Collection { return m.clientMix.Database(DBContactCustomerServiceIdSeq).Collection(COLContactCustomerServiceIdSeq) } +// ContactCustomerServiceSessionIdSeq序列表 +func (m *Mongo) getColContactCustomerServiceSessionIdSeq() *qmgo.Collection { + return m.clientMix.Database(DBContactCustomerServiceSessionIdSeq).Collection(COLContactCustomerServiceIdSeq) +} + // account_id发号器 func (m *Mongo) GetAndUpdateAccountIdSeq(ctx *gin.Context) (accountIdSeq *dbstruct.AccountIdSeq, err error) { col := m.getColAccountIdSeq() @@ -255,7 +263,7 @@ func (m *Mongo) GetAndUpdateRealNameAuthenticationIdSeq(ctx *gin.Context) (realn } // contact_customer_service_id发号器 -func (m *Mongo) GetAndUpdateContactCustomerServiceIdSeq(ctx *gin.Context) (contact_customer_serviceIdSeq *dbstruct.ContactCustomerServiceIdSeq, err error) { +func (m *Mongo) GetAndUpdateContactCustomerServiceIdSeq(ctx *gin.Context) (contactCustomerServiceIdSeq *dbstruct.ContactCustomerServiceIdSeq, err error) { col := m.getColContactCustomerServiceIdSeq() change := qmgo.Change{ @@ -273,6 +281,25 @@ func (m *Mongo) GetAndUpdateContactCustomerServiceIdSeq(ctx *gin.Context) (conta return &contactCustomerServiceIdSeqInstance, err } +// contact_customer_service_session_id发号器 +func (m *Mongo) GetAndUpdateContactCustomerServiceSessionIdSeq(ctx *gin.Context) (sessionIdSeq *dbstruct.ContactCustomerServiceSessionIdSeq, err error) { + col := m.getColContactCustomerServiceSessionIdSeq() + + change := qmgo.Change{ + Update: qmgo.M{"$inc": qmgo.M{"seq": 1}}, + Upsert: true, + ReturnNew: false, + } + + contactCustomerServiceSessionIdSeqInstance := dbstruct.ContactCustomerServiceSessionIdSeq{} + if err = col.Find(ctx, qmgo.M{"_id": "contact_customer_service_session_id_seq_id"}).Apply(change, &contactCustomerServiceSessionIdSeqInstance); err != nil { + logger.Error("change error : %v", err) + return + } + + return &contactCustomerServiceSessionIdSeqInstance, err +} + // media_id发号器 func (m *Mongo) GetAndUpdateMediaSeq(ctx *gin.Context) (mediaIdSeq *dbstruct.MediaIdSeq, err error) { col := m.getColMomentIdSeq() diff --git a/app/mix/service/apiservice.go b/app/mix/service/apiservice.go index 27f2ff6d..bce6c300 100644 --- a/app/mix/service/apiservice.go +++ b/app/mix/service/apiservice.go @@ -7,6 +7,7 @@ import ( accountproto "service/api/proto/account/proto" accountrelationproto "service/api/proto/accountrelation/proto" contact_customer_service_proto "service/api/proto/contact_customer_service/proto" + contact_customer_service_sessionproto "service/api/proto/contact_customer_service_session/proto" feedbackproto "service/api/proto/feedback/proto" loginproto "service/api/proto/login/proto" realname_authenticationproto "service/api/proto/realname_authentication/proto" @@ -1462,20 +1463,59 @@ func (s *Service) ApiCreateContactCustomerService(ctx *gin.Context, req *contact ec = errcode.ErrCodeContactCustomerServiceSrvFail return } + + //更新session表的内容 + if err := _DefaultContactCustomerServiceSession.OpUpdate(ctx, &contact_customer_service_sessionproto.OpUpdateReq{ + ContactCustomerServiceSession: &dbstruct.ContactCustomerServiceSession{ + Id: req.SessionId, + RecentMessage: req.Message, + }, + }); err != nil { + logger.Error("Update session fail, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeContactCustomerServiceSessionSrvFail + return + } return } -func (s *Service) ApiGetContactCustomerServiceListByMid(ctx *gin.Context, req *contact_customer_service_proto.ApiListByMidReq) (list []*dbstruct.ContactCustomerService, ec errcode.ErrCode) { +func (s *Service) ApiGetContactCustomerServiceListBySessionId(ctx *gin.Context, req *contact_customer_service_proto.ApiListBySessionIdReq) (list []*dbstruct.ContactCustomerService, ec errcode.ErrCode) { ec = errcode.ErrCodeContactCustomerServiceSrvOk - list, err := _DefaultContactCustomerService.OpListByMid(ctx, &contact_customer_service_proto.OpListByMidReq{ - Mid: req.Mid, - Offset: req.Offset, - Limit: req.Limit, + list, err := _DefaultContactCustomerService.OpListBySessionId(ctx, &contact_customer_service_proto.OpListBySessionIdReq{ + SessionId: req.SessionId, + Offset: req.Offset, + Limit: req.Limit, }) if err != nil { - logger.Error("OpGetContactCustomerServiceListByMid fail, req: %v, err: %v", util.ToJson(req), err) + logger.Error("OpGetContactCustomerServiceListBySessionId fail, req: %v, err: %v", util.ToJson(req), err) ec = errcode.ErrCodeContactCustomerServiceSrvFail return } return } + +// ContactCustomerServiceSession +func (s *Service) ApiCreateContactCustomerServiceSession(ctx *gin.Context, req *contact_customer_service_sessionproto.ApiCreateReq) (ec errcode.ErrCode) { + ec = errcode.ErrCodeContactCustomerServiceSessionSrvOk + err := _DefaultContactCustomerServiceSession.OpCreate(ctx, &contact_customer_service_sessionproto.OpCreateReq{ + ContactCustomerServiceSession: req.ContactCustomerServiceSession, + }) + if err != nil { + logger.Error("ApiCreate fail, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeContactCustomerServiceSessionSrvFail + return + } + return +} + +func (s *Service) ApiGetContactCustomerServiceSessionListByMid(ctx *gin.Context, req *contact_customer_service_sessionproto.ApiListByMidReq) (session *dbstruct.ContactCustomerServiceSession, ec errcode.ErrCode) { + ec = errcode.ErrCodeContactCustomerServiceSessionSrvOk + session, err := _DefaultContactCustomerServiceSession.OpListByMid(ctx, &contact_customer_service_sessionproto.OpListByMidReq{ + Mid: req.Mid, + }) + if err != nil { + logger.Error("OpListByMid fail, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeContactCustomerServiceSessionSrvFail + return + } + return +} diff --git a/app/mix/service/logic/contact_customer_service.go b/app/mix/service/logic/contact_customer_service.go index a946a276..14953060 100644 --- a/app/mix/service/logic/contact_customer_service.go +++ b/app/mix/service/logic/contact_customer_service.go @@ -63,19 +63,19 @@ func (p *ContactCustomerService) OpDelete(ctx *gin.Context, id int64) error { return nil } -func (p *ContactCustomerService) OpListUnread(ctx *gin.Context, req *contact_customer_serviceproto.OpListReq) ([]*dbstruct.ContactCustomerService, error) { - list, err := p.store.GetContactCustomerServiceListUnread(ctx, req) +func (p *ContactCustomerService) OpListUnread(ctx *gin.Context, req *contact_customer_serviceproto.OpListUnreadReq) ([]*dbstruct.ContactCustomerService, error) { + list, err := p.store.GetUnreadContactCustomerServiceList(ctx, req) if err != nil { - logger.Error("GetContactCustomerServiceListUnread fail, err: %v", err) + logger.Error("GetUnreadContactCustomerServiceList fail, err: %v", err) return make([]*dbstruct.ContactCustomerService, 0), err } return list, nil } -func (p *ContactCustomerService) OpListByMid(ctx *gin.Context, req *contact_customer_serviceproto.OpListByMidReq) ([]*dbstruct.ContactCustomerService, error) { - list, err := p.store.GetContactCustomerServiceListByMid(ctx, req) +func (p *ContactCustomerService) OpListBySessionId(ctx *gin.Context, req *contact_customer_serviceproto.OpListBySessionIdReq) ([]*dbstruct.ContactCustomerService, error) { + list, err := p.store.GetContactCustomerServiceListBySessionId(ctx, req) if err != nil { - logger.Error("GetContactCustomerServiceListByMid fail, err: %v", err) + logger.Error("GetContactCustomerServiceListBySessionId fail, err: %v", err) return make([]*dbstruct.ContactCustomerService, 0), err } return list, nil diff --git a/app/mix/service/logic/contact_customer_service_session.go b/app/mix/service/logic/contact_customer_service_session.go new file mode 100644 index 00000000..2827bc73 --- /dev/null +++ b/app/mix/service/logic/contact_customer_service_session.go @@ -0,0 +1,72 @@ +package logic + +import ( + "service/api/consts" + contact_customer_service_sessionproto "service/api/proto/contact_customer_service_session/proto" + "service/app/mix/dao" + "service/dbstruct" + "service/library/logger" + "time" + + "github.com/gin-gonic/gin" + goproto "google.golang.org/protobuf/proto" +) + +type ContactCustomerServiceSession struct { + store *dao.Store +} + +func NewContactCustomerServiceSession(store *dao.Store) (a *ContactCustomerServiceSession) { + a = &ContactCustomerServiceSession{ + store: store, + } + return +} + +func (p *ContactCustomerServiceSession) OpCreate(ctx *gin.Context, req *contact_customer_service_sessionproto.OpCreateReq) error { + + //生成自增id + sessionSeqId, err := p.store.GetAndUpdateContactCustomerServiceSessionIdSeq(ctx) + if err != nil { + logger.Error("GetAndUpdateContactCustomerServiceSessionIdSeq fail, err: %v", err) + return err + } + + req.ContactCustomerServiceSession.Id = goproto.Int64(sessionSeqId.Seq) + req.ContactCustomerServiceSession.Ct = goproto.Int64(time.Now().Unix()) + req.ContactCustomerServiceSession.Ut = goproto.Int64(time.Now().Unix()) + req.ContactCustomerServiceSession.DelFlag = goproto.Int64(consts.Exist) + err = p.store.CreateContactCustomerServiceSession(ctx, req.ContactCustomerServiceSession) + if err != nil { + logger.Error("CreateContactCustomerServiceSession fail, err: %v", err) + return err + } + return nil +} + +func (p *ContactCustomerServiceSession) OpUpdate(ctx *gin.Context, req *contact_customer_service_sessionproto.OpUpdateReq) error { + err := p.store.UpdateContactCustomerServiceSession(ctx, req.ContactCustomerServiceSession) + if err != nil { + logger.Error("UpdateContactCustomerServiceSession fail, err: %v", err) + return err + } + return nil +} + +func (p *ContactCustomerServiceSession) OpListByMid(ctx *gin.Context, req *contact_customer_service_sessionproto.OpListByMidReq) (*dbstruct.ContactCustomerServiceSession, error) { + session, err := p.store.GetContactCustomerServiceSessionListByMid(ctx, req) + if err != nil { + logger.Error("GetContactCustomerServiceSessionList fail, err: %v", err) + return nil, err + } + return session, nil +} + +func (p *ContactCustomerServiceSession) OpList(ctx *gin.Context, req *contact_customer_service_sessionproto.OpListReq) ([]*dbstruct.ContactCustomerServiceSession, error) { + list, err := p.store.GetContactCustomerServiceSessionList(ctx, req) + if err != nil { + logger.Error("GetContactCustomerServiceSessionList fail, err: %v", err) + return nil, err + } + return list, nil +} diff --git a/app/mix/service/opservice_business_validation.go b/app/mix/service/opservice_business_validation.go index 3dba127f..c0d8cd2a 100644 --- a/app/mix/service/opservice_business_validation.go +++ b/app/mix/service/opservice_business_validation.go @@ -6,6 +6,7 @@ import ( accountrelationproto "service/api/proto/accountrelation/proto" callhistoryproto "service/api/proto/callhistory/proto" contact_customer_service_proto "service/api/proto/contact_customer_service/proto" + contact_customer_service_sessionproto "service/api/proto/contact_customer_service_session/proto" feedbackproto "service/api/proto/feedback/proto" imageaudittaskproto "service/api/proto/imageaudittask/proto" loginproto "service/api/proto/login/proto" @@ -919,7 +920,7 @@ func (s *Service) OpUpdateContactCustomerServiceByIdsBusinessValidate(ctx *gin.C return } -func (s *Service) OpGetContactCustomerServiceListByMidBusinessValidate(ctx *gin.Context, req *contact_customer_service_proto.OpListByMidReq) (ec errcode.ErrCode) { +func (s *Service) OpGetContactCustomerServiceListBySessionIdBusinessValidate(ctx *gin.Context, req *contact_customer_service_proto.OpListBySessionIdReq) (ec errcode.ErrCode) { ec = errcode.ErrCodeContactCustomerServiceSrvOk // 1.业务校验 @@ -930,13 +931,13 @@ func (s *Service) OpGetContactCustomerServiceListByMidBusinessValidate(ctx *gin. Validate(). Collect() if ec = result[0].(errcode.ErrCode); ec != errcode.ErrCodeOk { - logger.Error("OpGetContactCustomerServiceListByMid business validation failed") + logger.Error("OpGetContactCustomerServiceListBySessionId business validation failed") return } return } -func (s *Service) OpGetContactCustomerServiceListUnreadGroupByMidBusinessValidate(ctx *gin.Context, req *contact_customer_service_proto.OpListReq) (ec errcode.ErrCode) { +func (s *Service) OpGetContactCustomerServiceListUnreadGroupByMidBusinessValidate(ctx *gin.Context, req *contact_customer_service_proto.OpListUnreadReq) (ec errcode.ErrCode) { ec = errcode.ErrCodeContactCustomerServiceSrvOk // 1.业务校验 @@ -1020,3 +1021,54 @@ func (s *Service) OpPassTextAuditTaskBatchBusinessValidate(ctx *gin.Context, req } return } + +func (s *Service) OpCreateContactCustomerServiceSessionBusinessValidate(ctx *gin.Context, req *contact_customer_service_sessionproto.OpCreateReq) (ec errcode.ErrCode) { + ec = errcode.ErrCodeContactCustomerServiceSessionSrvOk + + // 1.业务校验 + result := businessvalidator.NewAuthBusinessValidator(ctx, req). + QueryAccount(_DefaultAccount.OpListByMid). + EnsureAccountExist(). + EnsureIsOpRole(). + Validate(). + Collect() + if ec = result[0].(errcode.ErrCode); ec != errcode.ErrCodeOk { + logger.Error("OpCreateContactCustomerServiceSession business validation failed") + return + } + return +} + +func (s *Service) OpGetContactCustomerServiceSessionListByMidBusinessValidate(ctx *gin.Context, req *contact_customer_service_sessionproto.OpListByMidReq) (ec errcode.ErrCode) { + ec = errcode.ErrCodeContactCustomerServiceSessionSrvOk + + // 1.业务校验 + result := businessvalidator.NewAuthBusinessValidator(ctx, req). + QueryAccount(_DefaultAccount.OpListByMid). + EnsureAccountExist(). + EnsureIsOpRole(). + Validate(). + Collect() + if ec = result[0].(errcode.ErrCode); ec != errcode.ErrCodeOk { + logger.Error("OpGetContactCustomerServiceSessionListByMid business validation failed") + return + } + return +} + +func (s *Service) OpGetContactCustomerServiceSessionListBusinessValidate(ctx *gin.Context, req *contact_customer_service_sessionproto.OpListReq) (ec errcode.ErrCode) { + ec = errcode.ErrCodeContactCustomerServiceSessionSrvOk + + // 1.业务校验 + result := businessvalidator.NewAuthBusinessValidator(ctx, req). + QueryAccount(_DefaultAccount.OpListByMid). + EnsureAccountExist(). + EnsureIsOpRole(). + Validate(). + Collect() + if ec = result[0].(errcode.ErrCode); ec != errcode.ErrCodeOk { + logger.Error("OpGetContactCustomerServiceSessionList business validation failed") + return + } + return +} diff --git a/app/mix/service/service.go b/app/mix/service/service.go index 28dcc9ed..70ca64e8 100644 --- a/app/mix/service/service.go +++ b/app/mix/service/service.go @@ -13,6 +13,7 @@ import ( callhistoryproto "service/api/proto/callhistory/proto" catalogproto "service/api/proto/catalog/proto" contact_customer_service_proto "service/api/proto/contact_customer_service/proto" + contact_customer_service_sessionproto "service/api/proto/contact_customer_service_session/proto" feedbackproto "service/api/proto/feedback/proto" footprintproto "service/api/proto/footprint/proto" imageauditproto "service/api/proto/imageaudit/proto" @@ -62,33 +63,34 @@ var ( ) var ( - _DefaultToken *logic.Token - _DefaultVeriCode *logic.VeriCode - _DefaultAccount *logic.Account - _DefaultProduct *logic.Product - _DefaultCatalog *logic.Catalog - _DefaultVas *logic.Vas - _DefaultResource *logic.Resource - _DefaultBanner *logic.Banner - _DefaultLogin *logic.Login - _DefaultMoment *logic.Moment - _DefaultFootPrint *logic.FootPrint - _DefaultThumbsUp *logic.ThumbsUp - _DefaultAccountRelation *logic.AccountRelation - _DefaultStreamerAuthApproval *logic.StreamerAuthApproval - _DefaultStreamer *logic.Streamer - _DefaultMedia *logic.Media - _DefaultFeedback *logic.Feedback - _DefaultCallHistory *logic.CallHistory - _DefaultStreamerLink *logic.StreamerLink - _DefaultUserWxAddCheck *logic.UserWxAddCheck - _DefaultUserId *logic.UserIdSeq - _DefaultRealNameAuthentication *logic.RealNameAuthentication - _DefaultContactCustomerService *logic.ContactCustomerService - _DefaultImageAudit *logic.ImageAudit - _DefaultImageAuditTask *logic.ImageAuditTask - _DefaultTextAudit *logic.TextAudit - _DefaultTextAuditTask *logic.TextAuditTask + _DefaultToken *logic.Token + _DefaultVeriCode *logic.VeriCode + _DefaultAccount *logic.Account + _DefaultProduct *logic.Product + _DefaultCatalog *logic.Catalog + _DefaultVas *logic.Vas + _DefaultResource *logic.Resource + _DefaultBanner *logic.Banner + _DefaultLogin *logic.Login + _DefaultMoment *logic.Moment + _DefaultFootPrint *logic.FootPrint + _DefaultThumbsUp *logic.ThumbsUp + _DefaultAccountRelation *logic.AccountRelation + _DefaultStreamerAuthApproval *logic.StreamerAuthApproval + _DefaultStreamer *logic.Streamer + _DefaultMedia *logic.Media + _DefaultFeedback *logic.Feedback + _DefaultCallHistory *logic.CallHistory + _DefaultStreamerLink *logic.StreamerLink + _DefaultUserWxAddCheck *logic.UserWxAddCheck + _DefaultUserId *logic.UserIdSeq + _DefaultRealNameAuthentication *logic.RealNameAuthentication + _DefaultContactCustomerService *logic.ContactCustomerService + _DefaultImageAudit *logic.ImageAudit + _DefaultImageAuditTask *logic.ImageAuditTask + _DefaultTextAudit *logic.TextAudit + _DefaultTextAuditTask *logic.TextAuditTask + _DefaultContactCustomerServiceSession *logic.ContactCustomerServiceSession ) type Service struct { @@ -152,6 +154,7 @@ func (s *Service) Init(c any) (err error) { _DefaultImageAuditTask = logic.NewImageAuditTask(store) _DefaultTextAudit = logic.NewTextAudit(store) _DefaultTextAuditTask = logic.NewTextAuditTask(store) + _DefaultContactCustomerServiceSession = logic.NewContactCustomerServiceSession(store) return } @@ -2190,12 +2193,23 @@ func (s *Service) OpCreateContactCustomerService(ctx *gin.Context, req *contact_ return } - err := _DefaultContactCustomerService.OpCreate(ctx, req) - if err != nil { + if err := _DefaultContactCustomerService.OpCreate(ctx, req); err != nil { logger.Error("OpCreate fail, req: %v, err: %v", util.ToJson(req), err) ec = errcode.ErrCodeContactCustomerServiceSrvFail return } + + //更新session表的内容 + if err := _DefaultContactCustomerServiceSession.OpUpdate(ctx, &contact_customer_service_sessionproto.OpUpdateReq{ + ContactCustomerServiceSession: &dbstruct.ContactCustomerServiceSession{ + Id: req.SessionId, + RecentMessage: req.Message, + }, + }); err != nil { + logger.Error("Update session fail, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeContactCustomerServiceSessionSrvFail + return + } return } @@ -2220,34 +2234,23 @@ func (s *Service) OpUpdateContactCustomerServiceByIds(ctx *gin.Context, req *con return } -func (s *Service) OpDeleteContactCustomerService(ctx *gin.Context, id int64) (ec errcode.ErrCode) { +func (s *Service) OpGetContactCustomerServiceListBySessionId(ctx *gin.Context, req *contact_customer_service_proto.OpListBySessionIdReq) (list []*dbstruct.ContactCustomerService, ec errcode.ErrCode) { ec = errcode.ErrCodeContactCustomerServiceSrvOk - err := _DefaultContactCustomerService.OpDelete(ctx, id) + + if ec = s.OpGetContactCustomerServiceListBySessionIdBusinessValidate(ctx, req); ec != errcode.ErrCodeContactCustomerServiceSrvOk { + return + } + + list, err := _DefaultContactCustomerService.OpListBySessionId(ctx, req) if err != nil { - logger.Error("OpDelete fail, id: %v, err: %v", id, err) + logger.Error("OpGetContactCustomerServiceListBySessionId fail, req: %v, err: %v", util.ToJson(req), err) ec = errcode.ErrCodeContactCustomerServiceSrvFail return } return } -func (s *Service) OpGetContactCustomerServiceListByMid(ctx *gin.Context, req *contact_customer_service_proto.OpListByMidReq) (list []*dbstruct.ContactCustomerService, ec errcode.ErrCode) { - ec = errcode.ErrCodeContactCustomerServiceSrvOk - - if ec = s.OpGetContactCustomerServiceListByMidBusinessValidate(ctx, req); ec != errcode.ErrCodeContactCustomerServiceSrvOk { - return - } - - list, err := _DefaultContactCustomerService.OpListByMid(ctx, req) - if err != nil { - logger.Error("OpGetContactCustomerServiceListByMid fail, req: %v, err: %v", util.ToJson(req), err) - ec = errcode.ErrCodeContactCustomerServiceSrvFail - return - } - return -} - -func (s *Service) OpGetContactCustomerServiceListUnreadGroupByMid(ctx *gin.Context, req *contact_customer_service_proto.OpListReq) (volist []*contact_customer_service_proto.ContactCustomerServiceOpVO, ec errcode.ErrCode) { +func (s *Service) OpGetContactCustomerServiceListUnreadGroupByMid(ctx *gin.Context, req *contact_customer_service_proto.OpListUnreadReq) (volist []*contact_customer_service_proto.ContactCustomerServiceOpUnreadVO, ec errcode.ErrCode) { ec = errcode.ErrCodeContactCustomerServiceSrvOk if ec = s.OpGetContactCustomerServiceListUnreadGroupByMidBusinessValidate(ctx, req); ec != errcode.ErrCodeContactCustomerServiceSrvOk { @@ -2263,17 +2266,17 @@ func (s *Service) OpGetContactCustomerServiceListUnreadGroupByMid(ctx *gin.Conte _map := make(map[int64][]*dbstruct.ContactCustomerService) for _, contact_customer_service := range list { - subMid := util.DerefInt64(contact_customer_service.SubMid) - contents := _map[subMid] - _map[subMid] = append(contents, contact_customer_service) + sessionId := util.DerefInt64(contact_customer_service.SessionId) + contents := _map[sessionId] + _map[sessionId] = append(contents, contact_customer_service) } index := 0 - volist = make([]*contact_customer_service_proto.ContactCustomerServiceOpVO, len(_map)) - for mid, contents := range _map { - volist[index] = &contact_customer_service_proto.ContactCustomerServiceOpVO{ - Mid: mid, - List: contents, + volist = make([]*contact_customer_service_proto.ContactCustomerServiceOpUnreadVO, len(_map)) + for sessionId, contents := range _map { + volist[index] = &contact_customer_service_proto.ContactCustomerServiceOpUnreadVO{ + SessionId: sessionId, + List: contents, } index++ } @@ -2417,3 +2420,54 @@ func (s *Service) OpPassTextAuditTaskBatch(ctx *gin.Context, req *textaudittaskp return } + +// ContactCustomerServiceSession +func (s *Service) OpCreateContactCustomerServiceSession(ctx *gin.Context, req *contact_customer_service_sessionproto.OpCreateReq) (ec errcode.ErrCode) { + ec = errcode.ErrCodeContactCustomerServiceSessionSrvOk + + if ec = s.OpCreateContactCustomerServiceSessionBusinessValidate(ctx, req); ec != errcode.ErrCodeContactCustomerServiceSessionSrvOk { + return + } + + err := _DefaultContactCustomerServiceSession.OpCreate(ctx, req) + if err != nil { + logger.Error("OpCreate fail, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeContactCustomerServiceSessionSrvFail + return + } + return +} + +func (s *Service) OpGetContactCustomerServiceSessionListByMid(ctx *gin.Context, req *contact_customer_service_sessionproto.OpListByMidReq) (session *dbstruct.ContactCustomerServiceSession, ec errcode.ErrCode) { + + ec = errcode.ErrCodeContactCustomerServiceSessionSrvOk + + if ec = s.OpGetContactCustomerServiceSessionListByMidBusinessValidate(ctx, req); ec != errcode.ErrCodeContactCustomerServiceSessionSrvOk { + return + } + + session, err := _DefaultContactCustomerServiceSession.OpListByMid(ctx, req) + if err != nil { + logger.Error("OpListByMid fail, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeContactCustomerServiceSessionSrvFail + return + } + return +} + +func (s *Service) OpGetContactCustomerServiceSessionList(ctx *gin.Context, req *contact_customer_service_sessionproto.OpListReq) (list []*dbstruct.ContactCustomerServiceSession, ec errcode.ErrCode) { + + ec = errcode.ErrCodeContactCustomerServiceSessionSrvOk + + if ec = s.OpGetContactCustomerServiceSessionListBusinessValidate(ctx, req); ec != errcode.ErrCodeContactCustomerServiceSessionSrvOk { + return + } + + list, err := _DefaultContactCustomerServiceSession.OpList(ctx, req) + if err != nil { + logger.Error("OpList fail, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeContactCustomerServiceSessionSrvFail + return + } + return +} diff --git a/codecreate/codecreate.go b/codecreate/codecreate.go index 9d8bd59e..994fb891 100644 --- a/codecreate/codecreate.go +++ b/codecreate/codecreate.go @@ -9,13 +9,13 @@ import ( func main() { genSource := &generator.GenSource{ - EntityName: "ImageAuditTask", - ModuleName: "imageaudittask", - EntityCNName: "图片审核任务", - ErrCodeSeq: "21", + EntityName: "ContactCustomerServiceSession", + ModuleName: "contact_customer_service_session", + EntityCNName: "联系客服对话表", + ErrCodeSeq: "24", } - // generator.CreateFileDirectory(genSource) + generator.CreateFileDirectory(genSource) // genSource.InPath = consts.EntityInPath // genSource.OutPath = fmt.Sprintf("%v%v%v.go", consts.RootPath, consts.EntityOutPath, genSource.ModuleName) @@ -29,9 +29,9 @@ func main() { // genSource.OutPath = fmt.Sprintf("%v%v%v_mongo.go", consts.RootPath, consts.MongoOutPath, genSource.ModuleName) // generator.GenerateModule(genSource) - // genSource.InPath = consts.ProtoInPath - // genSource.OutPath = fmt.Sprintf("%v%v%v/proto/%v_op.go", consts.RootPath, consts.ProtoOutPath, genSource.ModuleName, genSource.ModuleName) - // generator.GenerateModule(genSource) + genSource.InPath = consts.ProtoInPath + genSource.OutPath = fmt.Sprintf("%v%v%v/proto/%v_op.go", consts.RootPath, consts.ProtoOutPath, genSource.ModuleName, genSource.ModuleName) + generator.GenerateModule(genSource) // genSource.InPath = consts.ServiceInPath // genSource.OutPath = fmt.Sprintf("%v%v%v.go", consts.RootPath, consts.ServiceOutPath, genSource.ModuleName) @@ -45,12 +45,12 @@ func main() { // genSource.OutPath = fmt.Sprintf("%v%v%v_errcode.go", consts.RootPath, consts.ErrcodeOutPath, genSource.ModuleName) // generator.GenerateModule(genSource) - genSource.InPath = consts.ControllerInPath - genSource.OutPath = fmt.Sprintf("%v%v%v_op.go", consts.RootPath, consts.ControllerOutPath, genSource.ModuleName) - generator.GenerateModule(genSource) + // genSource.InPath = consts.ControllerInPath + // genSource.OutPath = fmt.Sprintf("%v%v%v_op.go", consts.RootPath, consts.ControllerOutPath, genSource.ModuleName) + // generator.GenerateModule(genSource) - genSource.InPath = consts.ControllerCenterInPath - genSource.OutPath = fmt.Sprintf("%v%v%v_controller_center.go", consts.RootPath, consts.ControllerCenterOutPath, genSource.ModuleName) - generator.GenerateModule(genSource) + // genSource.InPath = consts.ControllerCenterInPath + // genSource.OutPath = fmt.Sprintf("%v%v%v_controller_center.go", consts.RootPath, consts.ControllerCenterOutPath, genSource.ModuleName) + // generator.GenerateModule(genSource) } diff --git a/codecreate/consts/consts.go b/codecreate/consts/consts.go index fbd53964..39e71ce4 100644 --- a/codecreate/consts/consts.go +++ b/codecreate/consts/consts.go @@ -1,7 +1,7 @@ package consts // root -const RootPath = "/Users/PC/Desktop/wishpal-ironfan/" +const RootPath = "/Users/PC/Desktop/wishpal_ironfan_service/service/" const ExcelPath = "codecreate/resource/EntityDefine.xlsx" diff --git a/codecreate/resource/EntityDefine.xlsx b/codecreate/resource/EntityDefine.xlsx index 4ee60a92a40c54302c9ecc66cda0b201be671829..f384b89b69718351c24f6917dd134942e4b871b3 100644 GIT binary patch delta 8182 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 XM->Ga*niHae%rMW->Nl<|5*P6fPgW3 delta 7449 zcmZXZWmHvP)b0=GkRsg&q`O-rC8aq6(%sSxf^d)qL1NP-UDDmsT?Y{)Bt^PG8eaZ> zxZd~Py}$gPwdPoJ$Jld?{mh1D@Ld&{6s810CIq2^&_N&&4T$@BxNIvD2$Y3gO-cub zt2i!jjAz^P8vnHDPc@XrwLK4lA}=wKC_y`0~*ZaO~} zvPwVO`TME4t_PKlAABV*fLdzo_}N8Bo*O!I$uPQ9A8|DOSi)Xx=}+hq-UHce?Iwt^ z{Z^u0>w;~Ur;+(As-2mZrmy*Z=mB>YwKv^nm^M5>wFLII9X%8D^y+!cJjcb24Hispl zC$tVeozhg{O5Yk2MAEwq@i!v{HoB0qE|&VbOgCu{UWZeRTJMo&{Ef>5RQWvwJH@%k6HN-^)o zhn@KqNR&gJVk$Zvalo(#qL|gvX<#!93$qL;jV;%TtwCa6u)gqWC9x?urJ8Hmc3Ans zoL)2Ed>`kQK0B#EzC)StnWcU^{}e8*acuOPNf#+5k9pNfFUXQaAV@ln*Nyr1dLZY5 z?ZH?-*%Igh#+OB42gCTc8tFheQU({R3JRy?rgl;cU-~4Q)olxDHMJHUaWRFh5V$}Z z3(b5D4e-IU!5`Wh3axce)qd54S3eKoIAOKyLV^XpBKbP7Kk?WF+r{`>Ie!V2R#@RY(h7% zxNNZ5>^OPLR$kj1=%PoOr@~5Wrq9K=-1$abZ^CgSVoZ+C)9{2h@<(kdK2Zl2>LB^0 zR!Pm+kX@l0g!VPCQ*_sYskPWGcgn%_u`7g-GJWAed9#*=BRaT>b;w)~9W#V42KidRaFrn`jWtVGIIS{>zji z^3aV+ony~fpR9jnUPto7xDN&+8KbB`wvy6Bd_o#NmoGJkB1FR0qN z5QUqx=E;q_A>qJoT;Qd}gVnhNS!}WM5!auGOS`k^DP-)$5O5_%<}+-0^zILAjCe*v zFSH`wM7rCk43JBNJzUo@DS(QtKx3HB}tX+lVf;^B^C8{@hKe>t(1iS zl85g%$AQyKNhd+Kz{$_T>s2Je{?=rkW*x3!`1(lbu zN@^}v-S>sZ-$eW<{JyqK+kJKK^r`Z*cITSto{X7UP2AvFmPTKq-kYD%zgxtRMt3zX z@2JsXpf2}cw)2PINX;=K4HuLD>2w-H{w1d-g=MLq!rYLD*)~Ni%xug@HBS;%^s4{j z$mMJ!u-Xn*A;%7@JkT<#?*j{i7@L=BSdsW8^Sy9!c&6~u8;qMND?$ZETl(j}Hgs$n zjG?qTZ;@hW+J(ugTd`fKlaeEc`Z+sDFa{?#`v^zB>%oGne?QtWY87L%iHwTm8MHDW z3_RqH3T1@&Je?*(Up^JsleCNWt4xF_t`jlp_fU?H^>@G>B)Z;ShbZ**rLXdG8g^>dcxf$C4mq9MkRDU@K&_V}6PkM~Tikal&+-%n9DhC_7g=z! z`+e~dKSLX6lfH~mk9J(e-h3MLA_(lXc#8UAeN5O;ifIeyeG!%Wdux2Ej12 zKDfJH?ORT5$C?9%nqOsXlQ4*FX|f|$zMn=^999JbdH4mGqN^( z0=NJZJ3*~AcAZ4+miuoAfR=(=OQ#)*iCw9hvuTF~0%fX$K!hORa|$sqW`+Szd_YrF zYt}UFygYG1$)}*uE>^5Ci152uadjxqmz}q$|FU3swVoC0fW+GmCWrT$L_K?nj5b7S zAKW`2kvA7pbc;DFQX7w|@Ij|NF|78BftB^v!T$F4_Pxgh_WO5qIqrslyY$R-7i@WZ z36`8MS65utk1CzaWw>=OI&BG0$%PUTsU)IGKWCsj+n6kgL4De@7r6W;x(l|*4?Jvb z^U%7m&_Ijo^Ygn0iwl`#2>hqb#pKkJdyDas>G^WbGPd{c>X>MTZHw7*3pCxh&#p-B zaw-y7AD9@hYMJittfizOp`!dI*m2QJTC2}eQz^H2!ccwM!fs#I*O|h_vTVrov#J_q z<{v`ab!k3ojG2#*)I(z|D2!$j=t#~hUaJVa$_=8yooq`*#wwn#D!hhbo>Ro3F>{;7 zx^pUoDirL(uy=nZXuLn?h_WzO}qFcXUlnah9xufjk~;LY#wR(yqn$3FYM-4 ztLOsL56-<7-*3;@<^*dXQTC~J9Bl04j=>8{fp;>($eQ%A)0 ze!=+`R;Vg+QR2`$bryZKrB<~D9`$@@e;fY=pIcNeSxxoe*RnF@n5hP?aHEDK#tg8n zp-=13=+2?<#dS-0k!+Oh*KhdowoqmgLpM57T34wkg_ZBHcmn{Q7az*tD*0{nW7Pvp zaIq{g|J3Wz;qkmaYrkh)qQO=^FG(R&dKWmz1FOXTpiM6j@n^zpN_)z#=s z{79l!?LRd664|3ii}8YF{NlkxDlIkk%#DT5pEGs3-Yz##cVhQ>?4P*>hlzdI0IE+< zUqNoFROTdZkJ&FKP13P|#zlBtt1y{zuohj2s3CLeODWq6g)YDI z>EVqGX~JH-#OQENrfLK?L71}r%e@HQX7pdKxTM3mfx>4xGfzqm2bAaF(z4h5>3_nj zbD{Z3Ad6-s4-QBH z@hzqWl>bu=E|_LTw2n7l#k5%|TG{-175m|5Si*L>(Di`8}QZ6pbrARaN@nu9hI z-HCOS-A3({Os7bm1ahf2;+L(R7^jz8&-%4k(NiR1-3Ca|{GJ*K(%;!N z42&xkGV?KiV3W8}Qg~i}&gsxThfQB~v{4Di>kT1^{bSvbjcX*Wy(^KqU6E91svkSr zaC%QddT@w(ln4)1Y#(t_aLGu}=8@?|yw*(|ODBc)8}v?*KYo$$X;8eO zVoBynV_Vh;mQ-J0nN-RKQ9fALn+nRw;SIL(pjOWj4CU9?FF&`c@dmzdfVpJ_XJ9z&1K<4&}7APsr7OF zLeeUfqxqw6(~#B^iXJjo=j&Yx?cdteMGeof(*`azQ*RQ}2yBP8pwQo;y>_%O&ByYG zP6V6Bz=n>FVp=O%x@yaF?@&kJu4?81&I%lw<<+98?|+W6fj#)~XybNil`MsLRei9b zIMg<@z8u~Bkw4yV^7An--?>0c=U%Ol8_s;65=KY%arpM(86umYMV7^B>c9=P#yKIn zsqkjsv5h)CW^aSnc)^aFII#+za-zgN$=&C2Si%d=W1WRiGtk3)3h?7??7J7v) zyGA=&9k6h)W+_RQh8}mBfL5-K1}N0f{ObJh4Y=@L1Q@vY`)NbX}+ z)5ryniF3=8Fcs8pJNJ zBn=Jq7ti6ov22L-Za=qQP@Jf1$n9*1ol(4@BeQiybq*-TV;^uJbd z-h*fJuUUK?s9v|Ovz`@R$C+a>F&f~pi)ys~E_xppm0Q;kQuG@a$r78@#Y(=l)DebO3OV`Q*2-3Z)&vDrvdhsautS^N^k=k^?~{F3N*2t~o> z`;(1W6JjMURxq5bB$->qldpk9Co?iV{E}& zyfS<2K*2W#KQM)-KmG6^-8;X`%Xh}^Oq+A&`}XTzXsG=hJMB*TC480MQd@BFq_DOf zyvdTya6~SuwFB@#DOFgUYqg@}N5R3nHDn~Va!6as6ymo5Iq>cR;GpBTK9fLH z3al8V2ih_tR_88+(oW^fu!W}Zbq0>gYt~t!{Y5854jK$}C?&zm8^b%(H7{o7q(^<5 zV1g{{+DaSZPZNVJNCl$mzR@~45<^dQV#DwXmcl+h$+Nv{6x#ij{MD#bFNJZ$YmY1; z2DNAT-AGiE|3YMvjH;iE9rd}d_FZ%&y0WMe+`%95j9la#vE2G)gLh81WP<;08s7PY znfs3K0ZC@z?78@%=SjfCxR#f7b}hk}K<8oq8kYr8VSKH`&Du4n@l zjxuw9RQb+$EYDC>&Al%vN9j9fcnN!aaM>-cL=On;LhDx7BG2QGY^=^g&-;ob8i;4^ zbjZakB@flQDS+C9!iM1^dFtBiek;?1P8VHus~#2G4# z0Fd8=prs#NmrF>-nEEhHxz#Q>_IbGt9M^xl$Of*}2E8s9jZ{91 zPJ~^mCLH3w4@Wm7I{hjg?0CPjy+ST-TTqtW5&tW(vZ^%|-eZd*9BP!iT-yu;jMKdl#d4h^2Mr`wsk#JckiE>gE#lrcjapRFS$ z^RnNR#`~eoOW?`@S-oodNZ!Kcy{%Lj{D&`T;oJOxL_86!ObKar<^#l6JIg~(evX5# zw~mdi;<$1b;W|nTcW;B$b7hZPwxc%eLj0)K6EI!tycxXJIC;(QWee)dcohf?^Ca4>$19n{i&@w!YPw zBE%Y*Z)C_E?e+$RXP(MEh?A@%rkZMzU|Nvc`SXj zE67toG>jLZN=K;``vAp2w#y!vtksVcK!TT#PI|6bcsp}D=XzrfHJ1O#r-|^qxhUM% z5+RHky7@f>#QZu9DhuM`Zm`w-AWxVFB6N}|@I>*MWA7n*Qf97L)$$saO{c@pRaxm; z!Q%9^OeL?Y>52V{!mxS;W}TO+Mij?#kcHq0)<%zmIlY_zTTdOn`!3G_K&Yb+p8`%W z%Cm?hEryT4V7UyR{#-^j*lFpi%x`yP%Q!u8No*RgC?@?Bxv%PN7#|V+Qail z^jcmqlX(1^S>kTC)MlbUp0l?oI)s0!aG3qos-LUh8RJvY4+@g{L3z&6;%(u^9k?3S znC|(LXc^s?uT$`dHedg;gonPkG!Mn1!`yYH7H_e%Ai-ofo*L$TC5;#|afYr&PUfdj z5;=5N4S5?O9vD^1%dW1%EDX6v0?OnjdDtKpIJab(PoR#lQD#Gw~`G{vFHsz z@H}G77$w((2?j7HDP`;2!pVbMY&2lw1G%}UPZ)Gbyw6idJn>Q8;72;&$U=yOP(o9* zp3P7QHgJkIU(Q|(e3t0Ay}wqGiF)1~yu5xtq@ui_L<4P-p6#^xE0<5@r-HhI9Fb{qEMIf0 zU%0OB>+!rv({oRB7U6~%`gY_$)bM#SgKnoWRbTFsZ|Kf?aQPo149lC2#f)e_?7$zk z`5wZ5@V+lnmWaE~iTUD^rphbYdG3I%M3G#wLt>7`v~okG;4O&5WP=F0IK(~k)ygLh z_Fa2>e~oE}^&wwjsNNF8dW&_5a01nYPknI+etyvV8zn^mQf~N0`%^kgb@I!mXS04q zDV8hT8yHRl%^u<8S(-(=^uW_m{BI7UZrMhhIAf^MVMPN$+K?skXP?78TY2vxAkf1D z1O!t;0&_h2SpR=33b2TO{p9R1%-Ge^-i-_3Nh6~8*VcHf-;eJ4zk>`7-~iYu{$Kn3 zUqBxQa1>ATXskiO1lYjpqigmN-~_MY0Z0i%z$CzhL=c}K3!W$Ys|^ykz#pIe#pP!x zKv4oK*qP?9+?|27Bz-8~3oW%y_nGE)58{XihH|9c(;vHP1wLKOw5 zO2!4|-{WHbn|}qYq>uo^X+(g=;}yBN1FJsdKx8UE*uEQCxDlx2KM#(JM!R* z2`D_en0n5*Pyek0_kK*m_E@K(U@SiXmQIhv15l@Pg0bNLiN|uy1!fL>L=CWX$lsBn zbRvrXYPa%ZGfV%m^5Fej>yeDYf$MZSng3Ik9=CPe{}r$r2?AL-nZsP2oZYxgot^*d X!N8PIQ2z^}e>`y>+cY|#{!{-0AF