package cms import ( "fmt" "strings" "time" "git.rosy.net.cn/jx-callback/globals" "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/model" "git.rosy.net.cn/jx-callback/business/model/dao" ) const ( AcceptMaxCount = 2 CancelMaxCount = 5 ) var ( DayTimeBegin time.Time DayTimeEnd time.Time WeekTimeBegin time.Time WeekTimeEnd time.Time ) func init() { DayTimeBegin = utils.Str2Time(utils.Time2Str(utils.Time2Date(time.Now())) + " 00:00:00") DayTimeEnd = utils.Str2Time(utils.Time2Str(utils.Time2Date(time.Now())) + " 23:59:59") WeekTimeBegin, WeekTimeEnd = getWeekTime() } func getWeekTime() (weekTimeBegin, weekTimeEnd time.Time) { offset := int(time.Now().Weekday() - 1) if offset == -1 { offset = -6 } weekTimeBegin = time.Now().AddDate(0, 0, offset) weekTimeEnd = weekTimeBegin.AddDate(0, 0, 7) return weekTimeBegin, weekTimeEnd } func PublishJob(ctx *jxcontext.Context, job *model.Job) (err error) { var ( db = dao.GetDB() ) //需根据任务类型做一些参数判断,比如门店商品链接,地址 // switch job.JobCategoryID { // case 1: // } if ctx.GetUserID() != job.UserID { return fmt.Errorf("用户信息已过期,请重新登录!") } //发布任务要扣除任务总额的保证金,不够扣就要进行充值 userBill, err := dao.GetUserBill(db, job.UserID, "") if userBill == nil { return fmt.Errorf("未查询到该用户的账单!") } job.TotalPrice = job.Count * job.AvgPrice if userBill.AccountBalance < job.TotalPrice { job.Status = model.JobStatusFailed } else { job.Status = model.JobStatusDoing } if job.Count <= 0 { return fmt.Errorf("任务数量不能为0!") } if job.UserID == "" { return fmt.Errorf("任务发起人不能为空!") } jobs, err := dao.GetJobsNoPage(db, []string{job.UserID}, nil, nil, DayTimeBegin, DayTimeEnd, false) if len(jobs) > 0 { members, err := dao.GetUserMember(db, job.UserID, model.MemberTypeNormal) if err != nil { return err } if len(members) <= 0 { return fmt.Errorf("非会员一天只能发布一起任务,请确认!") } } if job.Address != "" && (job.Lng == 0 || job.Lat == 0) { lng, lat, err := api.AutonaviAPI.GetCoordinateFromAddressByPage(job.Address) if err != nil { return err } job.Lng = jxutils.StandardCoordinate2Int(lng) job.Lat = jxutils.StandardCoordinate2Int(lat) } dao.WrapAddIDCULEntity(job, ctx.GetUserName()) dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() err = dao.CreateEntity(db, job) for _, v := range job.JobSteps { dao.WrapAddIDCULEntity(v, ctx.GetUserName()) v.JobID = job.ID err = dao.CreateEntity(db, v) } for _, v := range job.JobImgs { dao.WrapAddIDCULEntity(v, ctx.GetUserName()) v.JobID = job.ID err = dao.CreateEntity(db, v) } if err != nil { dao.Rollback(db) } //发布任务要扣除任务总额的保证金,不够扣就要进行充值 if err == nil && job.Status != model.JobStatusFailed { //1、账户支出增加一条记录 if err = financial.AddBillExpend(db, userBill.BillID, model.BillTypeDeposit, job.TotalPrice); err != nil { dao.Rollback(db) } //2、账户表余额减少相应值 userBill.AccountBalance -= job.TotalPrice if _, err = dao.UpdateEntity(db, userBill, "AccountBalance"); err != nil { dao.Rollback(db) } } dao.Commit(db) return 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) } }() //1、根据任务剩余数量退钱到账户余额中 userBill.AccountBalance += job.SurplusCount * job.AvgPrice if _, err = dao.UpdateEntity(db, userBill, "AccountBalance"); err != nil { dao.Rollback(db) } //2、账户收入增加一条记录 if err = financial.AddBillIncome(db, userBill.BillID, model.BillTypeJobCancelOverdue, job.SurplusCount*job.AvgPrice); err != nil { dao.Rollback(db) } //3、任务状态被取消 job.Status = model.JobStatusFailed if _, err = dao.UpdateEntity(db, job, "Status"); err != nil { dao.Rollback(db) } dao.Commit(db) return err } func GetJobs(ctx *jxcontext.Context, userIDs []string, categoryIDs, statuss, vendorIDs []int, includeStep bool, fromTime, toTime string, pageSize, offset int) (pagedInfo *model.PagedInfo, err error) { return dao.GetJobs(dao.GetDB(), userIDs, categoryIDs, statuss, vendorIDs, includeStep, utils.Str2Time(fromTime), utils.Str2Time(toTime), pageSize, offset) } func AcceptJob(ctx *jxcontext.Context, jobID int) (errCode string, err error) { var ( db = dao.GetDB() userID = ctx.GetUserID() num int ) 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 errCode, fmt.Errorf("未找到该任务或该任务状态不正常,无法接单!") } num, err = checkJobOrders(db, 0, "<= "+utils.Int2Str(model.JobOrderStatusAccept), userID, utils.ZeroTimeValue, utils.ZeroTimeValue) if num >= AcceptMaxCount { return errCode, fmt.Errorf("每人最多接取" + utils.Int2Str(AcceptMaxCount) + "个任务,请核实!") } num, err = checkJobOrders(db, jobID, "<= "+utils.Int2Str(model.JobOrderStatusWaitAudit), userID, utils.ZeroTimeValue, utils.ZeroTimeValue) if num > 0 { return errCode, fmt.Errorf("您还有此任务未完成,请完成后再接取!") } // num, err = checkJobOrders(db, "= "+utils.Int2Str(model.JobOrderStatusAuditUnPass), userID, utils.ZeroTimeValue, utils.ZeroTimeValue) // if num > 0 { // return 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 errCode, fmt.Errorf("此任务只支持每人做一次!") } case model.JobLimitCountTypePDO: num, err = checkJobOrders(db, jobID, "<> "+utils.Int2Str(model.JobOrderStatusCancel), userID, DayTimeBegin, DayTimeEnd) if num > 0 { return errCode, fmt.Errorf("此任务只支持每人每天做一次!") } case model.JobLimitCountTypePWO: num, err = checkJobOrders(db, jobID, "<> "+utils.Int2Str(model.JobOrderStatusCancel), userID, WeekTimeBegin, WeekTimeEnd) if num > 0 { return errCode, fmt.Errorf("此任务只支持每人每周做一次!") } case model.JobLimitCountTypeNoLimit: default: return errCode, fmt.Errorf("不支持的任务限次类型!%v", job.LimitCountType) } jobOrder := &model.JobOrder{ JobID: jobID, JobOrderID: jxutils.GenJobOrderNo(), UserID: job.UserID, // Status: model.JobOrderStatusAccept, } //美团会员任务 if jobID == model.JobIDMtMembers { jobOrder.Status = model.JobOrderStatusSpec } else { jobOrder.Status = model.JobOrderStatusAccept } dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() if err = dao.CreateEntity(db, jobOrder); err != nil { dao.Rollback(db) } //用户接受任务,任务剩余次数-1 job.SurplusCount -= 1 if _, err = dao.UpdateEntity(db, job, "SurplusCount"); err != nil { dao.Rollback(db) } dao.Commit(db) //任务限时完成 checkLimitJobOrders(db, job, jobOrder) //特殊任务,如美团会员,是直接要支付 if jobID == model.JobIDMtMembers { userBill, err := dao.GetUserBill(db, ctx.GetUserID(), "") if err != nil { return errCode, err } if userBill.AccountBalance < job.AvgPrice { return model.ErrCodeAccountBalanceNotEnough, err } dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() //账户支出明细 //1、账户支出增加一条记录 if err = financial.AddBillExpend(db, userBill.BillID, model.BillTypeSpJob, job.AvgPrice); err != nil { dao.Rollback(db) } //2、账户表余额减少相应值 userBill.AccountBalance -= job.AvgPrice if _, err = dao.UpdateEntity(db, userBill, "AccountBalance"); err != nil { dao.Rollback(db) } dao.Commit(db) } return 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") job := &model.Job{} job.ID = jobID err = dao.GetEntity(db, &job) 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) } } else { userBill, err := dao.GetUserBill(db, job.UserID, "") if userBill == nil { return fmt.Errorf("未查询到该用户的账单!") } //如果状态不正常(取消或者过期)就要把这一笔退回去 //1、根据任务剩余数量退钱到任务保证金余额中 userBill.AccountBalance += job.AvgPrice if _, err = dao.UpdateEntity(db, userBill, "AccountBalance"); err != nil { dao.Rollback(db) } //2、账户收入增加一条记录 if err = financial.AddBillIncome(db, userBill.BillID, model.BillTypeJobCancelOverdue, job.AvgPrice); err != nil { dao.Rollback(db) } //3、任务订单状态被取消 jobOrder.Status = model.JobOrderStatusCancel if _, err = dao.UpdateEntity(db, jobOrder, "Status"); err != nil { dao.Rollback(db) } } dao.Commit(db) 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) { utils.AfterFuncWithRecover(time.Hour*time.Duration(job.JobLimitAt), func() { utils.CallFuncAsync(func() { globals.SugarLogger.Debugf("checkLimitJobOrders jobID: %v, jobOrderID: %v", job.ID, jobOrder.JobOrderID) jobOrders, _ := dao.GetJobOrdersNoPage(db, job.ID, jobOrder.JobOrderID, job.UserID, "", utils.ZeroTimeValue, utils.ZeroTimeValue, nil) if len(jobOrders) == 0 { return } jobOrder := jobOrders[0] if jobOrder.Status == model.JobOrderStatusCancel { return } jobOrder.Status = model.JobOrderStatusCancel dao.UpdateEntity(db, jobOrder, "Status") }) }) } func checkLimitAuditJobOrders(db *dao.DaoDB, job *model.Job, jobOrder *model.JobOrder) { utils.AfterFuncWithRecover(time.Hour*time.Duration(job.AuditLimitAt), func() { utils.CallFuncAsync(func() { globals.SugarLogger.Debugf("checkLimitAuditJobOrders jobID: %v, jobOrderID: %v", job.ID, jobOrder.JobOrderID) if jobOrder.Status == model.JobOrderStatusWaitAudit { err := AuditJob(jxcontext.AdminCtx, int(jobOrder.JobOrderID), model.JobOrderStatusAuditPass, "超时系统通过") if err != nil { globals.SugarLogger.Debugf("checkLimitAuditJobOrders err: %v jobID: %v, jobOrderID: %v", err, job.ID, jobOrder.JobOrderID) } } }) }) } 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 jobOrder2.JobID == 0 { return fmt.Errorf("未查询到相应的任务!") } if jobOrder2.Status >= model.JobOrderStatusWaitAudit { return fmt.Errorf("任务订单状态有误!") } if ctx.GetUserID() != jobOrder2.UserID { return fmt.Errorf("任务订单接收人有误!") } jobOrder2.Img = jobOrder.Img jobOrder2.Content = jobOrder.Content jobOrder2.SubmitAuditAt = time.Now() jobOrder2.Status = model.JobOrderStatusWaitAudit if _, err = dao.UpdateEntity(db, jobOrder2, "Img", "Content", "SubmitAuditAt", "Status"); err == nil { //审核定时 checkLimitAuditJobOrders(db, job, jobOrder2) } return err } func AuditJob(ctx *jxcontext.Context, jobOrderID, status int, comment 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 { return fmt.Errorf("任务发起人才能审核!") } //审核时,若此任务已过期或者已取消,不通过则应将此任务保证金退还给发起人,通过则应将单次任务保证金给接受人 //若此任务未过期,不通过则此任务剩余数量将+1,通过则应将单次任务保证金给接受人 jobOrder.Status = status jobOrder.Comment = comment jobOrder.AuditAt = time.Now() jobOrder.LastOperator = ctx.GetUserName() 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) } if status == model.JobOrderStatusAuditPass { //接收人账户增加任务单次 userBillJobOrder, err := dao.GetUserBill(db, jobOrder.UserID, "") userBillJobOrder.AccountBalance += job.AvgPrice if _, err = dao.UpdateEntity(db, userBillJobOrder, "AccountBalance"); err != nil { dao.Rollback(db) } //接收人账户收入增加一条 if err = financial.AddBillIncome(db, userBillJobOrder.BillID, model.BillTypeJob, job.AvgPrice); err != nil { dao.Rollback(db) } } else { if job.Status < 0 { userBill, err := dao.GetUserBill(db, job.UserID, "") //1、根据任务剩余数量退钱到任务保证金余额中 userBill.AccountBalance += job.AvgPrice if _, err = dao.UpdateEntity(db, userBill, "AccountBalance"); err != nil { dao.Rollback(db) } //2、账户收入增加一条记录 if err = financial.AddBillIncome(db, userBill.BillID, model.BillTypeJobAuditUnPassWithCancelOverdue, job.AvgPrice); err != nil { dao.Rollback(db) } } else { job.SurplusCount += 1 if _, err = dao.UpdateEntity(db, job, "SurplusCount"); err != nil { dao.Rollback(db) } } } dao.Commit(db) 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}, utils.ZeroTimeValue, utils.ZeroTimeValue, false) if err != nil { globals.SugarLogger.Debugf("RefreshJobStatus err :%v", err) return } for _, job := range jobs { if time.Now().Sub(job.FinishedAt) >= 0 { job.Status = model.JobStatusOverdue dao.UpdateEntity(db, job, "Status") } } 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 := &model.Job{} job.ID = model.JobIDMtMembers dao.GetEntity(db, job) if job != nil { job.Count += len(mtMembers) dao.UpdateEntity(db, job, "Count") } } return err } func RechargeMtMembers(ctx *jxcontext.Context, phone int) (err error) { var ( db = dao.GetDB() ) mtMember, err := dao.GetMtMember(db) if err != nil { return err } if err = api.MtMemberAPI.RechargeExchange(phone, mtMember.ShortLink); err == nil { mtMember.DeletedAt = time.Now() dao.UpdateEntity(db, mtMember, "DeletedAt") } return err }