package apply import ( "context" "lone-services/pkg/redis" "lone-services/pkg/utils" "lone-services/services/product/internal/dao" "lone-services/services/product/internal/model" "lone-services/services/product/internal/wecom" "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:" ) // Data Redis 中的编辑申请状态 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 } // Get 读取申请状态 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() } // Set 发起编辑申请:写 Redis + action_apply_log,企微消息预留 func Set(ctx context.Context, 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 } add := dao.ActionApplyLogCreate{ AdminId: uId, ObjId: objId, Type: dao.EditLogTypeProduct, Reason: reason, Status: dao.ApplyStatusWaitPass, } err = model.ActionApplyLogModel{}.Init().Create(&add) if err != nil { _ = Del(ctx, uId, objId) logx.WithContext(ctx).Errorf("action apply log create: %v", err) return err } wecom.NotifyProduct(wecom.NotifyJob{ ObjId: objId, ObjType: dao.EditLogTypeProduct, Type: wecom.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 } // CanEdit 是否已获编辑权限(申请通过) 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 } // Pass 临时:将编辑申请置为通过(企微审批对接后删除) func Pass(ctx context.Context, 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 } add := dao.ActionApplyLogCreate{ AdminId: uId, ObjId: objId, Type: dao.EditLogTypeProduct, Reason: reason, Status: dao.ApplyStatusDone, } err = model.ActionApplyLogModel{}.Init().Create(&add) if err != nil { logx.WithContext(ctx).Errorf("action apply log pass: %v", err) return err } wecom.NotifyProduct(wecom.NotifyJob{ ObjId: objId, ObjType: dao.EditLogTypeProduct, Type: wecom.ProductNeedReview, Action: dao.ApplyStatusDone, Reason: reason, }) return nil } // 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 }