作者 yangfu

feat: 打卡机数据解析

@@ -4,6 +4,7 @@ go 1.16 @@ -4,6 +4,7 @@ go 1.16
4 4
5 require ( 5 require (
6 github.com/ajg/form v1.5.1 // indirect 6 github.com/ajg/form v1.5.1 // indirect
  7 + github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394
7 github.com/beego/beego/v2 v2.0.1 8 github.com/beego/beego/v2 v2.0.1
8 github.com/emirpasic/gods v1.12.0 9 github.com/emirpasic/gods v1.12.0
9 github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 // indirect 10 github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 // indirect
@@ -11,6 +12,7 @@ require ( @@ -11,6 +12,7 @@ require (
11 github.com/gavv/httpexpect v2.0.0+incompatible 12 github.com/gavv/httpexpect v2.0.0+incompatible
12 github.com/go-pg/pg/v10 v10.9.0 13 github.com/go-pg/pg/v10 v10.9.0
13 github.com/google/go-querystring v1.1.0 // indirect 14 github.com/google/go-querystring v1.1.0 // indirect
  15 + github.com/gookit/event v1.0.6
14 github.com/imkira/go-interpol v1.1.0 // indirect 16 github.com/imkira/go-interpol v1.1.0 // indirect
15 github.com/linmadan/egglib-go v0.0.0-20210313060205-8b5e456b11f7 17 github.com/linmadan/egglib-go v0.0.0-20210313060205-8b5e456b11f7
16 github.com/mattn/go-colorable v0.1.8 // indirect 18 github.com/mattn/go-colorable v0.1.8 // indirect
@@ -19,7 +21,7 @@ require ( @@ -19,7 +21,7 @@ require (
19 github.com/onsi/gomega v1.11.0 21 github.com/onsi/gomega v1.11.0
20 github.com/sergi/go-diff v1.2.0 // indirect 22 github.com/sergi/go-diff v1.2.0 // indirect
21 github.com/smartystreets/goconvey v1.6.4 // indirect 23 github.com/smartystreets/goconvey v1.6.4 // indirect
22 - github.com/stretchr/testify v1.7.0 24 + github.com/stretchr/testify v1.7.1
23 github.com/tal-tech/go-queue v1.0.5 25 github.com/tal-tech/go-queue v1.0.5
24 github.com/tal-tech/go-zero v1.0.27 26 github.com/tal-tech/go-zero v1.0.27
25 github.com/valyala/fasthttp v1.23.0 // indirect 27 github.com/valyala/fasthttp v1.23.0 // indirect
@@ -21,6 +21,8 @@ github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+Dx @@ -21,6 +21,8 @@ github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+Dx
21 github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc= 21 github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc=
22 github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= 22 github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
23 github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= 23 github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
  24 +github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ=
  25 +github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394/go.mod h1:Q8n74mJTIgjX4RBBcHnJ05h//6/k6foqmgE45jTQtxg=
24 github.com/beanstalkd/go-beanstalk v0.1.0/go.mod h1:/G8YTyChOtpOArwLTQPY1CHB+i212+av35bkPXXj56Y= 26 github.com/beanstalkd/go-beanstalk v0.1.0/go.mod h1:/G8YTyChOtpOArwLTQPY1CHB+i212+av35bkPXXj56Y=
25 github.com/beego/beego/v2 v2.0.1 h1:07a7Z0Ok5vbqyqh+q53sDPl9LdhKh0ZDy3gbyGrhFnE= 27 github.com/beego/beego/v2 v2.0.1 h1:07a7Z0Ok5vbqyqh+q53sDPl9LdhKh0ZDy3gbyGrhFnE=
26 github.com/beego/beego/v2 v2.0.1/go.mod h1:8zyHi1FnWO1mZLwTn62aKRIZF/aIKvkCBB2JYs+eqQI= 28 github.com/beego/beego/v2 v2.0.1/go.mod h1:8zyHi1FnWO1mZLwTn62aKRIZF/aIKvkCBB2JYs+eqQI=
@@ -164,6 +166,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 @@ -164,6 +166,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
164 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 166 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
165 github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= 167 github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
166 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 168 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
  169 +github.com/gookit/event v1.0.6 h1:/U95T1tBzt9RSSi23pg4VR3B9VWkyM4xv8TXAGi60IQ=
  170 +github.com/gookit/event v1.0.6/go.mod h1:7Udf/q/HQcrK9XE4JZUvbqi46rI1V8r/Pvao2NbPajA=
167 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 171 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
168 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 172 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
169 github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 173 github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
@@ -308,6 +312,8 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT @@ -308,6 +312,8 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
308 github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= 312 github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
309 github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8= 313 github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
310 github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 314 github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
  315 +github.com/qiniu/iconv v1.2.0 h1:2LJKwoF+4LJ3lNM+7cE3P1kNQzAI/HMZuWhkmFoY2U8=
  316 +github.com/qiniu/iconv v1.2.0/go.mod h1:5bxb2h9lptZt2eHLgY+Jw4X06TMtKb6tvvok0DwSwGA=
311 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ= 317 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ=
312 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= 318 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
313 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 319 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
@@ -348,8 +354,9 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV @@ -348,8 +354,9 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
348 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 354 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
349 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 355 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
350 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 356 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
351 -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=  
352 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 357 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
  358 +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
  359 +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
353 github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= 360 github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
354 github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= 361 github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
355 github.com/tal-tech/go-queue v1.0.5 h1:cd2o0lPjAFJKIXuEbQvsGypUhzz6FLib4FVVAyxsMtY= 362 github.com/tal-tech/go-queue v1.0.5 h1:cd2o0lPjAFJKIXuEbQvsGypUhzz6FLib4FVVAyxsMtY=
  1 +package command
  2 +
  3 +import (
  4 + "fmt"
  5 + "reflect"
  6 + "strings"
  7 +
  8 + "github.com/beego/beego/v2/core/validation"
  9 +)
  10 +
  11 +type TerminalReportCommand struct {
  12 + TerminalType string `json:"terminalType"`
  13 + TerminalId string `json:"terminalId"`
  14 + Command string `json:"command"`
  15 + Content string `json:"content"`
  16 + Table string `json:"table"`
  17 +
  18 + CompanyId int64 `json:"companyId"`
  19 + OrgId int64 `json:"orgId"`
  20 +}
  21 +
  22 +func (terminalReportCommand *TerminalReportCommand) Valid(validation *validation.Validation) {
  23 +
  24 +}
  25 +
  26 +func (terminalReportCommand *TerminalReportCommand) ValidateCommand() error {
  27 + valid := validation.Validation{}
  28 + b, err := valid.Valid(terminalReportCommand)
  29 + if err != nil {
  30 + return err
  31 + }
  32 + if !b {
  33 + elem := reflect.TypeOf(terminalReportCommand).Elem()
  34 + for _, validErr := range valid.Errors {
  35 + field, isExist := elem.FieldByName(validErr.Field)
  36 + if isExist {
  37 + return fmt.Errorf(strings.Replace(validErr.Message, validErr.Field, field.Tag.Get("cname"), -1))
  38 + } else {
  39 + return fmt.Errorf(validErr.Message)
  40 + }
  41 + }
  42 + }
  43 + return nil
  44 +}
  1 +package service
  2 +
  3 +import (
  4 + "bytes"
  5 + "fmt"
  6 + "github.com/axgle/mahonia"
  7 + "github.com/beego/beego/v2/adapter/utils"
  8 + "github.com/gookit/event"
  9 + "github.com/linmadan/egglib-go/core/application"
  10 + "gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/application/factory"
  11 + "gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/application/terminal/command"
  12 + "gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/domain"
  13 + "gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/log"
  14 + "io"
  15 +)
  16 +
  17 +var GlobalTerminalManager *TerminalManager
  18 +
  19 +func init() {
  20 + GlobalTerminalManager = NewTerminalManager()
  21 + event.On(domain.UserCreateEvent, event.ListenerFunc(func(e event.Event) error {
  22 +
  23 + return nil
  24 + }))
  25 + event.On(domain.UserUpdateEvent, event.ListenerFunc(func(e event.Event) error {
  26 +
  27 + return nil
  28 + }))
  29 + event.On(domain.UserDeleteEvent, event.ListenerFunc(func(e event.Event) error {
  30 +
  31 + return nil
  32 + }))
  33 + event.On(domain.UserEnableEvent, event.ListenerFunc(func(e event.Event) error {
  34 +
  35 + return nil
  36 + }))
  37 + event.On(domain.UserEnableEvent, event.ListenerFunc(func(e event.Event) error {
  38 +
  39 + return nil
  40 + }))
  41 + event.On(DownEntityEvent, event.ListenerFunc(GlobalTerminalManager.DownEntityEvent))
  42 +}
  43 +
  44 +type TerminalService struct {
  45 +}
  46 +
  47 +func NewTerminalService(options map[string]interface{}) *TerminalService {
  48 + newUserService := &TerminalService{}
  49 + return newUserService
  50 +}
  51 +
  52 +func (svr *TerminalService) TerminalReport(cmd *command.TerminalReportCommand) (interface{}, error) {
  53 + if err := cmd.ValidateCommand(); err != nil {
  54 + return nil, application.ThrowError(application.ARG_ERROR, err.Error())
  55 + }
  56 + transactionContext, err := factory.CreateTransactionContext(nil)
  57 + if err != nil {
  58 + return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
  59 + }
  60 + if err := transactionContext.StartTransaction(); err != nil {
  61 + return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
  62 + }
  63 + defer func() {
  64 + transactionContext.RollbackTransaction()
  65 + }()
  66 + response, err := terminalReport(cmd, transactionContext)
  67 + if err != nil {
  68 + log.Logger.Error(err.Error())
  69 + return nil, err
  70 + }
  71 + if err := transactionContext.CommitTransaction(); err != nil {
  72 + return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
  73 + }
  74 + return response, nil
  75 +}
  76 +
  77 +func terminalReport(cmd *command.TerminalReportCommand, transactionContext application.TransactionContext) (interface{}, error) {
  78 + var (
  79 + response string = "OK"
  80 + )
  81 +
  82 + switch cmd.Command {
  83 + case "getrequest":
  84 + device, ok := GlobalTerminalManager.GetDevice(cmd.TerminalId)
  85 + if !ok || device == nil {
  86 + break
  87 + }
  88 + if downEntity, ok := device.PopDownEntity(); ok && downEntity != nil {
  89 + response = downEntity.DownCommand()
  90 + }
  91 + break
  92 + case "cdata":
  93 + parseEntities := ParseData(cmd)
  94 + var (
  95 + down DownEntity
  96 + err error
  97 + handler Handler = ZKClockHandler{TransactionContext: transactionContext}
  98 + )
  99 + for _, entity := range parseEntities {
  100 + switch TableType(cmd.Table) {
  101 + case AttLOG:
  102 + down, err = handler.Attendance(entity.(AttLOGUpEntity))
  103 + case BIODATA:
  104 + down, err = handler.BioData(entity.(BIODATAEntity))
  105 + case OPERLOG:
  106 + //if v, ok := entity.(USEREntity); ok {
  107 + // down, err = handler.ReportUser(v)
  108 + //}
  109 + if v, ok := entity.(BIODATAEntity); ok {
  110 + down, err = handler.BioData(v)
  111 + }
  112 + }
  113 + if err != nil {
  114 + log.Logger.Error(err.Error())
  115 + continue
  116 + }
  117 + if down != nil {
  118 + response = down.DownCommand()
  119 + }
  120 + break
  121 + }
  122 + }
  123 +
  124 + return map[string]interface{}{
  125 + "response": response,
  126 + }, nil
  127 +}
  128 +
  129 +func ParseData(cmd *command.TerminalReportCommand) []interface{} {
  130 + result := make([]interface{}, 0)
  131 + buf := bytes.NewBufferString(cmd.Content)
  132 + for {
  133 + line, err := buf.ReadBytes('\n')
  134 + if err == io.EOF {
  135 + break
  136 + }
  137 + if err != nil {
  138 + log.Logger.Error(err.Error())
  139 + break
  140 + }
  141 +
  142 + switch TableType(cmd.Table) {
  143 + case AttLOG:
  144 + columns := bytes.Split(line, ([]byte)("\t"))
  145 + if len(columns) != 11 { //10 + 1 空格
  146 + continue
  147 + }
  148 + result = append(result, AttLOGUpEntity{
  149 + Pin: string(columns[0]),
  150 + CompanyId: cmd.CompanyId,
  151 + OrgId: cmd.OrgId,
  152 + })
  153 + case BIODATA:
  154 + /*BIODATA Pin=3\tNo=0\tIndex=0\tValid=1\tDuress=0\tType=9\tMajorVer=39\tMinorVer=1\tFormat=
  155 + 0\tTmp=apUBEBABQo4JACcBAWiOADA7dP4pU9F31Uxom7NAGjM4eO/8X5Ee4uahkIT11c3188+VguMsd3oCO0O29efRyxofdLiohI4QL7woK3U*/
  156 + columns := readLineToMap(line, "BIODATA")
  157 + result = append(result, BIODATAEntity{
  158 + Pin: columns["Pin"],
  159 + No: columns["No"],
  160 + Index: columns["Index"],
  161 + Duress: columns["Duress"],
  162 + Type: columns["Type"],
  163 + MinorVer: columns["MinorVer"],
  164 + MajorVer: columns["MajorVer"],
  165 + Format: columns["Format"],
  166 + Tmp: columns["Tmp"],
  167 + CompanyId: cmd.CompanyId,
  168 + OrgId: cmd.OrgId,
  169 + })
  170 + case OPERLOG:
  171 + if bytes.HasPrefix(line, []byte("FP")) {
  172 + /*
  173 + FP PIN=3 FID=6 Size=1336 Valid=1 TMP=SqtTUzIxAAAD6O8ECA
  174 + */
  175 + columns := readLineToMap(line, "FP")
  176 + result = append(result, BIODATAEntity{
  177 + Pin: columns["PIN"],
  178 + Type: fmt.Sprintf("%v", BioDataType1),
  179 + Tmp: columns["TMP"],
  180 + CompanyId: cmd.CompanyId,
  181 + OrgId: cmd.OrgId,
  182 + })
  183 + } else if bytes.HasPrefix(line, []byte("USER")) {
  184 + /*
  185 + USER PIN=3 Name=杨xx Pri=14 Passwd= Card=3731588478 Grp=1 TZ=0000000100000000 Verify=-1 ViceCard= StartDatetime=0 EndDatetime=0
  186 + */
  187 + columns := readLineToMap(line, "USER")
  188 + name := columns["Name"] //GbkToUtf8(columns["Name"])
  189 + result = append(result, USEREntity{
  190 + OPERLOGType: "USER",
  191 + Pin: columns["PIN"],
  192 + Name: name,
  193 + CompanyId: cmd.CompanyId,
  194 + OrgId: cmd.OrgId,
  195 + })
  196 + }
  197 + }
  198 + }
  199 + return result
  200 +}
  201 +
  202 +func readLineToMap(line []byte, prefix string) map[string]string {
  203 + var result = make(map[string]string)
  204 + line = bytes.TrimLeft(line, prefix)
  205 + line = bytes.TrimSpace(line)
  206 + columns := bytes.Fields(line)
  207 + for i := range columns {
  208 + kv := bytes.SplitN(columns[i], []byte("="), 2)
  209 + if len(kv) < 2 {
  210 + continue
  211 + }
  212 + //if string(kv[0]) == "Name" {
  213 + // log.Logger.Debug(fmt.Sprintf("%x", kv[1]))
  214 + // log.Logger.Debug(fmt.Sprintf("%v", kv[1]))
  215 + // log.Logger.Debug(fmt.Sprintf("%s", string(kv[1])))
  216 + //}
  217 + result[string(kv[0])] = string(kv[1])
  218 + }
  219 + return result
  220 +}
  221 +
  222 +type Handler interface {
  223 + Attendance(entity AttLOGUpEntity) (DownEntity, error)
  224 + BioData(entity BIODATAEntity) (DownEntity, error)
  225 + ReportUser(entity USEREntity) (DownEntity, error)
  226 +}
  227 +
  228 +type ZKClockHandler struct {
  229 + TransactionContext application.TransactionContext
  230 +}
  231 +
  232 +func (handler ZKClockHandler) Attendance(entity AttLOGUpEntity) (DownEntity, error) {
  233 + var (
  234 + userRepository, _, _ = factory.FastPgUser(handler.TransactionContext, 0)
  235 + userBaseRepository, _, _ = factory.FastPgUserBase(handler.TransactionContext, 0)
  236 + user *domain.User
  237 + userBase *domain.UserBase
  238 + err error
  239 + )
  240 + if user, err = userRepository.FindOne(map[string]interface{}{"companyId": entity.CompanyId, "orgId": entity.OrgId, "icCardNumber": entity.Pin}); err != nil {
  241 + return nil, err
  242 + }
  243 + if userBase, err = userBaseRepository.FindOne(map[string]interface{}{"userBaseId": user.UserBaseId}); err != nil {
  244 + return nil, err
  245 + }
  246 + var sendQuery bool
  247 + // 请求 人脸识别-用户画像
  248 + if len(userBase.UserInfo.FacePortrait) == 0 {
  249 + sendQuery = true
  250 + //event.Fire(DownEntityEvent, map[string]interface{}{"entity": NewQueryBIODATADownEntity(generateSn(), entity.Pin, BioDataType2)})
  251 + }
  252 + // 请求 人脸识别-指纹
  253 + if len(userBase.UserInfo.FingerprintPortrait) == 0 {
  254 + sendQuery = true
  255 + //event.Fire(DownEntityEvent, map[string]interface{}{"entity": NewQueryBIODATADownEntity(generateSn(), entity.Pin, BioDataType1)})
  256 + }
  257 + if sendQuery {
  258 + event.Fire(DownEntityEvent, map[string]interface{}{"entity": NewQueryUserInfoDownEntity(generateSn(), entity.Pin)})
  259 + }
  260 + return nil, nil
  261 +}
  262 +
  263 +func (handler ZKClockHandler) BioData(entity BIODATAEntity) (DownEntity, error) {
  264 + var (
  265 + userRepository, _, _ = factory.FastPgUser(handler.TransactionContext, 0)
  266 + userBaseRepository, _, _ = factory.FastPgUserBase(handler.TransactionContext, 0)
  267 + user *domain.User
  268 + userBase *domain.UserBase
  269 + err error
  270 + updateFlag bool
  271 + )
  272 + if user, err = userRepository.FindOne(map[string]interface{}{"companyId": entity.CompanyId, "orgId": entity.OrgId, "icCardNumber": entity.Pin}); err != nil {
  273 + return nil, err
  274 + }
  275 + if userBase, err = userBaseRepository.FindOne(map[string]interface{}{"userBaseId": user.UserBaseId}); err != nil {
  276 + return nil, err
  277 + }
  278 + // 请求 人脸识别-用户画像
  279 + if len(userBase.UserInfo.FacePortrait) == 0 && len(entity.Tmp) > 0 && entity.Type == "9" {
  280 + userBase.UserInfo.FacePortrait = entity.Tmp
  281 + updateFlag = true
  282 + }
  283 + // 请求 人脸识别-指纹
  284 + if len(userBase.UserInfo.FingerprintPortrait) == 0 && len(entity.Tmp) > 0 && entity.Type == "1" {
  285 + userBase.UserInfo.FingerprintPortrait = entity.Tmp
  286 + updateFlag = true
  287 + }
  288 + if updateFlag {
  289 + _, err = userBaseRepository.Save(userBase)
  290 + if err != nil {
  291 + log.Logger.Error(err.Error())
  292 + }
  293 + }
  294 + return nil, err
  295 +}
  296 +
  297 +func (handler ZKClockHandler) ReportUser(entity USEREntity) (DownEntity, error) {
  298 + var (
  299 + userRepository, _, _ = factory.FastPgUser(handler.TransactionContext, 0)
  300 + userBaseRepository, _, _ = factory.FastPgUserBase(handler.TransactionContext, 0)
  301 + user *domain.User
  302 + userBase *domain.UserBase
  303 + err error
  304 + updateFlag bool
  305 + )
  306 + if user, err = userRepository.FindOne(map[string]interface{}{"companyId": entity.CompanyId, "orgId": entity.OrgId, "icCardNumber": entity.Pin}); err != nil {
  307 + return nil, err
  308 + }
  309 + if userBase, err = userBaseRepository.FindOne(map[string]interface{}{"userBaseId": user.UserBaseId}); err != nil {
  310 + return nil, err
  311 + }
  312 + // 请求 人脸识别-用户画像
  313 + //if len(entity.Pin) > 0 && userBase.UserInfo.Pin != entity.Pin {
  314 + // updateFlag = true
  315 + // userBase.UserInfo.Pin = entity.Pin
  316 + //}
  317 + if updateFlag {
  318 + _, err = userBaseRepository.Save(userBase)
  319 + }
  320 + return nil, err
  321 +}
  322 +
  323 +func generateSn() string {
  324 + return string(utils.RandomCreateBytes(10))
  325 +}
  326 +
  327 +// GbkToUtf8 GBK 转 UTF-8
  328 +func GbkToUtf8(s string) string {
  329 + decoder := mahonia.NewDecoder("gbk")
  330 + _, data, _ := decoder.Translate([]byte(s), true)
  331 + //return decoder.ConvertString(s)
  332 + return string(data)
  333 +}
  334 +
  335 +// Utf8ToGbk UTF-8 转 GBK
  336 +func Utf8ToGbk(s string) string {
  337 + encoder := mahonia.NewEncoder("gbk")
  338 + return encoder.ConvertString(s)
  339 +}
  340 +
  341 +func ConvertToString(src string, srcCode string, tagCode string) string {
  342 +
  343 + srcCoder := mahonia.NewDecoder(srcCode)
  344 +
  345 + srcResult := srcCoder.ConvertString(src)
  346 +
  347 + tagCoder := mahonia.NewDecoder(tagCode)
  348 +
  349 + _, cdata, _ := tagCoder.Translate([]byte(srcResult), true)
  350 +
  351 + result := string(cdata)
  352 +
  353 + return result
  354 +
  355 +}
  1 +package service
  2 +
  3 +import (
  4 + "container/list"
  5 + "github.com/gookit/event"
  6 + "gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/log"
  7 + "sync"
  8 +)
  9 +
  10 +var (
  11 + DownEntityEvent = "down_entity"
  12 +)
  13 +
  14 +type TerminalManager struct {
  15 + TerminalDeviceList []*TerminalDevice
  16 + TerminalDevices sync.Map
  17 +}
  18 +
  19 +func NewTerminalManager() *TerminalManager {
  20 + return &TerminalManager{
  21 + TerminalDeviceList: make([]*TerminalDevice, 0),
  22 + TerminalDevices: sync.Map{},
  23 + }
  24 +}
  25 +
  26 +func (term *TerminalManager) GetDevice(terminalId string) (*TerminalDevice, bool) {
  27 + device, ok := term.TerminalDevices.Load(terminalId)
  28 + if !ok {
  29 + device := NewTerminalDevice(terminalId)
  30 + term.TerminalDevices.Store(terminalId, device)
  31 + log.Logger.Debug("【TerminalManager】 add new device:"+terminalId, map[string]interface{}{"device": device})
  32 + term.TerminalDeviceList = append(term.TerminalDeviceList, device)
  33 + return nil, false
  34 + }
  35 + if v, ok := device.(*TerminalDevice); ok {
  36 + return v, ok
  37 + }
  38 + return nil, false
  39 +}
  40 +
  41 +func (term *TerminalManager) PopDownEntityByDevice(sn string) (DownEntity, bool) {
  42 + device, ok := term.GetDevice(sn)
  43 + if !ok {
  44 + return nil, false
  45 + }
  46 + return device.PopDownEntity()
  47 +}
  48 +
  49 +func (term *TerminalManager) AddDownEntityByDevice(sn string, entity interface{}) bool {
  50 + device, ok := term.GetDevice(sn)
  51 + if !ok {
  52 + return false
  53 + }
  54 + device.AddDownEntity(entity)
  55 + return true
  56 +}
  57 +
  58 +func (term *TerminalManager) BroadcastDownEntity(downEntity interface{}) {
  59 + for i := range term.TerminalDeviceList {
  60 + term.TerminalDeviceList[i].AddDownEntity(downEntity)
  61 + }
  62 +}
  63 +
  64 +// Listen Event
  65 +
  66 +func (term *TerminalManager) DownEntityEvent(e event.Event) error {
  67 + //fmt.Printf("handle down entity event: %s\n", e.Name())
  68 + entity := e.Get("entity")
  69 + if entity != nil {
  70 + term.BroadcastDownEntity(entity)
  71 + }
  72 + return nil
  73 +}
  74 +
  75 +type TerminalDevice struct {
  76 + Id string
  77 + DownEntityList *list.List
  78 +}
  79 +
  80 +func NewTerminalDevice(terminalId string) *TerminalDevice {
  81 + return &TerminalDevice{
  82 + Id: terminalId,
  83 + DownEntityList: list.New(),
  84 + }
  85 +}
  86 +
  87 +func (device *TerminalDevice) PopDownEntity() (DownEntity, bool) {
  88 + element := device.DownEntityList.Front()
  89 + if element == nil {
  90 + return nil, false
  91 + }
  92 + device.DownEntityList.Remove(element)
  93 + if v, ok := element.Value.(DownEntity); ok {
  94 + log.Logger.Debug("【TerminalManager】 pop down entity to Sender", map[string]interface{}{"entity": v})
  95 + return v, ok
  96 + }
  97 + return nil, false
  98 +}
  99 +
  100 +func (device *TerminalDevice) AddDownEntity(downEntity interface{}) {
  101 + log.Logger.Debug("【TerminalManager】 add down entity to Profile", map[string]interface{}{"entity": downEntity})
  102 + device.DownEntityList.PushBack(downEntity)
  103 +}
  1 +package service
  2 +
  3 +import (
  4 + "fmt"
  5 + "github.com/stretchr/testify/assert"
  6 + "testing"
  7 +)
  8 +
  9 +func TestGbkToUtf8(t *testing.T) {
  10 + input := "刘"
  11 + out := Utf8ToGbk(input)
  12 + t.Log("gbk:" + out)
  13 + //"efbfbdeeb8a3"
  14 + t.Log(fmt.Sprintf("%v", []byte(out)))
  15 + out = GbkToUtf8(out)
  16 + t.Log("utf8:" + out)
  17 + t.Log("utf8:" + fmt.Sprintf("%v", []byte(out)))
  18 + assert.Equal(t, input, out)
  19 +}
  1 +package service
  2 +
  3 +import "fmt"
  4 +
  5 +type TableType string
  6 +
  7 +var (
  8 + AttLOG TableType = "ATTLOG" // AttLOG 打卡记录
  9 + UserInfo TableType = "USERINFO" // 用户信息
  10 + FingerTMP TableType = "FINGERTMP" //指纹
  11 + Face TableType = "FACE" //脸部
  12 + OPERLOG TableType = "OPERLOG" //操作记录
  13 + BIODATA TableType = "BIODATA" //一体化数据 (人脸识别等)
  14 +)
  15 +
  16 +// AttLOGUpEntity 打卡记录实体 - 上行命令
  17 +type AttLOGUpEntity struct {
  18 + Pin string
  19 + Time string
  20 + Status string
  21 + Verify string
  22 + Workcode string
  23 + Reserved1 string
  24 + Reserved2 string
  25 + MaskFlag string
  26 + Temperature string
  27 + ConvTemperature string
  28 +
  29 + CompanyId int64 `json:"companyId"`
  30 + OrgId int64 `json:"orgId"`
  31 +}
  32 +
  33 +// FPEntity 指纹 - 上行命令 OPERLOG
  34 +type FPEntity struct {
  35 + OPERLOGType string //"FP" "USER" "BIODATA"
  36 + Pin string
  37 + TFID string
  38 + TSize string
  39 + TValid string
  40 + TTMP string
  41 +
  42 + CompanyId int64 `json:"companyId"`
  43 + OrgId int64 `json:"orgId"`
  44 +}
  45 +
  46 +// USEREntity 用户 - 上行命令 OPERLOG
  47 +type USEREntity struct {
  48 + OPERLOGType string //"FP" "USER" "BIODATA"
  49 + Pin string `json:"pin"`
  50 + Name string `json:"Name"`
  51 + Pri string `json:"Pri"`
  52 + Passwd string `json:"Passwd"`
  53 + Grp string `json:"Grp"`
  54 + TZ string `json:"TZ"`
  55 + Verify string `json:"Verify"`
  56 + ViceCard string `json:"ViceCard"`
  57 + StartDatetime string `json:"StartDatetime"`
  58 + EndDatetime string `json:"EndDatetime"`
  59 +
  60 + CompanyId int64 `json:"companyId"`
  61 + OrgId int64 `json:"orgId"`
  62 +}
  63 +
  64 +// BIODATAEntity 一体化数据实体 - 上行命令 BIODATA
  65 +type BIODATAEntity struct {
  66 + Pin string `json:"pin"`
  67 + No string `json:"tNo"`
  68 + Index string `json:"tIndex"`
  69 + Valid string `json:"tValid"`
  70 + Duress string `json:"tDuress"`
  71 + Type string `json:"tType"`
  72 + MajorVer string `json:"tMajorVer"`
  73 + MinorVer string `json:"tMinorVer"`
  74 + Format string `json:"tFormat"`
  75 + Tmp string `json:"Tmp"`
  76 +
  77 + CompanyId int64 `json:"companyId"`
  78 + OrgId int64 `json:"orgId"`
  79 +}
  80 +
  81 +type DownEntity interface {
  82 + DownCommand() string
  83 +}
  84 +
  85 +func QueryUserInfoRequest(sn string, entity AttLOGUpEntity) string {
  86 + return fmt.Sprintf("C:%v:DATA QUERY USERINFO PIN=%v", sn, entity.Pin)
  87 +}
  88 +
  89 +// QueryUserInfoDownEntity 查询用户信息-下行命令
  90 +type QueryUserInfoDownEntity struct {
  91 + Sn string
  92 + Pin string
  93 +}
  94 +
  95 +func (entity QueryUserInfoDownEntity) DownCommand() string {
  96 + return fmt.Sprintf("C:%v:DATA QUERY USERINFO PIN=%v", entity.Sn, entity.Pin)
  97 +}
  98 +
  99 +func NewQueryUserInfoDownEntity(sn string, pin string) QueryUserInfoDownEntity {
  100 + return QueryUserInfoDownEntity{
  101 + Sn: sn,
  102 + Pin: pin,
  103 + }
  104 +}
  105 +
  106 +type BioDataType int
  107 +
  108 +var (
  109 + BioDataType0 BioDataType = 0 //通用
  110 + BioDataType1 BioDataType = 1 //指纹
  111 + BioDataType2 BioDataType = 0 //面部
  112 + BioDataType3 BioDataType = 3 //声纹
  113 + BioDataType4 BioDataType = 4 //虹膜
  114 + BioDataType5 BioDataType = 5 //视网膜
  115 + BioDataType6 BioDataType = 6 //掌纹
  116 + BioDataType7 BioDataType = 7 //指静脉
  117 + BioDataType8 BioDataType = 8 //手掌
  118 + BioDataType9 BioDataType = 9 //可见光面部
  119 +)
  120 +
  121 +// QueryBioDataDownEntity 查询一体化-下行命令
  122 +type QueryBioDataDownEntity struct {
  123 + Sn string
  124 + Pin string
  125 + Type BioDataType
  126 +}
  127 +
  128 +func (entity QueryBioDataDownEntity) DownCommand() string {
  129 + return fmt.Sprintf("C:%v:DATA QUERY BIODATA Type=%v PIN=%v", entity.Sn, entity.Type, entity.Pin)
  130 +}
  131 +
  132 +func NewQueryBIODATADownEntity(sn string, pin string, t BioDataType) QueryBioDataDownEntity {
  133 + return QueryBioDataDownEntity{
  134 + Sn: sn,
  135 + Pin: pin,
  136 + Type: t,
  137 + }
  138 +}
  139 +
  140 +// DeleteUserDownEntity 删除用户-下行命令
  141 +type DeleteUserDownEntity struct {
  142 + Sn string
  143 + Pin string
  144 + Table string
  145 +}
  146 +
  147 +func (entity DeleteUserDownEntity) DownCommand() string {
  148 + return fmt.Sprintf("C:%v:DATA DELETE %v PIN=%v", entity.Sn, entity.Table, entity.Pin)
  149 +}
  150 +
  151 +func NewClearUserDownEntity(sn string, pin string, t string) DeleteUserDownEntity {
  152 + return DeleteUserDownEntity{
  153 + Sn: sn,
  154 + Pin: pin,
  155 + Table: t,
  156 + }
  157 +}
@@ -26,6 +26,14 @@ const ( @@ -26,6 +26,14 @@ const (
26 UserStatusDestroy UserStatus = 3 26 UserStatusDestroy UserStatus = 3
27 ) 27 )
28 28
  29 +const (
  30 + UserUpdateEvent = "user_update"
  31 + UserDeleteEvent = "user_delete"
  32 + UserCreateEvent = "user_create"
  33 + UserEnableEvent = "user_enable" //禁用启用事件
  34 + UserSyncEvent = "user_sync" // 用户同步
  35 +)
  36 +
29 // 用户 37 // 用户
30 type User struct { 38 type User struct {
31 // 用户Id 用户唯一标识 39 // 用户Id 用户唯一标识
@@ -13,9 +13,16 @@ type UserInfo struct { @@ -13,9 +13,16 @@ type UserInfo struct {
13 13
14 // 员工类型 1:固定 2:派遣 3.临时 14 // 员工类型 1:固定 2:派遣 3.临时
15 EmployeeType int `json:"employeeType,omitempty"` 15 EmployeeType int `json:"employeeType,omitempty"`
16 - // IC卡号 16 + // IC卡号 = Pin
17 IcCardNumber string `json:"icCardNumber,omitempty"` 17 IcCardNumber string `json:"icCardNumber,omitempty"`
18 18
  19 + // 面部识别
  20 + FacePortrait string `json:"facePortrait,omitempty"`
  21 + // 指纹识别
  22 + FingerprintPortrait string `json:"fingerprintPortrait,omitempty"`
  23 + // 打卡机标识
  24 + // Pin string `json:"pin"`
  25 +
19 Referer string `json:"-"` 26 Referer string `json:"-"`
20 // 部门 27 // 部门
21 DepartmentName string `cname:"部门" json:"-"` 28 DepartmentName string `cname:"部门" json:"-"`
  1 +package controllers
  2 +
  3 +import (
  4 + "github.com/linmadan/egglib-go/web/beego"
  5 + "gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/application/terminal/command"
  6 + "gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/application/terminal/service"
  7 +)
  8 +
  9 +type TerminalController struct {
  10 + beego.BaseController
  11 +}
  12 +
  13 +func (controller *TerminalController) TerminalReport() {
  14 + service := service.NewTerminalService(nil)
  15 + terminalReportCommand := &command.TerminalReportCommand{}
  16 + controller.Unmarshal(terminalReportCommand)
  17 + data, err := service.TerminalReport(terminalReportCommand)
  18 + controller.Response(data, err)
  19 +}
  1 +package routers
  2 +
  3 +import (
  4 + "github.com/beego/beego/v2/server/web"
  5 + "gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/port/beego/controllers"
  6 +)
  7 +
  8 +func init() {
  9 + web.Router("/terminal/report", &controllers.TerminalController{}, "Post:TerminalReport")
  10 +}