作者 tangxvhui

更新

@@ -432,6 +432,3 @@ func (attendanceService *AttendanceService) SearchWorkshopWorkTimeStatics(operat @@ -432,6 +432,3 @@ func (attendanceService *AttendanceService) SearchWorkshopWorkTimeStatics(operat
432 } 432 }
433 return count, result, nil 433 return count, result, nil
434 } 434 }
435 -  
436 -//ImportDataAttendance 工时管理,导入工时数据  
437 -func (attendanceService *AttendanceService) ImportDataAttendance() {}  
  1 +package service
  2 +
  3 +import (
  4 + "errors"
  5 + "strconv"
  6 + "strings"
  7 + "time"
  8 +
  9 + "github.com/linmadan/egglib-go/core/application"
  10 + "github.com/linmadan/egglib-go/utils/excel"
  11 + "gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/application/ecelData/command"
  12 + "gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/application/factory"
  13 + "gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/domain"
  14 + "gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/infrastructure/utils/converter"
  15 +)
  16 +
  17 +type importAttendance struct {
  18 + ProductDate string `json:"productDate"` //日期
  19 + WorkshopName string `json:"workshopName"` //车间名称
  20 + LineName string `json:"lineName"` //线别名称
  21 + SectionName string `json:"sectionName"` //工位名称
  22 + WorkerName string `json:"workerName"` //工人姓名
  23 + AttendanceType string `json:"attendanceType"` //考勤类型 正常 支援
  24 + SignIn string `json:"signIn"` //上岗时间
  25 + SignOut string `json:"signOut"` //离岗时间
  26 + BreakTime string `json:"breakTime"` //休息时间
  27 + FailReason string `json:"failReason"` //数据校验失败的理由
  28 +}
  29 +
  30 +func (data *importAttendance) validField() error {
  31 + if len(data.ProductDate) == 0 {
  32 + return errors.New("日期未填写。")
  33 + }
  34 + if len(data.WorkerName) == 0 {
  35 + return errors.New("车间名称未填写。")
  36 + }
  37 + if len(data.LineName) == 0 {
  38 + return errors.New("线别名称未填写。")
  39 + }
  40 + if len(data.SectionName) == 0 {
  41 + return errors.New("工位名称未填写。")
  42 + }
  43 + if len(data.WorkerName) == 0 {
  44 + return errors.New("工人姓名未填写。")
  45 + }
  46 + if len(data.AttendanceType) == 0 {
  47 + return errors.New("类型未填写。")
  48 + }
  49 + if len(data.SignIn) == 0 {
  50 + return errors.New("上岗时间未填写。")
  51 + }
  52 + if len(data.SignOut) == 0 {
  53 + return errors.New("下岗时间未填写。")
  54 + }
  55 + if len(data.BreakTime) == 0 {
  56 + return errors.New("休息时间未填写。")
  57 + }
  58 +
  59 + return nil
  60 +}
  61 +
  62 +func (srv ExcelDataService) ImportDataAttendance(importDataCommand *command.ImportDataCommand) (interface{}, error) {
  63 + excelImport := excel.NewExcelImport()
  64 + excelImport.RowBegin = 2 //第二行开始读取
  65 + excelImport.DataFields = []excel.DataField{
  66 + {EnName: "ProductDate", CnName: "日期"},
  67 + {EnName: "WorkshopName", CnName: "车间"},
  68 + {EnName: "LineName", CnName: "线别"},
  69 + {EnName: "SectionName", CnName: "工段"},
  70 + {EnName: "WorkerName", CnName: "姓名"},
  71 + {EnName: "AttendanceType", CnName: "类别"},
  72 + {EnName: "SignIn", CnName: "上岗时间"},
  73 + {EnName: "SignOut", CnName: "离岗时间"},
  74 + {EnName: "BreakTime", CnName: "休息时长(小时)"},
  75 + }
  76 + excelData, err := converter.OpenImportFileFromIoReader(excelImport, importDataCommand.Reader, importDataCommand.FileExt) //excelImport.OpenExcelFromIoReader(importDataCommand.Reader)
  77 + if err != nil {
  78 + return nil, application.ThrowError(application.BUSINESS_ERROR, err.Error())
  79 + }
  80 + items := make([]importAttendance, 0)
  81 + for _, v := range excelData {
  82 + item := importAttendance{
  83 + ProductDate: strings.TrimSpace(v["ProductDate"]),
  84 + WorkshopName: strings.TrimSpace(v["WorkshopName"]),
  85 + LineName: strings.TrimSpace(v["LineName"]),
  86 + SectionName: strings.TrimSpace(v["SectionName"]),
  87 + WorkerName: strings.TrimSpace(v["WorkerName"]),
  88 + AttendanceType: strings.TrimSpace(v["AttendanceType"]),
  89 + SignIn: strings.TrimSpace(v["SignIn"]),
  90 + SignOut: strings.TrimSpace(v["SignOut"]),
  91 + BreakTime: strings.TrimSpace(v["BreakTime"]),
  92 + FailReason: "",
  93 + }
  94 + items = append(items, item)
  95 + }
  96 + failRows, err := srv.BatchAddAttendance(*importDataCommand.Operator, items)
  97 + if err != nil {
  98 + return nil, application.ThrowError(application.BUSINESS_ERROR, err.Error())
  99 + }
  100 + return srv.importResultWithHeader(excelImport.DataFields, failRows, len(items)), nil
  101 +}
  102 +
  103 +// BatchAddAttendance 工时管理,导入工时数据
  104 +func (srv ExcelDataService) BatchAddAttendance(operate domain.OperateInfo, param []importAttendance) (
  105 + failRows []interface{}, err error) {
  106 + transactionContext, err := factory.CreateTransactionContext(nil)
  107 + if err != nil {
  108 + return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
  109 + }
  110 + if err := transactionContext.StartTransaction(); err != nil {
  111 + return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
  112 + }
  113 + defer func() {
  114 + transactionContext.RollbackTransaction()
  115 + }()
  116 + //车间数据
  117 + //生产班组 数据
  118 + productGroupRepo, _ := factory.CreateProductGroupRepository(map[string]interface{}{
  119 + "transactionContext": transactionContext,
  120 + })
  121 +
  122 + _, productGroupList, err := productGroupRepo.Find(map[string]interface{}{
  123 + "companyId": operate.CompanyId,
  124 + "orgId": operate.OrgId,
  125 + })
  126 + if err != nil {
  127 + return nil, application.ThrowError(application.BUSINESS_ERROR, err.Error())
  128 + }
  129 + //车间名称+/+线别名称+/+工段名称 作为键名
  130 + workStationMap := map[string]*domain.WorkStation{}
  131 + //车间名称+/+工人名 作为键名
  132 + workerMap := map[string][]*domain.User{}
  133 + for _, v := range productGroupList {
  134 + workStationName := strings.Join([]string{
  135 + v.WorkStation.WorkshopName, v.WorkStation.LineName, v.WorkStation.SectionName,
  136 + }, "/")
  137 + workStationMap[workStationName] = v.WorkStation
  138 + for _, vv := range v.GroupMembers {
  139 + k := v.WorkStation.WorkshopName + "/" + vv.UserName
  140 + isIn := false
  141 + for _, vvv := range workerMap[k] {
  142 + if vvv.UserId == vv.UserId {
  143 + isIn = true
  144 + break
  145 + }
  146 + }
  147 + if !isIn {
  148 + workerMap[k] = append(workerMap[k], vv)
  149 + }
  150 + }
  151 + }
  152 + var attendanceList []*domain.ProductAttendanceRecord
  153 + nowTime := time.Now()
  154 + //检查导入的数据
  155 + for i := range param {
  156 + //检查字段值
  157 + err := param[i].validField()
  158 + if err != nil {
  159 + param[i].FailReason = err.Error()
  160 + failRows = append(failRows, param[i])
  161 + continue
  162 + }
  163 + //检查日期格式
  164 + productDate, err := time.ParseInLocation("2006-01-02", param[i].ProductDate, time.Local)
  165 + if err != nil {
  166 + param[i].FailReason = "日期格式错误,例 2006-01-02。"
  167 + failRows = append(failRows, param[i])
  168 + continue
  169 + }
  170 + //检查类型
  171 + attendanceType := 0
  172 + // 考勤类型 1.正常 2.支援
  173 + if param[i].AttendanceType == "支援" {
  174 + attendanceType = 1
  175 + } else if param[i].AttendanceType == "正常" {
  176 + attendanceType = 2
  177 + } else {
  178 + param[i].FailReason = "类型内容异常。"
  179 + failRows = append(failRows, param[i])
  180 + continue
  181 + }
  182 + //检查上岗时间格式
  183 + signIn, err := time.ParseInLocation("15:04:05", param[i].SignIn, time.Local)
  184 + if err != nil {
  185 + param[i].FailReason = "上岗时间格式错误,例 15:04:05。"
  186 + failRows = append(failRows, param[i])
  187 + continue
  188 + }
  189 + signIn = productDate.Add(time.Duration(signIn.Second()) * time.Second)
  190 + //检查离岗时间格式
  191 + signOut, err := time.ParseInLocation("15:04:05", param[i].SignOut, time.Local)
  192 + if err != nil {
  193 + param[i].FailReason = "离岗时间格式错误,例 15:04:05。"
  194 + failRows = append(failRows, param[i])
  195 + continue
  196 + }
  197 + signOut = productDate.Add(time.Duration(signOut.Second()) * time.Second)
  198 + //检查员工姓名
  199 + var worker *domain.User
  200 + workKey := param[i].WorkshopName + "/" + param[i].WorkerName
  201 + if u, ok := workerMap[workKey]; ok {
  202 + if len(u) > 1 {
  203 + param[i].FailReason = "当前车间存在重复的用户名"
  204 + failRows = append(failRows, param[i])
  205 + continue
  206 + }
  207 + worker = u[0]
  208 + } else {
  209 + param[i].FailReason = "当前车间不存在用户" + param[i].WorkerName
  210 + failRows = append(failRows, param[i])
  211 + continue
  212 + }
  213 + //检查工位
  214 + var workStation *domain.WorkStation
  215 + workStationName := param[i].WorkerName + "/" + param[i].LineName + "/" + param[i].SectionName
  216 + if v, ok := workStationMap[workStationName]; ok {
  217 + workStation = v
  218 + } else {
  219 + param[i].FailReason = "车间、线别、工段不存在"
  220 + failRows = append(failRows, param[i])
  221 + continue
  222 + }
  223 + //休息时间(小时)
  224 + beakTime, err := strconv.ParseFloat(param[i].BreakTime, 64)
  225 + if err != nil {
  226 + param[i].FailReason = "休息时长填写错误"
  227 + failRows = append(failRows, param[i])
  228 + continue
  229 + }
  230 +
  231 + tempItem := &domain.ProductAttendanceRecord{
  232 + ProductAttendanceId: 0,
  233 + CompanyId: operate.CompanyId,
  234 + OrgId: operate.OrgId,
  235 + AttendanceType: attendanceType,
  236 + ProductWorker: worker,
  237 + WorkStation: workStation,
  238 + SignIn: signIn,
  239 + SignOut: signOut,
  240 + AttendanceStatus: domain.AttendanceAutoApproved, //自动审核
  241 + WorkTimeBefore: 0.0,
  242 + WorkTimeAfter: 0.0,
  243 + CreatedAt: nowTime,
  244 + UpdatedAt: nowTime,
  245 + DeletedAt: time.Time{},
  246 + ProductDate: productDate,
  247 + }
  248 + //计算工时
  249 + workTime := tempItem.ComputeWorkTime(beakTime)
  250 + tempItem.WorkTimeAfter = workTime
  251 + tempItem.WorkTimeBefore = workTime
  252 + attendanceList = append(attendanceList, tempItem)
  253 + }
  254 + if len(failRows) > 0 {
  255 + return failRows, nil
  256 + }
  257 + attendanceRepo, _ := factory.CreateProductAttendanceRecordRepository(map[string]interface{}{
  258 + "transactionContext": transactionContext,
  259 + })
  260 + for i := range attendanceList {
  261 + _, err = attendanceRepo.Save(attendanceList[i])
  262 + if err != nil {
  263 + return nil, application.ThrowError(application.INTERNAL_SERVER_ERROR, err.Error())
  264 + }
  265 + }
  266 + if err := transactionContext.CommitTransaction(); err != nil {
  267 + return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
  268 + }
  269 + return nil, err
  270 +}
