package apply import ( "context" "lone-services/pkg/redis" "lone-services/pkg/rpcclient" "lone-services/pkg/utils" "lone-services/pkg/wecomrpc" chorepb "lone-services/rpc/chore/pb" "lone-services/services/product/internal/dao" "strconv" "time" jsoniter "github.com/json-iterator/go" "github.com/zeromicro/go-zero/core/logx" ) const ( applyProductEditKey = "apply:edit:product:" applyEditTimeOut = 60 * 24 // 分钟 applyMd5Key = "keys:" ) type Data struct { Uid int `json:"uid"` Id int `json:"id"` Reason string `json:"reason"` Status uint8 `json:"status"` // 9待审 1通过 2驳回 0未申请 Time string `json:"time"` Type uint8 `json:"type"` } func redisKey(uId, objId int) string { return applyProductEditKey + strconv.Itoa(uId) + "_" + strconv.Itoa(objId) } func md5MapKey(md5 string) string { return applyProductEditKey + applyMd5Key + md5 } func Get(ctx context.Context, uId, objId int) (Data, error) { var data Data if redis.Client == nil { return data, nil } result, err := redis.Client.Get(ctx, redisKey(uId, objId)).Result() if err == redis.Nil { return data, nil } if err != nil { return data, err } if len(result) < utils.NumberFive { return data, nil } if err = jsoniter.Unmarshal([]byte(result), &data); err != nil { return data, err } return data, nil } // Del 删除申请缓存(编辑成功后清除) func Del(ctx context.Context, uId, objId int) error { if redis.Client == nil { return nil } return redis.Client.Del(ctx, redisKey(uId, objId)).Err() } func Set(ctx context.Context, choreSvcName, wecomSvcName string, uId, objId int, reason string) error { data := Data{ Uid: uId, Id: objId, Reason: reason, Status: dao.ApplyStatusWaitPass, Type: dao.EditLogTypeProduct, Time: time.Now().Format(time.DateTime), } md5, err := saveRedis(ctx, &data) if err != nil { return err } if err = createApplyLog(ctx, choreSvcName, uId, objId, dao.EditLogTypeProduct, dao.ApplyStatusWaitPass, reason); err != nil { _ = Del(ctx, uId, objId) logx.WithContext(ctx).Errorf("action apply log create: %v", err) return err } wecomrpc.SendMessage(ctx, wecomSvcName, wecomrpc.SendMessageJob{ BizId: objId, BizType: wecomrpc.BizTypeProduct, Type: wecomrpc.ProductApplyReview, Reason: reason, Key: md5, }) return nil } func saveRedis(ctx context.Context, data *Data) (string, error) { if redis.Client == nil { return "", nil } key := redisKey(data.Uid, data.Id) md5 := utils.MD5Encrypt(strconv.Itoa(data.Uid) + strconv.Itoa(data.Id) + data.Time) payload, err := jsoniter.Marshal(data) if err != nil { return md5, err } expire := time.Duration(applyEditTimeOut) * time.Minute if err = redis.Client.Set(ctx, key, string(payload), expire).Err(); err != nil { return md5, err } if data.Status == dao.ApplyStatusWaitPass { if err = redis.Client.Set(ctx, md5MapKey(md5), key, expire).Err(); err != nil { return md5, err } } return md5, nil } func CanEdit(ctx context.Context, uId, objId int) bool { data, err := Get(ctx, uId, objId) if err != nil { logx.WithContext(ctx).Errorf("apply get: %v", err) return false } return data.Status == dao.ApplyStatusDone } func Pass(ctx context.Context, choreSvcName, wecomSvcName string, uId, objId int) error { data, err := Get(ctx, uId, objId) if err != nil { return err } if data.Status == dao.ApplyStatusDone { return nil } if data.Status != dao.ApplyStatusWaitPass && data.Status != 0 { return errAlreadyHandled } reason := data.Reason if data.Status == 0 { data = Data{ Uid: uId, Id: objId, Reason: "临时通过", Type: dao.EditLogTypeProduct, Time: time.Now().Format(time.DateTime), } reason = data.Reason } data.Status = dao.ApplyStatusDone if data.Time == "" { data.Time = time.Now().Format(time.DateTime) } if _, err = saveRedis(ctx, &data); err != nil { return err } if err = createApplyLog(ctx, choreSvcName, uId, objId, dao.EditLogTypeProduct, dao.ApplyStatusDone, reason); err != nil { logx.WithContext(ctx).Errorf("action apply log pass: %v", err) return err } wecomrpc.SendMessage(ctx, wecomSvcName, wecomrpc.SendMessageJob{ BizId: objId, BizType: wecomrpc.BizTypeProduct, Type: wecomrpc.ProductNeedReview, Action: dao.ApplyStatusDone, Reason: reason, }) return nil } func createApplyLog(ctx context.Context, choreSvcName string, adminId, objId int, typ, status uint8, reason string) error { cli, err := rpcclient.Get(choreSvcName) if err != nil { return err } _, err = chorepb.NewChoreClient(cli.Conn()).InternalActionApplyLogCreate(ctx, &chorepb.InternalActionApplyLogCreateReq{ AdminId: int64(adminId), ObjId: int64(objId), Type: uint32(typ), Reason: reason, Status: uint32(status), }) return err } // errAlreadyHandled 申请已驳回等不可再通过 var errAlreadyHandled = errApply("申请状态不可通过") type errApply string func (e errApply) Error() string { return string(e) } // IsAlreadyHandled 是否为「状态不可通过」 func IsAlreadyHandled(err error) bool { _, ok := err.(errApply) return ok }