访客登记
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
visitor/internal/service/visitor.go

473 lines
13 KiB

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)
10 months ago
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("获取用户列表失败")
}
condition := g.Map{
"language": "zh_CN",
"cursor": req.PageNum,
"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()
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 {
10 months ago
diff := s.GetDiffDays(gtime.New(req.EndDate).Time, gtime.New(req.StartDate).Time)
duration := gconv.Int(diff) + 1
10 months ago
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
}
10 months ago
// GetDiffDays 获取两个时间相差的天数,0表同一天,正数表t1>t2,负数表t1<t2
func (s *visitorService) GetDiffDays(t1, t2 time.Time) int {
t1 = time.Date(t1.Year(), t1.Month(), t1.Day(), 0, 0, 0, 0, time.Local)
t2 = time.Date(t2.Year(), t2.Month(), t2.Day(), 0, 0, 0, 0, time.Local)
return int(t1.Sub(t2).Hours() / 24)
}
func (s *visitorService) getYearStatistic(ctx context.Context, lastYearNum int, condition g.Map) (xAxis g.SliceStr, yAxis g.SliceStr) {
for ; 1 <= lastYearNum; lastYearNum-- {
//获取当年第一天
yearStart := gtime.New(gtime.Now().AddDate(-(lastYearNum - 1), 0, 0).Format("Y-01-01 00:00:00")).Unix()
yearEnd := gtime.New(gtime.Now().AddDate(-(lastYearNum - 2), 0, 0).Format("Y-01-01 00:00:00")).Unix()
count, err := dao.VisitorRecord.Ctx(ctx).As("vr").Where(condition).WhereBetween(dao.VisitorRecord.Columns().VisitAt, yearStart, yearEnd).Count()
if err != nil {
return g.SliceStr{}, g.SliceStr{}
}
//获取统计数据
xAxis = append(xAxis, gtime.New(yearStart).Format("Y"))
yAxis = append(yAxis, gconv.String(count))
}
return
}
func (s *visitorService) getMonthStatistic(ctx context.Context, lastMonthNum int, condition g.Map) (xAxis g.SliceStr, yAxis g.SliceStr) {
for ; 1 <= lastMonthNum; lastMonthNum-- {
//获取当月第一天
monthStart := gtime.New(gtime.Now().AddDate(0, -(lastMonthNum - 1), 0).Format("Y-m-01 00:00:00")).Unix()
monthEnd := gtime.New(gtime.Now().AddDate(0, -(lastMonthNum - 2), 0).Format("Y-m-01 00:00:00")).Unix()
count, err := dao.VisitorRecord.Ctx(ctx).As("vr").Where(condition).WhereBetween(dao.VisitorRecord.Columns().VisitAt, monthStart, monthEnd).Count()
if err != nil {
return g.SliceStr{}, g.SliceStr{}
}
//获取统计数据
xAxis = append(xAxis, gtime.New(monthStart).Format("Y-m"))
yAxis = append(yAxis, gconv.String(count))
}
return
}
func (s *visitorService) getWeekStatistic(ctx context.Context, lastWeekNum int, condition g.Map) (xAxis g.SliceStr, yAxis g.SliceStr) {
for ; 1 <= lastWeekNum; lastWeekNum-- {
weekDate, startTimestamp := s.getLastWeekFirstDate(lastWeekNum)
endTimestamp := gtime.New(weekDate).AddDate(0, 0, 7).Unix()
count, err := dao.VisitorRecord.Ctx(ctx).As("vr").Where(condition).WhereBetween(dao.VisitorRecord.Columns().VisitAt, startTimestamp, endTimestamp).Count()
if err != nil {
return g.SliceStr{}, g.SliceStr{}
}
//获取第几周
_, weekNum := gtime.New(weekDate).ISOWeek()
xAxis = append(xAxis, fmt.Sprintf("第%d周", weekNum))
yAxis = append(yAxis, gconv.String(count))
}
return
}
/*
*
获取本周周一的日期
*/
func (s *visitorService) getFirstDateOfWeek() (weekMonday string, dateTimestamp int64) {
now := time.Now()
offset := int(time.Monday - now.Weekday())
if offset > 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
}