@@ -3,10 +3,11 @@ package domain @@ -3,10 +3,11 @@ package domain
3 import ( 3 import (
4 "errors" 4 "errors"
5 "fmt" 5 "fmt"
  6 + "time"
  7 +
6 "github.com/linmadan/egglib-go/utils/xtime" 8 "github.com/linmadan/egglib-go/utils/xtime"
7 "gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/infrastructure/utils" 9 "gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/infrastructure/utils"
8 "gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/log" 10 "gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/log"
9 - "time"  
10 ) 11 )
11 12
12 const ( 13 const (
@@ -230,3 +231,12 @@ func (productAttendanceRecord *ProductAttendanceRecord) AttendanceBreakTime(prod @@ -230,3 +231,12 @@ func (productAttendanceRecord *ProductAttendanceRecord) AttendanceBreakTime(prod
230 } 231 }
231 return bt 232 return bt
232 } 233 }
  234 +
  235 +//计算上岗到 离岗之间的工作时间,
  236 +//breakTime 休息时间(小时)
  237 +func (productAttendanceRecord *ProductAttendanceRecord) ComputeWorkTime(breakTime float64) float64 {
  238 + signIn := roundTime(productAttendanceRecord.SignIn)
  239 + signOut := roundTime(productAttendanceRecord.SignOut)
  240 + wt := utils.Round(signOut.Sub(signIn).Hours()-breakTime, 2)
  241 + return roundWorkTime(wt)
  242 +}
