作者 Administrator

合并分支 'test' 到 'master'

Test



查看合并请求 !9
... ... @@ -11,6 +11,7 @@ require (
github.com/gavv/httpexpect v2.0.0+incompatible
github.com/go-pg/pg/v10 v10.9.0
github.com/google/go-querystring v1.1.0 // indirect
github.com/gookit/event v1.0.6
github.com/imkira/go-interpol v1.1.0 // indirect
github.com/linmadan/egglib-go v0.0.0-20210313060205-8b5e456b11f7
github.com/mattn/go-colorable v0.1.8 // indirect
... ... @@ -19,7 +20,7 @@ require (
github.com/onsi/gomega v1.11.0
github.com/sergi/go-diff v1.2.0 // indirect
github.com/smartystreets/goconvey v1.6.4 // indirect
github.com/stretchr/testify v1.7.0
github.com/stretchr/testify v1.7.1
github.com/tal-tech/go-queue v1.0.5
github.com/tal-tech/go-zero v1.0.27
github.com/valyala/fasthttp v1.23.0 // indirect
... ...
... ... @@ -164,6 +164,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gookit/event v1.0.6 h1:/U95T1tBzt9RSSi23pg4VR3B9VWkyM4xv8TXAGi60IQ=
github.com/gookit/event v1.0.6/go.mod h1:7Udf/q/HQcrK9XE4JZUvbqi46rI1V8r/Pvao2NbPajA=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
... ... @@ -348,8 +350,9 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
github.com/tal-tech/go-queue v1.0.5 h1:cd2o0lPjAFJKIXuEbQvsGypUhzz6FLib4FVVAyxsMtY=
... ...
package command
import (
"fmt"
"reflect"
"strings"
"github.com/beego/beego/v2/core/validation"
)
type TerminalReportCommand struct {
TerminalType string `json:"terminalType"`
TerminalId string `json:"terminalId"`
Command string `json:"command"`
Content string `json:"content"`
Table string `json:"table"`
CompanyId int64 `json:"companyId"`
OrgId int64 `json:"orgId"`
}
func (terminalReportCommand *TerminalReportCommand) Valid(validation *validation.Validation) {
}
func (terminalReportCommand *TerminalReportCommand) ValidateCommand() error {
valid := validation.Validation{}
b, err := valid.Valid(terminalReportCommand)
if err != nil {
return err
}
if !b {
elem := reflect.TypeOf(terminalReportCommand).Elem()
for _, validErr := range valid.Errors {
field, isExist := elem.FieldByName(validErr.Field)
if isExist {
return fmt.Errorf(strings.Replace(validErr.Message, validErr.Field, field.Tag.Get("cname"), -1))
} else {
return fmt.Errorf(validErr.Message)
}
}
}
return nil
}
... ...
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"
)
log.Logger.Debug(fmt.Sprintf("【TerminalManager】 收到上行命令 command:%v table:%v content:%v", cmd.Command, cmd.Table, cmd.Content))
switch cmd.Command {
case "getrequest":
device, ok := GlobalTerminalManager.GetDevice(cmd.TerminalId, cmd.CompanyId)
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":
}
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, entity.CompanyId)})
}
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(entity.Tmp) > 0 && entity.Type == "9" {
userBase.UserInfo.FacePortrait = entity.Tmp
updateFlag = true
if len(GlobalTerminalManager.TerminalDeviceList) >= 1 { //TODO:后期移除掉,多台设备时才进行广播
event.Fire(DownEntityEvent, map[string]interface{}{"entity": NewUpdateUserFacePortraitDownEntity(generateSn(), entity.Pin, entity.Tmp, entity.CompanyId)})
}
}
// 请求 人脸识别-指纹
if len(entity.Tmp) > 0 && entity.Type == "1" {
userBase.UserInfo.FingerprintPortrait = entity.Tmp
updateFlag = true
if len(GlobalTerminalManager.TerminalDeviceList) >= 1 {
event.Fire(DownEntityEvent, map[string]interface{}{"entity": NewUpdateUserFingerprintPortraitDownEntity(generateSn(), entity.Pin, entity.Tmp, entity.CompanyId)})
}
}
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))
}
... ...
package service
import (
"container/list"
"fmt"
"github.com/gookit/event"
"github.com/linmadan/egglib-go/utils/json"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/domain"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/log"
"sync"
)
var (
DownEntityEvent = "down_entity"
)
type TerminalManager struct {
TerminalDeviceList []*TerminalDevice
TerminalDevices sync.Map
}
func NewTerminalManager() *TerminalManager {
return &TerminalManager{
TerminalDeviceList: make([]*TerminalDevice, 0),
TerminalDevices: sync.Map{},
}
}
func (term *TerminalManager) GetDevice(terminalId string, params ...interface{}) (*TerminalDevice, bool) {
device, ok := term.TerminalDevices.Load(terminalId)
if !ok {
var companyId int64
if len(params) > 0 {
companyId = params[0].(int64)
}
device := NewTerminalDevice(terminalId, companyId)
term.TerminalDevices.Store(terminalId, device)
log.Logger.Debug(fmt.Sprintf("【TerminalManager】 终端上线 add new device:%v", terminalId), map[string]interface{}{"device": device})
term.TerminalDeviceList = append(term.TerminalDeviceList, device)
return nil, false
}
if v, ok := device.(*TerminalDevice); ok {
return v, ok
}
return nil, false
}
func (term *TerminalManager) PopDownEntityByDevice(sn string) (DownEntity, bool) {
device, ok := term.GetDevice(sn)
if !ok {
return nil, false
}
return device.PopDownEntity()
}
func (term *TerminalManager) AddDownEntityByDevice(sn string, entity interface{}) bool {
device, ok := term.GetDevice(sn)
if !ok {
return false
}
device.AddDownEntity(entity)
return true
}
func (term *TerminalManager) BroadcastDownEntity(downEntity interface{}) {
if len(term.TerminalDeviceList) == 0 {
log.Logger.Debug("【TerminalManager】 当前在线终端:0 广播命令退出")
return
}
for i := range term.TerminalDeviceList {
term.TerminalDeviceList[i].AddDownEntity(downEntity)
}
}
// Listen Event
func (term *TerminalManager) DownEntityEvent(e event.Event) error {
entity := e.Get("entity")
if entity != nil {
term.BroadcastDownEntity(entity)
}
return nil
}
func (term *TerminalManager) SyncUser(e event.Event) error {
user := e.Get("user")
userBase := e.Get("userBase")
if user != nil && userBase != nil {
assertUser := user.(*domain.User)
assertUserBase := userBase.(*domain.UserBase)
if len(assertUser.Ext.IcCardNumber) == 0 {
log.Logger.Debug("【TerminalManager】 当前用户ICCard未设置 不进行同步", map[string]interface{}{"user": user})
return nil
}
term.BroadcastDownEntity(NewUpdateUserDownEntity(generateSn(), assertUser.Ext.IcCardNumber, assertUserBase.UserInfo.UserName))
if len(assertUserBase.UserInfo.FacePortrait) > 0 {
term.BroadcastDownEntity(NewUpdateUserFacePortraitDownEntity(generateSn(), assertUser.Ext.IcCardNumber, assertUserBase.UserInfo.FacePortrait, assertUser.CompanyId))
}
if len(assertUserBase.UserInfo.FingerprintPortrait) > 0 {
term.BroadcastDownEntity(NewUpdateUserFingerprintPortraitDownEntity(generateSn(), assertUser.Ext.IcCardNumber, assertUserBase.UserInfo.FingerprintPortrait, assertUser.CompanyId))
}
}
return nil
}
func (term *TerminalManager) EnableUser(e event.Event) error {
user := e.Get("user")
if user != nil {
assertUser := user.(*domain.User)
if assertUser.EnableStatus == int(domain.UserStatusEnable) {
term.SyncUser(e)
} else if assertUser.EnableStatus == int(domain.UserStatusDisable) {
term.BroadcastDownEntity(NewDeleteDownEntity(generateSn(), assertUser.Ext.IcCardNumber, string(UserInfo), assertUser.CompanyId))
}
}
return nil
}
func (term *TerminalManager) CreateUser(e event.Event) error {
return term.SyncUser(e)
}
func (term *TerminalManager) UpdateUser(e event.Event) error {
return term.EnableUser(e)
}
type TerminalDevice struct {
Id string
CompanyId int64
DownEntityList *list.List
}
func NewTerminalDevice(terminalId string, companyId int64) *TerminalDevice {
return &TerminalDevice{
Id: terminalId,
CompanyId: companyId,
DownEntityList: list.New(),
}
}
func (device *TerminalDevice) PopDownEntity() (DownEntity, bool) {
element := device.DownEntityList.Front()
if element == nil {
return nil, false
}
device.DownEntityList.Remove(element)
if v, ok := element.Value.(DownEntity); ok {
log.Logger.Debug("【TerminalManager】 发送命令 pop down entity to Sender cmd:"+v.DownCommand(), map[string]interface{}{"entity": v})
return v, ok
}
return nil, false
}
func (device *TerminalDevice) AddDownEntity(downEntity interface{}) {
v, ok := downEntity.(DownEntity)
var cmd string
if !ok {
return
}
cmd = v.DownCommand()
options := objectJsonToMap(downEntity)
if v, ok := options["CompanyId"]; ok {
companyId, ok := v.(int64)
if ok && device.CompanyId != companyId {
log.Logger.Debug("【TerminalManager】 丢弃命令(公司不匹配) cmd:"+cmd, map[string]interface{}{"entity": downEntity, "device": device})
return
}
}
log.Logger.Debug("【TerminalManager】 添加命令 add down entity to Profile cmd:"+cmd, map[string]interface{}{"entity": downEntity})
device.DownEntityList.PushBack(downEntity)
}
func objectJsonToMap(v interface{}) map[string]interface{} {
result := make(map[string]interface{})
data := json.MarshalToString(v)
json.UnmarshalFromString(data, &result)
return result
}
... ...
package service
import "fmt"
type TableType string
var (
AttLOG TableType = "ATTLOG" // AttLOG 打卡记录
UserInfo TableType = "USERINFO" // 用户信息
FingerTMP TableType = "FINGERTMP" //指纹
Face TableType = "FACE" //脸部
OPERLOG TableType = "OPERLOG" //操作记录
BIODATA TableType = "BIODATA" //一体化数据 (人脸识别等)
)
type BioDataType int
var (
BioDataType0 BioDataType = 0 //通用
BioDataType1 BioDataType = 1 //指纹
BioDataType2 BioDataType = 0 //面部
BioDataType3 BioDataType = 3 //声纹
BioDataType4 BioDataType = 4 //虹膜
BioDataType5 BioDataType = 5 //视网膜
BioDataType6 BioDataType = 6 //掌纹
BioDataType7 BioDataType = 7 //指静脉
BioDataType8 BioDataType = 8 //手掌
BioDataType9 BioDataType = 9 //可见光面部
)
// AttLOGUpEntity 打卡记录实体 - 上行命令
type AttLOGUpEntity struct {
Pin string
Time string
Status string
Verify string
Workcode string
Reserved1 string
Reserved2 string
MaskFlag string
Temperature string
ConvTemperature string
CompanyId int64 `json:"companyId"`
OrgId int64 `json:"orgId"`
}
// FPEntity 指纹 - 上行命令 OPERLOG
type FPEntity struct {
OPERLOGType string //"FP" "USER" "BIODATA"
Pin string
TFID string
TSize string
TValid string
TTMP string
CompanyId int64 `json:"companyId"`
OrgId int64 `json:"orgId"`
}
// USEREntity 用户 - 上行命令 OPERLOG
type USEREntity struct {
OPERLOGType string //"FP" "USER" "BIODATA"
Pin string `json:"pin"`
Name string `json:"Name"`
Pri string `json:"Pri"`
Passwd string `json:"Passwd"`
Grp string `json:"Grp"`
TZ string `json:"TZ"`
Verify string `json:"Verify"`
ViceCard string `json:"ViceCard"`
StartDatetime string `json:"StartDatetime"`
EndDatetime string `json:"EndDatetime"`
CompanyId int64 `json:"companyId"`
OrgId int64 `json:"orgId"`
}
// BIODATAEntity 一体化数据实体 - 上行命令 BIODATA
type BIODATAEntity struct {
Pin string `json:"pin"`
No string `json:"tNo"`
Index string `json:"tIndex"`
Valid string `json:"tValid"`
Duress string `json:"tDuress"`
Type string `json:"tType"`
MajorVer string `json:"tMajorVer"`
MinorVer string `json:"tMinorVer"`
Format string `json:"tFormat"`
Tmp string `json:"Tmp"`
CompanyId int64 `json:"companyId"`
OrgId int64 `json:"orgId"`
}
type DownEntity interface {
DownCommand() string
}
func QueryUserInfoRequest(sn string, entity AttLOGUpEntity) string {
return fmt.Sprintf("C:%v:DATA QUERY USERINFO PIN=%v", sn, entity.Pin)
}
// QueryUserInfoDownEntity 查询用户信息-下行命令
type QueryUserInfoDownEntity struct {
Sn string
Pin string
CompanyId int64
}
func (entity QueryUserInfoDownEntity) DownCommand() string {
return fmt.Sprintf("C:%v:DATA QUERY USERINFO PIN=%v", entity.Sn, entity.Pin)
}
func NewQueryUserInfoDownEntity(sn string, pin string, companyId int64) QueryUserInfoDownEntity {
return QueryUserInfoDownEntity{
Sn: sn,
Pin: pin,
CompanyId: companyId,
}
}
// QueryBioDataDownEntity 查询一体化-下行命令
type QueryBioDataDownEntity struct {
Sn string
Pin string
Type BioDataType
CompanyId int64
}
func (entity QueryBioDataDownEntity) DownCommand() string {
return fmt.Sprintf("C:%v:DATA QUERY BIODATA Type=%v PIN=%v", entity.Sn, entity.Type, entity.Pin)
}
func NewQueryBIODATADownEntity(sn string, pin string, t BioDataType) QueryBioDataDownEntity {
return QueryBioDataDownEntity{
Sn: sn,
Pin: pin,
Type: t,
}
}
// DeleteDownEntity 删除-下行命令
type DeleteDownEntity struct {
Sn string
Pin string
Table string
CompanyId int64
}
func (entity DeleteDownEntity) DownCommand() string {
return fmt.Sprintf("C:%v:DATA DELETE %v PIN=%v", entity.Sn, entity.Table, entity.Pin)
}
func NewDeleteDownEntity(sn string, pin string, t string, companyId int64) DeleteDownEntity {
return DeleteDownEntity{
Sn: sn,
Pin: pin,
Table: t,
}
}
// UpdateUserDownEntity 更新用户-下行命令
type UpdateUserDownEntity struct {
Sn string
Pin string
Name string
Card string
CompanyId int64
}
func (entity UpdateUserDownEntity) DownCommand() string {
return fmt.Sprintf("C:%v:DATA UPDATE USERINFO PIN=%v\tName=%v\tCard=%v", entity.Sn, entity.Pin, entity.Name, entity.Pin)
}
func NewUpdateUserDownEntity(sn string, pin string, name string) UpdateUserDownEntity {
return UpdateUserDownEntity{
Sn: sn,
Pin: pin,
Name: name,
}
}
// UpdateUserFacePortraitDownEntity 更新用户-人脸识别 -下行命令
type UpdateUserFacePortraitDownEntity struct {
Sn string
Pin string
FacePortrait string
CompanyId int64
}
func (entity UpdateUserFacePortraitDownEntity) DownCommand() string {
return fmt.Sprintf("C:%v:DATA UPDATE BIODATA Pin=%v\tNo=0\tType=9\tMajorVer=39\tMinorVer=1\tFormat=0\tIndex=0\tValid=1\tDuress=0\tTmp=%v", entity.Sn, entity.Pin, entity.FacePortrait)
}
func NewUpdateUserFacePortraitDownEntity(sn string, pin string, facePortrait string, companyId int64) UpdateUserFacePortraitDownEntity {
return UpdateUserFacePortraitDownEntity{
Sn: sn,
Pin: pin,
FacePortrait: facePortrait,
}
}
// UpdateUserFingerprintPortraitDownEntity 更新用户-指纹 -下行命令
type UpdateUserFingerprintPortraitDownEntity struct {
Sn string
Pin string
FingerprintPortrait string
CompanyId int64
}
func (entity UpdateUserFingerprintPortraitDownEntity) DownCommand() string {
return fmt.Sprintf("C:%v:DATA UPDATE FINGERTMP PIN=%v\tFID=5\tSize=%v\tValid=1\tTMP=%v", entity.Sn, entity.Pin, len([]byte(entity.FingerprintPortrait)), entity.FingerprintPortrait)
}
func NewUpdateUserFingerprintPortraitDownEntity(sn string, pin string, fingerprintPortrait string, companyId int64) UpdateUserFingerprintPortraitDownEntity {
return UpdateUserFingerprintPortraitDownEntity{
Sn: sn,
Pin: pin,
FingerprintPortrait: fingerprintPortrait,
}
}
... ...
package command
import (
"fmt"
"github.com/beego/beego/v2/core/validation"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/domain"
"reflect"
"strings"
)
type SyncToAttendanceMachineCommand struct {
OperateInfo *domain.OperateInfo `json:"-"`
// 用户关联的角色
Users []string `cname:"用户关联的角色" json:"userIds"`
}
func (syncToAttendance *SyncToAttendanceMachineCommand) Valid(validation *validation.Validation) {
//validation.SetError("CustomValid", "未实现的自定义认证")
}
func (syncToAttendance *SyncToAttendanceMachineCommand) ValidateCommand() error {
valid := validation.Validation{}
b, err := valid.Valid(syncToAttendance)
if err != nil {
return err
}
if !b {
elem := reflect.TypeOf(syncToAttendance).Elem()
for _, validErr := range valid.Errors {
field, isExist := elem.FieldByName(validErr.Field)
if isExist {
return fmt.Errorf(strings.Replace(validErr.Message, validErr.Field, field.Tag.Get("cname"), -1))
} else {
return fmt.Errorf(validErr.Message)
}
}
}
return nil
}
... ...
package service
import (
"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/user/command"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/domain"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/infrastructure/utils"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/log"
)
func (userService *UserService) SyncToAttendanceMachine(cmd *command.SyncToAttendanceMachineCommand) (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()
}()
userRepository, _, _ := factory.FastPgUser(transactionContext, 0)
//orgRepository, _, _ := factory.FastPgOrg(transactionContext, 0)
var users []*domain.User
// 指定用户进行同步
if len(cmd.Users) > 0 {
_, users, err = userRepository.Find(map[string]interface{}{"companyId": cmd.OperateInfo.CompanyId, "inUserIds": utils.ToArrayInt64(cmd.Users)})
if err != nil {
return nil, application.ThrowError(application.BUSINESS_ERROR, err.Error())
}
} else {
//org, _ := orgRepository.FindOne(map[string]interface{}{"companyId": cmd.OperateInfo.CompanyId, "orgName": "制造中心"})
//if org == nil {
// return nil, nil
//}
_, users, err = userRepository.Find(map[string]interface{}{"companyId": cmd.OperateInfo.CompanyId, "icCardNumberNotEqual": ""})
if err != nil {
return nil, application.ThrowError(application.BUSINESS_ERROR, err.Error())
}
}
for i := range users {
_, userBase, err := factory.FastPgUserBase(transactionContext, users[i].UserBaseId)
if err != nil {
return nil, application.ThrowError(application.BUSINESS_ERROR, err.Error())
}
if err, _ := event.Fire(domain.UserSyncEvent, event.M{"user": users[i], "userBase": userBase}); err != nil {
log.Logger.Error(err.Error())
return nil, application.ThrowError(application.BUSINESS_ERROR, err.Error())
}
}
if err := transactionContext.CommitTransaction(); err != nil {
return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
}
return struct{}{}, nil
}
... ...
... ... @@ -2,6 +2,7 @@ package service
import (
"fmt"
"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/user/command"
... ... @@ -101,15 +102,25 @@ func (userService *UserService) BatchEnable(batchEnableCommand *command.BatchEna
}
for i := 0; i < len(batchEnableCommand.UserIds); i++ {
if user, err := userRepository.FindOne(map[string]interface{}{"userId": batchEnableCommand.UserIds[i]}); err != nil {
var (
user *domain.User
userBase *domain.UserBase
)
user, err = userRepository.FindOne(map[string]interface{}{"userId": batchEnableCommand.UserIds[i]})
if err != nil {
return nil, application.ThrowError(application.INTERNAL_SERVER_ERROR, err.Error())
}
if err := user.SetEnableStatus(batchEnableCommand.EnableStatus); err != nil {
return nil, application.ThrowError(application.INTERNAL_SERVER_ERROR, err.Error())
}
if _, err := userRepository.Save(user); err != nil {
return nil, application.ThrowError(application.INTERNAL_SERVER_ERROR, err.Error())
}
if _, userBase, err = factory.FastPgUserBase(transactionContext, user.UserBaseId); err != nil {
return nil, application.ThrowError(application.INTERNAL_SERVER_ERROR, err.Error())
}
if err, _ := event.Fire(domain.UserEnableEvent, map[string]interface{}{"user": user, "userBase": userBase}); err != nil {
return nil, application.ThrowError(application.INTERNAL_SERVER_ERROR, err.Error())
} else {
if err := user.SetEnableStatus(batchEnableCommand.EnableStatus); err != nil {
return nil, application.ThrowError(application.INTERNAL_SERVER_ERROR, err.Error())
}
if _, err := userRepository.Save(user); err != nil {
return nil, application.ThrowError(application.INTERNAL_SERVER_ERROR, err.Error())
}
}
}
... ...
... ... @@ -26,6 +26,14 @@ const (
UserStatusDestroy UserStatus = 3
)
const (
UserUpdateEvent = "user_update"
UserDeleteEvent = "user_delete"
UserCreateEvent = "user_create"
UserEnableEvent = "user_enable" //禁用启用事件
UserSyncEvent = "user_sync" // 用户同步
)
// 用户
type User struct {
// 用户Id 用户唯一标识
... ...
... ... @@ -13,9 +13,16 @@ type UserInfo struct {
// 员工类型 1:固定 2:派遣 3.临时
EmployeeType int `json:"employeeType,omitempty"`
// IC卡号
// IC卡号 = Pin
IcCardNumber string `json:"icCardNumber,omitempty"`
// 面部识别
FacePortrait string `json:"facePortrait,omitempty"`
// 指纹识别
FingerprintPortrait string `json:"fingerprintPortrait,omitempty"`
// 打卡机标识
// Pin string `json:"pin"`
Referer string `json:"-"`
// 部门
DepartmentName string `cname:"部门" json:"-"`
... ...
... ... @@ -2,6 +2,7 @@ package domainService
import (
"fmt"
"github.com/gookit/event"
pgTransaction "github.com/linmadan/egglib-go/transaction/pg"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/domain"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/infrastructure/repository"
... ... @@ -96,6 +97,10 @@ func (ptr *PgCreateUserService) CreateUser(optUser *domain.User, newUser *domain
return nil, err
}
}
// 新建用户事件
if err, _ := event.Fire(domain.UserCreateEvent, event.M{"user": newUser, "userBase": userBase}); err != nil {
return nil, err
}
return user, nil
}
... ...
... ... @@ -2,6 +2,7 @@ package domainService
import (
"fmt"
"github.com/gookit/event"
pgTransaction "github.com/linmadan/egglib-go/transaction/pg"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/domain"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/infrastructure/repository"
... ... @@ -108,6 +109,10 @@ func (ptr *PgUpdateUserService) UpdateUser(optUser *domain.OperateInfo, user *do
if user, err = userRepository.Save(user); err != nil {
return nil, err
}
// 新建用户事件
if err, _ := event.Fire(domain.UserUpdateEvent, event.M{"user": user, "userBase": userBase}); err != nil {
return nil, err
}
return user, nil
}
... ...
... ... @@ -204,12 +204,18 @@ func (repository *UserRepository) Find(queryOptions map[string]interface{}) (int
}
query.SetWhereByQueryOption("company_id=?", "companyId")
query.SetWhereByQueryOption("organization_id=?", "organizationId")
if v, ok := queryOptions["inUserIds"]; ok && len(v.([]int64)) > 0 {
query.Where(`user_id in (?)`, pg.In(v))
}
if v, ok := queryOptions["inOrgIds"]; ok && len(v.([]int64)) > 0 {
query.Where(`organization_id in (?)`, pg.In(v))
}
if v, ok := queryOptions["inCompanyIds"]; ok && len(v.([]int64)) > 0 {
query.Where(`company_id in (?)`, pg.In(v))
}
if v, ok := queryOptions["inDepartmentIds"]; ok && len(v.([]int64)) > 0 {
query.Where(`department_id in (?)`, pg.In(v))
}
query.SetWhereByQueryOption("user_code = ?", "userCode")
query.SetWhereByQueryOption("ext->>'icCardNumber' = ?", "icCardNumber")
query.SetWhereByQueryOption("user_base_id=?", "userBaseId")
... ... @@ -231,6 +237,9 @@ func (repository *UserRepository) Find(queryOptions map[string]interface{}) (int
if v, ok := queryOptions["cooperationCompany"]; ok && len(v.(string)) > 0 {
query.Where(fmt.Sprintf(`cooperation_info->>'cooperationCompany' like '%%%v%%'`, v))
}
if _, ok := queryOptions["icCardNumberNotEqual"]; ok {
query.Where(fmt.Sprintf(`ext->>'icCardNumber' <> '%v'`, ""))
}
query.SetOffsetAndLimit(domain.MaxQueryRow)
query.SetOrderDirect("user_id", "DESC")
if count, err := query.SelectAndCount(); err != nil {
... ...
... ... @@ -262,3 +262,12 @@ func CopyObject(src, dst interface{}) {
}
}
}
func ToArrayInt64(inputs []string) []int64 {
result := make([]int64, 0)
for i := range inputs {
v, _ := strconv.ParseInt(inputs[i], 10, 64)
result = append(result, v)
}
return result
}
... ...
package controllers
import (
"github.com/linmadan/egglib-go/web/beego"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/application/terminal/command"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/application/terminal/service"
)
type TerminalController struct {
beego.BaseController
}
func (controller *TerminalController) TerminalReport() {
service := service.NewTerminalService(nil)
terminalReportCommand := &command.TerminalReportCommand{}
controller.Unmarshal(terminalReportCommand)
data, err := service.TerminalReport(terminalReportCommand)
controller.Response(data, err)
}
... ...
... ... @@ -179,3 +179,12 @@ func (controller *UserController) UpdateAdminUser() {
data, err := userService.UpdateAdminUser(updateUserCommand)
controller.Response(data, err)
}
func (controller *UserController) SyncToAttendanceMachine() {
userService := service.NewUserService(nil)
cmd := &command.SyncToAttendanceMachineCommand{}
Must(controller.Unmarshal(cmd))
cmd.OperateInfo = ParseOperateInfo(controller.BaseController)
data, err := userService.SyncToAttendanceMachine(cmd)
controller.Response(data, err)
}
... ...
package routers
import (
"github.com/beego/beego/v2/server/web"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-user/pkg/port/beego/controllers"
)
func init() {
web.Router("/terminal/report", &controllers.TerminalController{}, "Post:TerminalReport")
}
... ...
... ... @@ -21,6 +21,8 @@ func init() {
web.Router("/user/cooperator", &controllers.UserController{}, "Post:CreateCooperator")
web.Router("/user/cooperator/:userId", &controllers.UserController{}, "Put:UpdateCooperator")
web.Router("/user/sync-to-attendance-machine", &controllers.UserController{}, "Post:SyncToAttendanceMachine")
web.Router("/admin-user/", &controllers.UserController{}, "Post:CreateAdminUser")
web.Router("/admin-user/:userId", &controllers.UserController{}, "Put:UpdateAdminUser")
}
... ...