| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022 |
- package opsdev
- import (
- "context"
- "fmt"
- "strconv"
- "strings"
- "dashoo.cn/opms_libary/myerrors"
- eventdao "dashoo.cn/opms_parent/app/dao/opsdev"
- eventmodel "dashoo.cn/opms_parent/app/model/opsdev"
- "dashoo.cn/opms_parent/app/service"
- "github.com/gogf/gf/database/gdb"
- "github.com/gogf/gf/frame/g"
- "github.com/gogf/gf/os/gtime"
- "github.com/gogf/gf/util/gconv"
- )
- // DeliveryProjectEventService 交付项目事件业务逻辑实现类
- type DeliveryProjectEventService struct {
- *service.ContextService
- EventDao *eventdao.OpsDeliveryProjectEventDao
- RecordDao *eventdao.OpsDeliveryProjectEventRecordDao
- AttachmentDao *eventdao.OpsDeliveryProjectEventAttachmentDao
- ProjectDao *eventdao.OpsDeliveryProjectDao
- TaskDao *eventdao.OpsEventTaskDao
- TaskRecordDao *eventdao.OpsEventTaskRecordDao
- TaskAttachmentDao *eventdao.OpsEventTaskAttachmentDao
- }
- // NewDeliveryProjectEventService 初始化service
- func NewDeliveryProjectEventService(ctx context.Context) (svc *DeliveryProjectEventService, err error) {
- svc = new(DeliveryProjectEventService)
- if svc.ContextService, err = svc.Init(ctx); err != nil {
- return nil, err
- }
- svc.EventDao = eventdao.NewOpsDeliveryProjectEventDao(svc.Tenant)
- svc.RecordDao = eventdao.NewOpsDeliveryProjectEventRecordDao(svc.Tenant)
- svc.AttachmentDao = eventdao.NewOpsDeliveryProjectEventAttachmentDao(svc.Tenant)
- svc.ProjectDao = eventdao.NewOpsDeliveryProjectDao(svc.Tenant)
- svc.TaskDao = eventdao.NewOpsEventTaskDao(svc.Tenant)
- svc.TaskRecordDao = eventdao.NewOpsEventTaskRecordDao(svc.Tenant)
- svc.TaskAttachmentDao = eventdao.NewOpsEventTaskAttachmentDao(svc.Tenant)
- return svc, nil
- }
- // GetList 分页查询事件列表
- func (s *DeliveryProjectEventService) GetList(req *eventmodel.OpsDeliveryProjectEventSearchReq) (total int, list []*eventmodel.OpsDeliveryProjectEventRsp, err error) {
- db := s.EventDao.FieldsEx(s.EventDao.Columns.DeletedTime)
- // 项目ID筛选
- if req.ProjectId > 0 {
- db = db.Where(s.EventDao.Columns.ProjectId, req.ProjectId)
- } else {
- userId := s.GetCxtUserId()
- if userId > 0 {
- projectWhere := service.BuildProjectPermissionWhere(userId, s.GetCxtUserRoles())
- if projectWhere != "" {
- db = db.Where(fmt.Sprintf(
- "EXISTS (SELECT 1 FROM ops_delivery_project WHERE id = %s AND %s)",
- s.EventDao.Columns.ProjectId,
- projectWhere,
- ))
- }
- }
- }
- // 事件标题模糊查询
- if req.DeliveryEventTitle != "" {
- db = db.Where(s.EventDao.Columns.DeliveryEventTitle+" like ?", "%"+req.DeliveryEventTitle+"%")
- }
- // 事件描述模糊查询
- if req.DeliveryEventDesc != "" {
- db = db.Where(s.EventDao.Columns.DeliveryEventDesc+" like ?", "%"+req.DeliveryEventDesc+"%")
- }
- // 事件类型筛选(多选)
- if len(req.DeliveryEventType) > 0 {
- db = db.Where(s.EventDao.Columns.DeliveryEventType+" in (?)", req.DeliveryEventType)
- }
- // 事件状态筛选(多选)
- if len(req.DeliveryEventStatus) > 0 {
- db = db.Where(s.EventDao.Columns.DeliveryEventStatus+" in (?)", req.DeliveryEventStatus)
- }
- // 事件结果筛选(多选)
- if len(req.DeliveryEventResult) > 0 {
- db = db.Where(s.EventDao.Columns.DeliveryEventResult+" in (?)", req.DeliveryEventResult)
- }
- // 反馈来源筛选
- if req.FeedbackSource != "" {
- db = db.Where(s.EventDao.Columns.FeedbackSource, req.FeedbackSource)
- }
- // 反馈人模糊查询
- if req.FeedbackReporter != "" {
- db = db.Where(s.EventDao.Columns.FeedbackReporter+" like ?", "%"+req.FeedbackReporter+"%")
- }
- // 负责人筛选(多选精确匹配)
- if len(req.OpsUserName) > 0 {
- db = db.Where(s.EventDao.Columns.OpsUserName+" in (?)", req.OpsUserName)
- }
- // 反馈时间范围
- if req.FeedbackDateStart != "" {
- db = db.Where(s.EventDao.Columns.FeedbackDate+">= ?", req.FeedbackDateStart)
- }
- if req.FeedbackDateEnd != "" {
- db = db.Where(s.EventDao.Columns.FeedbackDate+"<= ?", req.FeedbackDateEnd)
- }
- // 处理时间范围
- if req.CompleteTimeStart != "" {
- db = db.Where(s.EventDao.Columns.CompleteTime+">= ?", req.CompleteTimeStart)
- }
- if req.CompleteTimeEnd != "" {
- db = db.Where(s.EventDao.Columns.CompleteTime+"<= ?", req.CompleteTimeEnd)
- }
- // 统计总数
- total, err = db.Count()
- if err != nil {
- g.Log().Error(err)
- return 0, nil, myerrors.DbError("获取事件总数失败")
- }
- // 分页查询
- pageNum, pageSize := req.GetPage()
- var entityList []*eventmodel.OpsDeliveryProjectEvent
- // 处理排序
- if len(req.SortFields) > 0 {
- orderClauses := []string{}
- for _, sort := range req.SortFields {
- // 将前端字段名转换为数据库列名
- colName := s.getSortColumnName(sort.Field)
- if colName != "" {
- orderClauses = append(orderClauses, colName+" "+strings.ToUpper(sort.Order))
- }
- }
- if len(orderClauses) > 0 {
- db = db.Order(strings.Join(orderClauses, ", "))
- } else {
- db = db.Order(s.EventDao.Columns.CreatedTime + " desc")
- }
- } else {
- db = db.Order(s.EventDao.Columns.CreatedTime + " desc")
- }
- err = db.Page(pageNum, pageSize).Scan(&entityList)
- if err != nil {
- g.Log().Error(err)
- return 0, nil, myerrors.DbError("查询事件列表失败")
- }
- // 转换为响应结构体
- if err = gconv.Structs(entityList, &list); err != nil {
- g.Log().Error(err)
- return 0, nil, myerrors.DbError("数据转换失败")
- }
- if len(list) > 0 {
- projectIds := make([]int, 0, len(list))
- for _, item := range list {
- if item.ProjectId > 0 {
- projectIds = append(projectIds, item.ProjectId)
- }
- }
- if len(projectIds) > 0 {
- var projects []*eventmodel.OpsDeliveryProject
- err := s.ProjectDao.Fields(
- s.ProjectDao.Columns.Id,
- s.ProjectDao.Columns.ProjectName,
- ).WhereIn(s.ProjectDao.Columns.Id, projectIds).Scan(&projects)
- if err != nil {
- g.Log().Error(err)
- } else {
- projectMap := make(map[int]string)
- for _, p := range projects {
- projectMap[p.Id] = p.ProjectName
- }
- for _, item := range list {
- if name, ok := projectMap[item.ProjectId]; ok {
- item.ProjectName = name
- }
- }
- }
- }
- }
- return
- }
- // getSortColumnName 将前端排序字段名转换为数据库列名
- func (s *DeliveryProjectEventService) getSortColumnName(field string) string {
- // 前端字段名到数据库列名的映射
- fieldMap := map[string]string{
- "deliveryEventType": s.EventDao.Columns.DeliveryEventType,
- "deliveryEventStatus": s.EventDao.Columns.DeliveryEventStatus,
- "deliveryEventResult": s.EventDao.Columns.DeliveryEventResult,
- "opsUserName": s.EventDao.Columns.OpsUserName,
- "feedbackReporter": s.EventDao.Columns.FeedbackReporter,
- "feedbackDate": s.EventDao.Columns.FeedbackDate,
- "completeTime": s.EventDao.Columns.CompleteTime,
- }
- if colName, ok := fieldMap[field]; ok {
- return colName
- }
- return ""
- }
- // Create 新增事件,包含事务控制和过程记录、附件存储
- func (s *DeliveryProjectEventService) Create(req *eventmodel.OpsDeliveryProjectEventAddReq) error {
- // 生成事件编码
- eventNo := s.generateEventNo()
- // 硬件发货(40)和硬件安装(41)默认状态为处理中(20)
- status := "10" // 待处理
- if req.DeliveryEventType == "40" || req.DeliveryEventType == "41" {
- status = "20"
- }
- // 构造数据,负责人默认为当前登录人,允许前端传入
- opsUserId := s.GetCxtUserId()
- opsUserName := s.GetCxtUserName()
- if req.OpsUserId > 0 {
- opsUserId = req.OpsUserId
- opsUserName = req.OpsUserName
- }
- data := g.Map{
- s.EventDao.Columns.ProjectId: req.ProjectId,
- s.EventDao.Columns.DeliveryEventNo: eventNo,
- s.EventDao.Columns.DeliveryEventTitle: req.DeliveryEventTitle,
- s.EventDao.Columns.DeliveryEventDesc: req.DeliveryEventDesc,
- s.EventDao.Columns.DeliveryEventType: req.DeliveryEventType,
- s.EventDao.Columns.DeliveryEventStatus: status,
- s.EventDao.Columns.FeedbackSource: req.FeedbackSource,
- s.EventDao.Columns.FeedbackReporter: req.FeedbackReporter,
- s.EventDao.Columns.FeedbackDate: gtime.Now(),
- s.EventDao.Columns.OnSite: req.OnSite,
- s.EventDao.Columns.OpsUserId: opsUserId,
- s.EventDao.Columns.OpsUserName: opsUserName,
- }
- if req.Attribute1 != "" {
- data[s.EventDao.Columns.Attribute1] = req.Attribute1
- }
- if req.CompleteTime != "" {
- data[s.EventDao.Columns.CompleteTime] = req.CompleteTime
- }
- if req.ActualWorkHour > 0 {
- data[s.EventDao.Columns.ActualWorkHour] = req.ActualWorkHour
- }
- // 补齐审计字段
- service.SetCreatedInfo(data, s.GetCxtUserId(), s.GetCxtUserName())
- // 使用事务控制
- return s.EventDao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
- // 1. 创建事件记录
- result, err := s.EventDao.TX(tx).Data(data).Insert()
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("新增事件失败")
- }
- // 获取新创建的事件ID
- eventId, err := result.LastInsertId()
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("获取事件ID失败")
- }
- // 2. 创建事件过程记录
- recordData := g.Map{
- s.RecordDao.Columns.DeliveryEventId: eventId,
- s.RecordDao.Columns.HandleUserId: s.GetCxtUserId(),
- s.RecordDao.Columns.HandleUserName: s.GetCxtUserName(),
- s.RecordDao.Columns.HandleContent: "创建事件<br/>事件标题: " + req.DeliveryEventTitle,
- }
- service.SetCreatedInfo(recordData, s.GetCxtUserId(), s.GetCxtUserName())
- recordResult, err := s.RecordDao.TX(tx).Data(recordData).Insert()
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("新增事件过程记录失败")
- }
- recordId, err := recordResult.LastInsertId()
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("获取过程记录ID失败")
- }
- // 3. 保存附件信息
- if len(req.Attachments) > 0 {
- for _, att := range req.Attachments {
- attData := g.Map{
- s.AttachmentDao.Columns.DeliveryEventId: eventId,
- s.AttachmentDao.Columns.EventId: eventId,
- s.AttachmentDao.Columns.EventRecordId: recordId,
- s.AttachmentDao.Columns.FileName: att.FileName,
- s.AttachmentDao.Columns.FileUrl: att.FileUrl,
- s.AttachmentDao.Columns.FileType: att.FileType,
- }
- service.SetCreatedInfo(attData, s.GetCxtUserId(), s.GetCxtUserName())
- _, err = s.AttachmentDao.TX(tx).Data(attData).Insert()
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("保存附件信息失败")
- }
- }
- }
- // 4. 根据前端选项决定是否创建研发任务
- if req.CreateRdTask && req.RdTaskType != "" {
- taskNo := s.generateTaskNo()
- taskData := g.Map{
- s.TaskDao.Columns.TaskNo: taskNo,
- s.TaskDao.Columns.ProjectId: req.ProjectId,
- s.TaskDao.Columns.ProjectName: s.getProjectName(req.ProjectId),
- s.TaskDao.Columns.TaskTitle: req.DeliveryEventTitle,
- s.TaskDao.Columns.TaskDesc: req.DeliveryEventDesc,
- s.TaskDao.Columns.FunctionName: req.DeliveryEventTitle,
- s.TaskDao.Columns.TaskType: req.RdTaskType,
- s.TaskDao.Columns.TaskStatus: eventmodel.TaskStatusTodo,
- s.TaskDao.Columns.Priority: "20",
- s.TaskDao.Columns.EventId: int(eventId),
- s.TaskDao.Columns.EventType: eventmodel.EventTypeDelivery,
- }
- if req.RdTaskOpsUserId > 0 {
- taskData[s.TaskDao.Columns.OpsUserId] = req.RdTaskOpsUserId
- taskData[s.TaskDao.Columns.OpsUserName] = req.RdTaskOpsUserName
- }
- service.SetCreatedInfo(taskData, s.GetCxtUserId(), s.GetCxtUserName())
- taskResult, err := s.TaskDao.TX(tx).Data(taskData).Insert()
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("创建研发任务失败")
- }
- taskId, _ := taskResult.LastInsertId()
- taskRecordData := g.Map{
- s.TaskRecordDao.Columns.TaskId: taskId,
- s.TaskRecordDao.Columns.HandleUserId: s.GetCxtUserId(),
- s.TaskRecordDao.Columns.HandleUserName: s.GetCxtUserName(),
- s.TaskRecordDao.Columns.HandleContent: "创建研发任务<br/>说明: 由交付事件创建 (" + req.DeliveryEventTitle + ")",
- }
- service.SetCreatedInfo(taskRecordData, s.GetCxtUserId(), s.GetCxtUserName())
- taskRecordResult, err := s.TaskRecordDao.TX(tx).Data(taskRecordData).Insert()
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("新增任务过程记录失败")
- }
- taskRecordId, _ := taskRecordResult.LastInsertId()
- // 5. 复制交付事件附件到研发任务
- if len(req.Attachments) > 0 {
- for _, att := range req.Attachments {
- attData := g.Map{
- s.TaskAttachmentDao.Columns.TaskId: taskId,
- s.TaskAttachmentDao.Columns.TaskRecordId: int(taskRecordId),
- s.TaskAttachmentDao.Columns.FileName: att.FileName,
- s.TaskAttachmentDao.Columns.FileUrl: att.FileUrl,
- s.TaskAttachmentDao.Columns.FileType: att.FileType,
- }
- service.SetCreatedInfo(attData, s.GetCxtUserId(), s.GetCxtUserName())
- _, err = s.TaskAttachmentDao.TX(tx).Data(attData).Insert()
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("复制任务附件失败")
- }
- }
- }
- }
- return nil
- })
- }
- // UpdateById 根据ID更新事件
- func (s *DeliveryProjectEventService) UpdateById(req *eventmodel.OpsDeliveryProjectEventUpdateReq) error {
- // 校验数据是否存在
- var entity eventmodel.OpsDeliveryProjectEvent
- err := s.EventDao.FieldsEx(s.EventDao.Columns.DeletedTime).WherePri(s.EventDao.Columns.Id, req.Id).Scan(&entity)
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("查询事件数据失败")
- }
- if entity.Id <= 0 {
- return myerrors.TipsError("事件数据不存在")
- }
- // 构造更新数据 - 从请求中动态构建,支持前端传来的任何有效字段
- data := g.Map{}
- // 基础字段(编辑时可能更新,但关闭/处理事件时不传这些字段)
- if req.DeliveryEventTitle != "" {
- data[s.EventDao.Columns.DeliveryEventTitle] = req.DeliveryEventTitle
- }
- if req.DeliveryEventDesc != "" {
- data[s.EventDao.Columns.DeliveryEventDesc] = req.DeliveryEventDesc
- }
- if req.DeliveryEventType != "" {
- data[s.EventDao.Columns.DeliveryEventType] = req.DeliveryEventType
- }
- if req.FeedbackSource != "" {
- data[s.EventDao.Columns.FeedbackSource] = req.FeedbackSource
- }
- if req.FeedbackReporter != "" {
- data[s.EventDao.Columns.FeedbackReporter] = req.FeedbackReporter
- }
- if req.OnSite != "" {
- data[s.EventDao.Columns.OnSite] = req.OnSite
- }
- // 处理状态和结果字段(关闭事件时传入)
- if req.DeliveryEventStatus != "" {
- data[s.EventDao.Columns.DeliveryEventStatus] = req.DeliveryEventStatus
- }
- if req.DeliveryEventResult != "" {
- data[s.EventDao.Columns.DeliveryEventResult] = req.DeliveryEventResult
- }
- // 处理完成相关字段(处理/关闭事件时传入)
- if req.CompleteDesc != "" {
- data[s.EventDao.Columns.CompleteDesc] = req.CompleteDesc
- }
- if req.CompleteTime != "" {
- data[s.EventDao.Columns.CompleteTime] = req.CompleteTime
- }
- if req.ActualWorkHour > 0 {
- data[s.EventDao.Columns.ActualWorkHour] = req.ActualWorkHour
- }
- // 处理负责人相关字段(分配时传入)
- if req.OpsUserId > 0 {
- data[s.EventDao.Columns.OpsUserId] = req.OpsUserId
- data[s.EventDao.Columns.OpsUserName] = req.OpsUserName
- }
- // 物流单号
- if req.Attribute1 != "" {
- data[s.EventDao.Columns.Attribute1] = req.Attribute1
- }
- // 补齐审计字段
- service.SetUpdatedInfo(data, s.GetCxtUserId(), s.GetCxtUserName())
- // 使用事务控制
- return s.EventDao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
- // 更新操作,排除不可改字段
- _, err = s.EventDao.TX(tx).FieldsEx(service.UpdateFieldEx...).Data(data).WherePri(s.EventDao.Columns.Id, req.Id).Update()
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("更新事件失败")
- }
- // 编辑时创建研发任务(仅当 createRdTask=true 且该事件尚未关联研发任务)
- if req.CreateRdTask && req.RdTaskType != "" {
- // 检查是否已存在关联的研发任务
- existCount, err := s.TaskDao.TX(tx).Where(s.TaskDao.Columns.EventId, req.Id).
- Where(s.TaskDao.Columns.EventType, eventmodel.EventTypeDelivery).
- Count()
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("检查研发任务失败")
- }
- if existCount == 0 {
- taskNo := s.generateTaskNo()
- taskData := g.Map{
- s.TaskDao.Columns.TaskNo: taskNo,
- s.TaskDao.Columns.ProjectId: entity.ProjectId,
- s.TaskDao.Columns.ProjectName: s.getProjectName(entity.ProjectId),
- s.TaskDao.Columns.TaskTitle: entity.DeliveryEventTitle,
- s.TaskDao.Columns.TaskDesc: entity.DeliveryEventDesc,
- s.TaskDao.Columns.FunctionName: entity.DeliveryEventTitle,
- s.TaskDao.Columns.TaskType: req.RdTaskType,
- s.TaskDao.Columns.TaskStatus: eventmodel.TaskStatusTodo,
- s.TaskDao.Columns.Priority: "20",
- s.TaskDao.Columns.EventId: req.Id,
- s.TaskDao.Columns.EventType: eventmodel.EventTypeDelivery,
- }
- if req.RdTaskOpsUserId > 0 {
- taskData[s.TaskDao.Columns.OpsUserId] = req.RdTaskOpsUserId
- taskData[s.TaskDao.Columns.OpsUserName] = req.RdTaskOpsUserName
- }
- service.SetCreatedInfo(taskData, s.GetCxtUserId(), s.GetCxtUserName())
- taskResult, err := s.TaskDao.TX(tx).Data(taskData).Insert()
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("创建研发任务失败")
- }
- taskId, _ := taskResult.LastInsertId()
- taskRecordData := g.Map{
- s.TaskRecordDao.Columns.TaskId: taskId,
- s.TaskRecordDao.Columns.HandleUserId: s.GetCxtUserId(),
- s.TaskRecordDao.Columns.HandleUserName: s.GetCxtUserName(),
- s.TaskRecordDao.Columns.HandleContent: "创建研发任务<br/>说明: 由交付事件创建 (" + entity.DeliveryEventTitle + ")",
- }
- service.SetCreatedInfo(taskRecordData, s.GetCxtUserId(), s.GetCxtUserName())
- _, err = s.TaskRecordDao.TX(tx).Data(taskRecordData).Insert()
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("新增任务过程记录失败")
- }
- }
- }
- // 事件关闭时,根据事件类型自动推进关联项目的状态和节点(只允许前进,不允许回退)
- if req.DeliveryEventStatus == eventmodel.DeliveryEventStatusClosed {
- s.advanceProjectStatusOnEventClose(entity.ProjectId, req.DeliveryEventType)
- }
- return nil
- })
- }
- func (s *DeliveryProjectEventService) advanceProjectStatusOnEventClose(projectId int, eventType string) {
- targetStatus, targetNode := s.getTargetStatusAndNode(eventType)
- if targetStatus == "" && targetNode == "" {
- return
- }
- var project eventmodel.OpsDeliveryProject
- err := s.ProjectDao.Fields(
- s.ProjectDao.Columns.Id,
- s.ProjectDao.Columns.ProjectStatus,
- s.ProjectDao.Columns.DeliveryNode,
- ).WherePri(s.ProjectDao.Columns.Id, projectId).Scan(&project)
- if err != nil || project.Id <= 0 {
- g.Log().Error("查询项目数据失败:", err)
- return
- }
- updateData := g.Map{}
- currentStatus := project.ProjectStatus
- currentNode := project.DeliveryNode
- if targetStatus != "" && s.isForwardTransition(currentStatus, targetStatus) {
- updateData[s.ProjectDao.Columns.ProjectStatus] = targetStatus
- }
- if targetNode != "" && s.isForwardTransition(currentNode, targetNode) {
- updateData[s.ProjectDao.Columns.DeliveryNode] = targetNode
- }
- if len(updateData) == 0 {
- return
- }
- service.SetUpdatedInfo(updateData, s.GetCxtUserId(), s.GetCxtUserName())
- _, err = s.ProjectDao.Data(updateData).WherePri(s.ProjectDao.Columns.Id, projectId).Update()
- if err != nil {
- g.Log().Error("更新项目状态失败:", err)
- }
- }
- func (s *DeliveryProjectEventService) getTargetStatusAndNode(eventType string) (status string, node string) {
- statusNodeMap := map[string][2]string{
- "10": {"20", "10"},
- "15": {"20", "15"},
- "20": {"", "20"},
- "30": {"", "30"},
- "31": {"", "30"},
- "32": {"", "30"},
- "33": {"", "30"},
- "35": {"", "30"},
- "37": {"", "30"},
- "38": {"", "30"},
- "39": {"", "30"},
- "40": {"", "30"},
- "41": {"", "30"},
- "42": {"", "30"},
- "50": {"", "30"},
- "55": {"40", "40"},
- "60": {"50", "60"},
- }
- if entry, ok := statusNodeMap[eventType]; ok {
- return entry[0], entry[1]
- }
- return "", ""
- }
- func (s *DeliveryProjectEventService) isForwardTransition(current, target string) bool {
- currentNum, err1 := strconv.Atoi(current)
- targetNum, err2 := strconv.Atoi(target)
- if err1 != nil || err2 != nil {
- return target > current
- }
- return targetNum > currentNum
- }
- // DeleteByIds 根据ID批量删除
- func (s *DeliveryProjectEventService) DeleteByIds(ids []int64) error {
- if len(ids) == 0 {
- return myerrors.TipsError("请选择需要删除的事件")
- }
- return s.EventDao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
- // 1. 删除事件
- _, err := s.EventDao.TX(tx).WhereIn(s.EventDao.Columns.Id, ids).Delete()
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("删除事件失败")
- }
- // 2. 删除关联的过程记录
- _, err = s.RecordDao.TX(tx).WhereIn(s.RecordDao.Columns.DeliveryEventId, ids).Delete()
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("删除事件过程记录失败")
- }
- // 3. 删除关联的附件
- _, err = s.AttachmentDao.TX(tx).WhereIn(s.AttachmentDao.Columns.DeliveryEventId, ids).Delete()
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("删除事件附件失败")
- }
- return nil
- })
- }
- // Cancel 作废事件
- func (s *DeliveryProjectEventService) Cancel(req *eventmodel.OpsDeliveryProjectEventCancelReq) error {
- // 校验数据是否存在
- var entity eventmodel.OpsDeliveryProjectEvent
- err := s.EventDao.FieldsEx(s.EventDao.Columns.DeletedTime).WherePri(s.EventDao.Columns.Id, req.Id).Scan(&entity)
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("查询事件数据失败")
- }
- if entity.Id <= 0 {
- return myerrors.TipsError("事件数据不存在")
- }
- // 已关闭的事件不能作废
- if entity.DeliveryEventStatus == eventmodel.DeliveryEventStatusClosed {
- return myerrors.TipsError("已关闭的事件不能作废")
- }
- // 已作废的事件不能再次作废
- if entity.DeliveryEventStatus == eventmodel.DeliveryEventStatusCancelled {
- return myerrors.TipsError("该事件已作废")
- }
- return s.EventDao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
- // 1. 更新事件状态为已作废
- data := g.Map{
- s.EventDao.Columns.DeliveryEventStatus: eventmodel.DeliveryEventStatusCancelled,
- s.EventDao.Columns.Remark: req.CancelReason,
- }
- service.SetUpdatedInfo(data, s.GetCxtUserId(), s.GetCxtUserName())
- _, err := s.EventDao.TX(tx).FieldsEx(service.UpdateFieldEx...).Data(data).WherePri(s.EventDao.Columns.Id, req.Id).Update()
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("作废事件失败")
- }
- // 2. 添加作废过程记录
- recordContent := "作废事件<br/>作废原因: " + req.CancelReason
- recordData := g.Map{
- s.RecordDao.Columns.DeliveryEventId: req.Id,
- s.RecordDao.Columns.HandleUserId: s.GetCxtUserId(),
- s.RecordDao.Columns.HandleUserName: s.GetCxtUserName(),
- s.RecordDao.Columns.HandleContent: recordContent,
- }
- service.SetCreatedInfo(recordData, s.GetCxtUserId(), s.GetCxtUserName())
- recordResult, err := s.RecordDao.TX(tx).Data(recordData).Insert()
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("新增过程记录失败")
- }
- recordId, _ := recordResult.LastInsertId()
- // 3. 保存附件
- if len(req.Attachments) > 0 {
- for _, att := range req.Attachments {
- attData := g.Map{
- s.AttachmentDao.Columns.DeliveryEventId: req.Id,
- s.AttachmentDao.Columns.EventId: req.Id,
- s.AttachmentDao.Columns.EventRecordId: recordId,
- s.AttachmentDao.Columns.FileName: att.FileName,
- s.AttachmentDao.Columns.FileUrl: att.FileUrl,
- s.AttachmentDao.Columns.FileType: att.FileType,
- }
- service.SetCreatedInfo(attData, s.GetCxtUserId(), s.GetCxtUserName())
- _, err = s.AttachmentDao.TX(tx).Data(attData).Insert()
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("保存附件信息失败")
- }
- }
- }
- _ = recordId // 避免未使用变量警告
- // 4. 查询关联的任务并作废
- var tasks []*eventmodel.OpsEventTask
- err = s.TaskDao.TX(tx).Where(s.TaskDao.Columns.EventId, req.Id).
- Where(s.TaskDao.Columns.EventType, eventmodel.EventTypeDelivery).
- Scan(&tasks)
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("查询关联任务失败")
- }
- // 5. 作废关联的任务
- for _, task := range tasks {
- // 已完成的任务不能作废
- if task.TaskStatus == "30" {
- continue
- }
- // 已作废的任务不需要再次作废
- if task.TaskStatus == "90" {
- continue
- }
- // 更新任务状态为作废
- taskData := g.Map{
- s.TaskDao.Columns.TaskStatus: "90",
- }
- service.SetUpdatedInfo(taskData, s.GetCxtUserId(), s.GetCxtUserName())
- _, err := s.TaskDao.TX(tx).FieldsEx(service.UpdateFieldEx...).Data(taskData).
- WherePri(s.TaskDao.Columns.Id, task.Id).Update()
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("作废关联任务失败")
- }
- // 添加任务作废过程记录
- taskRecordData := g.Map{
- s.TaskRecordDao.Columns.TaskId: task.Id,
- s.TaskRecordDao.Columns.HandleUserId: s.GetCxtUserId(),
- s.TaskRecordDao.Columns.HandleUserName: s.GetCxtUserName(),
- s.TaskRecordDao.Columns.HandleContent: "作废任务<br/>作废原因: 关联事件被作废 - " + req.CancelReason,
- }
- service.SetCreatedInfo(taskRecordData, s.GetCxtUserId(), s.GetCxtUserName())
- _, err = s.TaskRecordDao.TX(tx).Data(taskRecordData).Insert()
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("新增任务过程记录失败")
- }
- }
- return nil
- })
- }
- // GetById 根据ID查询单条(关联项目信息)
- func (s *DeliveryProjectEventService) GetById(id int) (*eventmodel.OpsDeliveryProjectEventRsp, error) {
- var entity eventmodel.OpsDeliveryProjectEvent
- err := s.EventDao.FieldsEx(s.EventDao.Columns.DeletedTime).WherePri(s.EventDao.Columns.Id, id).Scan(&entity)
- if err != nil {
- g.Log().Error(err)
- return nil, myerrors.DbError("查询事件数据失败")
- }
- if entity.Id <= 0 {
- return nil, myerrors.TipsError("事件数据不存在")
- }
- var rsp eventmodel.OpsDeliveryProjectEventRsp
- if err := gconv.Struct(entity, &rsp); err != nil {
- g.Log().Error(err)
- return nil, myerrors.DbError("数据转换失败")
- }
- // 查询是否已关联研发任务
- taskCount, err := s.TaskDao.Where(s.TaskDao.Columns.EventId, id).
- Where(s.TaskDao.Columns.EventType, eventmodel.EventTypeDelivery).
- Count()
- if err != nil {
- g.Log().Error(err)
- } else {
- rsp.HasRdTask = taskCount > 0
- }
- // 查询关联的项目信息
- if entity.ProjectId > 0 {
- var project eventmodel.OpsDeliveryProject
- err := s.ProjectDao.FieldsEx(s.ProjectDao.Columns.DeletedTime).
- WherePri(s.ProjectDao.Columns.Id, entity.ProjectId).
- Scan(&project)
- if err != nil {
- g.Log().Error(err)
- } else if project.Id > 0 {
- // 填充项目信息
- rsp.ProjectName = project.ProjectName
- rsp.ContractNo = project.ContractNo
- rsp.CustName = project.CustName
- rsp.ProductLine = project.ProductLine
- rsp.SalesUserName = project.SalesUserName
- // 填充关键节点时间
- if project.InternalKickoffTime != nil {
- rsp.InternalKickoffTime = project.InternalKickoffTime.Format("Y-m-d H:i:s")
- }
- if project.ExternalKickoffTime != nil {
- rsp.ExternalKickoffTime = project.ExternalKickoffTime.Format("Y-m-d H:i:s")
- }
- if project.DeliveryPlanSubmitTime != nil {
- rsp.DeliveryPlanSubmitTime = project.DeliveryPlanSubmitTime.Format("Y-m-d H:i:s")
- }
- if project.DeploymentTime != nil {
- rsp.DeploymentTime = project.DeploymentTime.Format("Y-m-d H:i:s")
- }
- if project.TrialRunTime != nil {
- rsp.TrialRunTime = project.TrialRunTime.Format("Y-m-d H:i:s")
- }
- if project.GoLiveTime != nil {
- rsp.GoLiveTime = project.GoLiveTime.Format("Y-m-d H:i:s")
- }
- }
- }
- return &rsp, nil
- }
- // GetAttachments 获取事件附件列表
- func (s *DeliveryProjectEventService) GetAttachments(eventId int) ([]*eventmodel.OpsDeliveryProjectEventAttachment, error) {
- var list []*eventmodel.OpsDeliveryProjectEventAttachment
- err := s.AttachmentDao.Where(s.AttachmentDao.Columns.DeliveryEventId, eventId).Scan(&list)
- if err != nil {
- g.Log().Error(err)
- return nil, myerrors.DbError("查询附件列表失败")
- }
- return list, nil
- }
- // GetRecords 获取事件过程记录列表(包含附件)
- func (s *DeliveryProjectEventService) GetRecords(req *eventmodel.OpsDeliveryProjectEventRecordSearchReq) ([]*eventmodel.OpsDeliveryProjectEventRecordWithAttachments, error) {
- var records []*eventmodel.OpsDeliveryProjectEventRecord
- err := s.RecordDao.Where(s.RecordDao.Columns.DeliveryEventId, req.DeliveryEventId).
- Order(s.RecordDao.Columns.CreatedTime + " desc").
- Scan(&records)
- if err != nil {
- g.Log().Error(err)
- return nil, myerrors.DbError("查询过程记录失败")
- }
- result := make([]*eventmodel.OpsDeliveryProjectEventRecordWithAttachments, 0, len(records))
- for _, record := range records {
- recordRsp := &eventmodel.OpsDeliveryProjectEventRecordWithAttachments{
- OpsDeliveryProjectEventRecord: *record,
- Attachments: []*eventmodel.OpsDeliveryProjectEventAttachment{},
- }
- // 查询该记录关联的附件
- if record.Id > 0 {
- var attachments []*eventmodel.OpsDeliveryProjectEventAttachment
- err := s.AttachmentDao.Where(s.AttachmentDao.Columns.EventRecordId, record.Id).
- Scan(&attachments)
- if err != nil {
- g.Log().Error(err)
- } else {
- recordRsp.Attachments = attachments
- }
- }
- result = append(result, recordRsp)
- }
- return result, nil
- }
- // AddRecord 添加事件过程记录(带附件)
- func (s *DeliveryProjectEventService) AddRecord(req *eventmodel.OpsDeliveryProjectEventRecordAddReqWithAttachments) error {
- return s.EventDao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
- // 1. 创建过程记录
- recordData := g.Map{
- s.RecordDao.Columns.DeliveryEventId: req.DeliveryEventId,
- s.RecordDao.Columns.HandleUserId: s.GetCxtUserId(),
- s.RecordDao.Columns.HandleUserName: s.GetCxtUserName(),
- s.RecordDao.Columns.HandleContent: req.HandleContent,
- }
- service.SetCreatedInfo(recordData, s.GetCxtUserId(), s.GetCxtUserName())
- result, err := s.RecordDao.TX(tx).Data(recordData).Insert()
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("新增过程记录失败")
- }
- // 获取记录ID
- recordId, err := result.LastInsertId()
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("获取记录ID失败")
- }
- // 2. 保存附件
- if len(req.Attachments) > 0 {
- for _, att := range req.Attachments {
- attData := g.Map{
- s.AttachmentDao.Columns.DeliveryEventId: req.DeliveryEventId,
- s.AttachmentDao.Columns.EventId: req.DeliveryEventId,
- s.AttachmentDao.Columns.EventRecordId: recordId,
- s.AttachmentDao.Columns.FileName: att.FileName,
- s.AttachmentDao.Columns.FileUrl: att.FileUrl,
- s.AttachmentDao.Columns.FileType: att.FileType,
- }
- service.SetCreatedInfo(attData, s.GetCxtUserId(), s.GetCxtUserName())
- _, err = s.AttachmentDao.TX(tx).Data(attData).Insert()
- if err != nil {
- g.Log().Error(err)
- return myerrors.DbError("保存附件信息失败")
- }
- }
- }
- return nil
- })
- }
- // generateTaskNo 生成任务编号(与OpsEventTaskService保持统一格式,使用数据库序列防并发冲突)
- func (s *DeliveryProjectEventService) generateTaskNo() string {
- now := gtime.Now()
- prefix := "TSK" + now.Format("Ymd")
- // 优先使用数据库序列(并发安全),序列异常时降级为查最大+1
- seqVal, err := s.TaskDao.DB.GetValue("SELECT next_day_reset_val('task_no_seq')")
- if err == nil {
- return prefix + fmt.Sprintf("%04d", seqVal.Int())
- }
- // 降级方案:查询当天最大序号+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"
- }
- // getProjectName 根据项目ID获取项目名称
- func (s *DeliveryProjectEventService) getProjectName(projectId int) string {
- if projectId <= 0 {
- return ""
- }
- var project eventmodel.OpsDeliveryProject
- err := s.ProjectDao.FieldsEx(s.ProjectDao.Columns.DeletedTime).
- WherePri(s.ProjectDao.Columns.Id, projectId).
- Scan(&project)
- if err != nil {
- g.Log().Error(err)
- return ""
- }
- return project.ProjectName
- }
- // generateEventNo 生成事件编码(使用数据库序列防并发冲突,与generateTaskNo保持一致)
- func (s *DeliveryProjectEventService) generateEventNo() string {
- now := gtime.Now()
- prefix := "EVT" + now.Format("Ymd")
- seqVal, err := s.EventDao.DB.GetValue("SELECT next_day_reset_val('event_no_seq')")
- if err == nil {
- return prefix + fmt.Sprintf("%04d", seqVal.Int())
- }
- var maxNoResult struct {
- DeliveryEventNo string
- }
- err = s.EventDao.Where(s.EventDao.Columns.DeliveryEventNo+" like ?", prefix+"%").
- Order(s.EventDao.Columns.DeliveryEventNo + " desc").
- Fields(s.EventDao.Columns.DeliveryEventNo).
- Scan(&maxNoResult)
- if err != nil || maxNoResult.DeliveryEventNo == "" {
- return prefix + "0001"
- }
- maxNoStr := maxNoResult.DeliveryEventNo
- if len(maxNoStr) >= len(prefix)+4 {
- seq := maxNoStr[len(prefix):]
- seqNum := gconv.Int(seq)
- seqNum++
- return prefix + fmt.Sprintf("%04d", seqNum)
- }
- return prefix + "0001"
- }
|