package cms import ( "encoding/json" "fmt" "math" "strings" "sync" "time" "git.rosy.net.cn/jx-callback/business/jxstore/event" "git.rosy.net.cn/jx-callback/business/jxstore/partner" "git.rosy.net.cn/baseapi/platformapi/txcloudapi" "git.rosy.net.cn/jx-callback/business/jxutils/ddmsg" "git.rosy.net.cn/baseapi/platformapi/dingdingapi" "git.rosy.net.cn/baseapi/platformapi/jdeclpapi" "git.rosy.net.cn/jx-callback/globals" "git.rosy.net.cn/jx-callback/business/auth2" "git.rosy.net.cn/jx-callback/business/jxstore/financial" "git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/globals/api" "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" "git.rosy.net.cn/jx-callback/business/jxutils/tasksch" "git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model/dao" ) const ( AcceptMaxCount = 2 CancelMaxCount = 5 waybillKgPrice = 200 mtwmMemberPrice = 1100 ) type JobTimer struct { JobTimerMap map[int64]*time.Timer JobAuditTimerMap map[int64]*time.Timer s *sync.RWMutex } var ( JobTimers = &JobTimer{} ) func init() { JobTimerMap := make(map[int64]*time.Timer) JobAuditTimerMap := make(map[int64]*time.Timer) JobTimers.JobTimerMap = JobTimerMap JobTimers.JobAuditTimerMap = JobAuditTimerMap JobTimers.s = new(sync.RWMutex) } func PublishJob(ctx *jxcontext.Context, jobExt *model.JobExt) (errCode string, err error) { var ( db = dao.GetDB() job = &model.Job{} finishedAt time.Time DayTimeBegin, DayTimeEnd = jxutils.GetDayTime() ) if data, err := json.Marshal(jobExt); err == nil { json.Unmarshal(data, &job) } finishedAt = utils.Str2Time(jobExt.FinishedAtStr) job.FinishedAt = &finishedAt // 需根据任务类型做一些参数判断,比如门店商品链接,地址 if job.AvgPrice == 0 { return errCode, fmt.Errorf("请输入单个任务奖励(保证)金额!") } switch job.JobCategoryID { case model.JobCategoryIDwmtg: if job.Address == "" { return errCode, fmt.Errorf("外卖推广任务请输入门店地址!") } case model.JobCategoryIDOther: case model.JobCategoryIDDropShipping: if job.DropShippingAt == 0 { return errCode, fmt.Errorf("一件代发任务请输入承诺x天内发货!") } if job.DropShippingSkuPrice == 0 { return errCode, fmt.Errorf("一件代发任务请输入商品价格!") } job.CashbackType = model.JobCashbackPrice job.LimitCountType = model.JobLimitCountTypeNoLimit job.JobLimitAt = 0 job.AuditLimitAt = 0 default: return errCode, fmt.Errorf("暂不支持的任务类型! %v", job.JobCategoryID) } // 需根据返现类型做一些参数判断 switch job.CashbackType { case model.JobCashbackPercentage: if job.Percentage <= 0 || job.Percentage > 100 { return errCode, fmt.Errorf("返现比例请输入1-100之间的比例!") } case model.JobCashbackPrice: default: return errCode, fmt.Errorf("暂不支持的返现类型! %v", job.CashbackType) } if job.UserID == "" { return errCode, fmt.Errorf("参数有误!") } if ctx.GetUserID() != job.UserID { return errCode, fmt.Errorf("用户信息已过期,请重新登录!") } if job.FinishedAt.Sub(time.Now()) <= 0 { return errCode, fmt.Errorf("任务截止日期必须大于今天!") } if job2, _ := dao.GetJobWithTitle(db, job.Title); job2 != nil { return errCode, fmt.Errorf("任务标题重复,请重新输入!") } if job.JobCityCode != model.JobCountrywideCode { _, _, job.JobCityCode, err = getAddressInfoFromCoord(db, job.JobLng, job.JobLat) } //验证微信绑定 if err = auth2.CheckWeixinminiAuthBind(ctx.GetUserID()); err != nil { return "", err } //发布任务要扣除任务总额的保证金,不够扣就要进行充值 userBill, err := dao.GetUserBill(db, job.UserID, "") if userBill == nil { return errCode, fmt.Errorf("未查询到该用户的账单!") } job.TotalPrice = job.Count * job.AvgPrice if userBill.AccountBalance < job.TotalPrice { return model.ErrCodeAccountBalanceNotEnough, fmt.Errorf("用户余额不足!") } if job.Count <= 0 { return errCode, fmt.Errorf("任务数量不能为0!") } job.SurplusCount = job.Count if job.UserID == "" { return errCode, fmt.Errorf("任务发起人不能为空!") } jobs, err := dao.GetJobsNoPage(db, []string{job.UserID}, nil, nil, nil, DayTimeBegin, DayTimeEnd, 0, false) if len(jobs) > 0 { members, err := dao.GetUserMember(db, job.UserID, model.MemberTypeNormal) if err != nil { return errCode, err } if len(members) <= 0 { return errCode, fmt.Errorf("非会员一天只能发布一起任务,请确认!") } } if job.Address != "" && (job.Lng == 0 || job.Lat == 0) { lng, lat, _ := api.AutonaviAPI.GetCoordinateFromAddressByPage(job.Address, job.JobCityCode) if lng == 0 || lat == 0 { return errCode, fmt.Errorf("请填写完整且正确的门店地址!") } job.Lng = jxutils.StandardCoordinate2Int(lng) job.Lat = jxutils.StandardCoordinate2Int(lat) } dao.WrapAddIDCULDEntity(job, ctx.GetUserName()) dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() if err = dao.CreateEntity(db, job); err != nil { dao.Rollback(db) return } for _, v := range jobExt.JobSteps { dao.WrapAddIDCULDEntity(v, ctx.GetUserName()) v.JobID = job.ID err = dao.CreateEntity(db, v) } for _, v := range jobExt.JobImgs { dao.WrapAddIDCULEntity(v, ctx.GetUserName()) v.JobID = job.ID err = dao.CreateEntity(db, v) } if err != nil { dao.Rollback(db) return } //发布任务要扣除任务总额的保证金,不够扣就要进行充值 if err == nil && job.Status != model.JobStatusFailed { if err = financial.AddExpendUpdateAccount(db, userBill, model.BillTypeDeposit, job.TotalPrice, job.ID); err != nil { dao.Rollback(db) return } } dao.Commit(db) content := new(strings.Builder) content.WriteString("您的任务:") content.WriteString(job.Title) content.WriteString("。已发布成功,已扣除您的余额:") content.WriteString(utils.Float64ToStr(jxutils.IntPrice2Standard(int64(job.TotalPrice)))) content.WriteString("元") event.SendSysMessageSimple(content.String(), job.UserID) return errCode, err } func CancelPublishJob(ctx *jxcontext.Context, jobID int) (err error) { var ( db = dao.GetDB() ) job := &model.Job{} job.ID = jobID err = dao.GetEntity(db, job) if job.UserID == "" || job.Status == model.JobStatusFailed || job.Status == model.JobStatusOverdue || job.FinishedAt.Sub(time.Now()) <= 0 || job.SurplusCount <= 0 || job.LimitCountType <= 0 { return fmt.Errorf("未找到该任务或该任务状态不正常,无法取消!") } //取消已发布的任务 userBill, err := dao.GetUserBill(db, job.UserID, "") if userBill == nil { return fmt.Errorf("未查询到该用户的账单!") } dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() //如果是固定返现,退给任务发起人剩余数量*单价 //如果是比例返现,推给任务发起人任务总价-接取这个任务的用户实际返现的价格之和 var price int if job.CashbackType == model.JobCashbackPrice { price = job.SurplusCount * job.AvgPrice } else { if billIncomes, err := dao.GetBillIncome(db, jobID); err == nil { for _, v := range billIncomes { price += v.IncomePrice } } price = job.TotalPrice - price } if err = financial.AddIncomeUpdateAccount(db, userBill, model.BillTypeJobCancelOverdue, price, jobID); err != nil { dao.Rollback(db) return } //3、任务状态被取消 job.Status = model.JobStatusFailed // job.DeletedAt = time.Now() if _, err = dao.UpdateEntity(db, job, "Status"); err != nil { dao.Rollback(db) return } dao.Commit(db) content := new(strings.Builder) content.WriteString("您的任务:") content.WriteString(job.Title) content.WriteString("。已过期或被取消,") content.WriteString(utils.Float64ToStr(jxutils.IntPrice2Standard(int64(price)))) content.WriteString("元已返回您的余额中!") event.SendSysMessageSimple(content.String(), job.UserID) return err } func GetJobs(ctx *jxcontext.Context, userIDs []string, categoryIDs, statuss, vendorIDs, cityCodes []int, includeStep bool, fromTime, toTime string, lng, lat float64, span int, keyword string, sortType, pageSize, offset int) (pagedInfo *model.PagedInfo, err error) { var ( db = dao.GetDB() userID = ctx.GetUserID() _, _, cityCode, _ = getAddressInfoFromCoord(db, lng, lat) ) pagedInfo, err = dao.GetJobs(db, userIDs, categoryIDs, statuss, vendorIDs, []int{model.JobTypeNormal}, cityCodes, includeStep, utils.Str2Time(fromTime), utils.Str2Time(toTime), lng, lat, cityCode, span, keyword, sortType, pageSize, offset) //插入用户搜索关键词 if keyword != "" { if userSearchs, _ := dao.GetUserSearch(db, userID, keyword); len(userSearchs) > 0 { userSearchs[0].Count++ userSearchs[0].UpdatedAt = time.Now() dao.UpdateEntity(db, userSearchs[0], "Count", "UpdatedAt") } else { userSearch := &model.UserSearch{ UserID: userID, Keyword: keyword, } dao.WrapAddIDCULEntity(userSearch, ctx.GetUserName()) dao.CreateEntity(db, userSearch) } } return pagedInfo, err } func GetJobDetail(ctx *jxcontext.Context, jobID int, lng, lat float64) (job *dao.GetJobsResult, err error) { var ( db = dao.GetDB() ) job, err = dao.GetJobDetail(db, jobID) if job.Lng != 0 && job.Lat != 0 { job.Distance = jxutils.EarthDistance(lng, lat, jxutils.IntCoordinate2Standard(job.Lng), jxutils.IntCoordinate2Standard(job.Lat)) } utils.CallFuncAsync(func() { job2 := &model.Job{} job2.ID = job.ID dao.GetEntity(db, job2) job2.BrowseCount++ dao.UpdateEntity(db, job2, "BrowseCount") }) return job, err } func AcceptJob(ctx *jxcontext.Context, jobID, dropShippingDeliveryID, dropShippingCount int) (jobOrderID int64, errCode string, err error) { var ( db = dao.GetDB() userID = ctx.GetUserID() num int DayTimeBegin, DayTimeEnd = jxutils.GetDayTime() WeekTimeBegin, WeekTimeEnd = jxutils.GetWeekTime() ) job := &model.Job{} job.ID = jobID err = dao.GetEntity(db, job) if job.UserID == "" || job.Status == model.JobStatusFailed || job.Status == model.JobStatusOverdue || job.FinishedAt.Sub(time.Now()) <= 0 || job.SurplusCount <= 0 || job.LimitCountType <= 0 { return 0, errCode, fmt.Errorf("未找到该任务或该任务状态不正常,无法接单!") } // num, err = checkJobOrders(db, 0, "<= "+utils.Int2Str(model.JobOrderStatusAccept), userID, utils.ZeroTimeValue, utils.ZeroTimeValue) // if num >= AcceptMaxCount { // return 0, errCode, fmt.Errorf("每人最多接取" + utils.Int2Str(AcceptMaxCount) + "个任务,请核实!") // } //非快递任务 if jobID != 2 { num, err = checkJobOrders(db, jobID, "<= "+utils.Int2Str(model.JobOrderStatusWaitAudit), userID, utils.ZeroTimeValue, utils.ZeroTimeValue) if num > 0 { return 0, errCode, fmt.Errorf("您还有此任务未完成,请完成后再接取!") } num, err = checkJobOrders(db, jobID, "= "+utils.Int2Str(model.JobOrderStatusAuditUnPass), userID, utils.ZeroTimeValue, utils.ZeroTimeValue) if num > 0 { return 0, errCode, fmt.Errorf("您还有此任务未审核通过记录,可直接在未审核中重新提交!") } } switch job.LimitCountType { case model.JobLimitCountTypePO: num, err = checkJobOrders(db, jobID, "<> "+utils.Int2Str(model.JobOrderStatusCancel), userID, utils.ZeroTimeValue, utils.ZeroTimeValue) if num > 0 { return 0, errCode, fmt.Errorf("此任务只支持每人做一次!") } case model.JobLimitCountTypePDO: num, err = checkJobOrders(db, jobID, "<> "+utils.Int2Str(model.JobOrderStatusCancel), userID, DayTimeBegin, DayTimeEnd) if num > 0 { return 0, errCode, fmt.Errorf("此任务只支持每人每天做一次!") } case model.JobLimitCountTypePWO: num, err = checkJobOrders(db, jobID, "<> "+utils.Int2Str(model.JobOrderStatusCancel), userID, WeekTimeBegin, WeekTimeEnd) if num > 0 { return 0, errCode, fmt.Errorf("此任务只支持每人每周做一次!") } case model.JobLimitCountTypeNoLimit: default: return 0, errCode, fmt.Errorf("不支持的任务限次类型!%v", job.LimitCountType) } jobOrder := &model.JobOrder{ JobID: jobID, JobOrderID: jxutils.GenJobOrderNo(), UserID: ctx.GetUserID(), } jobOrder.Status = model.JobOrderStatusAccept //如果是一件代发任务,用户需要支付订单金额 var userBill *model.UserBill if job.JobCategoryID == model.JobCategoryIDDropShipping { if dropShippingCount == 0 || dropShippingDeliveryID == 0 { return 0, errCode, fmt.Errorf("一件代发订单请输入商品数量和收件人地址!") } //验证微信绑定 if err = auth2.CheckWeixinminiAuthBind(ctx.GetUserID()); err != nil { return 0, "", err } //发布任务要扣除任务总额的保证金,不够扣就要进行充值 userBill, err = dao.GetUserBill(db, ctx.GetUserID(), "") if userBill == nil { return 0, errCode, fmt.Errorf("未查询到该用户的账单!") } jobOrder.UserActualPrice = job.DropShippingSkuPrice * dropShippingCount if userBill.AccountBalance < jobOrder.UserActualPrice { return 0, model.ErrCodeAccountBalanceNotEnough, fmt.Errorf("用户余额不足!") } if deliveryList, _, err2 := dao.QueryUserDeliveryAddress(db, int64(dropShippingDeliveryID), nil, 0, 0, 0); err2 == nil { delivery := deliveryList[0] jobOrder.DropShippingCount = dropShippingCount jobOrder.DropShippingDeliveryID = dropShippingDeliveryID jobOrder.DropShippingName = delivery.ConsigneeName jobOrder.DropShippingMobile = delivery.ConsigneeMobile jobOrder.DropShippingAddress = delivery.Address jobOrder.DropShippingDetailAddress = delivery.DetailAddress jobOrder.DropShippingLng = delivery.Lng jobOrder.DropShippingLat = delivery.Lat jobOrder.DropShippingAutoAddress = delivery.AutoAddress jobOrder.DropShippingCityCode = delivery.CityCode jobOrder.DropShippingDistrictCode = delivery.DistrictCode } // jobOrder.Status = model.JobOrderStatusWaitAudit } else if job.JobCategoryID == model.JobCategoryIDUnion { jobOrder.Status = model.JobOrderStatusSpec } dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() dao.WrapAddIDCULEntity(jobOrder, ctx.GetUserName()) if err = dao.CreateEntity(db, jobOrder); err != nil { dao.Rollback(db) return } //用户接受任务,任务剩余次数-1 job.SurplusCount -= 1 if _, err = dao.UpdateEntity(db, job, "SurplusCount"); err != nil { dao.Rollback(db) return } dao.Commit(db) if job.JobCategoryID == model.JobCategoryIDDropShipping { dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() if err = financial.AddExpendUpdateAccount(db, userBill, model.BillTypeDropShipping, jobOrder.UserActualPrice, job.ID); err != nil { dao.Rollback(db) return } //一件代发 if err = SubmitJob(ctx, jobOrder); err != nil { dao.Rollback(db) return } dao.Commit(db) } else { //任务限时完成 timer := checkLimitJobOrders(db, job, jobOrder, model.JobTimerTypeAccept) JobTimers.s.Lock() JobTimers.JobTimerMap[jobOrder.JobOrderID] = timer JobTimers.s.Unlock() } return jobOrder.JobOrderID, errCode, err } func CancelAcceptJob(ctx *jxcontext.Context, jobID int, jobOrderID int64) (err error) { var ( db = dao.GetDB() ) jobOrder := &model.JobOrder{} jobOrder.JobOrderID = jobOrderID err = dao.GetEntity(db, jobOrder, "JobOrderID") if jobOrder.ID != 0 && jobOrder.Status == model.JobOrderStatusCancel { return fmt.Errorf("此任务已被取消了!") } job := &model.Job{} job.ID = jobID err = dao.GetEntity(db, job) //系统消息 content := new(strings.Builder) content.WriteString("您接取的任务:") content.WriteString(job.Title) if ctx.GetUserName() == "jxadmin" { content.WriteString(",因超时未完成已被系统自动取消。") } else { content.WriteString(",已被取消。") } dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() //如果当前任务状态正常,剩余数量就加1 if job.Status >= 0 { job.SurplusCount += 1 if _, err = dao.UpdateEntity(db, job, "SurplusCount"); err != nil { dao.Rollback(db) return } } else { userBill, err := dao.GetUserBill(db, job.UserID, "") if userBill == nil { return fmt.Errorf("未查询到该用户的账单!") } //如果状态不正常(取消或者过期)就要把这一笔退回去 //2、账户收入 //是固定返现才会退一笔任务单价 if job.CashbackType == model.JobCashbackPrice { if err = financial.AddIncomeUpdateAccount(db, userBill, model.BillTypeJobCancelOverdue, job.AvgPrice, jobID); err != nil { dao.Rollback(db) return err } } } //一件代发任务要退钱给用户 if job.JobCategoryID == model.JobCategoryIDDropShipping { userBill, err := dao.GetUserBill(db, jobOrder.UserID, "") if userBill == nil { return fmt.Errorf("未查询到该用户的账单!") } if err = financial.AddIncomeUpdateAccount(db, userBill, model.BillTypeJobCancelOverdue, jobOrder.UserActualPrice, jobID); err != nil { dao.Rollback(db) return err } } //3、任务订单状态被取消 jobOrder.Status = model.JobOrderStatusCancel if _, err = dao.UpdateEntity(db, jobOrder, "Status"); err != nil { dao.Rollback(db) return err } dao.Commit(db) event.SendSysMessageSimple(content.String(), jobOrder.UserID) return err } func checkJobOrders(db *dao.DaoDB, jobID int, statusCompareStr, userID string, fromTime, toTime time.Time) (num int, err error) { jobOrders, err := dao.GetJobOrdersNoPage(db, jobID, 0, userID, statusCompareStr, fromTime, toTime, nil) return len(jobOrders), err } func checkLimitJobOrders(db *dao.DaoDB, job *model.Job, jobOrder *model.JobOrder, jobTimerType int) (timer *time.Timer) { //插到定时任务表里,主要是重启项目后的重启定时器用 //main 里有重启的函数 jobTimer := &model.JobTimer{ JobID: job.ID, JobOrderID: jobOrder.JobOrderID, Type: jobTimerType, Status: model.JobTimerStatusWait, StartAt: jobOrder.CreatedAt, // LimitAt: job.JobLimitAt, } switch jobTimerType { case model.JobTimerTypeAccept: timer = time.NewTimer(time.Hour * time.Duration(job.JobLimitAt)) jobTimer.LimitAt = job.JobLimitAt case model.JobTimerTypeSubmit: timer = time.NewTimer(time.Hour * time.Duration(job.AuditLimitAt)) jobTimer.LimitAt = job.AuditLimitAt case model.JobTimerTypeDropShipping: timer = time.NewTimer(time.Hour * time.Duration(job.DropShippingAt)) jobTimer.LimitAt = job.DropShippingAt } dao.WrapAddIDCULEntity(jobTimer, jxcontext.AdminCtx.GetUserName()) dao.CreateEntity(db, jobTimer) utils.CallFuncAsync(func() { select { case <-timer.C: switch jobTimerType { case model.JobTimerTypeAccept: UpdateLimitJobOrders(db, timer, job.ID, jobOrder.JobOrderID, jobTimer) case model.JobTimerTypeSubmit: UpdateLimitAuditJobOrders(db, timer, job.ID, jobOrder.JobOrderID, jobTimer) case model.JobTimerTypeDropShipping: UpdateDropShippingJobOrders(db, timer, job.ID, jobOrder.JobOrderID, jobTimer) } } }) return timer } func UpdateLimitJobOrders(db *dao.DaoDB, timer *time.Timer, jobID int, jobOrderID int64, jobTimer *model.JobTimer) { globals.SugarLogger.Debugf("updateLimitJobOrders jobID: %v, jobOrderID: %v", jobID, jobOrderID) defer timer.Stop() jobOrder := &model.JobOrder{JobOrderID: jobOrderID} if err := dao.GetEntity(db, jobOrder, "JobOrderID"); err == nil { if jobOrder.Status > model.JobOrderStatusAccept { return } if err := CancelAcceptJob(jxcontext.AdminCtx, jobID, jobOrderID); err == nil { jobTimer.Status = model.JobTimerStatusFinish dao.UpdateEntity(db, jobTimer, "Status") } } } func UpdateLimitAuditJobOrders(db *dao.DaoDB, timer *time.Timer, jobID int, jobOrderID int64, jobTimer *model.JobTimer) { globals.SugarLogger.Debugf("checkLimitAuditJobOrders jobID: %v, jobOrderID: %v", jobID, jobOrderID) defer timer.Stop() jobOrder := &model.JobOrder{JobOrderID: jobOrderID} if err := dao.GetEntity(db, jobOrder, "JobOrderID"); err == nil { if jobOrder.Status == model.JobOrderStatusWaitAudit { err := AuditJob(jxcontext.AdminCtx, int(jobOrderID), model.JobOrderStatusAuditPass, "超时系统通过", "") if err != nil { globals.SugarLogger.Debugf("checkLimitAuditJobOrders err: %v jobID: %v, jobOrderID: %v", err, jobID, jobOrderID) } else { jobTimer.Status = model.JobTimerStatusFinish dao.UpdateEntity(db, jobTimer, "Status") } } } } func UpdateDropShippingJobOrders(db *dao.DaoDB, timer *time.Timer, jobID int, jobOrderID int64, jobTimer *model.JobTimer) { globals.SugarLogger.Debugf("UpdateDropShippingJobOrders jobID: %v, jobOrderID: %v", jobID, jobOrderID) defer timer.Stop() jobOrder := &model.JobOrder{JobOrderID: jobOrderID} job := &model.Job{} job.ID = jobID if err := dao.GetEntity(db, jobOrder, "JobOrderID"); err == nil { if err := dao.GetEntity(db, job); err == nil { //如果限时内还没发货 if jobOrder.Status < model.JobOrderStatusFinish { userBill, err := dao.GetUserBill(db, jobOrder.UserID, "") dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() if err = financial.AddIncomeUpdateAccount(db, userBill, model.BillTypeDropShippingDeposit, job.AvgPrice, job.ID); err != nil { dao.Rollback(db) return } dao.Commit(db) } } } jobTimer.Status = model.JobTimerStatusFinish dao.UpdateEntity(db, jobTimer, "Status") } func SubmitJob(ctx *jxcontext.Context, jobOrder *model.JobOrder) (err error) { var ( db = dao.GetDB() ) if jobOrder.JobID == 0 || jobOrder.JobOrderID == 0 { return fmt.Errorf("传入数据有误!") } job := &model.Job{} job.ID = jobOrder.JobID err = dao.GetEntity(db, job) jobOrder2 := &model.JobOrder{} jobOrder2.JobOrderID = jobOrder.JobOrderID err = dao.GetEntity(db, jobOrder2, "JobOrderID") //如果是比例返现 if job.CashbackType == model.JobCashbackPercentage { if jobOrder.UserActualPrice == 0 { return fmt.Errorf("比例返现任务请输入订单实际实付金额,用于任务发起人审核!") } } if jobOrder2.JobID == 0 { return fmt.Errorf("未查询到相应的任务!") } if jobOrder2.Status >= model.JobOrderStatusWaitAudit { return fmt.Errorf("任务订单状态有误!") } if ctx.GetUserID() != jobOrder2.UserID { return fmt.Errorf("任务订单接收人有误!") } jobOrder2.Imgs = jobOrder.Imgs jobOrder2.Content = jobOrder.Content jobOrder2.SubmitAuditAt = time.Now() jobOrder2.UserActualPrice = jobOrder.UserActualPrice jobOrder2.Status = model.JobOrderStatusWaitAudit if _, err = dao.UpdateEntity(db, jobOrder2, "Imgs", "Content", "SubmitAuditAt", "Status", "UserActualPrice"); err == nil { //任务定时器停止 JobTimers.s.RLock() if JobTimers.JobTimerMap[jobOrder2.JobOrderID] != nil { JobTimers.JobTimerMap[jobOrder2.JobOrderID].Stop() //任务定时表状态完成 jobTimer := &model.JobTimer{ JobID: job.ID, JobOrderID: jobOrder2.JobOrderID, Type: model.JobTimerTypeAccept, } if err = dao.GetEntity(db, jobTimer, "JobID", "JobOrderID", "Type"); err == nil { jobTimer.Status = model.JobTimerStatusFinish dao.UpdateEntity(db, jobTimer, "Status") } } JobTimers.s.RUnlock() //一件代发 var timerType int if job.JobCategoryID == model.JobCategoryIDDropShipping { timerType = model.JobTimerTypeDropShipping } else { timerType = model.JobTimerTypeSubmit } //审核定时开启 timer := checkLimitJobOrders(db, job, jobOrder2, timerType) JobTimers.s.Lock() JobTimers.JobAuditTimerMap[jobOrder2.JobOrderID] = timer JobTimers.s.Unlock() } content := new(strings.Builder) content.WriteString("您的任务:") content.WriteString(job.Title) content.WriteString("。已有人提交了审核,请及时审核!") event.SendSysMessageSimple(content.String(), job.UserID) return err } func AuditJob(ctx *jxcontext.Context, jobOrderID, status int, comment, vendorWaybillID string) (err error) { var ( db = dao.GetDB() ) jobOrder := &model.JobOrder{} jobOrder.JobOrderID = int64(jobOrderID) err = dao.GetEntity(db, jobOrder, "JobOrderID") job := &model.Job{} job.ID = jobOrder.JobID err = dao.GetEntity(db, job) if ctx.GetUserID() != job.UserID && ctx.GetUserName() != "jxadmin" { return fmt.Errorf("任务发起人才能审核!") } if job.JobCategoryID == model.JobCategoryIDDropShipping && vendorWaybillID == "" { return fmt.Errorf("一件代发任务发货请输入运单号!") } if job.JobCategoryID == model.JobCategoryIDDropShipping && status != model.JobOrderStatusAuditPass { return fmt.Errorf("一件代发任务发货参数有误!") } if jobOrder.Status != model.JobOrderStatusWaitAudit { return fmt.Errorf("审核状态不正确!") } //固定返现 //1、审核时,若此任务已过期或者已取消,不通过则应将此任务保证金退还给发起人,通过则应将单次任务保证金给接受人 //2、若此任务未过期,不通过则此任务剩余数量将+1,通过则应将单次任务保证金给接受人 jobOrder.Status = status jobOrder.Comment = comment jobOrder.AuditAt = time.Now() jobOrder.LastOperator = ctx.GetUserName() userBillJobOrder, err := dao.GetUserBill(db, jobOrder.UserID, "") //系统消息 content := new(strings.Builder) content.WriteString("恭喜您完成了任务:") content.WriteString(job.Title) if ctx.GetUserName() == "jxadmin" { content.WriteString(",因超时未审核已被系统自动审核") } else { content.WriteString(",商家已经审核") } dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() if _, err = dao.UpdateEntity(db, jobOrder, "Status", "Comment", "AuditAt", "LastOperator"); err != nil { dao.Rollback(db) return } if status == model.JobOrderStatusAuditPass { content.WriteString("通过,") if job.JobCategoryID != model.JobCategoryIDDropShipping { var price int if job.CashbackType == model.JobCashbackPrice { price = job.AvgPrice } else { price = jobOrder.UserActualPrice * job.Percentage / 100 if price > job.AvgPrice { price = job.AvgPrice } } //若完成任务的人在某个群组中,则要向群主分成 if messageGroupMembers, err := dao.GetMessageGroupMembers(db, 0, model.GroupTypeMulit, jobOrder.UserID); err == nil { if len(messageGroupMembers) > 1 { return fmt.Errorf("审核异常,该任务提交人加入了多个群组!") } else if len(messageGroupMembers) == 1 { if messageGroupsResult, err := dao.GetMessageGroups(db, "", messageGroupMembers[0].GroupID, model.GroupTypeMulit, false, ""); err == nil { if len(messageGroupsResult) == 1 { if messageGroupsResult[0].DividePercentage != 0 { if userBillGroupMaster, err := dao.GetUserBill(db, messageGroupsResult[0].UserID, ""); err == nil { if err = financial.AddIncomeUpdateAccount(db, userBillGroupMaster, model.BillTypeDivide, price*messageGroupsResult[0].DividePercentage/100, job.ID); err != nil { dao.Rollback(db) return err } } //接收人账户收入 if err = financial.AddIncomeUpdateAccount(db, userBillJobOrder, model.BillTypeJobDivide, price*(100-messageGroupsResult[0].DividePercentage)/100, job.ID); err != nil { dao.Rollback(db) return err } } else { //接收人账户收入 if err = financial.AddIncomeUpdateAccount(db, userBillJobOrder, model.BillTypeJob, price, job.ID); err != nil { dao.Rollback(db) return err } } } } } else if len(messageGroupMembers) == 0 { //若没有在某个群组,则得到全部 //接收人账户收入 if err = financial.AddIncomeUpdateAccount(db, userBillJobOrder, model.BillTypeJob, price, job.ID); err != nil { dao.Rollback(db) return err } } } jobOrder.Status = model.JobOrderStatusFinish if _, err = dao.UpdateEntity(db, jobOrder, "Status"); err != nil { dao.Rollback(db) return err } content.WriteString(utils.Float64ToStr(jxutils.IntPrice2Standard(int64(price)))) content.WriteString("元已存入您的余额中!") } else { //一件代发处理,审核相当于发货 jobOrder.VendorWaybillID = vendorWaybillID jobOrder.Status = model.JobOrderStatusFinish if _, err = dao.UpdateEntity(db, jobOrder, "Status", "VendorWaybillID"); err != nil { dao.Rollback(db) return err } userBill, err := dao.GetUserBill(db, job.UserID, "") if err = financial.AddIncomeUpdateAccount(db, userBill, model.BillTypeDropShippingDeposit, job.AvgPrice, job.ID); err != nil { dao.Rollback(db) return err } } event.SendSysMessageSimple(content.String(), jobOrder.UserID) } else { content2 := new(strings.Builder) content2.WriteString("非常抱歉,您提交的任务: ") content2.WriteString(job.Title) content2.WriteString("未通过审核,请您修改后重新提交!") if job.Status < 0 { if job.CashbackType == model.JobCashbackPrice { userBill, err := dao.GetUserBill(db, job.UserID, "") if err = financial.AddIncomeUpdateAccount(db, userBill, model.BillTypeJobAuditUnPassWithCancelOverdue, job.AvgPrice, job.ID); err != nil { dao.Rollback(db) return err } } } else { //审核不通过的话,要重新变成待上传,再重新开个定时器 jobOrder.Status = model.JobOrderStatusAccept if _, err = dao.UpdateEntity(db, jobOrder, "Status"); err != nil { dao.Rollback(db) return } //之前的定时表删了? jobTimer := &model.JobTimer{ JobID: job.ID, JobOrderID: jobOrder.JobOrderID, } if _, err = dao.DeleteEntity(db, jobTimer, "JobID", "JobOrderID"); err != nil { dao.Rollback(db) return } //任务限时完成 timer := checkLimitJobOrders(db, job, jobOrder, model.JobTimerTypeAccept) JobTimers.s.Lock() JobTimers.JobTimerMap[jobOrder.JobOrderID] = timer JobTimers.s.Unlock() } event.SendSysMessageSimple(content2.String(), jobOrder.UserID) } dao.Commit(db) //任务定时器停止 JobTimers.s.RLock() if JobTimers.JobAuditTimerMap[int64(jobOrderID)] != nil { JobTimers.JobAuditTimerMap[int64(jobOrderID)].Stop() } JobTimers.s.RUnlock() //任务定时表状态完成 jobTimer := &model.JobTimer{ JobID: job.ID, JobOrderID: jobOrder.JobOrderID, // Type: model.JobTimerTypeSubmit, } if job.JobCategoryID != model.JobCategoryIDDropShipping { jobTimer.Type = model.JobTimerTypeSubmit } else { jobTimer.Type = model.JobTimerTypeDropShipping } if err = dao.GetEntity(db, jobTimer, "JobID", "JobOrderID", "Type"); err == nil { jobTimer.Status = model.JobTimerStatusFinish dao.UpdateEntity(db, jobTimer, "Status") } return err } func RefreshJobStatus(ctx *jxcontext.Context) (err error) { var ( db = dao.GetDB() ) globals.SugarLogger.Debugf("RefreshJobStatus begin...") jobs, err := dao.GetJobsNoPage(db, nil, nil, []int{model.JobStatusDoing}, nil, utils.ZeroTimeValue, utils.ZeroTimeValue, 0, false) if err != nil { globals.SugarLogger.Debugf("RefreshJobStatus err :%v", err) return } for _, job := range jobs { if time.Now().Sub(*job.FinishedAt) >= 0 { //取消已发布的任务 userBill, err := dao.GetUserBill(db, job.UserID, "") if userBill == nil { return fmt.Errorf("未查询到该用户的账单!") } dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() //如果是固定返现,退给任务发起人剩余数量*单价 //如果是比例返现,推给任务发起人任务总价-接取这个任务的用户实际返现的价格之和 var price int if job.CashbackType == model.JobCashbackPrice { price = job.SurplusCount * job.AvgPrice } else { if billIncomes, err := dao.GetBillIncome(db, job.ID); err == nil { for _, v := range billIncomes { price += v.IncomePrice } } price = job.TotalPrice - price } if err = financial.AddIncomeUpdateAccount(db, userBill, model.BillTypeJobCancelOverdue, price, job.ID); err != nil { dao.Rollback(db) return err } //3、任务状态被取消 job2 := &model.Job{} job2.ID = job.ID dao.GetEntity(db, job2) if job2 != nil { job2.Status = model.JobStatusOverdue // job.DeletedAt = time.Now() if _, err = dao.UpdateEntity(db, job2, "Status"); err != nil { dao.Rollback(db) return err } } dao.Commit(db) } } globals.SugarLogger.Debugf("RefreshJobStatus end...") return err } func ImprotMtMembers(ctx *jxcontext.Context, mtMembers []*model.MtMember) (err error) { var ( db = dao.GetDB() ) for _, v := range mtMembers { v.ShortLink = v.URL[strings.LastIndex(v.URL, "/")+1 : len(v.URL)] dao.WrapAddIDCULDEntity(v, ctx.GetUserName()) } if err = dao.CreateMultiEntities(db, mtMembers); err == nil { job, err := dao.GetJob(db, nil, nil, nil, []int{model.JobTypeMtMember}, utils.ZeroTimeValue, utils.ZeroTimeValue, false) if job != nil && err == nil { job.Count += len(mtMembers) job.SurplusCount += len(mtMembers) dao.UpdateEntity(db, job, "Count", "SurplusCount") } } return err } func RechargeMtMembers(ctx *jxcontext.Context, phone int) (errCode string, err error) { var ( db = dao.GetDB() db2 = dao.GetDB() userID = ctx.GetUserID() ) dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() nums, err := dao.GetMtMembers(db) if nums < 10 { utils.CallFuncAsync(func() { ddmsg.SendUserMessage(dingdingapi.MsgTyeText, "F4836F9238F611EB9101525400C36BDA", "美团会员券", "美团会员券仅剩"+utils.Int2Str(nums)+"张了!") ddmsg.SendUserMessage(dingdingapi.MsgTyeText, "EFA9876238FC11EB9101525400C36BDA", "美团会员券", "美团会员券仅剩"+utils.Int2Str(nums)+"张了!") }) } if err != nil { dao.Rollback(db) return errCode, err } //验证微信绑定 if err = auth2.CheckWeixinminiAuthBind(userID); err != nil { dao.Rollback(db) return "", err } //特殊任务,如美团会员,是直接要支付 userBill, err := dao.GetUserBill(db, userID, "") if err != nil { return errCode, err } if userBill.AccountBalance < mtwmMemberPrice { return model.ErrCodeAccountBalanceNotEnough, fmt.Errorf("用户余额不足,请充值!") } //账户支出 if err = financial.AddExpendUpdateAccount(db, userBill, model.BillTypeSpJob, mtwmMemberPrice, 1); err != nil { dao.Rollback(db) return errCode, err } dao.Commit(db) for { dao.Begin(db2) defer func() { if r := recover(); r != nil { dao.Rollback(db2) panic(r) } }() mtMember, err := dao.GetMtMember(db2) if mtMember == nil { return errCode, fmt.Errorf("补券中,请稍后再试!") } err = api.MtMemberAPI.RechargeExchange(phone, mtMember.ShortLink) mtMember.DeletedAt = time.Now() dao.UpdateEntity(db2, mtMember, "DeletedAt") dao.Commit(db2) if err == nil { job, err := dao.GetJob(db2, nil, nil, nil, []int{model.JobTypeMtMember}, utils.ZeroTimeValue, utils.ZeroTimeValue, false) _, errCode, err = AcceptJob(ctx, job.ID, 0, 0) if errCode != "" { return errCode, err } if err != nil { return errCode, err } break } } return errCode, err } func SendJdDelivery(ctx *jxcontext.Context, dOrder *model.DeliveryOrder) (errCode string, err error) { var ( db = dao.GetDB() ) if dOrder.Weight == 0 { return errCode, fmt.Errorf("重量必须填写!") } if dOrder.PayPrice == 0 { if dOrder.Weight <= 3 { dOrder.PayPrice = 1000 } else { dOrder.PayPrice = 1000 + int(float64(waybillKgPrice)*math.Ceil(dOrder.Weight-3)) } } var ( sendDelivery *dao.UserDeliveryAddressEx receiveDelivery *dao.UserDeliveryAddressEx ) sendDeliveryList, _, err := dao.QueryUserDeliveryAddress(db, int64(dOrder.DeliverySendID), nil, 0, 0, 0) receiveDeliveryList, _, err := dao.QueryUserDeliveryAddress(db, int64(dOrder.DeliveryReceiveID), nil, 0, 0, 0) userBill, err := dao.GetUserBill(db, ctx.GetUserID(), "") if err != nil { return errCode, err } //验证微信绑定 if err = auth2.CheckWeixinminiAuthBind(ctx.GetUserID()); err != nil { return "", err } if userBill.AccountBalance < dOrder.PayPrice { return model.ErrCodeAccountBalanceNotEnough, fmt.Errorf("用户余额不足,请充值!") } if len(sendDeliveryList) == 0 { return errCode, fmt.Errorf("未找到寄件人地址!") } else { sendDelivery = sendDeliveryList[0] } if len(receiveDeliveryList) == 0 { return errCode, fmt.Errorf("未找到取件人地址!") } else { receiveDelivery = receiveDeliveryList[0] } //内蒙古海南统一35 //新疆西藏统一45 if receiveDelivery.ParentCode == 150000 || receiveDelivery.ParentCode == 460000 || receiveDelivery.ParentCode == 650000 || receiveDelivery.ParentCode == 540000 || sendDelivery.ParentCode == 150000 || sendDelivery.ParentCode == 460000 || sendDelivery.ParentCode == 650000 || sendDelivery.ParentCode == 540000 { return errCode, fmt.Errorf("暂不支持该地区的快递业务!") } sendProvinceName := "" receiveProvinceName := "" if place1, err := dao.GetPlaceByCode(db, sendDelivery.CityCode); err == nil { if place2, err2 := dao.GetPlaceByCode(db, place1.ParentCode); err2 == nil { sendProvinceName = place2.Name } } if place1, err := dao.GetPlaceByCode(db, receiveDelivery.CityCode); err == nil { if place2, err2 := dao.GetPlaceByCode(db, place1.ParentCode); err2 == nil { receiveProvinceName = place2.Name } } dao.WrapAddIDCULEntity(dOrder, ctx.GetUserName()) if vendorWaybillID, err := api.JdEclpAPI.WaybillReceive(&jdeclpapi.WaybillReceiveParam{ SalePlat: jdeclpapi.SalePlatSourceDelivery, CustomerCode: jdeclpapi.CustomerCode, OrderID: utils.Int64ToStr(jxutils.GenOrderNo()), SenderName: sendDelivery.ConsigneeName, SenderAddress: sendProvinceName + sendDelivery.CityName + sendDelivery.DistrictName + sendDelivery.Address + sendDelivery.DetailAddress, SenderTel: sendDelivery.ConsigneeMobile, ReceiveName: receiveDelivery.ConsigneeName, ReceiveAddress: receiveProvinceName + receiveDelivery.CityName + receiveDelivery.DistrictName + receiveDelivery.Address + receiveDelivery.DetailAddress, ReceiveTel: receiveDelivery.ConsigneeMobile, Weight: dOrder.Weight, Vloumn: dOrder.Vloumn, PackageCount: dOrder.PackageCount, Description: dOrder.Description, Aging: 5, PromiseTimeType: 1, //特惠送 }); err == nil { dOrder.VendorWaybillID = vendorWaybillID } else { return errCode, err } dOrder.Status = model.OrderStatusNew dOrder.UserID = ctx.GetUserID() dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() //账户支出明细 if err = financial.AddExpendUpdateAccount(db, userBill, model.BillTypeSpJob, dOrder.PayPrice, 2); err != nil { dao.Rollback(db) return } dOrder.SendName = sendDelivery.ConsigneeName dOrder.SendMobile = sendDelivery.ConsigneeMobile dOrder.SendAddress = sendDelivery.Address dOrder.SendAutoAddress = sendDelivery.AutoAddress dOrder.SendCityCode = sendDelivery.CityCode dOrder.SendDetailAddress = sendDelivery.DetailAddress dOrder.SendLng = sendDelivery.Lng dOrder.SendLat = sendDelivery.Lat dOrder.ReceiveName = receiveDelivery.ConsigneeName dOrder.ReceiveMobile = receiveDelivery.ConsigneeMobile dOrder.ReceiveAddress = receiveDelivery.Address dOrder.ReceiveAutoAddress = receiveDelivery.AutoAddress dOrder.ReceiveCityCode = receiveDelivery.CityCode dOrder.ReceiveDetailAddress = receiveDelivery.DetailAddress dOrder.ReceiveLng = receiveDelivery.Lng dOrder.ReceiveLat = receiveDelivery.Lat if err = dao.CreateEntity(db, dOrder); err != nil { dao.Rollback(db) return } dao.Commit(db) utils.CallFuncAsync(func() { job, _ := dao.GetJob(db, nil, nil, nil, []int{model.JobTypeJdDelivery}, utils.ZeroTimeValue, utils.ZeroTimeValue, false) if jobOrderID, _, err := AcceptJob(ctx, job.ID, 0, 0); err == nil { dOrder.JobOrderID = utils.Int64ToStr(jobOrderID) dao.UpdateEntity(db, dOrder, "JobOrderID") } }) return errCode, err } func CancelJdDelivery(ctx *jxcontext.Context, vendorWaybillID, reason string) (err error) { var ( db = dao.GetDB() dOrder = &model.DeliveryOrder{ VendorWaybillID: vendorWaybillID, } // DayTimeBegin, DayTimeEnd = jxutils.GetDayTime() ) err = dao.GetEntity(db, dOrder, "VendorWaybillID") userBill, err := dao.GetUserBill(db, ctx.GetUserID(), "") // dOrders, err := dao.GetDeliveryOrdersNoPage(db, []string{ctx.GetUserID()}, []int{model.OrderStatusCanceled}, DayTimeBegin, DayTimeEnd, nil) // if err != nil { // return err // } // if len(dOrders) > 0 { // return fmt.Errorf("抱歉,您已经在今天取消过京东物流订单!") // } if dOrder.ID == 0 { return fmt.Errorf("未找到该运单!") } if err = api.JdEclpAPI.CancelWayBill(&jdeclpapi.CancelWayBillParam{ WaybillCode: vendorWaybillID, CustomerCode: jdeclpapi.CustomerCode, Source: "JOS", CancelReason: reason, OperatorName: ctx.GetUserName(), }); err != nil { return err } dOrder.Status = model.OrderStatusCanceled dOrder.OrderFinishedAt = time.Now() dOrder.Comment = reason dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() if _, err = dao.UpdateEntity(db, dOrder, "Status", "OrderFinishedAt", "Comment"); err != nil { dao.Rollback(db) return } if err = financial.AddIncomeUpdateAccount(db, userBill, model.BillTypeSpJob, dOrder.PayPrice, 2); err != nil { dao.Rollback(db) return } if err = CancelAcceptJob(ctx, 2, utils.Str2Int64(dOrder.JobOrderID)); err != nil { dao.Rollback(db) } dao.Commit(db) return err } func GetJdDelivery(ctx *jxcontext.Context, status int, fromTime, toTime string, pageSize, offset int) (pagedInfo *model.PagedInfo, err error) { var ( db = dao.GetDB() statuss []int ) if status != 0 { statuss = append(statuss, status) } RefreshJdDelivery(ctx) return dao.GetDeliveryOrders(db, []string{ctx.GetUserID()}, statuss, utils.Str2Time(fromTime), utils.Str2Time(toTime), pageSize, offset) } func RefreshJdDelivery(ctx *jxcontext.Context) (err error) { var ( db = dao.GetDB() userID string userIDs []string ) if ctx.GetUserName() == "jxadmin" { userID = "" userIDs = nil } else { userID = ctx.GetUserID() userIDs = append(userIDs, userID) } pages, _ := dao.GetDeliveryOrders(db, userIDs, []int{model.OrderStatusNew, model.OrderStatusDelivering}, utils.ZeroTimeValue, utils.ZeroTimeValue, 9999, 0) list := pages.Data.([]*dao.GetDeliveryOrdersResult) task := tasksch.NewParallelTask("RefreshJdDelivery", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(true), jxcontext.AdminCtx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { v := batchItemList[0].(*dao.GetDeliveryOrdersResult) if v != nil && v.VendorWaybillID != "" { var ( isDeliverying, isFinished, isCancel bool ) if results, err := api.JdEclpAPI.QueryDynamicTraceInfo(v.VendorWaybillID); err == nil { for _, result := range results { if result.State == jdeclpapi.TraceInfoStateM640 { isDeliverying = true } if result.State == jdeclpapi.TraceInfoStateM650 || result.State == jdeclpapi.TraceInfoStateM790 { isCancel = true break } if result.State == jdeclpapi.TraceInfoState150 { isFinished = true break } } } dOrder := &model.DeliveryOrder{} dOrder.VendorWaybillID = v.VendorWaybillID if err = dao.GetEntity(db, dOrder, "VendorWaybillID"); err == nil { if isCancel { //退钱给发快递的 dOrder.Status = model.OrderStatusCanceled userBill, _ := dao.GetUserBill(db, v.UserID, "") err = financial.AddIncomeUpdateAccount(db, userBill, model.BillTypeSpJob, v.PayPrice+v.DiffPrice, 2) } else if isFinished { //快递任务要完成 dOrder.Status = model.OrderStatusFinished err = AuditJob(ctx, utils.Str2Int(dOrder.JobOrderID), model.JobOrderStatusAuditPass, "快递完成,自动审核通过", "") } else if isDeliverying { dOrder.Status = model.OrderStatusDelivering } else { return retVal, err } if err == nil { dao.UpdateEntity(db, dOrder, "Status") } else { if strings.Contains(err.Error(), "审核状态不正确") { dao.UpdateEntity(db, dOrder, "Status") } } } } return retVal, err }, list) tasksch.HandleTask(task, nil, true).Run() task.GetID() return err } func GetDeliveryDetail(ctx *jxcontext.Context, vendorWaybillID string) (queryDynamicTraceInfo []*jdeclpapi.QueryDynamicTraceInfoResult, err error) { return api.JdEclpAPI.QueryDynamicTraceInfo(vendorWaybillID) } func GetAllDeliveryDetail(ctx *jxcontext.Context, vendorWaybillID, comType string) (result *txcloudapi.GetWaybillDetailInfoResult, err error) { var ( db = dao.GetDB() getWaybillDetailInfoResult = &txcloudapi.GetWaybillDetailInfoResult{} ) jobOrder := &model.JobOrder{ VendorWaybillID: vendorWaybillID, } err = dao.GetEntity(db, jobOrder, "VendorWaybillID") if jobOrder == nil { return result, fmt.Errorf("运单号有误,无法查询!") } if jobOrder.WaybillStatus >= model.OrderStatusFinished { json.Unmarshal([]byte(jobOrder.WaybillInfo), &getWaybillDetailInfoResult) return getWaybillDetailInfoResult, err } //距上次查询时间要大于12小时的,才去真正查 if jobOrder.WaybillQueryTime != utils.ZeroTimeValue { if time.Now().Sub(jobOrder.WaybillQueryTime) <= time.Hour*12 { json.Unmarshal([]byte(jobOrder.WaybillInfo), &getWaybillDetailInfoResult) return getWaybillDetailInfoResult, err } } if getWaybillDetailInfoResult, err = api.TxAPI.GetWaybillDetailInfo(vendorWaybillID, comType); err != nil { return nil, err } jobOrder.WaybillQueryTime = time.Now() if waybillInfo, err := json.Marshal(getWaybillDetailInfoResult); err == nil { jobOrder.WaybillInfo = string(waybillInfo) } if getWaybillDetailInfoResult.State == utils.Int2Str(txcloudapi.StatusFinished) && jobOrder.WaybillStatus < model.OrderStatusFinished { jobOrder.WaybillStatus = model.OrderStatusFinished } dao.UpdateEntity(db, jobOrder, "WaybillQueryTime", "WaybillInfo", "WaybillStatus") return getWaybillDetailInfoResult, err } func CheckJdDeliveryWeight(ctx *jxcontext.Context) (err error) { var ( db = dao.GetDB() ) userBill, err := dao.GetUserBill(db, ctx.GetUserID(), "") if userBill.AccountBalance < 0 { return fmt.Errorf("您有京东物流订单实际超重,请支付欠款!") } deliveryOrders, err := dao.GetDeliveryOrdersNoPage(db, []string{ctx.GetUserID()}, nil, utils.ZeroTimeValue, utils.ZeroTimeValue, []int{0}) if err != nil { return err } task := tasksch.NewParallelTask("CheckJdDeliveryWeight", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(true), jxcontext.AdminCtx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { deliveryOrder := batchItemList[0].(*model.DeliveryOrder) waybill, err := api.JdEclpAPI.WaybillQuery(deliveryOrder.VendorWaybillID) if err == nil { return retVal, err } if waybill.DeliveryID == "" { return retVal, err } if waybill.DeliveryID == deliveryOrder.VendorWaybillID { deliveryOrder.IsWeight = 1 //合格 if waybill.Weight > 3 && math.Floor(deliveryOrder.Weight) < math.Floor(waybill.Weight) { diffPrice := (math.Floor(waybill.Weight) - math.Floor(deliveryOrder.Weight)) * waybillKgPrice if err != nil { return retVal, err } if err = financial.AddExpendUpdateAccount(db, userBill, model.BillTypeJdWaybillOverWeight, utils.Float64TwoInt(diffPrice), 2); err != nil { return retVal, err } deliveryOrder.IsWeight = 2 //超重 deliveryOrder.DiffPrice = utils.Float64TwoInt(diffPrice) } } deliveryOrder.ActualWeight = waybill.Weight dao.UpdateEntity(db, deliveryOrder, "IsWeight", "ActualWeight", "DiffPrice") return retVal, err }, deliveryOrders) tasksch.HandleTask(task, nil, true).Run() task.GetID() return err } func ResetJobTimers() { var ( db = dao.GetDB() ) jobTimers, err := dao.GetJobTimers(db, model.JobTimerStatusWait) if err != nil { return } task := tasksch.NewParallelTask("ResetJobTimers", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(true), jxcontext.AdminCtx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { jobTimer := batchItemList[0].(*model.JobTimer) timer := time.NewTimer(jobTimer.StartAt.Add(time.Duration(jobTimer.LimitAt) * time.Hour).Sub(time.Now())) jobOrders, err := dao.GetJobOrdersNoPage(db, jobTimer.JobID, jobTimer.JobOrderID, "", "", utils.ZeroTimeValue, utils.ZeroTimeValue, nil) if err != nil { return retVal, err } utils.CallFuncAsync(func() { select { case <-timer.C: switch jobTimer.Type { case model.JobTimerTypeAccept: UpdateLimitJobOrders(db, timer, jobTimer.JobID, jobOrders[0].JobOrderID, jobTimer) case model.JobTimerTypeSubmit: UpdateLimitAuditJobOrders(db, timer, jobTimer.JobID, jobOrders[0].JobOrderID, jobTimer) case model.JobTimerTypeDropShipping: UpdateDropShippingJobOrders(db, timer, jobTimer.JobID, jobOrders[0].JobOrderID, jobTimer) } } }) return retVal, err }, jobTimers) tasksch.HandleTask(task, nil, true).Run() task.GetID() } func CreateJobSpan(ctx *jxcontext.Context, jobIDs []int, endAt string, span int) (err error) { var ( db = dao.GetDB() maxSeq = 0 ) if span == model.JobSpanTop { maxSeq, err = dao.GetMaxJobTopSeq(db) } else { maxSeq, err = dao.GetMaxJobRecmdSeq(db) } for k, jobID := range jobIDs { endAt2 := utils.Str2Time(endAt) jonSpan := &model.JobSpan{ JobID: jobID, EndAt: &endAt2, SpanType: span, } if err = dao.CreateEntity(db, jonSpan); err == nil { job := &model.Job{} job.ID = jobID if err = dao.GetEntity(db, job); err == nil { if span == model.JobSpanTop { job.TopSeq = k + 1 + maxSeq job.JobSpanTop = 1 dao.UpdateEntity(db, job, "TopSeq", "JobSpanTop") } else { job.RecmdSeq = k + 1 + maxSeq job.JobSpanRecmd = 1 dao.UpdateEntity(db, job, "RecmdSeq", "JobSpanRecmd") } } } } return err } func RefreshJobSpan(ctx *jxcontext.Context) (err error) { var ( db = dao.GetDB() ) jobSpans, err := dao.GetJobSpans(db) task := tasksch.NewParallelTask("RefreshJobSpan", tasksch.NewParallelConfig().SetIsContinueWhenError(true), jxcontext.AdminCtx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { jobSpan := batchItemList[0].(*model.JobSpan) if time.Now().Sub(*jobSpan.EndAt) >= 0 { job := &model.Job{} job.ID = jobSpan.JobID if err = dao.GetEntity(db, job); err == nil { if jobSpan.SpanType == model.JobSpanTop { job.TopSeq = 0 job.JobSpanTop = 0 dao.UpdateEntity(db, job, "TopSeq", "JobSpanTop") } else { job.RecmdSeq = 0 job.JobSpanRecmd = 0 dao.UpdateEntity(db, job, "RecmdSeq", "JobSpanRecmd") } } jobSpan.DeletedAt = time.Now() dao.UpdateEntity(db, jobSpan, "DeletedAt") } return retVal, err }, jobSpans) tasksch.HandleTask(task, nil, true).Run() task.GetID() return err } func ReloadJobSpan(ctx *jxcontext.Context, jobIDs []int, span int) (err error) { var ( db = dao.GetDB() ) for k, v := range jobIDs { job := &model.Job{} job.ID = v if err = dao.GetEntity(db, job); err == nil { if span == model.JobSpanTop { if job.JobSpanTop == model.JobSpanTop { job.TopSeq = k + 1 dao.UpdateEntity(db, job, "TopSeq") } else { continue } } else { if job.JobSpanRecmd == 1 { job.RecmdSeq = k + 1 dao.UpdateEntity(db, job, "RecmdSeq") } else { continue } } } } return err } func ConfirmDropShippingJob(ctx *jxcontext.Context, jobOrderID int) (err error) { var ( db = dao.GetDB() ) jobOrder := &model.JobOrder{ JobOrderID: int64(jobOrderID), } if err = dao.GetEntity(db, jobOrder, "JobOrderID"); err != nil { return err } job := &model.Job{} job.ID = jobOrder.JobID if err = dao.GetEntity(db, job); err != nil { return err } if ctx.GetUserID() != jobOrder.UserID && ctx.GetUserName() != "jxadmin" { return fmt.Errorf("只有任务接取人才能确认收货!") } dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() //确认收货更新时间和收货人 jobOrder.DropShippingConfirmTime = time.Now() jobOrder.DropShippingConfirmUser = ctx.GetUserName() jobOrder.Status = model.OrderStatusConfirm if _, err = dao.UpdateEntity(db, jobOrder, "DropShippingConfirmTime", "DropShippingConfirmUser", "Status"); err != nil { dao.Rollback(db) return } userBill, err := dao.GetUserBill(db, job.UserID, "") if err = financial.AddIncomeUpdateAccount(db, userBill, model.BillTypeDropShipping, jobOrder.UserActualPrice, job.ID); err != nil { dao.Rollback(db) return } dao.Commit(db) return err } func RefreshDropShippingJob(ctx *jxcontext.Context) (err error) { var ( db = dao.GetDB() jobOrders []*model.JobOrder ) if time.Now().Weekday() != 5 && time.Now().Weekday() != 1 { return } //找出没有完成的,顺便刷成完成 sql := ` SELECT a.* FROM job_order a JOIN job b ON a.job_id = b.id AND b.job_category_id = ? WHERE a.waybill_status <= ? AND a.drop_shipping_confirm_user = '' AND a.status = ? AND a.vendor_waybill_id <> '' ` sqlParams := []interface{}{ model.JobCategoryIDDropShipping, model.OrderStatusFinished, model.OrderStatusFinished, } err = dao.GetRows(db, &jobOrders, sql, sqlParams) task := tasksch.NewParallelTask("RefreshDropShippingJob", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(true), ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { jobOrder := batchItemList[0].(*model.JobOrder) result, err := GetAllDeliveryDetail(ctx, jobOrder.VendorWaybillID, "") //完成了的,现在判断是如果收货时间已经过了3天了就自动确认收货 if result.State == utils.Int2Str(txcloudapi.StatusFinished) && time.Now().Sub(utils.Str2Time(result.UpdateTime)) > time.Hour*72 { err = ConfirmDropShippingJob(ctx, int(jobOrder.JobOrderID)) } return retVal, err }, jobOrders) tasksch.HandleTask(task, nil, true).Run() task.GetID() return err } func AddressDistinguish(ctx *jxcontext.Context, address string) (result *txcloudapi.AddressDistinguishResult, err error) { var ( db = dao.GetDB() addressDistinguish = &model.AddressDistinguish{Address: address} ) if len(address) <= 11 { return result, fmt.Errorf("地址长度过短,请确认后再进行识别!") } //建了个表,有查过的记录就存一下吧 if err = dao.GetEntity(db, addressDistinguish, "Address"); err == nil && addressDistinguish.ID != 0 { if err = json.Unmarshal([]byte(addressDistinguish.Info), &result); err == nil { return result, err } } result, err = api.TxAPI.AddressDistinguish(address) result.Lng, result.Lat, err = api.AutonaviAPI.GetCoordinateFromAddressByPage(result.AddressDetail, result.CityCode) if data, err := json.Marshal(result); err == nil { addressDistinguish.Info = string(data) dao.CreateEntity(db, addressDistinguish) } return result, err } func UpdateJob(ctx *jxcontext.Context, payload map[string]interface{}) (err error) { var ( db = dao.GetDB() jobExt = &model.JobExt{} job2 = &model.Job{} ) utils.Map2StructByJson(payload, &jobExt, false) job2.ID = int(utils.MustInterface2Int64(payload["id"])) dao.GetEntity(db, job2) valid := dao.StrictMakeMapByStructObject(payload, job2, ctx.GetUserName()) if len(valid) > 0 { dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() if _, err = dao.UpdateEntityByKV(db, job2, valid, nil); err != nil { dao.Rollback(db) return err } if len(jobExt.JobSteps) > 0 { steps, _ := dao.GetJobSteps(db, job2.ID) for _, v := range steps { v.DeletedAt = time.Now() dao.UpdateEntity(db, v, "DeletedAt") } for _, v := range jobExt.JobSteps { v.DeletedAt = utils.DefaultTimeValue v.LastOperator = ctx.GetUserName() v.JobID = job2.ID dao.CreateEntity(db, v) } } if len(jobExt.JobImgs) > 0 { imgs, _ := dao.GetJobImgs(db, job2.ID) for _, v := range imgs { dao.DeleteEntity(db, v) } for _, v := range jobExt.JobImgs { v.LastOperator = ctx.GetUserName() v.JobID = job2.ID dao.CreateEntity(db, v) } } dao.Commit(db) } return err } type Store struct { model.ModelIDCULD OriginalName string `orm:"-" json:"originalName"` Name string `orm:"size(255)" json:"name"` CityCode int `orm:"default(0);null" json:"cityCode"` // todo ? DistrictCode int `orm:"default(0);null" json:"districtCode"` // todo ? Address string `orm:"size(255)" json:"address"` Tel1 string `orm:"size(32);index" json:"tel1"` Tel2 string `orm:"size(32);index" json:"tel2"` OpenTime1 int16 `json:"openTime1" validate:"max=2359,min=1,ltfield=CloseTime1"` // 930就表示9点半,用两个的原因是为了支持中午休息,1与2的时间段不能交叉,为0表示没有 CloseTime1 int16 `json:"closeTime1" validate:"max=2359,min=1` // 格式同上 OpenTime2 int16 `json:"openTime2" validate:"max=2359,min=1,ltfield=CloseTime2"` // 格式同上 CloseTime2 int16 `json:"closeTime2" validate:"max=2359,min=1` // 格式同上 Lng int `json:"-"` // 乘了10的6次方 Lat int `json:"-"` // 乘了10的6次方 DeliveryRangeType int8 `json:"deliveryRangeType"` // 参见相关常量定义 DeliveryRange string `orm:"type(text)" json:"deliveryRange"` // 如果DeliveryRangeType为DeliveryRangeTypePolygon,则为逗号分隔坐标,分号分隔的坐标点(坐标与Lng和Lat一样,都是整数),比如 121361504,31189308;121420555,31150238。否则为半径,单位为米 Status int `json:"status"` AutoEnableAt *time.Time `orm:"type(datetime);null" json:"autoEnableAt"` // 自动营业时间(临时休息用) ChangePriceType int8 `json:"changePriceType"` // 修改价格类型,即是否需要审核 SMSNotify int8 `orm:"column(sms_notify);" json:"smsNotify"` // 是否通过短信接收订单消息(每天只推一条) SMSNotifyMark int8 `orm:"column(sms_notify_mark);" json:"smsNotifyMark"` //今天是否已经推送过订单消息 AutoReplyType int8 `json:"autoReplyType"` // 订单评价自动回复类型 LinkStoreID int `orm:"column(link_store_id);default(0);index" json:"linkStoreID"` // 关联门店ID StoreLevel string `orm:"default(C);size(32)" json:"storeLevel"` // 门店等级(筛选用,京西的) Comment string `orm:"size(255)" json:"comment"` //门店备注 PrinterDisabled int8 `orm:"default(0)" json:"printerDisabled"` // 是否禁用网络打印机 PrinterFontSize int8 `orm:"default(0)" json:"printerFontSize"` // 打印字体-1:小,0:正常,1:大 PrinterVendorID int `orm:"column(printer_vendor_id);" json:"printerVendorID"` PrinterSN string `orm:"size(32);column(printer_sn);index" json:"printerSN"` PrinterKey string `orm:"size(64)" json:"printerKey"` PrinterBindInfo string `orm:"size(1024)" json:"-"` IDCardFront string `orm:"size(255);column(id_card_front)" json:"idCardFront"` IDCardBack string `orm:"size(255);column(id_card_back)" json:"idCardBack"` IDCardHand string `orm:"size(255);column(id_card_hand)" json:"idCardHand"` Licence string `orm:"size(255)" json:"licence"` // 营业执照图片 LicenceCode string `orm:"size(32)" json:"licenceCode"` LicenceType int8 `json:"licenceType"` // 营业执照类型,0:个人,1:公司 LicenceCorpName string `orm:"size(64)" json:"licenceCorpName"` // 营业执照公司名称 LicenceOwnerName string `orm:"size(8)" json:"licenceOwnerName"` // 法人姓名 LicenceAddress string `orm:"size(255)" json:"licenceAddress"` // 地址 LicenceValid string `orm:"size(32)" json:"licenceValid"` // 有效期开始 LicenceExpire string `orm:"size(32)" json:"licenceExpire"` // 有效期结束 IDName string `orm:"size(8);column(id_name)" json:"idName"` // 身份证姓名 IDCode string `orm:"size(32);column(id_code)" json:"idCode"` // 身份证号 IDValid string `orm:"column(id_valid);size(32)" json:"idValid"` // 有效期开始 IDExpire string `orm:"column(id_expire);size(32)" json:"idExpire"` // 有效期结束 Licence2Image string `orm:"size(255)" json:"licence2Image"` // 食品经营许可证 Licence2Code string `orm:"size(32)" json:"licence2Code"` // 食品经营许可证编号 Licence2Valid string `orm:"size(32)" json:"licence2Valid"` // 有效期开始 Licence2Expire string `orm:"size(32)" json:"licence2Expire"` // 有效期结束 // MarketManName string `orm:"size(8)" json:"marketManName"` // 市场负责人姓名 MarketManPhone string `orm:"size(16)" json:"marketManPhone"` // 市场负责人电话 MarketManRole string `orm:"size(32)" json:"marketManRole"` // 市场负责人组(角色,单人) JxBrandFeeFactor int `json:"jxBrandFeeFactor"` // 京西品牌费因子 MarketAddFeeFactor int `json:"marketAddFeeFactor"` // 市场附加费因子 PayeeName string `orm:"size(8)" json:"payeeName"` // 收款人姓名 PayeeAccountNo string `orm:"size(255)" json:"payeeAccountNo"` // 收款账号 PayeeBankBranchName string `orm:"size(255)" json:"payeeBankBranchName"` // 开户支行 PayeeBankCode string `orm:"size(8)" json:"payeeBankCode"` // 开户行代码 PayPercentage int `json:"payPercentage"` OldPayPercentage int `json:"oldPayPercentage"` StoreFrontPic string `orm:"size(255)" json:"storeFrontPic"` //门面照 StoreInPic string `orm:"size(255)" json:"storeInPic"` //门店内照片 // OperatorName string `orm:"size(8)" json:"operatorName"` // 运营人姓名 OperatorPhone string `orm:"size(16)" json:"operatorPhone"` // 京东运营人电话 OperatorRole string `orm:"size(32)" json:"operatorRole"` // 京东运营人组(角色) OperatorPhone2 string `orm:"size(16)" json:"operatorPhone2"` // 美团运营人电话 OperatorRole2 string `orm:"size(32)" json:"operatorRole2"` // 美团运营人组(角色) OperatorPhone3 string `orm:"size(16)" json:"operatorPhone3"` // 饿百运营人电话 OperatorRole3 string `orm:"size(32)" json:"operatorRole3"` // 饿百运营人组(角色) PromoteInfo string `orm:"size(255)" json:"promoteInfo"` //门店公告(所有平台统一的公告) IsBoughtMatter int `json:"isBoughtMatter"` //这周是否申请过物料 SoundPercentage int `json:"soundPercentage"` //打印机声音大小比例 Banner string `orm:"size(9999)" json:"banner"` //门店商城bannar图 BrandID int `orm:"column(brand_id)" json:"brandID"` //品牌ID VendorStoreID string `orm:"column(vendor_store_id)" json:"vendorStoreID"` } func TempJob() (err error) { var ( db = dao.GetDB() ctx = jxcontext.AdminCtx storesJD []*Store storesMT []*Store ) db.Db.Using("c4") sql := ` SELECT a.*, b.vendor_store_id FROM store a JOIN store_map b ON a.id = b.store_id AND b.vendor_id = 0 AND b.deleted_at = ? AND b.vendor_store_id <> '' WHERE a.deleted_at = ? AND a.id <> 667281 AND a.id <> 667278 AND a.status IN (1,0) ` sqlParams := []interface{}{utils.DefaultTimeValue, utils.DefaultTimeValue} if err = dao.GetRows(db, &storesJD, sql, sqlParams); err != nil { return err } sql2 := ` SELECT a.*, b.vendor_store_id FROM store a JOIN store_map b ON a.id = b.store_id AND b.vendor_id = 1 AND b.deleted_at = ? AND b.vendor_store_id <> '' WHERE a.deleted_at = ? AND a.id <> 667281 AND a.id <> 667278 AND a.status IN (1,0) ` sqlParams2 := []interface{}{utils.DefaultTimeValue, utils.DefaultTimeValue} if err = dao.GetRows(db, &storesMT, sql2, sqlParams2); err != nil { return err } db.Db.Using("default") task := tasksch.NewParallelTask("TempJob", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(true), ctx, func(task2 *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { step := batchItemList[0].(int) switch step { case 0: task := tasksch.NewParallelTask("TempJob1", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(true), ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { store := batchItemList[0].(*Store) job := &model.Job{ UserID: "906380C7390E11EB8831525400C36BDA", JobCategoryID: 3, Title: "京东(" + store.Name + ")", Content: "领取任务后,需要尽快去完成,并提交截图。如超时未完成,任务会被取消", Count: 1000, AvgPrice: 300, TotalPrice: 300000, Status: 0, Address: store.Address, StoreURL: store.VendorStoreID, SurplusCount: 1000, JobLimitAt: 72, AuditLimitAt: 168, LimitCountType: 3, VendorID: 0, CashbackType: 1, JobLat: jxutils.IntCoordinate2Standard(store.Lat), JobLng: jxutils.IntCoordinate2Standard(store.Lng), Lng: store.Lng, Lat: store.Lat, JobCityCode: store.CityCode, } finishAt := utils.Str2Time("2021-12-31 00:00:00") job.FinishedAt = &finishAt dao.WrapAddIDCULDEntity(job, ctx.GetUserName()) if err = dao.CreateEntity(db, job); err == nil { jobsteps, _ := dao.GetJobSteps(db, 171) for _, v := range jobsteps { jobStep := &model.JobStep{ JobID: job.ID, StepCount: v.StepCount, Content: v.Content, Img: v.Img, Type: v.Type, } dao.WrapAddIDCULDEntity(jobStep, ctx.GetUserName()) err = dao.CreateEntity(db, jobStep) } jobImgs, _ := dao.GetJobImgs(db, 171) for _, v := range jobImgs { jobImg := &model.JobImg{ JobID: job.ID, Img: v.Img, } dao.WrapAddIDCULEntity(jobImg, ctx.GetUserName()) err = dao.CreateEntity(db, jobImg) } } return retVal, err }, storesJD) tasksch.HandleTask(task, task2, true).Run() task.GetResult(0) case 1: task := tasksch.NewParallelTask("TempJob2", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(true), ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { store := batchItemList[0].(*Store) job := &model.Job{ UserID: "906380C7390E11EB8831525400C36BDA", JobCategoryID: 3, Title: "美团(" + store.Name + ")", Content: "领取任务后,需要尽快去完成,并提交截图。如超时未完成,任务会被取消", Count: 1000, AvgPrice: 300, TotalPrice: 300000, Status: 0, Address: store.Address, StoreURL: store.VendorStoreID, SurplusCount: 1000, JobLimitAt: 72, AuditLimitAt: 168, LimitCountType: 3, VendorID: 1, CashbackType: 1, JobLat: jxutils.IntCoordinate2Standard(store.Lat), JobLng: jxutils.IntCoordinate2Standard(store.Lng), Lng: store.Lng, Lat: store.Lat, JobCityCode: store.CityCode, } finishAt := utils.Str2Time("2021-12-31 00:00:00") job.FinishedAt = &finishAt dao.WrapAddIDCULDEntity(job, ctx.GetUserName()) if err = dao.CreateEntity(db, job); err == nil { jobsteps, _ := dao.GetJobSteps(db, 173) for _, v := range jobsteps { jobStep := &model.JobStep{ JobID: job.ID, StepCount: v.StepCount, Content: v.Content, Img: v.Img, Type: v.Type, } dao.WrapAddIDCULDEntity(jobStep, ctx.GetUserName()) err = dao.CreateEntity(db, jobStep) } jobImgs, _ := dao.GetJobImgs(db, 173) for _, v := range jobImgs { jobImg := &model.JobImg{ JobID: job.ID, Img: v.Img, } dao.WrapAddIDCULEntity(jobImg, ctx.GetUserName()) err = dao.CreateEntity(db, jobImg) } } return retVal, err }, storesMT) tasksch.HandleTask(task, task2, true).Run() task.GetResult(0) } return retVal, err }, []int{0, 1}) tasksch.HandleTask(task, nil, true).Run() task.GetID() return err } func GetUnionActList(ctx *jxcontext.Context, vendorID, actType int) (actList []*partner.ActivityList, err error) { if handler := partner.GetHandler(vendorID); handler != nil { actList, err = handler.GetUnionActList(ctx, actType) } return actList, err } func ShareUnionLink(ctx *jxcontext.Context, vendorID, linkType int) (link string, err error) { return partner.GetHandler(vendorID).ShareUnionLink(ctx, linkType) }