123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531 |
- package utils
- import (
- "context"
- "errors"
- "github.com/gogf/gf/database/gdb"
- "github.com/gogf/gf/frame/g"
- "github.com/gogf/gf/os/gtime"
- "github.com/gogf/gf/util/gmeta"
- "youngmini_server/app/dao"
- "youngmini_server/app/model"
- )
- var OrderProcedureManager = orderProcedureManager{}
- type orderProcedureManager struct {
- }
- // MethodType 拍单方式
- type MethodType int
- const (
- MethodTypeNo MethodType = iota
- MethodTypeYes
- MethodTypeOther
- )
- type buySamplesType int
- const (
- buySamplesTypeNo buySamplesType = iota + 1
- )
- type procedureStage int
- // 订单执行阶段
- const (
- procedureStageWaitForSelect procedureStage = iota + 1 // 等待反选阶段
- procedureStageBuySamples // 拍单阶段
- procedureStageDraft // 初稿阶段
- procedureStageArticle // 作品阶段
- procedureStageLinkTest // 链接质检阶段
- procedureStageDataTest // 数据质检阶段
- procedureStageReturnSamples // 拍单样品寄回阶段
- procedureStageComplete // 订单完成阶段
- )
- type procedureState int
- const (
- procedureStateNotStart procedureState = iota + 1
- orderStepStateGoingOn
- orderStepStateFail
- orderStepStateSuc
- )
- // DeductReasonOrderOvertime 扣费原因超期扣费
- const DeductReasonOrderOvertime = 1
- // 下面的值会出现在"suc_next_step"或"fail_next_step"字段,表示当前步骤成功或失败后进入的下个步骤
- // 254 表示任务终止
- const orderFinishCode uint = 254
- // 255 表示进入订单的下一个stage
- const nextStageCode = 255
- // 0 表示保持在本步骤不动
- const selfStepCode = 0
- type OrderLimitAndReduceFeeInfo struct {
- gmeta.Meta `orm:"table:task_base_info"`
- TaskId int `json:"task_id"`
- DraftInfo *model.TaskProcedureDraftInfo `orm:"with:task_base_id=task_id"`
- ArticleInfo *model.TaskProcedureArticleInfo `orm:"with:task_base_id=task_id"`
- DataAndQualityInfo *model.TaskProcedureQualityDataTestInfo `orm:"with:task_base_id=task_id"`
- }
- func (*orderProcedureManager) setLimitDaysInfoForProcedure(node *model.WorkflowNodeContainer, limitInfo *OrderLimitAndReduceFeeInfo, stage int) {
- if node.ProcedureStage == int(procedureStageDraft) && node.StepInStage == 1 && limitInfo.DraftInfo != nil {
- // 上传初稿天数限制
- node.LimitDays1 = limitInfo.DraftInfo.UploadLimitDay
- // 上传初稿超期扣费率
- node.ReduceFeeRatio1 = limitInfo.DraftInfo.UploadReduceRate
- // 上传初稿天数限制
- node.LimitDays2 = limitInfo.DraftInfo.ReviseLimitDay
- // 上传初稿超期扣费率
- node.ReduceFeeRatio2 = limitInfo.DraftInfo.ReviseReduceRate
- }
- if node.ProcedureStage == int(procedureStageArticle) && node.StepInStage == 1 && limitInfo.ArticleInfo != nil {
- // 上传作品天数限制
- node.LimitDays1 = limitInfo.ArticleInfo.UploadLimitDay
- // 上传作品超期扣费率
- node.ReduceFeeRatio1 = limitInfo.ArticleInfo.UploadReduceRate
- // 上传作品天数限制
- node.LimitDays2 = limitInfo.ArticleInfo.ReviseLimitDay
- // 上传作品超期扣费率
- node.ReduceFeeRatio2 = limitInfo.ArticleInfo.ReviseReduceRate
- }
- if node.ProcedureStage == int(procedureStageLinkTest) && node.StepInStage == 1 && limitInfo.DataAndQualityInfo != nil {
- // 上传发布链接天数限制
- node.LimitDays1 = limitInfo.DataAndQualityInfo.QualityTestLimitDay
- // 上传发布链接超期扣费率
- node.ReduceFeeRatio1 = limitInfo.DataAndQualityInfo.QualityTestReduceRate
- // 上传发布链接天数限制
- node.LimitDays2 = limitInfo.DataAndQualityInfo.QualityReviseLimitDay
- // 上传发布链接超期扣费率
- node.ReduceFeeRatio2 = limitInfo.DataAndQualityInfo.QualityReviseReduseRate
- }
- if node.ProcedureStage == int(procedureStageDataTest) && node.StepInStage == 1 && limitInfo.DataAndQualityInfo != nil {
- // 上传质检链接天数限制
- node.LimitDays1 = limitInfo.DataAndQualityInfo.DataTestLimitDay
- // 上传质检链接超期扣费率
- node.ReduceFeeRatio1 = limitInfo.DataAndQualityInfo.DataTestReduceRate
- // 上传质检链接天数限制
- node.LimitDays2 = limitInfo.DataAndQualityInfo.DataReviceLimitDay
- // 上传质检链接超期扣费率
- node.ReduceFeeRatio2 = limitInfo.DataAndQualityInfo.DataReviceReduceRate
- }
- }
- //func (*orderProcedureManager)calPrevSortId(curSortId int, )
- func (t *orderProcedureManager)fillOrderWorkflowToDatabase(taskId, orderId int, stageList []int, ctx context.Context, tx *gdb.TX) error {
- var orderLimitInfo *OrderLimitAndReduceFeeInfo
- err := g.DB().Model(dao.TaskBaseInfo.Table).WithAll().Where(dao.TaskBaseInfo.Columns.TaskId, taskId).Scan(&orderLimitInfo)
- if err != nil {}
- res, err := g.DB().Model(dao.WorkflowNodeTemplate.Table).
- OrderAsc("procedure_stage, step_in_stage").
- All("procedure_stage in (?)", stageList)
- if err != nil {
- return err
- }
- workflowNodeList := make([]model.WorkflowNodeContainer, 0)
- var sucSortId, failSortId, nextStep uint
- //建立每一个流程的每一步到sortId的索引
- sortIdMap := make(map[uint]map[uint]uint, 0)
- for i := 0; i < res.Len(); i++ {
- curStage := res[i]["procedure_stage"].Uint()
- curStepInStage := res[i]["step_in_stage"].Uint()
- if _, ok := sortIdMap[curStage]; !ok {
- sortIdMap[curStage] = make(map[uint]uint)
- }
- sortIdMap[curStage][curStepInStage] = uint(i + 1)
- }
- for i := 0; i < res.Len(); i++ {
- curStage := res[i]["procedure_stage"].Uint()
- // 将成功后转到的本stage内的step值替换为完成后的sortId
- nextStep = res[i]["suc_next_step"].Uint()
- if nextStep == nextStageCode {
- // 如果是255则下一步为下一个节点
- sucSortId = uint(i + 2)
- } else if nextStep == orderFinishCode {
- // 如果下一步是254,则订单直接终止
- sucSortId = orderFinishCode
- } else if nextStep == selfStepCode {
- // 如果为0则是本节点
- sucSortId = uint(i + 1)
- } else {
- // 否则查找
- v, ok := sortIdMap[curStage][nextStep]
- if !ok {
- panic("not found sort id of success next step")
- }
- sucSortId = v
- }
- // 将失败后转到的本stage内的step值替换为完成后的sortId
- nextStep = res[i]["fail_next_step"].Uint()
- if nextStep == nextStageCode {
- // 如果是255则下一步为下一个节点
- failSortId = uint(i + 2)
- } else if nextStep == orderFinishCode {
- // 如果下一步是254,则订单直接终止
- failSortId = orderFinishCode
- } else if nextStep == selfStepCode {
- // 如果为0则是本节点
- failSortId = uint(i + 1)
- } else {
- // 否则根据已建立的
- v, ok := sortIdMap[curStage][nextStep]
- if !ok {
- panic("not found sort id of failed next step")
- }
- failSortId = v
- }
- node := model.WorkflowNodeContainer{
- NodeNameFirst: res[i]["node_name_first"].String(),
- NodeNameAfterSecond: res[i]["node_name_after_second"].String(),
- ProcedureStage: res[i]["procedure_stage"].Int(),
- CurExecutionTimes: res[i]["cur_execution_times"].Int(),
- MaxExecutionTimes: res[i]["max_execution_times"].Int(),
- StepInStage: res[i]["step_in_stage"].Uint(),
- Tip: res[i]["tip"].String(),
- SucNextStep: sucSortId,
- FailNextStep: failSortId,
- State: res[i]["state"].Int(),
- StartDate: gtime.Now(),
- CompleteDate: nil,
- CompleteUserName: "",
- LimitDays1: 0,
- ReduceFeeRatio1: 0,
- LimitDays2: 0,
- ReduceFeeRatio2: 0,
- OperateRoleRestrict: res[i]["operate_role_restrict"].Int(),
- SortId: i + 1,
- OrderId: orderId,
- }
- // 上传初稿 上传作品 上传链接 上传数据阶段设置限制天数和扣费信息
- t.setLimitDaysInfoForProcedure(&node, orderLimitInfo, node.ProcedureStage)
- workflowNodeList = append(workflowNodeList, node)
- }
- _, err = tx.Ctx(ctx).Model(dao.WorkflowNodeContainer.Table).Insert(workflowNodeList)
- if err != nil {
- return err
- }
- return nil
- }
- // GenOrderWorkflowList 生成订单的流程
- func (t *orderProcedureManager) GenOrderWorkflowList(taskId, orderId int, ctx context.Context, tx *gdb.TX) error {
- r, err := g.DB().Model(model.TaskProcedureDecisionCondition{}).One(dao.TaskProcedureDecisionCondition.Columns.TaskBaseId, taskId)
- if err != nil {
- return err
- }
- var procedureIdList []int
- // 添加等待反选步骤,必须添加
- procedureIdList = append(procedureIdList, 1)
- // 如果拍单类型不为“不拍单”,则将拍单对应的步骤加入到流程中
- if r[dao.TaskProcedureDecisionCondition.Columns.BuySamplesType].Int() != int(buySamplesTypeNo) {
- procedureIdList = append(procedureIdList, r[dao.TaskProcedureDecisionCondition.Columns.BuySamplesStage].Int())
- }
- // 如果需要检查初稿则把检查初稿相关步骤加入任务流程
- if r[dao.TaskProcedureDecisionCondition.Columns.ExamineDraft].Int() != int(MethodTypeNo) {
- procedureIdList = append(procedureIdList, r[dao.TaskProcedureDecisionCondition.Columns.ExamineDraftStage].Int())
- }
- // 根据是否审核作品添加相应的步骤
- if r.Map()[dao.TaskProcedureDecisionCondition.Columns.ReviewArticle] != 0 {
- procedureIdList = append(procedureIdList, r[dao.TaskProcedureDecisionCondition.Columns.ReviewArticleStage].Int())
- }
- // 添加质检链接步骤id
- procedureIdList = append(procedureIdList, r[dao.TaskProcedureDecisionCondition.Columns.QuilityTestStage].Int())
- // 添加数据质检步骤id
- procedureIdList = append(procedureIdList, r[dao.TaskProcedureDecisionCondition.Columns.DataTestStage].Int())
- // 根据是否寄回样品添加相应步骤
- //if r[dao.TaskProcedureDecisionCondition.Columns.ReturnSamples].Int() != 0 {
- // procedureIdList = append(procedureIdList, r[dao.TaskProcedureDecisionCondition.Columns.ReturnSamplesStage].Int())
- //}
- procedureIdList = append(procedureIdList, r[dao.TaskProcedureDecisionCondition.Columns.TaskFisnishStage].Int())
- return t.fillOrderWorkflowToDatabase(taskId, orderId, procedureIdList, ctx, tx)
- }
- // GetOrderWorkFlow 获取订单的所有流程节点
- func (*orderProcedureManager)GetOrderWorkFlow(orderId int) (gdb.Result, error) {
- res, err := g.DB().Model(dao.WorkflowNodeContainer.Table).
- OrderAsc(dao.WorkflowNodeContainer.Columns.SortId).
- All(dao.WorkflowNodeContainer.Columns.OrderId, orderId)
- return res, err
- }
- // handleProcedureOvertimeReduceFee 处理扣费 返回扣费金额和错误
- func (*orderProcedureManager)handleProcedureOvertimeReduceFee(procedureNode *model.WorkflowNodeContainer,
- orderInfo *model.OrderInfo, ctx context.Context, tx *gdb.TX) (int64, error) {
- // 天数显示为0表示不限制天数
- if procedureNode.LimitDays1 <= 0 && procedureNode.LimitDays2 <= 0 {
- return 0, nil
- }
- // 订单处于上传初稿 上传作品 上传链接 上传数据阶段时会产生超期扣费
- if procedureNode.ProcedureStage == int(procedureStageDraft) || procedureNode.ProcedureStage == int(procedureStageArticle) ||
- procedureNode.ProcedureStage == int(procedureStageLinkTest) || procedureNode.ProcedureStage == int(procedureStageDataTest) &&
- procedureNode.StepInStage == 1 {
- days := int(procedureNode.CompleteDate.Sub(procedureNode.StartDate).Seconds() / 86400)
- overTime := false
- reduceRatio := 0
- if procedureNode.CurExecutionTimes == 1 {
- if days > procedureNode.LimitDays1 {
- overTime = true
- reduceRatio = procedureNode.ReduceFeeRatio1
- }
- } else {
- if days > procedureNode.LimitDays2 {
- overTime = true
- reduceRatio = procedureNode.ReduceFeeRatio2
- }
- }
- if overTime && reduceRatio > 0{
- rec, err := tx.Ctx(ctx).Model(dao.TaskRecruitTalentLevel.Table).One(dao.TaskRecruitTalentLevel.Columns.TrtId, orderInfo.TaskLevelId)
- if err != nil {
- return 0, err
- }
- // 扣费金额
- deductBoBoCoinValue := int64(rec[dao.TaskRecruitTalentLevel.Columns.RewardRoyalties].Float64() * float64(reduceRatio) * 0.01)
- // 步骤名
- var stageName string
- if procedureNode.CurExecutionTimes <= 2 {
- stageName = procedureNode.NodeNameFirst
- } else {
- stageName = procedureNode.NodeNameAfterSecond
- }
- _, err = tx.Ctx(ctx).Model(dao.BobocoinDeductRecord.Table).Insert(model.BobocoinDeductRecord{
- TalentId: orderInfo.TalentId,
- BobocoinValue: deductBoBoCoinValue,
- OrderId: orderInfo.OrderId,
- ProcedureStage: procedureNode.ProcedureStage,
- Reason: DeductReasonOrderOvertime,
- CreatedAt: gtime.Now(),
- TaskName: orderInfo.TaskName,
- OperationStage: stageName,
- })
- if err != nil {
- return 0, err
- }
- return deductBoBoCoinValue, nil
- }
- }
- return 0, nil
- }
- // OnOperateOrder 根据当前流程节点的操作成功失败状态决定订单下一步流程节点
- func (t *orderProcedureManager)OnOperateOrder(orderId int, isSuccess bool, operatorId int, operatorName string, failedReason ...string) error {
- err := g.DB().Transaction(context.Background(), func(ctx context.Context, tx *gdb.TX) error {
- // 获取订单当前状态
- var orderInfo *model.OrderInfo
- err1 := tx.Ctx(ctx).Model(dao.OrderInfo.Table).Where(dao.OrderInfo.Columns.OrderId, orderId).Scan(&orderInfo)
- if err1 != nil {
- return err1
- }
- // 任务结束状态为已结束则禁止操作
- if orderInfo.CompleteStatus > 1 {
- return errors.New("order have completed")
- }
- var curWorkflowNode *model.WorkflowNodeContainer
- // 获取订单当前流程节点
- err1 = tx.Ctx(ctx).Model(dao.WorkflowNodeContainer.Table).
- Where("order_id = ? and sort_id = ?", orderInfo.OrderId, orderInfo.OrderStatus).Scan(&curWorkflowNode)
- if err1 != nil {
- return err1
- }
- // 根据成功状态分别进入下一个流程节点
- var nextStep uint
- if isSuccess {
- nextStep = curWorkflowNode.SucNextStep
- } else {
- nextStep = curWorkflowNode.FailNextStep
- }
- if nextStep == orderFinishCode {
- // 获取此步骤对应的结束类型
- rec, err2 := tx.Ctx(ctx).Model(dao.ROrderCompeleteStageToType.Table).
- One("order_procedure_stage = ? and order_procedure_step = ?",
- curWorkflowNode.ProcedureStage, curWorkflowNode.StepInStage)
- if err2 != nil {
- return errors.New("query order complete type failed")
- }
- // 更新当前流程节点的状态
- _, err2 = tx.Ctx(ctx).Model(dao.WorkflowNodeContainer.Table).Update(g.Map{
- dao.WorkflowNodeContainer.Columns.CompleteDate: gtime.Now(),
- dao.WorkflowNodeContainer.Columns.CompleteUserName: operatorName,
- dao.WorkflowNodeContainer.Columns.State: orderStepStateSuc,
- }, dao.WorkflowNodeContainer.Columns.ContainerId, curWorkflowNode.ContainerId)
- if err2 != nil {
- return err2
- }
- // 记录结束类型到订单表
- completeType := rec[dao.ROrderCompeleteStageToType.Columns.CompleteType].Int()
- // 下一步为任务完结则设置任务结束状态为已结束,并设置结束类型
- _, err2 = tx.Ctx(ctx).Model(dao.OrderInfo.Table).Update(g.Map{
- dao.OrderInfo.Columns.CompleteStatus: completeType,
- dao.OrderInfo.Columns.CompleteDate: gtime.Now(),
- }, dao.OrderInfo.Columns.OrderId, orderId)
- if err2 != nil {
- return err1
- }
- // 如果任务正常结束,则将任务稿费写入卜卜币收入表
- if completeType == int(OrderCompleteTypeNormal) {
- _, err2 = tx.Ctx(ctx).Model(dao.BobocoinIncomeRecord.Table).Insert(model.BobocoinIncomeRecord{
- BobocoinValue: orderInfo.SettleAmount,
- OrderId: orderId,
- RecruitLevelId: orderInfo.TaskLevelId,
- TalentId: orderInfo.TalentId,
- CompleteDate: gtime.Now(),
- TaskName: orderInfo.TaskName,
- })
- if err2 != nil {
- return err1
- }
- }
- return nil
- }
- // 获取订单下一个流程节点
- var nextWorkflowNode *model.WorkflowNodeContainer
- err1 = tx.Ctx(ctx).Model(dao.WorkflowNodeContainer.Table).Where("order_id = ? and sort_id = ?", orderId, nextStep).Scan(&nextWorkflowNode)
- if err1 != nil {
- return err1
- }
- var failResStr string
- if isSuccess {
- failResStr = "success"
- } else {
- if failedReason == nil {
- failResStr = "failed"
- }
- failResStr = failedReason[0]
- }
- if isSuccess {
- // 如果是执行成功,则更新当前节点为完成状态
- _, err2 := tx.Ctx(ctx).Model(dao.WorkflowNodeContainer.Table).Update(g.Map{
- dao.WorkflowNodeContainer.Columns.CompleteDate: gtime.Now(),
- dao.WorkflowNodeContainer.Columns.CompleteUserName: operatorName,
- dao.WorkflowNodeContainer.Columns.State: orderStepStateSuc,
- dao.WorkflowNodeContainer.Columns.FailReason: failResStr,
- }, dao.WorkflowNodeContainer.Columns.ContainerId, curWorkflowNode.ContainerId)
- if err2 != nil {
- return err2
- }
- } else {
- // 执行失败,当前节点状态更新为失败
- _, err2 := tx.Ctx(ctx).Model(dao.WorkflowNodeContainer.Table).Update(g.Map{
- dao.WorkflowNodeContainer.Columns.CompleteDate: nil,
- dao.WorkflowNodeContainer.Columns.CompleteUserName: nil,
- dao.WorkflowNodeContainer.Columns.State: orderStepStateFail,
- dao.WorkflowNodeContainer.Columns.FailReason: failResStr,
- }, dao.WorkflowNodeContainer.Columns.ContainerId, curWorkflowNode.ContainerId)
- if err2 != nil {
- return err2
- }
- }
- // 执行扣费
- deductValue, err1 := t.handleProcedureOvertimeReduceFee(curWorkflowNode, orderInfo, ctx, tx)
- if err1 != nil {
- return err1
- }
- orderSettleAmount := orderInfo.SettleAmount
- if deductValue > 0 {
- orderSettleAmount -= deductValue
- }
- if isSuccess {
- // 如果是成功状态则更新下个节点状态,增加下个节点执行次数
- _, err1 = tx.Ctx(ctx).Model(dao.WorkflowNodeContainer.Table).Update(g.Map{
- dao.WorkflowNodeContainer.Columns.StartDate: gtime.Now(),
- dao.WorkflowNodeContainer.Columns.CompleteDate: nil,
- dao.WorkflowNodeContainer.Columns.CompleteUserName: nil,
- dao.WorkflowNodeContainer.Columns.State: orderStepStateGoingOn,
- dao.WorkflowNodeContainer.Columns.CurExecutionTimes: nextWorkflowNode.CurExecutionTimes + 1,
- }, dao.WorkflowNodeContainer.Columns.ContainerId, nextWorkflowNode.ContainerId)
- if err1 != nil {
- return err1
- }
- } else {
- // 如果是失败状态则更新下个节点状态,增加下个节点执行次数并将失败原因添加到下个节点
- _, err1 = tx.Ctx(ctx).Model(dao.WorkflowNodeContainer.Table).Update(g.Map{
- dao.WorkflowNodeContainer.Columns.StartDate: gtime.Now(),
- dao.WorkflowNodeContainer.Columns.CompleteDate: nil,
- dao.WorkflowNodeContainer.Columns.CompleteUserName: nil,
- dao.WorkflowNodeContainer.Columns.State: orderStepStateGoingOn,
- dao.WorkflowNodeContainer.Columns.FailReason: failResStr,
- dao.WorkflowNodeContainer.Columns.CurExecutionTimes: nextWorkflowNode.CurExecutionTimes + 1,
- }, dao.WorkflowNodeContainer.Columns.ContainerId, nextWorkflowNode.ContainerId)
- if err1 != nil {
- return err1
- }
- }
- // 将下一个流程节点sortId记录在订单状态中
- _, err1 = tx.Ctx(ctx).Model(dao.OrderInfo.Table).Update(g.Map{
- dao.OrderInfo.Columns.SettleAmount: orderSettleAmount,
- dao.OrderInfo.Columns.OrderStatus: nextStep,
- }, dao.OrderInfo.Columns.OrderId, orderId)
- if err1 != nil {
- return err1
- }
- // 将操作记录插入记录表
- _, err1 = tx.Ctx(ctx).Model(dao.OrderStatusRecord.Table).Insert(model.OrderStatusRecord{
- OrderId: orderId,
- AlterBefore: curWorkflowNode.SortId,
- AlterAfter: nextWorkflowNode.SortId,
- RecordId: operatorId,
- RecordName: operatorName,
- CreatedAt: gtime.Now(),
- })
- return err1
- })
- return err
- }
|