terminal.go 9.8 KB
package service

import (
	"bytes"
	"fmt"
	"github.com/beego/beego/v2/adapter/utils"
	"github.com/gookit/event"
	"github.com/linmadan/egglib-go/core/application"
	"gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/application/factory"
	"gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/application/terminal/command"
	"gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/domain"
	"gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/log"
	"io"
)

var GlobalTerminalManager *TerminalManager

func init() {
	GlobalTerminalManager = NewTerminalManager()
	event.On(domain.UserCreateEvent, event.ListenerFunc(GlobalTerminalManager.SyncUser))
	event.On(domain.UserUpdateEvent, event.ListenerFunc(GlobalTerminalManager.SyncUser))
	event.On(domain.UserEnableEvent, event.ListenerFunc(GlobalTerminalManager.EnableUser))
	event.On(domain.UserSyncEvent, event.ListenerFunc(GlobalTerminalManager.SyncUser))
	event.On(DownEntityEvent, event.ListenerFunc(GlobalTerminalManager.DownEntityEvent))
}

type TerminalService struct {
}

func NewTerminalService(options map[string]interface{}) *TerminalService {
	newUserService := &TerminalService{}
	return newUserService
}

func (svr *TerminalService) TerminalReport(cmd *command.TerminalReportCommand) (interface{}, error) {
	if err := cmd.ValidateCommand(); err != nil {
		return nil, application.ThrowError(application.ARG_ERROR, err.Error())
	}
	transactionContext, err := factory.CreateTransactionContext(nil)
	if err != nil {
		return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
	}
	if err := transactionContext.StartTransaction(); err != nil {
		return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
	}
	defer func() {
		transactionContext.RollbackTransaction()
	}()
	response, err := terminalReport(cmd, transactionContext)
	if err != nil {
		log.Logger.Error(err.Error())
		return nil, err
	}
	if err := transactionContext.CommitTransaction(); err != nil {
		return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
	}
	return response, nil
}

func terminalReport(cmd *command.TerminalReportCommand, transactionContext application.TransactionContext) (interface{}, error) {
	var (
		response string = "OK"
	)

	switch cmd.Command {
	case "getrequest":
		device, ok := GlobalTerminalManager.GetDevice(cmd.TerminalId)
		if !ok || device == nil {
			break
		}
		if downEntity, ok := device.PopDownEntity(); ok && downEntity != nil {
			response = downEntity.DownCommand()
		}
		break
	case "cdata":
		parseEntities := ParseData(cmd)
		var (
			down    DownEntity
			err     error
			handler Handler = ZKClockHandler{TransactionContext: transactionContext}
		)
		for _, entity := range parseEntities {
			switch TableType(cmd.Table) {
			case AttLOG:
				down, err = handler.Attendance(entity.(AttLOGUpEntity))
			case BIODATA:
				down, err = handler.BioData(entity.(BIODATAEntity))
			case OPERLOG:
				//if v, ok := entity.(USEREntity); ok {
				//	down, err = handler.ReportUser(v)
				//}
				if v, ok := entity.(BIODATAEntity); ok {
					down, err = handler.BioData(v)
				}
			}
			if err != nil {
				log.Logger.Error(err.Error())
				continue
			}
			if down != nil {
				response = down.DownCommand()
			}
			break
		}
	case "devicecmd":
		log.Logger.Debug("【TerminalManager】 收到命令应答 cmd : " + cmd.Content)
	}

	return map[string]interface{}{
		"response": response,
	}, nil
}

func ParseData(cmd *command.TerminalReportCommand) []interface{} {
	result := make([]interface{}, 0)
	buf := bytes.NewBufferString(cmd.Content)
	for {
		line, err := buf.ReadBytes('\n')
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Logger.Error(err.Error())
			break
		}

		switch TableType(cmd.Table) {
		case AttLOG:
			columns := bytes.Split(line, ([]byte)("\t"))
			if len(columns) != 11 { //10 + 1 空格
				continue
			}
			result = append(result, AttLOGUpEntity{
				Pin:       string(columns[0]),
				CompanyId: cmd.CompanyId,
				OrgId:     cmd.OrgId,
			})
		case BIODATA:
			/*BIODATA Pin=3\tNo=0\tIndex=0\tValid=1\tDuress=0\tType=9\tMajorVer=39\tMinorVer=1\tFormat=
			0\tTmp=apUBEBABQo4JACcBAWiOADA7dP4pU9F31Uxom7NAGjM4eO/8X5Ee4uahkIT11c3188+VguMsd3oCO0O29efRyxofdLiohI4QL7woK3U*/
			columns := readLineToMap(line, "BIODATA")
			result = append(result, BIODATAEntity{
				Pin:       columns["Pin"],
				No:        columns["No"],
				Index:     columns["Index"],
				Duress:    columns["Duress"],
				Type:      columns["Type"],
				MinorVer:  columns["MinorVer"],
				MajorVer:  columns["MajorVer"],
				Format:    columns["Format"],
				Tmp:       columns["Tmp"],
				CompanyId: cmd.CompanyId,
				OrgId:     cmd.OrgId,
			})
		case OPERLOG:
			if bytes.HasPrefix(line, []byte("FP")) {
				/*
					FP PIN=3	FID=6	Size=1336	Valid=1	TMP=SqtTUzIxAAAD6O8ECA
				*/
				columns := readLineToMap(line, "FP")
				result = append(result, BIODATAEntity{
					Pin:       columns["PIN"],
					Type:      fmt.Sprintf("%v", BioDataType1),
					Tmp:       columns["TMP"],
					CompanyId: cmd.CompanyId,
					OrgId:     cmd.OrgId,
				})
			} else if bytes.HasPrefix(line, []byte("USER")) {
				/*
					USER PIN=3	Name=杨xx	Pri=14	Passwd=	Card=3731588478	Grp=1	TZ=0000000100000000	Verify=-1	ViceCard=	StartDatetime=0	EndDatetime=0
				*/
				columns := readLineToMap(line, "USER")
				name := columns["Name"] //GbkToUtf8(columns["Name"])
				result = append(result, USEREntity{
					OPERLOGType: "USER",
					Pin:         columns["PIN"],
					Name:        name,
					CompanyId:   cmd.CompanyId,
					OrgId:       cmd.OrgId,
				})
			}
		}
	}
	return result
}