@@ -2,14 +2,15 @@ package controllers @@ -2,14 +2,15 @@ package controllers
2 2
3 import ( 3 import (
4 "fmt" 4 "fmt"
  5 + "io"
  6 + "path/filepath"
  7 +
5 "github.com/beego/beego/v2/server/web/context" 8 "github.com/beego/beego/v2/server/web/context"
6 "github.com/linmadan/egglib-go/utils/excel" 9 "github.com/linmadan/egglib-go/utils/excel"
7 "github.com/linmadan/egglib-go/web/beego" 10 "github.com/linmadan/egglib-go/web/beego"
8 "gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/application/ecelData/command" 11 "gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/application/ecelData/command"
9 "gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/application/ecelData/service" 12 "gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/application/ecelData/service"
10 "gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/log" 13 "gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/log"
11 - "io"  
12 - "path/filepath"  
13 ) 14 )
14 15
15 type ExcelDataController struct { 16 type ExcelDataController struct {
@@ -36,7 +37,7 @@ func (controller *ExcelDataController) FileImport() { @@ -36,7 +37,7 @@ func (controller *ExcelDataController) FileImport() {
36 r io.Reader 37 r io.Reader
37 ext string 38 ext string
38 ) 39 )
39 - //excelService := service.NewExcelDataService(nil) 40 + excelService := service.NewExcelDataService(nil)
40 r, ext, err = controller.GetFileWithExt() 41 r, ext, err = controller.GetFileWithExt()
41 if err != nil { 42 if err != nil {
42 controller.Response(nil, err) 43 controller.Response(nil, err)
@@ -54,6 +55,8 @@ func (controller *ExcelDataController) FileImport() { @@ -54,6 +55,8 @@ func (controller *ExcelDataController) FileImport() {
54 // data, err = excelService.ImportCooperationUser(cmd) 55 // data, err = excelService.ImportCooperationUser(cmd)
55 //case domain.ImportOrganization: 56 //case domain.ImportOrganization:
56 // data, err = excelService.ImportOrganization(cmd) 57 // data, err = excelService.ImportOrganization(cmd)
  58 + case "ImportAttendance":
  59 + data, err = excelService.ImportDataAttendance(cmd)
57 default: 60 default:
58 err = fmt.Errorf("导入不存在 Code:%v", cmd.Code) 61 err = fmt.Errorf("导入不存在 Code:%v", cmd.Code)
59 } 62 }