diff --git a/business/jxstore/cms/cms.go b/business/jxstore/cms/cms.go index a9e885025..55acc3114 100644 --- a/business/jxstore/cms/cms.go +++ b/business/jxstore/cms/cms.go @@ -143,6 +143,7 @@ func InitServiceInfo(version string, buildTime time.Time, gitCommit string) { "supplementType": model.SupplementTypeName, "operateType": model.OperateTypeName, "thingType": model.ThingTypeName, + "apiFunctionName": model.ApiFunctionName, }, } } diff --git a/business/jxstore/cms/store_sku.go b/business/jxstore/cms/store_sku.go index 8d736c421..8aa2438ee 100644 --- a/business/jxstore/cms/store_sku.go +++ b/business/jxstore/cms/store_sku.go @@ -13,6 +13,8 @@ import ( "time" "unicode" + "git.rosy.net.cn/jx-callback/globals/refutil" + "git.rosy.net.cn/jx-callback/business/jxstore/event" "git.rosy.net.cn/baseapi" @@ -1037,7 +1039,7 @@ func updateStoresSkusWithoutSync(ctx *jxcontext.Context, db *dao.DaoDB, storeIDs if tmpStatus := getSkuSaleStatus(inSkuBind, skuBindInfo); tmpStatus != model.StoreSkuBindStatusNA { skuBind.Status = tmpStatus } - // err = AddEventDetail(model.OperateAdd, v.RealSkuID, model.ThingTypeSku, storeID, ctx.GetTrackInfo(), "", "") + err = AddEventDetail(model.OperateAdd, v.RealSkuID, model.ThingTypeSku, storeID, ctx.GetTrackInfo(), "", "") setStoreSkuBindStatus(skuBind, model.SyncFlagNewMask) dao.WrapAddIDCULDEntity(skuBind, userName) // globals.SugarLogger.Debug(utils.Format4Output(skuBind, false)) @@ -1061,9 +1063,10 @@ func updateStoresSkusWithoutSync(ctx *jxcontext.Context, db *dao.DaoDB, storeIDs } } } else { + beforeMsg := *v skuBind = &v.StoreSkuBind if skuBindInfo.IsFocus == -1 && isCanChangePrice { - // err = AddEventDetail(model.OperateDelete, skuBind.SkuID, model.ThingTypeSku, storeID, ctx.GetTrackInfo(), "", "") + err = AddEventDetail(model.OperateDelete, skuBind.SkuID, model.ThingTypeSku, storeID, ctx.GetTrackInfo(), "", "") if num, err = dao.DeleteEntityLogically(db, skuBind, map[string]interface{}{ model.FieldStatus: model.StoreSkuBindStatusDeleted, model.FieldJdSyncStatus: model.SyncFlagDeletedMask, @@ -1117,12 +1120,13 @@ func updateStoresSkusWithoutSync(ctx *jxcontext.Context, db *dao.DaoDB, storeIDs skuBind.StatusSaleEnd = skuBindInfo.StatusSaleEnd } } - // if updateFieldMap != nil { - // afterData := utils.MustMarshal(updateFieldMap) - // mapresult := refutil.FindMapAndStructMixed(updateFieldMap, skuBind) - // beforeData := utils.MustMarshal(mapresult) - // AddEventDetail(model.OperateUpdate, v.RealSkuID, model.ThingTypeSku, storeID, ctx.GetTrackInfo(), string(beforeData), string(afterData)) - // } + if len(updateFieldMap) > 0 { + mapAfter := refutil.FindMapAndStructMixed(updateFieldMap, skuBind) + afterData := utils.MustMarshal(mapAfter) + mapBefore := refutil.FindMapAndStructMixed(updateFieldMap, beforeMsg) + beforeData := utils.MustMarshal(mapBefore) + err = AddEventDetail(model.OperateUpdate, v.RealSkuID, model.ThingTypeSku, storeID, ctx.GetTrackInfo(), string(beforeData), string(afterData)) + } if len(updateFieldMap) > 0 { updateFieldMap[model.FieldJdSyncStatus] = 1 updateFieldMap[model.FieldEbaiSyncStatus] = 1 @@ -1225,6 +1229,7 @@ func updateStoreSkusSaleWithoutSync(ctx *jxcontext.Context, storeID int, skuBind for _, skuBind := range storeSkuList { if v := skuBindSkuInfosMap[skuBind.SkuID]; v != nil && v.IsSale != 0 { if !(!utils.IsTimeZero(autoSaleTime) && ignoreDontSale && skuBind.Status == model.StoreSkuBindStatusDontSale) { + statusResult := skuBind.Status if v.IsSale == -1 || !utils.IsTimeZero(autoSaleTime) { skuBind.Status = model.StoreSkuBindStatusDontSale } else if v.IsSale == 1 { @@ -1243,6 +1248,21 @@ func updateStoreSkusSaleWithoutSync(ctx *jxcontext.Context, storeID int, skuBind } else { kvs["AutoSaleAt"] = autoSaleTime } + var status int + if v.IsSale == -1 { + status = model.StoreSkuBindStatusDontSale + } else { + status = model.StoreSkuBindStatusNormal + } + if status != statusResult { + mapAfter := make(map[string]interface{}) + mapAfter["Status"] = status + afterData := utils.MustMarshal(mapAfter) + mapBefore := make(map[string]interface{}) + mapBefore["Status"] = statusResult + beforeData := utils.MustMarshal(mapBefore) + err = AddEventDetail(model.OperateUpdate, v.SkuID, model.ThingTypeSku, storeID, ctx.GetTrackInfo(), string(beforeData), string(afterData)) + } if num, err = dao.UpdateEntityLogically(db, skuBind, kvs, userName, nil); err != nil { dao.Rollback(db) return nil, err @@ -1300,7 +1320,6 @@ func CopyStoreSkus(ctx *jxcontext.Context, fromStoreID int, toStoreIDs []int, co if copyMode != CopyStoreSkuModeFresh && copyMode != CopyStoreSkuModeUpdate && copyMode != CopyStoreSkuModeUpdatePrice { return 0, fmt.Errorf("不支持的拷贝模式:%s", copyMode) } - db := dao.GetDB() fromStore, err := checkStoreExisting(db, fromStoreID) if err != nil { @@ -1559,6 +1578,15 @@ func CopyStoreSkus(ctx *jxcontext.Context, fromStoreID int, toStoreIDs []int, co globals.SugarLogger.Debugf("CopyStoreSkus fromStoreID:%d, toStoreID:%d, trackInfo:%s num3:%d", fromStoreID, toStoreID, ctx.GetTrackInfo(), num2) dao.Commit(db) } + mapAfter := make(map[string]interface{}) + mapAfter["ToStoreIDs"] = toStoreIDs + mapAfter["CopyMode"] = copyMode + mapAfter["IsScale"] = isScale + afterData := utils.MustMarshal(mapAfter) + mapBefore := make(map[string]interface{}) + mapBefore["FromStoreID"] = fromStoreID + beforeData := utils.MustMarshal(mapBefore) + err = AddEventDetail(model.OperateCopyStoreSkus, 0, model.ThingTypeSku, fromStoreID, ctx.GetTrackInfo(), string(beforeData), string(afterData)) return num, errList.GetErrListAsOne() } diff --git a/business/jxstore/event/event.go b/business/jxstore/event/event.go index 7a356cfe4..ebd7b5ab2 100644 --- a/business/jxstore/event/event.go +++ b/business/jxstore/event/event.go @@ -4,6 +4,9 @@ import ( "strings" "time" + "git.rosy.net.cn/baseapi/utils" + + "git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/business/model/dao" "git.rosy.net.cn/jx-callback/business/model" @@ -52,3 +55,43 @@ func AddOperateEventDetail(operateEventDetail *model.OperateEventDetail) (err er dao.Commit(db) return err } + +func DeleteOperateEventAndDetail(ctx *jxcontext.Context, deleteTime time.Time) (err error) { + db := dao.GetDB() + dao.Begin(db) + defer func() { + if r := recover(); r != nil { + dao.Rollback(db) + panic(r) + } + }() + dao.DeleteOperateEventDetail(db, deleteTime) + dao.DeleteOperateEvent(db, deleteTime) + dao.Commit(db) + return err +} + +func GetOperateEvents(ctx *jxcontext.Context, apiFunction, name string, operateType int, skuIDs, storeIDs []int, fromTime, toTime string, offset, pageSize int) (pageInfo *model.PagedInfo, err error) { + var ( + fromTimeP time.Time + toTimeP time.Time + ) + db := dao.GetDB() + if fromTime != "" { + fromTimeP = utils.Str2Time(fromTime) + } + if toTime != "" { + toTimeP = utils.Str2Time(toTime) + } + pageSize = jxutils.FormalizePageSize(pageSize) + offset = jxutils.FormalizePageOffset(offset) + operateEventExt, totalCount, err := dao.GetOperateEvents(db, apiFunction, name, operateType, skuIDs, storeIDs, fromTimeP, toTimeP, offset, pageSize) + if err != nil { + return pageInfo, err + } + pageInfo = &model.PagedInfo{ + Data: operateEventExt, + TotalCount: totalCount, + } + return pageInfo, err +} diff --git a/business/jxstore/misc/misc.go b/business/jxstore/misc/misc.go index daffca4fe..eed3456e2 100644 --- a/business/jxstore/misc/misc.go +++ b/business/jxstore/misc/misc.go @@ -6,6 +6,8 @@ import ( "sync" "time" + "git.rosy.net.cn/jx-callback/business/jxstore/event" + "git.rosy.net.cn/jx-callback/business/jxstore/report" "git.rosy.net.cn/baseapi/utils" @@ -226,6 +228,8 @@ func doDailyWork() { orderman.RefreshOrdersWithoutJxStoreID(jxcontext.AdminCtx, "", "", true, true) //刷新京东门店的等级 cms.RefreshJdLevel(jxcontext.AdminCtx) + //删除操作日志 + event.DeleteOperateEventAndDetail(jxcontext.AdminCtx, time.Now().AddDate(0, 0, -7)) } func RefreshRealMobile(ctx *jxcontext.Context, vendorID int, fromTime, toTime time.Time, isAsync, isContinueWhenError bool) (hint string, err error) { diff --git a/business/model/const.go b/business/model/const.go index 00b9bf488..f3292b7be 100644 --- a/business/model/const.go +++ b/business/model/const.go @@ -106,9 +106,10 @@ var ( } OperateTypeName = map[int]string{ - OperateAdd: "新增", - OperateUpdate: "修改", - OperateDelete: "删除", + OperateAdd: "新增", + OperateUpdate: "修改", + OperateDelete: "删除", + OperateCopyStoreSkus: "复制门店商品", } ThingTypeName = map[int]string{ @@ -118,6 +119,12 @@ var ( ThingTypeStore: "门店", } + ApiFunctionName = map[string]string{ + "UpdateStoresSkus": "门店商品管理", + "UpdateStoresSkusSale": "门店商品可售状态修改", + "CopyStoreSkus": "京西门店商品复制到京西", + } + MultiStoresVendorMap = map[int]int{ VendorIDJD: 1, VendorIDMTWM: 0, @@ -255,9 +262,10 @@ const ( ) const ( - OperateAdd = 1 //新增操作 - OperateUpdate = 2 //修改操作 - OperateDelete = 4 //删除操作 + OperateAdd = 2 //新增操作 + OperateUpdate = 1 //修改操作 + OperateDelete = 4 //删除操作 + OperateCopyStoreSkus = 3 //复制门店商品 ) const ( diff --git a/business/model/dao/event.go b/business/model/dao/event.go new file mode 100644 index 000000000..941be3e50 --- /dev/null +++ b/business/model/dao/event.go @@ -0,0 +1,101 @@ +package dao + +import ( + "time" + + "git.rosy.net.cn/baseapi/utils" + + "git.rosy.net.cn/jx-callback/business/model" +) + +type OperateEventExt struct { + model.OperateEvent + Detail []*model.OperateEventDetail + Name string `json:"name"` +} + +func DeleteOperateEventDetail(db *DaoDB, deleteTime time.Time) (err error) { + sql := ` + DELETE FROM a + USING operate_event_detail a,operate_event b + WHERE a.access_uuid = b.access_uuid + AND b.created_at < ? + ` + sqlParams := []interface{}{ + deleteTime, + } + _, err = ExecuteSQL(db, sql, sqlParams...) + return err +} + +func DeleteOperateEvent(db *DaoDB, deleteTime time.Time) (err error) { + sql := ` + DELETE FROM operate_event + WHERE created_at < ? + ` + sqlParams := []interface{}{ + deleteTime, + } + _, err = ExecuteSQL(db, sql, sqlParams...) + return err +} + +func GetOperateEvents(db *DaoDB, apiFunction, name string, operateType int, skuIDs, storeIDs []int, fromTime, toTime time.Time, offset, pageSize int) (operateEventExt []*OperateEventExt, totalCount int, err error) { + sql := ` + SELECT SQL_CALC_FOUND_ROWS DISTINCT a.*, c.name + FROM operate_event a + LEFT JOIN operate_event_detail b ON a.access_uuid = b.access_uuid + LEFT JOIN user c ON c.user_id = a.user_id + WHERE 1=1 + ` + sqlParams := []interface{}{} + if name != "" { + sql += " AND c.name LIKE ?" + sqlParams = append(sqlParams, "%"+name+"%") + } + if !utils.IsTimeZero(fromTime) { + sql += " AND a.created_at >= ?" + sqlParams = append(sqlParams, fromTime) + } + if !utils.IsTimeZero(toTime) { + sql += " AND a.created_at <= ?" + sqlParams = append(sqlParams, toTime) + } + if apiFunction != "" { + sql += " AND a.api_function = ?" + sqlParams = append(sqlParams, apiFunction) + } + if operateType != 0 { + sql += " AND b.operate_type = ?" + sqlParams = append(sqlParams, operateType) + } + if len(skuIDs) > 0 { + sql += " AND b.thing_id IN (" + GenQuestionMarks(len(skuIDs)) + ")" + sqlParams = append(sqlParams, skuIDs) + } + if len(storeIDs) > 0 { + sql += " AND b.store_id IN (" + GenQuestionMarks(len(storeIDs)) + ")" + sqlParams = append(sqlParams, storeIDs) + } + sql += " LIMIT ? OFFSET ?" + sqlParams = append(sqlParams, pageSize, offset) + Begin(db) + defer Commit(db) + if err = GetRows(db, &operateEventExt, sql, sqlParams...); err == nil { + totalCount = GetLastTotalRowCount(db) + } + for _, v := range operateEventExt { + var details []*model.OperateEventDetail + sql := ` + SELECT * + FROM operate_event_detail + WHERE access_uuid = ? + ` + sqlParams := []interface{}{ + v.AccessUUID, + } + err = GetRows(db, &details, sql, sqlParams...) + v.Detail = details + } + return operateEventExt, totalCount, err +} diff --git a/business/model/event.go b/business/model/event.go index 697dc803a..abb79ba88 100644 --- a/business/model/event.go +++ b/business/model/event.go @@ -24,8 +24,8 @@ type OperateEventDetail struct { ThingType int `json:"thingType"` //各字段类型 StoreID int `orm:"column(store_id)" json:"storeID"` AccessUUID string `orm:"column(access_uuid)" json:"accessUUID"` - BeforeData string `orm:"size(255)" json:"beforeData"` - AfterData string `orm:"size(255)" json:"afterData"` + BeforeData string `orm:"size(3200)" json:"beforeData"` + AfterData string `orm:"size(3200)" json:"afterData"` } func (v *OperateEventDetail) TableIndex() [][]string { diff --git a/controllers/event.go b/controllers/event.go new file mode 100644 index 000000000..09cb331a5 --- /dev/null +++ b/controllers/event.go @@ -0,0 +1,37 @@ +package controllers + +import ( + "git.rosy.net.cn/jx-callback/business/jxstore/event" + "git.rosy.net.cn/jx-callback/business/jxutils" + "github.com/astaxie/beego" +) + +// 操作事件明细相关API +type EventController struct { + beego.Controller +} + +// @Title 查询操作日志事件明细 +// @Description 查询操作日志事件明细 +// @Param token header string true "认证token" +// @Param apiFunction query string false "功能名" +// @Param name query string false "操作人姓名,支持模糊查询" +// @Param operateType query int false "操作类型,1为修改,2为新增,4为删除" +// @Param skuIDs query string false "商品ID列表" +// @Param storeIDs query string false "门店ID列表" +// @Param fromTime query string false "开始日期(包含),格式(2006-01-02 00:00:00)" +// @Param toTime query string false "结束日期(包含),格式(2006-01-02 00:00:00)" +// @Param offset query int false "门店列表起始序号(以0开始,缺省为0)" +// @Param pageSize query int false "门店列表页大小(缺省为30)" +// @Success 200 {object} controllers.CallResult +// @Failure 200 {object} controllers.CallResult +// @router /GetOperateEvents [get] +func (c *EventController) GetOperateEvents() { + var storeIDList, skuIDList []int + c.callGetOperateEvents(func(params *tEventGetOperateEventsParams) (retVal interface{}, errCode string, err error) { + if jxutils.Strings2Objs(params.StoreIDs, &storeIDList, params.SkuIDs, &skuIDList); err == nil { + retVal, err = event.GetOperateEvents(params.Ctx, params.ApiFunction, params.Name, params.OperateType, skuIDList, storeIDList, params.FromTime, params.ToTime, params.Offset, params.PageSize) + } + return retVal, "", err + }) +} diff --git a/routers/commentsRouter_controllers.go b/routers/commentsRouter_controllers.go index 5fb650f4b..dd3386fdb 100644 --- a/routers/commentsRouter_controllers.go +++ b/routers/commentsRouter_controllers.go @@ -394,6 +394,15 @@ func init() { Filters: nil, Params: nil}) + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:EventController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:EventController"], + beego.ControllerComments{ + Method: "GetOperateEvents", + Router: `/GetOperateEvents`, + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:FinancialController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:FinancialController"], beego.ControllerComments{ Method: "GetOrdersFinancial", diff --git a/routers/router.go b/routers/router.go index b35e7e0bf..4c5f056aa 100644 --- a/routers/router.go +++ b/routers/router.go @@ -136,6 +136,11 @@ func init() { &controllers.YongHuiController{}, ), ), + beego.NSNamespace("/event", + beego.NSInclude( + &controllers.EventController{}, + ), + ), ) beego.AddNamespace(ns) @@ -149,6 +154,7 @@ func init() { beego.AutoRouter(&controllers.WeixinController{}) beego.AutoRouter(&controllers.DingDingController{}) beego.AutoRouter(&controllers.WXPayController{}) + beego.AutoRouter(&controllers.EventController{}) // 如下都是用于检测存活的空接口 beego.Any("/", func(ctx *beecontext.Context) {