|
|
@@ -18,12 +18,14 @@ import (
|
|
|
// OpsEventTaskService 任务业务逻辑实现类
|
|
|
type OpsEventTaskService struct {
|
|
|
*service.ContextService
|
|
|
- TaskDao *opsdevdao.OpsEventTaskDao
|
|
|
- RecordDao *opsdevdao.OpsEventTaskRecordDao
|
|
|
- AttachmentDao *opsdevdao.OpsEventTaskAttachmentDao
|
|
|
- ProjectDao *opsdevdao.OpsDeliveryProjectDao
|
|
|
- ReleaseDao *opsdevdao.OpsEventTaskReleaseDao
|
|
|
- WorkHourDao *opsdevdao.OpsEventTaskWorkHourDao
|
|
|
+ TaskDao *opsdevdao.OpsEventTaskDao
|
|
|
+ RecordDao *opsdevdao.OpsEventTaskRecordDao
|
|
|
+ AttachmentDao *opsdevdao.OpsEventTaskAttachmentDao
|
|
|
+ ProjectDao *opsdevdao.OpsDeliveryProjectDao
|
|
|
+ ReleaseDao *opsdevdao.OpsEventTaskReleaseDao
|
|
|
+ WorkHourDao *opsdevdao.OpsEventTaskWorkHourDao
|
|
|
+ EventDao *opsdevdao.OpsDeliveryProjectEventDao
|
|
|
+ EventRecordDao *opsdevdao.OpsDeliveryProjectEventRecordDao
|
|
|
}
|
|
|
|
|
|
// NewOpsEventTaskService 初始化service
|
|
|
@@ -38,6 +40,8 @@ func NewOpsEventTaskService(ctx context.Context) (svc *OpsEventTaskService, err
|
|
|
svc.ProjectDao = opsdevdao.NewOpsDeliveryProjectDao(svc.Tenant)
|
|
|
svc.ReleaseDao = opsdevdao.NewOpsEventTaskReleaseDao(svc.Tenant)
|
|
|
svc.WorkHourDao = opsdevdao.NewOpsEventTaskWorkHourDao(svc.Tenant)
|
|
|
+ svc.EventDao = opsdevdao.NewOpsDeliveryProjectEventDao(svc.Tenant)
|
|
|
+ svc.EventRecordDao = opsdevdao.NewOpsDeliveryProjectEventRecordDao(svc.Tenant)
|
|
|
return svc, nil
|
|
|
}
|
|
|
|
|
|
@@ -50,6 +54,11 @@ func (s *OpsEventTaskService) GetList(req *opsdevmodel.OpsEventTaskSearchReq) (t
|
|
|
db = db.Where(s.TaskDao.Columns.ProjectId, req.ProjectId)
|
|
|
}
|
|
|
|
|
|
+ // 关联事件ID筛选
|
|
|
+ if req.EventId > 0 {
|
|
|
+ db = db.Where(s.TaskDao.Columns.EventId, req.EventId)
|
|
|
+ }
|
|
|
+
|
|
|
// 任务标题模糊查询
|
|
|
if req.TaskTitle != "" {
|
|
|
db = db.Where(s.TaskDao.Columns.TaskTitle+" like ?", "%"+req.TaskTitle+"%")
|
|
|
@@ -75,6 +84,11 @@ func (s *OpsEventTaskService) GetList(req *opsdevmodel.OpsEventTaskSearchReq) (t
|
|
|
db = db.Where(s.TaskDao.Columns.OpsUserName+" in (?)", req.OpsUserName)
|
|
|
}
|
|
|
|
|
|
+ // 发布版本为空筛选(用于发版任务选择未发版任务)
|
|
|
+ if req.ReleaseVersionEmpty {
|
|
|
+ db = db.Where(s.TaskDao.Columns.ReleaseVersion + " is null")
|
|
|
+ }
|
|
|
+
|
|
|
// 计划结束日期范围筛选
|
|
|
if req.PlanEndDateStart != "" {
|
|
|
db = db.Where(s.TaskDao.Columns.PlanEndTime+" >= ?", req.PlanEndDateStart+" 00:00:00")
|
|
|
@@ -167,14 +181,38 @@ func (s *OpsEventTaskService) getSortColumnName(field string) string {
|
|
|
return ""
|
|
|
}
|
|
|
|
|
|
+const maxTaskNoRetries = 3
|
|
|
+
|
|
|
+// isDuplicateEntryError 判断是否为数据库唯一键冲突
|
|
|
+func isDuplicateEntryError(err error) bool {
|
|
|
+ if err == nil {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ return strings.Contains(strings.ToLower(err.Error()), "duplicate entry")
|
|
|
+}
|
|
|
+
|
|
|
// Create 新增任务,包含事务控制和过程记录
|
|
|
-func (s *OpsEventTaskService) Create(req *opsdevmodel.OpsEventTaskAddReq) error {
|
|
|
+func (s *OpsEventTaskService) Create(req *opsdevmodel.OpsEventTaskAddReq) (err error) {
|
|
|
// 判断任务状态:如果执行人、计划开始时间、计划结束时间都填写了,则状态为处理中,否则为待处理
|
|
|
taskStatus := opsdevmodel.TaskStatusTodo
|
|
|
if req.OpsUserId > 0 && req.PlanStartTime != "" && req.PlanEndTime != "" {
|
|
|
taskStatus = opsdevmodel.TaskStatusProcessing
|
|
|
}
|
|
|
|
|
|
+ for i := 0; i < maxTaskNoRetries; i++ {
|
|
|
+ err = s.doCreate(req, taskStatus)
|
|
|
+ if err == nil {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ if !isDuplicateEntryError(err) {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return myerrors.DbError("任务编号生成冲突,请稍后重试")
|
|
|
+}
|
|
|
+
|
|
|
+// doCreate 执行新增任务(可在任务编号冲突时重试)
|
|
|
+func (s *OpsEventTaskService) doCreate(req *opsdevmodel.OpsEventTaskAddReq, taskStatus string) error {
|
|
|
// 生成任务编号
|
|
|
taskNo := s.generateTaskNo()
|
|
|
|
|
|
@@ -198,6 +236,7 @@ func (s *OpsEventTaskService) Create(req *opsdevmodel.OpsEventTaskAddReq) error
|
|
|
s.TaskDao.Columns.EventId: req.EventId,
|
|
|
s.TaskDao.Columns.EventType: req.EventType,
|
|
|
s.TaskDao.Columns.TaskParentId: req.TaskParentId,
|
|
|
+ s.TaskDao.Columns.Attribute2: req.Attribute2,
|
|
|
}
|
|
|
|
|
|
if req.PlanStartTime != "" {
|
|
|
@@ -216,6 +255,9 @@ func (s *OpsEventTaskService) Create(req *opsdevmodel.OpsEventTaskAddReq) error
|
|
|
result, err := s.TaskDao.TX(tx).Data(data).Insert()
|
|
|
if err != nil {
|
|
|
g.Log().Error(err)
|
|
|
+ if isDuplicateEntryError(err) {
|
|
|
+ return err
|
|
|
+ }
|
|
|
return myerrors.DbError("新增任务失败")
|
|
|
}
|
|
|
|
|
|
@@ -235,14 +277,14 @@ func (s *OpsEventTaskService) Create(req *opsdevmodel.OpsEventTaskAddReq) error
|
|
|
}
|
|
|
service.SetCreatedInfo(recordData, s.GetCxtUserId(), s.GetCxtUserName())
|
|
|
|
|
|
- _, err = s.RecordDao.TX(tx).Data(recordData).Insert()
|
|
|
+ recordResult, err := s.RecordDao.TX(tx).Data(recordData).Insert()
|
|
|
if err != nil {
|
|
|
g.Log().Error(err)
|
|
|
return myerrors.DbError("新增任务过程记录失败")
|
|
|
}
|
|
|
|
|
|
// 获取过程记录ID
|
|
|
- recordId, err := result.LastInsertId()
|
|
|
+ recordId, err := recordResult.LastInsertId()
|
|
|
if err != nil {
|
|
|
g.Log().Error(err)
|
|
|
return myerrors.DbError("获取过程记录ID失败")
|
|
|
@@ -290,21 +332,50 @@ func (s *OpsEventTaskService) UpdateById(req *opsdevmodel.OpsEventTaskUpdateReq)
|
|
|
return myerrors.TipsError("已完成或已作废状态的任务不允许编辑")
|
|
|
}
|
|
|
|
|
|
- // 构造更新数据
|
|
|
- data := g.Map{
|
|
|
- s.TaskDao.Columns.TaskTitle: req.TaskTitle,
|
|
|
- s.TaskDao.Columns.TaskDesc: req.TaskDesc,
|
|
|
- s.TaskDao.Columns.FunctionName: req.FunctionName,
|
|
|
- s.TaskDao.Columns.TaskType: req.TaskType,
|
|
|
- s.TaskDao.Columns.Priority: req.Priority,
|
|
|
- s.TaskDao.Columns.OpsUserId: req.OpsUserId,
|
|
|
- s.TaskDao.Columns.OpsUserName: req.OpsUserName,
|
|
|
- s.TaskDao.Columns.PlanStartTime: req.PlanStartTime,
|
|
|
- s.TaskDao.Columns.PlanEndTime: req.PlanEndTime,
|
|
|
- s.TaskDao.Columns.EstimateWorkHour: req.EstimateWorkHour,
|
|
|
- s.TaskDao.Columns.DefectType: req.DefectType,
|
|
|
- s.TaskDao.Columns.ReleaseVersion: req.ReleaseVersion,
|
|
|
- s.TaskDao.Columns.Remark: req.Remark,
|
|
|
+ // 构造更新数据 - 只更新传入的非空字段
|
|
|
+ data := g.Map{}
|
|
|
+
|
|
|
+ if req.TaskTitle != "" {
|
|
|
+ data[s.TaskDao.Columns.TaskTitle] = req.TaskTitle
|
|
|
+ }
|
|
|
+ if req.TaskDesc != "" {
|
|
|
+ data[s.TaskDao.Columns.TaskDesc] = req.TaskDesc
|
|
|
+ }
|
|
|
+ if req.FunctionName != "" {
|
|
|
+ data[s.TaskDao.Columns.FunctionName] = req.FunctionName
|
|
|
+ }
|
|
|
+ if req.TaskType != "" {
|
|
|
+ data[s.TaskDao.Columns.TaskType] = req.TaskType
|
|
|
+ }
|
|
|
+ if req.Priority != "" {
|
|
|
+ data[s.TaskDao.Columns.Priority] = req.Priority
|
|
|
+ }
|
|
|
+ if req.OpsUserId > 0 {
|
|
|
+ data[s.TaskDao.Columns.OpsUserId] = req.OpsUserId
|
|
|
+ }
|
|
|
+ if req.OpsUserName != "" {
|
|
|
+ data[s.TaskDao.Columns.OpsUserName] = req.OpsUserName
|
|
|
+ }
|
|
|
+ if req.PlanStartTime != "" {
|
|
|
+ data[s.TaskDao.Columns.PlanStartTime] = req.PlanStartTime
|
|
|
+ }
|
|
|
+ if req.PlanEndTime != "" {
|
|
|
+ data[s.TaskDao.Columns.PlanEndTime] = req.PlanEndTime
|
|
|
+ }
|
|
|
+ if req.EstimateWorkHour > 0 {
|
|
|
+ data[s.TaskDao.Columns.EstimateWorkHour] = req.EstimateWorkHour
|
|
|
+ }
|
|
|
+ if req.DefectType != "" {
|
|
|
+ data[s.TaskDao.Columns.DefectType] = req.DefectType
|
|
|
+ }
|
|
|
+ if req.ReleaseVersion != "" {
|
|
|
+ data[s.TaskDao.Columns.ReleaseVersion] = req.ReleaseVersion
|
|
|
+ }
|
|
|
+ if req.Remark != "" {
|
|
|
+ data[s.TaskDao.Columns.Remark] = req.Remark
|
|
|
+ }
|
|
|
+ if req.Attribute2 != "" {
|
|
|
+ data[s.TaskDao.Columns.Attribute2] = req.Attribute2
|
|
|
}
|
|
|
|
|
|
// 补齐审计字段
|
|
|
@@ -491,10 +562,12 @@ func (s *OpsEventTaskService) Start(req *opsdevmodel.OpsEventTaskStartReq) error
|
|
|
}
|
|
|
|
|
|
// Complete 完成任务
|
|
|
-func (s *OpsEventTaskService) Complete(req *opsdevmodel.OpsEventTaskCompleteReq) error {
|
|
|
+func (s *OpsEventTaskService) Complete(req *opsdevmodel.OpsEventTaskCompleteReq) (err error) {
|
|
|
+ g.Log().Infof("Handler received req: %+v", req)
|
|
|
+
|
|
|
// 校验数据是否存在
|
|
|
var entity opsdevmodel.OpsEventTask
|
|
|
- err := s.TaskDao.FieldsEx(s.TaskDao.Columns.DeletedTime).WherePri(s.TaskDao.Columns.Id, req.Id).Scan(&entity)
|
|
|
+ err = s.TaskDao.FieldsEx(s.TaskDao.Columns.DeletedTime).WherePri(s.TaskDao.Columns.Id, req.Id).Scan(&entity)
|
|
|
if err != nil {
|
|
|
g.Log().Error(err)
|
|
|
return myerrors.DbError("查询任务数据失败")
|
|
|
@@ -508,6 +581,20 @@ func (s *OpsEventTaskService) Complete(req *opsdevmodel.OpsEventTaskCompleteReq)
|
|
|
return myerrors.TipsError("只有处理中的任务可以完成")
|
|
|
}
|
|
|
|
|
|
+ for i := 0; i < maxTaskNoRetries; i++ {
|
|
|
+ err = s.doComplete(req, &entity)
|
|
|
+ if err == nil {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ if !isDuplicateEntryError(err) {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return myerrors.DbError("任务编号生成冲突,请稍后重试")
|
|
|
+}
|
|
|
+
|
|
|
+// doComplete 执行完成任务(可在下游任务编号冲突时重试)
|
|
|
+func (s *OpsEventTaskService) doComplete(req *opsdevmodel.OpsEventTaskCompleteReq, entity *opsdevmodel.OpsEventTask) error {
|
|
|
// 构造更新数据
|
|
|
data := g.Map{
|
|
|
s.TaskDao.Columns.TaskStatus: opsdevmodel.TaskStatusCompleted, // 已完成
|
|
|
@@ -527,6 +614,9 @@ func (s *OpsEventTaskService) Complete(req *opsdevmodel.OpsEventTaskCompleteReq)
|
|
|
if entity.TaskType == opsdevmodel.TaskTypeFeatureDev {
|
|
|
testTaskNo = s.generateTaskNo()
|
|
|
}
|
|
|
+ if entity.TaskType == opsdevmodel.TaskTypeBug {
|
|
|
+ testTaskNo = s.generateTaskNo()
|
|
|
+ }
|
|
|
|
|
|
// 使用事务
|
|
|
return s.TaskDao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
|
|
|
@@ -545,7 +635,7 @@ func (s *OpsEventTaskService) Complete(req *opsdevmodel.OpsEventTaskCompleteReq)
|
|
|
testResultText = "不通过"
|
|
|
}
|
|
|
handleContent = fmt.Sprintf("测试结果:%s <br/>测试时间:%s", testResultText, gtime.Now().Format("Y-m-d"))
|
|
|
- } else if entity.TaskType == opsdevmodel.TaskTypeSystemRelease && req.IsReleaseComplete {
|
|
|
+ } else if entity.TaskType == opsdevmodel.TaskTypeSystemReleaseEvt && req.IsReleaseComplete {
|
|
|
// 系统发版完成记录
|
|
|
handleContent = "发版完成<br/>任务标题: " + entity.TaskTitle + "<br/>发版工时: " + gconv.String(req.ActualWorkHour) + "小时"
|
|
|
if req.Remark != "" {
|
|
|
@@ -617,6 +707,9 @@ func (s *OpsEventTaskService) Complete(req *opsdevmodel.OpsEventTaskCompleteReq)
|
|
|
devResult, err := s.TaskDao.TX(tx).Data(devTaskData).Insert()
|
|
|
if err != nil {
|
|
|
g.Log().Error(err)
|
|
|
+ if isDuplicateEntryError(err) {
|
|
|
+ return err
|
|
|
+ }
|
|
|
return myerrors.DbError("自动创建功能开发任务失败")
|
|
|
}
|
|
|
|
|
|
@@ -659,6 +752,9 @@ func (s *OpsEventTaskService) Complete(req *opsdevmodel.OpsEventTaskCompleteReq)
|
|
|
testResult, err := s.TaskDao.TX(tx).Data(testTaskData).Insert()
|
|
|
if err != nil {
|
|
|
g.Log().Error(err)
|
|
|
+ if isDuplicateEntryError(err) {
|
|
|
+ return err
|
|
|
+ }
|
|
|
return myerrors.DbError("自动创建功能测试任务失败")
|
|
|
}
|
|
|
|
|
|
@@ -680,8 +776,53 @@ func (s *OpsEventTaskService) Complete(req *opsdevmodel.OpsEventTaskCompleteReq)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 4.2b BUG任务完成时,自动创建功能测试任务
|
|
|
+ if entity.TaskType == opsdevmodel.TaskTypeBug {
|
|
|
+ testTaskData := g.Map{
|
|
|
+ s.TaskDao.Columns.TaskNo: testTaskNo,
|
|
|
+ s.TaskDao.Columns.ProjectId: entity.ProjectId,
|
|
|
+ s.TaskDao.Columns.ProjectName: entity.ProjectName,
|
|
|
+ s.TaskDao.Columns.EventId: entity.EventId,
|
|
|
+ s.TaskDao.Columns.EventType: entity.EventType,
|
|
|
+ s.TaskDao.Columns.TaskTitle: entity.TaskTitle,
|
|
|
+ s.TaskDao.Columns.TaskDesc: entity.TaskDesc,
|
|
|
+ s.TaskDao.Columns.FunctionName: entity.FunctionName,
|
|
|
+ s.TaskDao.Columns.TaskType: opsdevmodel.TaskTypeFeatureTest, // 功能测试
|
|
|
+ s.TaskDao.Columns.TaskStatus: opsdevmodel.TaskStatusTodo, // 待处理
|
|
|
+ s.TaskDao.Columns.Priority: entity.Priority,
|
|
|
+ s.TaskDao.Columns.TaskParentId: req.Id,
|
|
|
+ }
|
|
|
+ service.SetCreatedInfo(testTaskData, s.GetCxtUserId(), s.GetCxtUserName())
|
|
|
+
|
|
|
+ testResult, err := s.TaskDao.TX(tx).Data(testTaskData).Insert()
|
|
|
+ if err != nil {
|
|
|
+ g.Log().Error(err)
|
|
|
+ if isDuplicateEntryError(err) {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ return myerrors.DbError("自动创建功能测试任务失败")
|
|
|
+ }
|
|
|
+
|
|
|
+ testTaskId, _ := testResult.LastInsertId()
|
|
|
+
|
|
|
+ // 创建功能测试任务的过程记录
|
|
|
+ testRecordData := g.Map{
|
|
|
+ s.RecordDao.Columns.TaskId: testTaskId,
|
|
|
+ s.RecordDao.Columns.HandleUserId: s.GetCxtUserId(),
|
|
|
+ s.RecordDao.Columns.HandleUserName: s.GetCxtUserName(),
|
|
|
+ s.RecordDao.Columns.HandleContent: "创建功能测试任务<br/>说明: 由BUG任务自动创建",
|
|
|
+ }
|
|
|
+ service.SetCreatedInfo(testRecordData, s.GetCxtUserId(), s.GetCxtUserName())
|
|
|
+
|
|
|
+ _, err = s.RecordDao.TX(tx).Data(testRecordData).Insert()
|
|
|
+ if err != nil {
|
|
|
+ g.Log().Error(err)
|
|
|
+ return myerrors.DbError("新增功能测试任务过程记录失败")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// 4.3 系统发版完成时,保存关联的研发任务到ops_event_task_release表
|
|
|
- if entity.TaskType == opsdevmodel.TaskTypeSystemRelease && req.IsReleaseComplete && len(req.DevTaskIds) > 0 {
|
|
|
+ if entity.TaskType == opsdevmodel.TaskTypeSystemReleaseEvt && req.IsReleaseComplete && len(req.DevTaskIds) > 0 {
|
|
|
for _, devTaskId := range req.DevTaskIds {
|
|
|
releaseData := g.Map{
|
|
|
s.ReleaseDao.Columns.ReleaseTaskId: req.Id,
|
|
|
@@ -698,6 +839,41 @@ func (s *OpsEventTaskService) Complete(req *opsdevmodel.OpsEventTaskCompleteReq)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 5. 任务完成时自动更新关联的交付事件状态为完成并生成过程记录
|
|
|
+ if entity.EventId > 0 && entity.EventType == opsdevmodel.EventTypeDelivery {
|
|
|
+ // 触发事件自动完成的任务类型:10(需求评审)、30(功能测试-通过)、38(系统发版)、40(系统发版/硬件发货)、41(硬件安装)
|
|
|
+ if s.shouldAutoCompleteEvent(entity.TaskType, req.TestResult) {
|
|
|
+ eventData := g.Map{
|
|
|
+ s.EventDao.Columns.DeliveryEventStatus: opsdevmodel.DeliveryEventStatusClosed,
|
|
|
+ s.EventDao.Columns.CompleteTime: gtime.Now(),
|
|
|
+ }
|
|
|
+ service.SetUpdatedInfo(eventData, s.GetCxtUserId(), s.GetCxtUserName())
|
|
|
+
|
|
|
+ _, err := s.EventDao.TX(tx).FieldsEx(service.UpdateFieldEx...).
|
|
|
+ Data(eventData).
|
|
|
+ WherePri(s.EventDao.Columns.Id, entity.EventId).
|
|
|
+ Update()
|
|
|
+ if err != nil {
|
|
|
+ g.Log().Error(err)
|
|
|
+ return myerrors.DbError("自动更新事件状态失败")
|
|
|
+ }
|
|
|
+
|
|
|
+ eventRecordData := g.Map{
|
|
|
+ s.EventRecordDao.Columns.DeliveryEventId: entity.EventId,
|
|
|
+ s.EventRecordDao.Columns.HandleUserId: s.GetCxtUserId(),
|
|
|
+ s.EventRecordDao.Columns.HandleUserName: s.GetCxtUserName(),
|
|
|
+ s.EventRecordDao.Columns.HandleContent: "研发任务已完成,自动关闭事件<br/>任务编号: " + entity.TaskNo + "<br/>任务标题: " + entity.TaskTitle,
|
|
|
+ }
|
|
|
+ service.SetCreatedInfo(eventRecordData, s.GetCxtUserId(), s.GetCxtUserName())
|
|
|
+
|
|
|
+ _, err = s.EventRecordDao.TX(tx).Data(eventRecordData).Insert()
|
|
|
+ if err != nil {
|
|
|
+ g.Log().Error(err)
|
|
|
+ return myerrors.DbError("新增事件过程记录失败")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
return nil
|
|
|
})
|
|
|
}
|
|
|
@@ -902,31 +1078,32 @@ func (s *OpsEventTaskService) GetRecords(req *opsdevmodel.OpsEventTaskRecordSear
|
|
|
|
|
|
// generateTaskNo 生成任务编号
|
|
|
func (s *OpsEventTaskService) generateTaskNo() string {
|
|
|
- // 格式: TSK + 年月日 + 4位序号
|
|
|
+ // 格式: TSK + 年月日 + 4位序列号
|
|
|
now := gtime.Now()
|
|
|
- prefix := "TSK" + now.Format("ymd")
|
|
|
+ prefix := "TSK" + now.Format("Ymd")
|
|
|
|
|
|
- // 查询当天最大序号
|
|
|
- var maxNo string
|
|
|
- err := s.TaskDao.Where(s.TaskDao.Columns.TaskNo+" like ?", prefix+"%").Order(s.TaskDao.Columns.TaskNo + " desc").Fields(s.TaskDao.Columns.TaskNo).Scan(&maxNo)
|
|
|
+ // 使用数据库序列生成唯一序号(按天重置)
|
|
|
+ seqVal, err := s.TaskDao.DB.GetValue("SELECT next_day_reset_val('task_no_seq')")
|
|
|
if err != nil {
|
|
|
+ // 如果序列不存在或出错,使用备用方案:查询当天最大序号+1
|
|
|
+ var maxNoResult struct {
|
|
|
+ TaskNo string
|
|
|
+ }
|
|
|
+ err = s.TaskDao.Where(s.TaskDao.Columns.TaskNo+" like ?", prefix+"%").Order(s.TaskDao.Columns.TaskNo + " desc").Fields(s.TaskDao.Columns.TaskNo).Scan(&maxNoResult)
|
|
|
+ if err != nil || maxNoResult.TaskNo == "" {
|
|
|
+ return prefix + "0001"
|
|
|
+ }
|
|
|
+ maxNoStr := maxNoResult.TaskNo
|
|
|
+ if len(maxNoStr) >= len(prefix)+4 {
|
|
|
+ seq := maxNoStr[len(prefix):]
|
|
|
+ seqNum := gconv.Int(seq)
|
|
|
+ seqNum++
|
|
|
+ return prefix + fmt.Sprintf("%04d", seqNum)
|
|
|
+ }
|
|
|
return prefix + "0001"
|
|
|
}
|
|
|
|
|
|
- if maxNo == "" {
|
|
|
- return prefix + "0001"
|
|
|
- }
|
|
|
-
|
|
|
- // 提取序号并+1
|
|
|
- if len(maxNo) >= len(prefix)+4 {
|
|
|
- seq := maxNo[len(prefix):]
|
|
|
- var seqNum int
|
|
|
- gconv.Struct(seq, &seqNum)
|
|
|
- seqNum++
|
|
|
- return prefix + fmt.Sprintf("%04d", seqNum)
|
|
|
- }
|
|
|
-
|
|
|
- return prefix + "0001"
|
|
|
+ return prefix + fmt.Sprintf("%04d", seqVal.Int())
|
|
|
}
|
|
|
|
|
|
// 状态校验辅助方法
|
|
|
@@ -939,7 +1116,7 @@ func (s *OpsEventTaskService) canSchedule(status string) bool {
|
|
|
}
|
|
|
|
|
|
func (s *OpsEventTaskService) canStart(status string) bool {
|
|
|
- return status == opsdevmodel.TaskStatusTodo || status == opsdevmodel.TaskStatusPaused
|
|
|
+ return status == opsdevmodel.TaskStatusTodo || status == opsdevmodel.TaskStatusPaused || status == opsdevmodel.TaskStatusBlocked
|
|
|
}
|
|
|
|
|
|
func (s *OpsEventTaskService) canComplete(status string) bool {
|
|
|
@@ -954,6 +1131,20 @@ func (s *OpsEventTaskService) canBlock(status string) bool {
|
|
|
return status == opsdevmodel.TaskStatusProcessing || status == opsdevmodel.TaskStatusPaused
|
|
|
}
|
|
|
|
|
|
+// shouldAutoCompleteEvent 判断当前任务完成时是否应自动完成关联的交付事件
|
|
|
+func (s *OpsEventTaskService) shouldAutoCompleteEvent(taskType, testResult string) bool {
|
|
|
+ switch taskType {
|
|
|
+ case "10": // 需求评审 — 完成即触发
|
|
|
+ return true
|
|
|
+ case "30": // 功能测试 — 仅通过时触发
|
|
|
+ return testResult == "pass"
|
|
|
+ case "38": // 系统发版(事件关联) — 完成即触发
|
|
|
+ return true
|
|
|
+ default:
|
|
|
+ return false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
func (s *OpsEventTaskService) canCancel(status string) bool {
|
|
|
return status != opsdevmodel.TaskStatusCompleted
|
|
|
}
|