func readLineToMap(line []byte, prefix string) map[string]string {
	var result = make(map[string]string)
	line = bytes.TrimLeft(line, prefix)
	line = bytes.TrimSpace(line)
	columns := bytes.Fields(line)
	for i := range columns {
		kv := bytes.SplitN(columns[i], []byte("="), 2)
		if len(kv) < 2 {
			continue
		}
		//if string(kv[0]) == "Name" {
		//	log.Logger.Debug(fmt.Sprintf("%x", kv[1]))
		//	log.Logger.Debug(fmt.Sprintf("%v", kv[1]))
		//	log.Logger.Debug(fmt.Sprintf("%s", string(kv[1])))
		//}
		result[string(kv[0])] = string(kv[1])
	}
	return result
}

type Handler interface {
	Attendance(entity AttLOGUpEntity) (DownEntity, error)
	BioData(entity BIODATAEntity) (DownEntity, error)
	ReportUser(entity USEREntity) (DownEntity, error)
}

type ZKClockHandler struct {
	TransactionContext application.TransactionContext
}

func (handler ZKClockHandler) Attendance(entity AttLOGUpEntity) (DownEntity, error) {
	var (
		userRepository, _, _     = factory.FastPgUser(handler.TransactionContext, 0)
		userBaseRepository, _, _ = factory.FastPgUserBase(handler.TransactionContext, 0)
		user                     *domain.User
		userBase                 *domain.UserBase
		err                      error
	)
	if user, err = userRepository.FindOne(map[string]interface{}{"companyId": entity.CompanyId, "orgId": entity.OrgId, "icCardNumber": entity.Pin}); err != nil {
		return nil, err
	}
	if userBase, err = userBaseRepository.FindOne(map[string]interface{}{"userBaseId": user.UserBaseId}); err != nil {
		return nil, err
	}
	var sendQuery bool
	// 请求 人脸识别-用户画像
	if len(userBase.UserInfo.FacePortrait) == 0 {
		sendQuery = true
		//event.Fire(DownEntityEvent, map[string]interface{}{"entity": NewQueryBIODATADownEntity(generateSn(), entity.Pin, BioDataType2)})
	}
	// 请求 人脸识别-指纹
	if len(userBase.UserInfo.FingerprintPortrait) == 0 {
		sendQuery = true
	}
	if sendQuery {
		event.Fire(DownEntityEvent, map[string]interface{}{"entity": NewQueryUserInfoDownEntity(generateSn(), entity.Pin)})
	}
	return nil, nil
}

func (handler ZKClockHandler) BioData(entity BIODATAEntity) (DownEntity, error) {
	var (
		userRepository, _, _     = factory.FastPgUser(handler.TransactionContext, 0)
		userBaseRepository, _, _ = factory.FastPgUserBase(handler.TransactionContext, 0)
		user                     *domain.User
		userBase                 *domain.UserBase
		err                      error
		updateFlag               bool
	)
	if user, err = userRepository.FindOne(map[string]interface{}{"companyId": entity.CompanyId, "orgId": entity.OrgId, "icCardNumber": entity.Pin}); err != nil {
		return nil, err
	}
	if userBase, err = userBaseRepository.FindOne(map[string]interface{}{"userBaseId": user.UserBaseId}); err != nil {
		return nil, err
	}
	// 请求 人脸识别-用户画像
	if len(userBase.UserInfo.FacePortrait) == 0 && len(entity.Tmp) > 0 && entity.Type == "9" {
		userBase.UserInfo.FacePortrait = entity.Tmp
		updateFlag = true
	}
	// 请求 人脸识别-指纹
	if len(userBase.UserInfo.FingerprintPortrait) == 0 && len(entity.Tmp) > 0 && entity.Type == "1" {
		userBase.UserInfo.FingerprintPortrait = entity.Tmp
		updateFlag = true
	}
	if updateFlag {
		_, err = userBaseRepository.Save(userBase)
		if err != nil {
			log.Logger.Error(err.Error())
		}
	}
	return nil, err
}

func (handler ZKClockHandler) ReportUser(entity USEREntity) (DownEntity, error) {
	var (
		userRepository, _, _     = factory.FastPgUser(handler.TransactionContext, 0)
		userBaseRepository, _, _ = factory.FastPgUserBase(handler.TransactionContext, 0)
		user                     *domain.User
		userBase                 *domain.UserBase
		err                      error
		updateFlag               bool
	)
	if user, err = userRepository.FindOne(map[string]interface{}{"companyId": entity.CompanyId, "orgId": entity.OrgId, "icCardNumber": entity.Pin}); err != nil {
		return nil, err
	}
	if userBase, err = userBaseRepository.FindOne(map[string]interface{}{"userBaseId": user.UserBaseId}); err != nil {
		return nil, err
	}
	// 请求 人脸识别-用户画像
	//if len(entity.Pin) > 0 && userBase.UserInfo.Pin != entity.Pin {
	//	updateFlag = true
	//	userBase.UserInfo.Pin = entity.Pin
	//}
	if updateFlag {
		_, err = userBaseRepository.Save(userBase)
	}
	return nil, err
}

func generateSn() string {
	return string(utils.RandomCreateBytes(10))
}