package service import ( "context" "encoding/json" "fmt" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/gconv" "time" v1 "vistor/api/v1" "vistor/internal/consts" "vistor/internal/dao" "vistor/internal/model" ) type visitorService struct{} var Visitor = visitorService{} //var statisticType = g.Map{ // "day": //} type IVisitor interface { VisitorAccess(ctx context.Context, req *v1.VisitorAccessReq) error VisitorStatistic(ctx context.Context, req *v1.VisitorAccessReq) (res *v1.VisitorAccessRes, err error) GetDiffDays(t1, t2 time.Time) int VisitorList(ctx context.Context, req *v1.VisitorListReq) (res *v1.VisitorListRes, err error) GetDingDepartmentList(ctx context.Context, req *v1.GetDepartmentListReq) (res *v1.GetDepartmentListRes, err error) GetDingUserList(ctx context.Context, req *v1.GetDingUserReq) (res *v1.GetDingUserRes, err error) } func (s *visitorService) sendDingMsgByUserId(ctx context.Context, userId string, departmentName string, visitorName string, visitorPhone string, visitAt int) (err error) { accessToken, err := s.getAccessToken(ctx) if err != nil || accessToken == "" { return gerror.Newf(fmt.Sprintf("%s 发送钉钉通知失败, err:获取access_token失败", userId)) } agentId, _ := g.Cfg().Get(ctx, "ding.agentId") if agentId.String() == "" { return gerror.Newf(fmt.Sprintf("%s 发送钉钉通知失败, agentId:%s, appKey:%s, secret:%s", userId, agentId)) } param := g.Map{ "agent_id": agentId, "userid_list": userId, "msg": g.Map{ "msgtype": "text", "text": g.Map{ "content": fmt.Sprintf("来自 %s 访客 %s (电话:%s),将于 %s 来访,请做好接待工作 ^_^ ", departmentName, visitorName, visitorPhone, gtime.New(visitAt).Format("Y-m-d H:i:s")), }, }, } url := fmt.Sprintf("https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2?access_token=%s", accessToken) r, err := g.Client().Post(ctx, url, param) if err != nil { return err } defer r.Close() var dingMsgResp model.DingMsgResp resStr := r.ReadAllString() err = json.Unmarshal(gconv.Bytes(resStr), &dingMsgResp) if err != nil { return err } if dingMsgResp.ErrCode != 0 { return gerror.Newf(dingMsgResp.ErrMsg) } return } func (s *visitorService) GetDingUserList(ctx context.Context, req *v1.GetDingUserReq) (res *v1.GetDingUserRes, err error) { accessToken, err := s.getAccessToken(ctx) if err != nil || accessToken == "" { return nil, gerror.Newf("获取用户列表失败") } cursor := (req.PageNum - 1) * req.PageSize condition := g.Map{ "language": "zh_CN", "cursor": cursor, "size": req.PageSize, "dept_id": req.DeptId, "contain_access_limit": true, } url := fmt.Sprintf("https://oapi.dingtalk.com/topapi/user/listsimple?access_token=%s", accessToken) r, err := g.Client().Post(ctx, url, condition) if err != nil { return nil, err } defer r.Close() var userResp model.UserListResp resStr := r.ReadAllString() err = json.Unmarshal(gconv.Bytes(resStr), &userResp) if err != nil { return nil, nil } if userResp.ErrCode != 0 { return nil, gerror.Newf(userResp.ErrMsg) } res = &v1.GetDingUserRes{ List: userResp.Result, } return res, nil } func (s *visitorService) getAccessToken(ctx context.Context) (accessToken string, err error) { cacheKey, _ := g.Cfg().Get(ctx, "ding.cacheKey") //有缓存 cache, _ := g.Redis().Do(ctx, "GET", cacheKey) if cache.String() != "" { return cache.String(), nil } //无缓存 key, _ := g.Cfg().Get(ctx, "ding.key") secret, _ := g.Cfg().Get(ctx, "ding.secret") url := fmt.Sprintf("https://oapi.dingtalk.com/gettoken?appkey=%s&appsecret=%s", key, secret) r, err := g.Client().Get(ctx, url) if err != nil { return "", err } defer r.Close() var accessTokenRes model.AccessTokenResp resStr := r.ReadAllString() err = json.Unmarshal(gconv.Bytes(resStr), &accessTokenRes) if err != nil { return "", nil } if accessTokenRes.ErrCode != 0 { return "", gerror.Newf(accessTokenRes.ErrMsg) } if accessTokenRes.AccessToken != "" && accessTokenRes.ExpiresIn > 0 { _, _ = g.Redis().Do(ctx, "SET", cacheKey, accessTokenRes.AccessToken) _, _ = g.Redis().Do(ctx, "EXPIRE", cacheKey, accessTokenRes.ExpiresIn-60) } return accessTokenRes.AccessToken, nil } func (s *visitorService) GetDingDepartmentList(ctx context.Context, req *v1.GetDepartmentListReq) (res *v1.GetDepartmentListRes, err error) { accessToken, err := s.getAccessToken(ctx) if err != nil || accessToken == "" { return nil, gerror.Newf("获取部门列表失败") } condition := g.Map{ "language": "zh_CN", } if req.DeptId != 0 { condition["dept_id"] = req.DeptId } url := fmt.Sprintf("https://oapi.dingtalk.com/topapi/v2/department/listsub?access_token=%s", accessToken) r, err := g.Client().Post(ctx, url, condition) if err != nil { return nil, err } defer r.Close() var deptResp model.DeptListResp resStr := r.ReadAllString() err = json.Unmarshal(gconv.Bytes(resStr), &deptResp) if err != nil { return nil, nil } if deptResp.ErrCode != 0 { return nil, gerror.Newf(deptResp.ErrMsg) } res = &v1.GetDepartmentListRes{ List: deptResp.Result, } return res, nil } func (s *visitorService) VisitorList(ctx context.Context, req *v1.VisitorListReq) (res *v1.VisitorListRes, err error) { condition := g.Map{} var records []*model.VisitorRecord if req.PageNum == 0 { req.PageNum = 1 } if req.PageSize == 0 { req.PageSize = 15 } if req.VisitorName != "" { condition["visitor_name like ?"] = "%" + req.VisitorName + "%" } if req.VisitorIdentity != "" { condition["visitor_identity like ?"] = "%" + req.VisitorIdentity + "%" } if req.VisitorPhone != "" { condition["visitor_phone like ?"] = "%" + req.VisitorPhone + "%" } query := dao.VisitorRecord.Ctx(ctx).Where(condition) if req.StartDate != 0 || req.EndDate != 0 { query = query.WhereBetween(dao.VisitorRecord.Columns().VisitAt, req.StartDate, req.EndDate) } total, err := query.Count() if err != nil { return nil, err } err = query.Page(req.PageNum, req.PageSize).Order("id desc").Scan(&records) if err != nil { return nil, err } res = &v1.VisitorListRes{ List: records, Total: total, Current: req.PageNum, } return } func (s *visitorService) VisitorAccess(ctx context.Context, req *v1.VisitorAccessReq) error { insert, err := dao.VisitorRecord.Ctx(ctx).Data(req).Insert() if err != nil { return err } affected, err := insert.RowsAffected() if err != nil { return err } if affected == 0 { return gerror.Newf("提交失败,请重试") } err = s.sendDingMsgByUserId(ctx, req.DstUserId, req.VisitorDepartment, req.VisitorName, req.VisitorPhone, req.VisitAt) if err != nil { g.Log().Info(ctx, fmt.Sprintf("sendDingMsgByUserId err------------------ %s", err.Error())) } return nil } func (s *visitorService) VisitorStatistic(ctx context.Context, req *v1.VisitorStatisticReq) (res *v1.VisitorStatisticRes, err error) { var ( resRecords []*model.ResRecord ) db := g.DB() xAxis := g.SliceStr{} yAxis := g.SliceStr{} condition := g.Map{} if req.VisitorName != "" { condition["vr.visitor_name like ?"] = "%" + req.VisitorName + "%" } if req.VisitorIdentity != "" { condition["vr.visitor_identity like ?"] = "%" + req.VisitorIdentity + "%" } if req.VisitorPhone != "" { condition["vr.visitor_phone like ?"] = "%" + req.VisitorPhone + "%" } if req.StatisticType == consts.StatisticTypeDay { diff := s.GetDiffDays(gtime.New(req.EndDate).Time, gtime.New(req.StartDate).Time) duration := gconv.Int(diff) + 1 if duration > 30 { return nil, gerror.Newf("请选择30天内日期进行查询") } for i := 0; duration > i; i++ { xAxis = append(xAxis, gtime.New(req.StartDate).AddDate(0, 0, i).Format("Y-m-d")) } fields := "id,visitor_name,visitor_identity,visitor_phone,visit_at,from_unixtime(visit_at,'%Y-%m-%d') visit_date" subQuery := db.Model("visitor_record vr"). Where(condition). WhereBetween("vr.visit_at", req.StartDate, req.EndDate). Fields(fields) mainQuery := db.Model("? as v", subQuery) err = mainQuery. Fields("count(v.id) y_axis, v.visit_date x_axis"). Group("x_axis"). Order("x_axis"). Scan(&resRecords) if err != nil { return nil, err } for _, x := range xAxis { y := "0" for _, record := range resRecords { if record.XAxis == x { y = record.YAxis } } yAxis = append(yAxis, y) } } else { var weekNum, monthNum, yearNum int if req.StatisticType == consts.StatisticTypeWeek { switch req.SubType { case consts.SubTypeLast4Week: weekNum = 4 case consts.SubTypeLast8Week: weekNum = 8 case consts.SubTypeLast12Week: weekNum = 12 } //获取按周统计数据 xAxis, yAxis = s.getWeekStatistic(ctx, weekNum, condition) } else if req.StatisticType == consts.StatisticTypeMonth { switch req.SubType { case consts.SubTypeLast4Month: monthNum = 4 case consts.SubTypeLast8Month: monthNum = 8 case consts.SubTypeLast12Month: monthNum = 12 } //获取按月统计数据 xAxis, yAxis = s.getMonthStatistic(ctx, monthNum, condition) } else if req.StatisticType == consts.StatisticTypeYear { switch req.SubType { case consts.SubTypeLastYear: yearNum = 1 case consts.SubTypeLast2Year: yearNum = 2 case consts.SubTypeLast3Year: yearNum = 3 } //获取按年统计数据 xAxis, yAxis = s.getYearStatistic(ctx, yearNum, condition) } } res = &v1.VisitorStatisticRes{ XAxis: xAxis, YAxis: yAxis, } return } // GetDiffDays 获取两个时间相差的天数,0表同一天,正数表t1>t2,负数表t1 0 { offset = -6 } weekStartDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local).AddDate(0, 0, offset) weekMonday = weekStartDate.Format("2006-01-02") dateTimestamp = gtime.New(weekMonday).Unix() return } /* * 获取本周周一的日期 */ func (s *visitorService) getLastWeekFirstDate(weekNum int) (weekMonday string, dateTimestamp int64) { thisWeekMonday, _ := s.getFirstDateOfWeek() TimeMonday, _ := time.Parse("2006-01-02", thisWeekMonday) lastWeekMonday := TimeMonday.AddDate(0, 0, -7*(weekNum-1)) weekMonday = lastWeekMonday.Format("2006-01-02") dateTimestamp = gtime.New(weekMonday).Unix() return }