212 lines
4.8 KiB
Go
212 lines
4.8 KiB
Go
package apply
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"strconv"
|
|
"time"
|
|
|
|
"product/internal/dao"
|
|
"product/internal/model"
|
|
"product/internal/wecom"
|
|
|
|
"pkg.local/log"
|
|
"pkg.local/redis"
|
|
"pkg.local/utils"
|
|
)
|
|
|
|
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 = json.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)
|
|
log.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 := json.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 {
|
|
log.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 {
|
|
log.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
|
|
}
|