作者 yangfu

feat: 创建工时、生产班组下拉选择、工时统计优化

-- 表product_calendar 增加列表 break_time_periods
ALTER TABLE manufacture.product_calendar ADD COLUMN break_time_periods jsonb;
\ No newline at end of file
-- 表product_calendar 增加字段 break_time_periods
ALTER TABLE manufacture.product_calendar ADD COLUMN break_time_periods jsonb;
-- 表product_attendance_record 增加字段 product_date
alter table manufacture.product_attendance_record add COLUMN product_date timestamptz;
update manufacture.product_attendance_record set product_date = sign_in where product_date is null ;
\ No newline at end of file
... ...
... ... @@ -13,27 +13,36 @@ type CreateAttendanceCommand struct {
// 考勤记录ID
//ProductAttendanceId int `cname:"考勤记录ID" json:"productAttendanceId" valid:"Required"`
// 考勤类型 1.正常 2.支援
AttendanceType int `json:"attendanceType,omitempty"`
AttendanceType int `cname:"考勤类型" json:"attendanceType,omitempty"`
// 生产班组Id
ProductGroupId int `json:"productGroupId,omitempty" valid:"Required"`
ProductGroupId int `cname:"生产班组Id" json:"productGroupId,omitempty" valid:"Required"`
// 生产工人
ProductWorkerId int `json:"productWorkerId,omitempty" valid:"Required"`
ProductWorkerId int `cname:"生产工人" json:"productWorkerId,omitempty" valid:"Required"`
// 车间ID
WorkshopId int `cname:"车间ID" json:"workshopId" valid:"Required"`
// 生产线ID
LineId int `cname:"生产线ID" json:"lineId" valid:"Required"`
// 工段ID
SectionId int `cname:"工段ID" json:"sectionId" valid:"Required"`
// 生产日期
ProductDate time.Time `cname:"生产日期" json:"productDate,omitempty" valid:"Required"`
// 签到
SignIn time.Time `json:"signIn,omitempty"`
SignIn time.Time `cname:"上岗时间" json:"signIn,omitempty" valid:"Required"`
// 签退
SignOut time.Time `json:"signOut,omitempty"`
// 考勤状态 1.未审核 2:已审核 3.自动审核
//AttendanceStatus int `json:"attendanceStatus,omitempty"`
SignOut time.Time `cname:"下岗时间" json:"signOut,omitempty" valid:"Required"`
// 考勤状态 1.未审核 2:审核
AttendanceStatus int `cname:"考勤状态" json:"attendanceStatus,omitempty"`
// 休息时长
BreakTime float64 `cname:"休息时长" json:"breakTime"`
// 工时
WorkTime float64 `cname:"工时" json:"workTime"`
// 准备(true:准备完毕 false:进行预查)
Prepared bool `json:"prepared"`
}
func (createAttendanceCommand *CreateAttendanceCommand) Valid(validation *validation.Validation) {
//validation.SetError("CustomValid", "未实现的自定义认证")
validation.Range(createAttendanceCommand.AttendanceStatus, 1, 2, "attendanceStatus")
}
func (createAttendanceCommand *CreateAttendanceCommand) ValidateCommand() error {
... ...
... ... @@ -50,7 +50,7 @@ func (d *AttendanceRecordDto) LoadDto(m *domain.ProductAttendanceRecord, orgId i
d.WorkStation = m.WorkStation
if !m.SignIn.IsZero() {
d.SignIn = m.SignIn.Local().Format("15:04:05")
d.SignDate = m.SignIn.Local().Format("2006-01-02")
d.SignDate = m.ProductTime().Format("2006-01-02")
}
if !m.SignOut.IsZero() {
d.SignOut = m.SignOut.Local().Format("15:04:05")
... ...
... ... @@ -40,10 +40,7 @@ func (d *EmployeeAttendanceRecordDto) LoadDto(m *domain.ProductAttendanceRecord,
d.AttendanceType = m.AttendanceType
d.ProductWorker = m.ProductWorker
d.WorkStation = m.WorkStation
if !m.SignIn.IsZero() {
//d.SignIn = m.SignIn.Format("15:04:05")
d.SignDate = m.SignIn.Local().Format("2006-01-02")
}
d.SignDate = m.ProductTime().Local().Format("2006-01-02")
d.WorkTime = utils.Round(m.WorkTimeAfter, 1)
//d.WorkTimeAfter = m.WorkTimeAfter
d.AttendanceStatus = m.AttendanceStatus
... ...
package service
import (
"fmt"
"github.com/linmadan/egglib-go/core/application"
"github.com/linmadan/egglib-go/transaction/pg"
"github.com/linmadan/egglib-go/utils/xtime"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/application/attendance/command"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/application/attendance/dto"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/application/attendance/query"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/application/factory"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/domain"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/infrastructure/dao"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/infrastructure/domainService"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/infrastructure/utils"
"time"
... ... @@ -100,6 +104,11 @@ func (attendanceService *AttendanceService) CreateAttendance(operateInfo *domain
if err != nil {
return nil, application.ThrowError(application.INTERNAL_SERVER_ERROR, err.Error())
}
var workTime float64 = 0
if cmd.WorkTime-cmd.BreakTime > 0 {
workTime = cmd.WorkTime - cmd.BreakTime
}
newAttendance := &domain.ProductAttendanceRecord{
//ProductAttendanceId: cmd.ProductAttendanceId,
CompanyId: operateInfo.CompanyId,
... ... @@ -109,8 +118,8 @@ func (attendanceService *AttendanceService) CreateAttendance(operateInfo *domain
WorkStation: workStation,
SignIn: cmd.SignIn,
SignOut: cmd.SignOut,
AttendanceStatus: domain.AttendanceNotApprove,
WorkTimeBefore: 0,
AttendanceStatus: cmd.AttendanceStatus,
WorkTimeBefore: workTime,
WorkTimeAfter: 0,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
... ... @@ -118,15 +127,33 @@ func (attendanceService *AttendanceService) CreateAttendance(operateInfo *domain
GroupName: productGroup.GroupName,
ProductGroupId: productGroup.ProductGroupId,
}),
ProductDate: xtime.New(cmd.ProductDate).BeginningOfDay(),
}
newAttendance.WorkTimeBefore = newAttendance.ComputeWorkTimeBefore(nil)
var attendanceRepository domain.ProductAttendanceRecordRepository
if cmd.AttendanceStatus == domain.AttendanceApproved {
newAttendance.WorkTimeAfter = domain.AttendanceApproved
newAttendance.WorkTimeAfter = workTime
}
var (
attendanceRepository domain.ProductAttendanceRecordRepository
attendanceRecordDao *dao.AttendanceRecordDao
)
attendanceRecordDao, _ = dao.NewAttendanceRecordDao(transactionContext.(*pg.TransactionContext))
attendanceRepository, _, _ = factory.FastPgAttendance(transactionContext, 0)
if !cmd.Prepared {
// 检查时间段内是否有重复的打卡记录
count, _, err := attendanceRecordDao.WorkerAttendanceRecordsByProductDate(operateInfo.CompanyId, operateInfo.OrgId, cmd.ProductWorkerId, newAttendance.ProductDate, cmd.SignIn, cmd.SignIn)
if err != nil {
return nil, application.ThrowError(application.INTERNAL_SERVER_ERROR, err.Error())
}
if count > 0 {
return nil, &application.ServiceError{Code: 10040001, Message: fmt.Sprintf("已存在员工%v的工时记录,是否继续新增", user.UserName)}
}
}
if attendance, err := attendanceRepository.Save(newAttendance); err != nil {
return nil, application.ThrowError(application.INTERNAL_SERVER_ERROR, err.Error())
} else {
domainService.SendWorkshopWorkTimeStaticJob(attendance)
if err := transactionContext.CommitTransaction(); err != nil {
return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
}
... ...
package dto
import (
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/domain"
)
// 生产班组
type ProductGroupSelectorDto struct {
// 生产小组ID
ProductGroupId int `json:"productGroupId,omitempty"`
// 班组名称
GroupName string `json:"groupName,omitempty"`
// 班组长
GroupLeader *domain.User `json:"groupLeader"`
// 帮组成员列表
GroupMembers []*domain.User `json:"groupMembers"`
}
func (d *ProductGroupSelectorDto) LoadDto(m *domain.ProductGroup, orgId int) *ProductGroupSelectorDto {
d.ProductGroupId = m.ProductGroupId
d.GroupName = m.GroupName
d.GroupLeader = m.GroupLeader
d.GroupMembers = m.GroupMembers
return d
}
... ...
... ... @@ -543,6 +543,46 @@ func (productGroupService *ProductGroupService) GetSignEmployee(cmd *query.GetSi
}, nil
}
// 返回生产班组服务列表
func (productGroupService *ProductGroupService) SelectorProductGroup(operateInfo *domain.OperateInfo, cmd *query.SearchProductGroupQuery) (interface{}, error) {
if err := cmd.ValidateQuery(); 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()
}()
workshops, _ := factory.FastPgWorkshops(transactionContext, operateInfo.CompanyId)
queryOptions := utils.ObjectToMap(cmd)
queryOptions = workshops.FindByNameWithQuery(queryOptions, cmd.WorkshopName, cmd.LineName, "")
var productGroupRepository domain.ProductGroupRepository
productGroupRepository, _, _ = factory.FastPgProductGroup(transactionContext, 0)
_, productGroups, err := productGroupRepository.Find(queryOptions)
if err != nil {
return nil, application.ThrowError(application.INTERNAL_SERVER_ERROR, err.Error())
}
var results = make([]*dto.ProductGroupSelectorDto, 0)
for i := range productGroups {
newItem := &dto.ProductGroupSelectorDto{}
newItem.LoadDto(productGroups[i], operateInfo.OrgId)
results = append(results, newItem)
}
if err := transactionContext.CommitTransaction(); err != nil {
return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
}
return map[string]interface{}{
"groups": results,
}, nil
}
func NewProductGroupService(options map[string]interface{}) *ProductGroupService {
newProductGroupService := &ProductGroupService{}
return newProductGroupService
... ...
... ... @@ -45,6 +45,8 @@ type ProductAttendanceRecord struct {
DeletedAt time.Time `json:"deletedAt,omitempty"`
// 扩展数据
Ext *Ext `json:"ext,omitempty"`
// 生产日期
ProductDate time.Time `json:"productDate,omitempty"`
}
type ProductAttendanceRecordRepository interface {
... ... @@ -89,7 +91,7 @@ func (productAttendanceRecord *ProductAttendanceRecord) ComputeWorkTimeBefore(pr
func (productAttendanceRecord *ProductAttendanceRecord) SetProductTimeByProductCalendar(productCalendar *ProductCalendar) error {
now := xtime.New(productAttendanceRecord.SignIn)
productAttendanceRecord.Ext.AttendanceExt.ProductTime = now.BeginningOfDay().Unix()
productAttendanceRecord.ProductDate = now.BeginningOfDay()
if productCalendar == nil {
return nil
}
... ... @@ -99,7 +101,7 @@ func (productAttendanceRecord *ProductAttendanceRecord) SetProductTimeByProductC
}
newNow := now.BeginningOfDay().Add(-time.Hour * 24)
if overDay && productCalendar.MatchCalendarSelected(newNow) {
productAttendanceRecord.Ext.AttendanceExt.ProductTime = newNow.Unix()
productAttendanceRecord.ProductDate = newNow
}
return nil
}
... ... @@ -123,11 +125,8 @@ func (productAttendanceRecord *ProductAttendanceRecord) Approve(approveUser *Use
func (productAttendanceRecord *ProductAttendanceRecord) ProductTime() time.Time {
attendanceExt := productAttendanceRecord.Ext.AttendanceExt
if attendanceExt != nil && attendanceExt.ProductTime > 0 {
t := time.Unix(attendanceExt.ProductTime, 0)
if !t.IsZero() {
return t
}
if attendanceExt != nil && !xtime.IsZero(productAttendanceRecord.ProductDate) {
return productAttendanceRecord.ProductDate
}
return productAttendanceRecord.SignIn
}
... ...
... ... @@ -7,7 +7,7 @@ type ProductAttendanceRecordExt struct {
// 班组名称
GroupName string `json:"groupName,omitempty"`
// 生产日期
ProductTime int64 `json:"productTime,omitempty"`
//ProductTime int64 `json:"productTime,omitempty"`
// 审核人Id
ApproveUserId int `json:"approveUserId,omitempty"`
// 审核人名称
... ...
... ... @@ -123,3 +123,35 @@ func (dao *AttendanceRecordDao) WorkerAttendanceRecords(companyId, orgId, worker
return int64(count), productAttendanceRecords, nil
}
}
func (dao *AttendanceRecordDao) WorkerAttendanceRecordsByProductDate(companyId, orgId, workerId int, productDate, beginTime, endTime time.Time) (int64, []*domain.ProductAttendanceRecord, error) {
tx := dao.transactionContext.PgTx
//endTime := time.Now()
//beginTime := endTime.Add(-(time.Hour * time.Duration(recentDay*24)))
var productAttendanceRecordModels []*models.ProductAttendanceRecord
productAttendanceRecords := make([]*domain.ProductAttendanceRecord, 0)
query := sqlbuilder.BuildQuery(tx.Model(&productAttendanceRecordModels), map[string]interface{}{})
query.Where("company_id =?", companyId)
query.Where("org_id =?", orgId)
query.Where("product_worker ->>'userId' = '?'", workerId)
query.Where("product_date = ?", productDate)
query.Where("sign_in >= ?", beginTime)
query.WhereGroup(func(query *orm.Query) (*orm.Query, error) {
query.Where("sign_out is null")
query.WhereOr("sign_out>?", endTime)
return query, nil
})
query.SetOrderDirect("product_attendance_id", "DESC")
if count, err := query.SelectAndCount(); err != nil {
return 0, productAttendanceRecords, err
} else {
for _, productAttendanceRecordModel := range productAttendanceRecordModels {
if productAttendanceRecord, err := transform.TransformToProductAttendanceRecordDomainModelFromPgModels(productAttendanceRecordModel); err != nil {
return 0, productAttendanceRecords, err
} else {
productAttendanceRecords = append(productAttendanceRecords, productAttendanceRecord)
}
}
return int64(count), productAttendanceRecords, nil
}
}
... ...
... ... @@ -6,7 +6,6 @@ import (
pgTransaction "github.com/linmadan/egglib-go/transaction/pg"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/domain"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/infrastructure/repository"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-manufacture/pkg/infrastructure/utils"
"time"
)
... ... @@ -21,7 +20,7 @@ func (ptr *PGWorkshopWorkTimeStaticService) WorkshopWorkTimeStatic(productRecord
"companyId": productRecord.CompanyId,
"orgId": productRecord.OrgId,
"workStationId": productRecord.WorkStation.WorkStationId,
"recordDate": utils.GetZeroTime(productRecord.SignIn),
"recordDate": productRecord.ProductDate,
})
if err != nil && errors.Is(err, domain.ErrorNotFound) {
err = nil
... ... @@ -31,7 +30,7 @@ func (ptr *PGWorkshopWorkTimeStaticService) WorkshopWorkTimeStatic(productRecord
CompanyId: productRecord.CompanyId,
OrgId: productRecord.OrgId,
Ext: productRecord.Ext,
RecordDate: utils.GetZeroTime(productRecord.ProductTime()), // utils.GetZeroTime(productRecord.SignIn),
RecordDate: productRecord.ProductDate, // utils.GetZeroTime(productRecord.SignIn),
WorkshopWorkTimeRecordInfo: &domain.WorkshopWorkTimeRecordInfo{},
}
}
... ...
... ... @@ -37,4 +37,6 @@ type ProductAttendanceRecord struct {
DeletedAt time.Time `pg:",soft_delete" comment:"删除时间"`
// 扩展数据
Ext *domain.Ext `comment:"扩展数据"`
// 生产日期
ProductDate time.Time `comment:"生产日期"`
}
... ...
... ... @@ -22,5 +22,6 @@ func TransformToProductAttendanceRecordDomainModelFromPgModels(productAttendance
UpdatedAt: productAttendanceRecordModel.UpdatedAt,
DeletedAt: productAttendanceRecordModel.DeletedAt,
Ext: productAttendanceRecordModel.Ext,
ProductDate: productAttendanceRecordModel.ProductDate,
}, nil
}
... ...
... ... @@ -42,6 +42,7 @@ func (repository *ProductAttendanceRecordRepository) Save(productAttendanceRecor
"updated_at",
"deleted_at",
"ext",
"product_date",
}
insertFieldsSnippet := sqlbuilder.SqlFieldsSnippet(sqlbuilder.RemoveSqlFields(sqlBuildFields, "product_attendance_id", "deleted_at"))
insertPlaceHoldersSnippet := sqlbuilder.SqlPlaceHoldersSnippet(sqlbuilder.RemoveSqlFields(sqlBuildFields, "product_attendance_id", "deleted_at"))
... ... @@ -67,6 +68,7 @@ func (repository *ProductAttendanceRecordRepository) Save(productAttendanceRecor
&productAttendanceRecord.UpdatedAt,
&productAttendanceRecord.DeletedAt,
&productAttendanceRecord.Ext,
&productAttendanceRecord.ProductDate,
),
fmt.Sprintf("INSERT INTO manufacture.product_attendance_record (%s) VALUES (%s) RETURNING %s", insertFieldsSnippet, insertPlaceHoldersSnippet, returningFieldsSnippet),
//productAttendanceRecord.ProductAttendanceId,
... ... @@ -84,6 +86,7 @@ func (repository *ProductAttendanceRecordRepository) Save(productAttendanceRecor
productAttendanceRecord.UpdatedAt,
//productAttendanceRecord.DeletedAt,
productAttendanceRecord.Ext,
productAttendanceRecord.ProductDate,
); err != nil {
return productAttendanceRecord, err
}
... ... @@ -105,6 +108,7 @@ func (repository *ProductAttendanceRecordRepository) Save(productAttendanceRecor
&productAttendanceRecord.UpdatedAt,
&productAttendanceRecord.DeletedAt,
&productAttendanceRecord.Ext,
&productAttendanceRecord.ProductDate,
),
fmt.Sprintf("UPDATE manufacture.product_attendance_record SET %s WHERE product_attendance_id=? RETURNING %s", updateFieldsSnippet, returningFieldsSnippet),
//productAttendanceRecord.ProductAttendanceId,
... ... @@ -122,6 +126,7 @@ func (repository *ProductAttendanceRecordRepository) Save(productAttendanceRecor
productAttendanceRecord.UpdatedAt,
//productAttendanceRecord.DeletedAt,
productAttendanceRecord.Ext,
productAttendanceRecord.ProductDate,
productAttendanceRecord.Identify(),
); err != nil {
return productAttendanceRecord, err
... ... @@ -189,10 +194,10 @@ func (repository *ProductAttendanceRecordRepository) Find(queryOptions map[strin
query.Where(fmt.Sprintf(`work_station->>'sectionName' like '%%%v%%'`, v))
}
if v, ok := queryOptions["signBeginTime"]; ok && !((v.(time.Time)).IsZero()) {
query.Where("created_at>=?", v.(time.Time))
query.Where("product_date>=?", v.(time.Time))
}
if v, ok := queryOptions["signEndTime"]; ok && !((v.(time.Time)).IsZero()) {
query.Where("created_at<?", v.(time.Time))
query.Where("product_date<?", v.(time.Time))
}
if v, ok := queryOptions["employeeType"]; ok && (v.(int)) > 0 {
query.Where("product_worker->>'employeeType'='?'", v.(int))
... ...
... ... @@ -100,3 +100,15 @@ func (controller *ProductGroupController) GetSignInEmployeeQuery() {
data, err := productGroupService.GetSignEmployee(cmd)
controller.Response(data, err)
}
func (controller *ProductGroupController) SelectorProductGroups() {
productGroupService := service.NewProductGroupService(nil)
cmd := &query.SearchProductGroupQuery{}
Must(controller.Unmarshal(cmd))
operateInfo := ParseOperateInfo(controller.BaseController)
//cmd.OrgId = operateInfo.OrgId
cmd.CompanyId = operateInfo.CompanyId
cmd.InOrgIds = operateInfo.OrgIds
data, err := productGroupService.SelectorProductGroup(ParseOperateInfo(controller.BaseController), cmd)
controller.Response(data, err)
}
... ...
... ... @@ -15,4 +15,5 @@ func init() {
web.Router("/product-groups/search", &controllers.ProductGroupController{}, "Post:SearchProductGroup")
web.Router("/product-groups/employees", &controllers.ProductGroupController{}, "Post:SearchProductGroupEmployees")
web.Router("/product-groups/employee-signing", &controllers.ProductGroupController{}, "Post:GetSignInEmployeeQuery")
web.Router("/product-groups/selector", &controllers.ProductGroupController{}, "Post:SelectorProductGroups")
}
... ...