- pending store op price change request.

This commit is contained in:
gazebo
2018-12-20 15:51:11 +08:00
parent 55f2445eb4
commit 5119791de2
12 changed files with 367 additions and 2 deletions

View File

@@ -46,6 +46,8 @@ func InitServiceInfo(version, buildDate, gitCommit string) {
"orderTypeName": model.OrderTypeName,
"storeDeliveryTypeName": scheduler.StoreDeliveryTypeName,
"taskStatusName": tasksch.TaskStatusName,
"opRequestType": model.RequestTypeName,
"opRequestStatusName": model.RequestStatusName,
},
}
Init()

View File

@@ -21,7 +21,7 @@ func init() {
}
func TestGetQiniuUploadToken(t *testing.T) {
token, err := GetQiniuUploadToken(jxcontext.AdminCtx, "")
token, err := GetQiniuUploadToken(jxcontext.AdminCtx, "", "")
if err != nil {
t.Fatal(err)
}

View File

@@ -8,6 +8,7 @@ import (
"time"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxcallback/auth/weixin"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/model"
@@ -71,6 +72,13 @@ type SkuSaleInfo struct {
Count int // 销售的总份数
}
type StoreOpRequestInfo struct {
model.StoreOpRequest
StoreName string
SkuNamePrefix string
SkuNameName string
}
// 商品不可售,直接排除
// 如果门店商品是可售状态,那么会忽略区域限制。否则有区域限制
func GetStoreSkus(ctx *jxcontext.Context, storeID int, isFocus bool, keyword string, params map[string]interface{}, offset, pageSize int) (skuNamesInfo *StoreSkuNamesInfo, err error) {
@@ -426,10 +434,15 @@ func checkStoresSkusSaleCity(ctx *jxcontext.Context, db *dao.DaoDB, storeIDs []i
}
func updateStoresSkusWithoutSync(ctx *jxcontext.Context, storeIDs []int, skuBindInfos []*StoreSkuBindInfo) (needSyncSkus []int, err error) {
globals.SugarLogger.Debugf("updateStoresSkusWithoutSync, storeIDs:%v, skuBindInfos:%s", storeIDs, utils.Format4Output(skuBindInfos, false))
db := dao.GetDB()
if err = checkStoresSkusSaleCity(ctx, db, storeIDs, skuBindInfos); err != nil {
return nil, err
}
if storeIDs, skuBindInfos, err = filterStorePriceChange(ctx, storeIDs, skuBindInfos); err != nil {
return nil, err
}
userName := ctx.GetUserName()
needSyncIDMap := make(map[int]int)
dao.Begin(db)
@@ -450,7 +463,7 @@ func updateStoresSkusWithoutSync(ctx *jxcontext.Context, storeIDs []int, skuBind
JOIN sku_name t3 ON t1.name_id = t3.id AND t3.deleted_at = ?
WHERE t1.name_id = ? AND t1.deleted_at = ?
`, storeID, utils.DefaultTimeValue, utils.DefaultTimeValue, skuBindInfo.NameID, utils.DefaultTimeValue); err == nil {
globals.SugarLogger.Debug(utils.Format4Output(allBinds, false))
// globals.SugarLogger.Debug(utils.Format4Output(allBinds, false))
inSkuBinsMap := make(map[int]*StoreSkuBindSkuInfo, len(inSkuBinds))
for _, v := range inSkuBinds {
inSkuBinsMap[v.SkuID] = v
@@ -832,6 +845,214 @@ func CopyStoreSkus(ctx *jxcontext.Context, fromStoreID, toStoreID int, copyMode
return num, err
}
func shouldPendingStorePriceChange(ctx *jxcontext.Context, storeID int, skuBindInfo *StoreSkuBindInfo) bool {
return globals.EnablePendingChange && ctx.GetLoginType() == weixin.LoginType
}
func filterStorePriceChange(ctx *jxcontext.Context, storeIDs []int, skuBindInfos []*StoreSkuBindInfo) (filteredStoreIDs []int, filteredSkuBindInfos []*StoreSkuBindInfo, err error) {
globals.SugarLogger.Debug("filterStorePriceChange")
if globals.EnablePendingChange {
db := dao.GetDB()
dao.Begin(db)
defer dao.Rollback(db)
for _, storeID := range storeIDs {
for _, skuBindInfo := range skuBindInfos {
shouldPending := shouldPendingStorePriceChange(ctx, storeID, skuBindInfo)
if shouldPending {
changeReq := &model.StoreOpRequest{
Type: model.RequestTypeChangePrice,
StoreID: storeID,
ItemID: skuBindInfo.NameID,
Status: model.RequestStatusNew,
UserID: ctx.GetUserName(),
IntParam1: skuBindInfo.UnitPrice,
IntParam2: skuBindInfo.IsSale,
}
if skuBindInfo.IsFocus == 1 {
changeReq.Type = model.RequestTypeFocusSkuName
}
if len(skuBindInfo.Skus) > 0 {
changeReq.JsonParam = string(utils.MustMarshal(skuBindInfo.Skus))
}
dao.WrapAddIDCULDEntity(changeReq, ctx.GetUserName())
if err = dao.CreateOrUpdate(db, changeReq); err != nil {
return nil, nil, err
}
// 去除价格相关的部分
if skuBindInfo.IsFocus == 1 {
skuBindInfo.IsFocus = 0
}
skuBindInfo.UnitPrice = 0
}
}
}
dao.Commit(db)
}
return storeIDs, skuBindInfos, nil
}
func AcceptStoreOpRequests(ctx *jxcontext.Context, reqIDs []int) (err error) {
if globals.EnablePendingChange {
if len(reqIDs) > 0 {
subErrors := make(map[int]error)
db := dao.GetDB()
for _, reqID := range reqIDs {
op := &model.StoreOpRequest{}
op.ID = reqID
if err2 := dao.GetEntity(db, op); err2 != nil {
subErrors[reqID] = err2
} else {
if op.Status == model.RequestStatusNew {
skuBindInfo := &StoreSkuBindInfo{
NameID: op.ItemID,
UnitPrice: op.IntParam1,
IsSale: op.IntParam2,
}
if op.Type == model.RequestTypeFocusSkuName {
skuBindInfo.IsFocus = 1
}
if op.JsonParam != "" {
if err2 = utils.UnmarshalUseNumber([]byte(op.JsonParam), &skuBindInfo.Skus); err2 != nil {
subErrors[reqID] = err2
}
}
if err2 == nil {
_, err2 := UpdateStoresSkus(ctx, []int{op.StoreID}, []*StoreSkuBindInfo{skuBindInfo})
isLocalSucess := true
if err2 != nil {
subErrors[reqID] = err2
if !isSyncError(err2) {
isLocalSucess = false
}
}
if isLocalSucess {
if err2 := changeStoreOpStatus(ctx, []int{reqID}, model.RequestStatusAccepted, ""); err2 != nil {
subErrors[reqID] = err2
}
}
}
}
}
}
if len(subErrors) > 0 {
errMsg := ""
for k, v := range subErrors {
errMsg += fmt.Sprintf("req:%d, error:%s\n", k, v.Error())
}
err = errors.New(errMsg)
}
}
}
return err
}
func RejectStoreOpRequests(ctx *jxcontext.Context, reqIDs []int, rejectReason string) (err error) {
return changeStoreOpStatus(ctx, reqIDs, model.RequestStatusRejected, rejectReason)
}
// 当前些函数只针对type为 RequestTypeChangePrice与RequestTypeFocusSkuName的查询才有效
func GetStoreOpRequests(ctx *jxcontext.Context, fromTime, toTime time.Time, keyword string, params map[string]interface{}, offset, pageSize int) (requestList []*StoreOpRequestInfo, err error) {
if globals.EnablePendingChange {
sql := `
SELECT t1.id, t1.created_at, t1.updated_at, t1.last_operator, t1.deleted_at,
t1.type, t1.store_id, t1.item_id, t1.status, t1.user_id, t1.int_param1, t1.int_param2,
t2.name store_name, t3.prefix sku_name_prefix, t3.name sku_name_name, AVG(t5.unit_price) unit_price
FROM store_op_request t1
JOIN store t2 ON t1.store_id = t2.id
JOIN sku_name t3 ON t1.item_id = t3.id
JOIN sku t4 ON t3.id = t4.name_id
LEFT JOIN store_sku_bind t5 ON t1.store_id = t5.store_id AND t4.id = t5.sku_id AND t5.deleted_at = ?
WHERE t1.created_at >= ? AND t1.created_at <= ?
`
sqlParams := []interface{}{
utils.DefaultTimeValue,
fromTime,
toTime,
}
if keyword != "" {
keywordLike := "%" + keyword + "%"
sql += " AND ( t2.name LIKE ? OR t3.name LIKE ?"
sqlParams = append(sqlParams, keywordLike, keywordLike)
if keywordInt64, err2 := strconv.ParseInt(keyword, 10, 64); err2 == nil {
sql += " OR t1.store_id = ? OR t1.item_id = ?"
sqlParams = append(sqlParams, keywordInt64, keywordInt64)
}
sql += ")"
}
if params["storeIDs"] != nil {
var storeIDs []int
if err = utils.UnmarshalUseNumber([]byte(params["storeIDs"].(string)), &storeIDs); err != nil {
return nil, err
}
if len(storeIDs) > 0 {
sql += " AND t1.store_id IN (" + dao.GenQuestionMarks(len(storeIDs)) + ")"
sqlParams = append(sqlParams, storeIDs)
}
}
if params["types"] != nil {
var typeList []int
if err = utils.UnmarshalUseNumber([]byte(params["types"].(string)), &typeList); err != nil {
return nil, err
}
if len(typeList) > 0 {
sql += " AND t1.type IN (" + dao.GenQuestionMarks(len(typeList)) + ")"
sqlParams = append(sqlParams, typeList)
}
}
if params["statuss"] != nil {
var statusList []int
if err = utils.UnmarshalUseNumber([]byte(params["statuss"].(string)), &statusList); err != nil {
return nil, err
}
if len(statusList) > 0 {
sql += " AND t1.status IN (" + dao.GenQuestionMarks(len(statusList)) + ")"
sqlParams = append(sqlParams, statusList)
}
}
sql += `
GROUP BY 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
LIMIT ? OFFSET ?`
pageSize = jxutils.FormalizePageSize(pageSize)
sqlOffset := offset
sqlPageSize := pageSize
sqlParams = append(sqlParams, sqlPageSize, sqlOffset)
db := dao.GetDB()
// globals.SugarLogger.Debug(sql)
// globals.SugarLogger.Debug(utils.Format4Output(sqlParams, false))
if err = dao.GetRows(db, &requestList, sql, sqlParams...); err == nil {
return requestList, nil
}
}
return nil, err
}
func changeStoreOpStatus(ctx *jxcontext.Context, reqIDs []int, status int8, rejectReason string) (err error) {
globals.SugarLogger.Debugf("changeStoreOpStatus, reqIDs:%v", reqIDs)
if globals.EnablePendingChange {
if len(reqIDs) > 0 {
db := dao.GetDB()
dao.Begin(db)
defer dao.Rollback(db)
for _, reqID := range reqIDs {
op := &model.StoreOpRequest{}
op.Remark = rejectReason
op.Status = status
dao.WrapUpdateULEntity(op, ctx.GetUserName())
op.DeletedAt = time.Now()
op.ID = reqID
// globals.SugarLogger.Debug(utils.Format4Output(op, false))
if _, err = dao.UpdateEntity(db, op, "Remark", "Status", "DeletedAt", "LastOperator", "UpdatedAt"); err != nil {
return err
}
}
dao.Commit(db)
}
}
return err
}
func setStoreSkuBindStatus(skuBind *model.StoreSkuBind, status int8) {
skuBind.JdSyncStatus |= status
skuBind.ElmSyncStatus |= status

View File

@@ -477,3 +477,8 @@ func makeSyncError(err error) (newErr error) {
func (e *SyncError) Error() string {
return fmt.Sprintf("本地数据修改成功,但同步失败,请根据错误提示处理!,同步错误信息:%s", e.Original.Error())
}
func isSyncError(err error) bool {
_, ok := err.(*SyncError)
return ok
}

View File

@@ -71,6 +71,13 @@ func (ctx *Context) GetUserName() string {
return userName
}
func (ctx *Context) GetLoginType() string {
if ctx.userInfo != nil {
return ctx.userInfo.LoginType
}
return ""
}
func (ctx *Context) GetUserID() string {
return ctx.token
}

View File

@@ -137,6 +137,14 @@ func CreateEntity(db *DaoDB, item interface{}) (err error) {
return err
}
func CreateOrUpdate(db *DaoDB, item interface{}, colConflitAndArgs ...string) (err error) {
if db == nil {
db = GetDB()
}
_, err = db.db.InsertOrUpdate(item, colConflitAndArgs...)
return err
}
func DeleteEntity(db *DaoDB, item interface{}, cols ...string) (num int64, err error) {
if db == nil {
db = GetDB()

View File

@@ -11,6 +11,29 @@ const (
MaxStoreSkuStockQty = 99999
)
const (
RequestTypeChangePrice = 1
RequestTypeFocusSkuName = 2
)
const (
RequestStatusNew = 0
RequestStatusRejected = 1
RequestStatusAccepted = 2
)
var (
RequestTypeName = map[int]string{
RequestTypeChangePrice: "更改价格",
RequestTypeFocusSkuName: "关注商品",
}
RequestStatusName = map[int]string{
RequestStatusNew: "待审核",
RequestStatusRejected: "拒绝",
RequestStatusAccepted: "已批准",
}
)
type StoreSkuCategoryMap struct {
ModelIDCULD
@@ -57,3 +80,23 @@ func (*StoreSkuBind) TableUnique() [][]string {
[]string{"StoreID", "SkuID", "DeletedAt"},
}
}
type StoreOpRequest struct {
ModelIDCULD // DeletedAt用于表示请求操作结束而并不一定是删除
Type int8
StoreID int `orm:"column(store_id)"`
ItemID int `orm:"column(item_id)"` // 这个根据type不同可能是SKUNAME ID或SKU ID
Status int8
UserID string `orm:"size(48);column(user_id)"`
IntParam1 int
IntParam2 int
JsonParam string `orm:"size(3000)"`
Remark string `orm:"size(255)"`
}
func (*StoreOpRequest) TableUnique() [][]string {
return [][]string{
[]string{"StoreID", "Type", "ItemID", "DeletedAt"},
}
}

View File

@@ -25,6 +25,7 @@ enableElmStoreWrite = true
enableMtwmStoreWrite = false
orderUseNewTable = true
enablePendingChange = true
aliKey = "LTAI6xJUGaP6WdMQ"
aliSecret = "CLmx5T93Bgi89EGAxWM4RTAXUsiHbM"
@@ -89,6 +90,7 @@ enableStoreWrite = true
enableEbaiStoreWrite = true
enableMtwmStoreWrite = true
enablePendingChange = false
[prod2]
httpport = 8082

View File

@@ -1,6 +1,7 @@
package controllers
import (
"fmt"
"math"
"time"
@@ -213,3 +214,57 @@ func (c *StoreSkuController) GetStoresSkusSaleInfo() {
return retVal, "", err
})
}
// @Title 得到商家商品修改价格请求信息
// @Description 得到商家商品修改价格请求信息
// @Param token header string true "认证token"
// @Param fromTime query string true "申请开始时间"
// @Param toTime query string false "申请结束时间"
// @Param keyword query string false "查询关键字(可以为空,为空表示不限制)"
// @Param storeIDs query string false "门店ID列表"
// @Param itemIDs query string false "id列表对象"
// @Param types query string false "类型列表对象"
// @Param statuss query string false "状态列表对象"
// @Param offset query int false "门店列表起始序号以0开始缺省为0"
// @Param pageSize query int false "门店列表页大小缺省为50-1表示全部"
// @Success 200 {object} controllers.CallResult
// @Failure 200 {object} controllers.CallResult
// @router /GetStoreOpRequests [get]
func (c *StoreSkuController) GetStoreOpRequests() {
c.callGetStoreOpRequests(func(params *tStoreSkuGetStoreOpRequestsParams) (retVal interface{}, errCode string, err error) {
var (
timeList []time.Time
)
if timeList, err = jxutils.BatchStr2Time(params.FromTime, params.ToTime); err != nil {
return retVal, "", err
}
retVal, err = cms.GetStoreOpRequests(params.Ctx, timeList[0], timeList[1], params.Keyword, params.MapData, params.Offset, params.PageSize)
return retVal, "", err
})
}
// @Title 处理商家商品价格申请
// @Description 处理商家商品价格申请
// @Param token header string true "认证token"
// @Param reqIDs formData string true "请求ID列表对象"
// @Param handleType formData int true "-1拒绝1批准"
// @Param rejectReason formData string false "拒绝理由,拒绝时要求"
// @Success 200 {object} controllers.CallResult
// @Failure 200 {object} controllers.CallResult
// @router /HandleStoreOpRequest [put]
func (c *StoreSkuController) HandleStoreOpRequest() {
c.callHandleStoreOpRequest(func(params *tStoreSkuHandleStoreOpRequestParams) (retVal interface{}, errCode string, err error) {
var reqIDs []int
if err = utils.UnmarshalUseNumber([]byte(params.ReqIDs), &reqIDs); err != nil {
return retVal, "", err
}
if params.HandleType == 1 {
err = cms.AcceptStoreOpRequests(params.Ctx, reqIDs)
} else if params.HandleType == -1 {
err = cms.RejectStoreOpRequests(params.Ctx, reqIDs, params.RejectReason)
} else {
err = fmt.Errorf("handleType=%d是非法值", params.HandleType)
}
return retVal, "", err
})
}

View File

@@ -37,6 +37,10 @@ func Init() {
orm.RegisterModel(&model.Promotion{}, &model.PromotionStore{}, &model.PromotionSku{})
}
if globals.EnablePendingChange {
orm.RegisterModel(&model.StoreOpRequest{})
}
// create table
orm.RunSyncdb("default", false, true)
}

View File

@@ -28,6 +28,7 @@ var (
EnableEbaiStoreWrite bool
EnableElmStoreWrite bool
EnableMtwmStoreWrite bool
EnablePendingChange bool
OrderUseNewTable bool
@@ -59,6 +60,7 @@ func Init() {
EnableEbaiStoreWrite = beego.AppConfig.DefaultBool("enableEbaiStoreWrite", false)
EnableElmStoreWrite = beego.AppConfig.DefaultBool("enableElmStoreWrite", false)
EnableMtwmStoreWrite = beego.AppConfig.DefaultBool("enableMtwmStoreWrite", false)
EnablePendingChange = beego.AppConfig.DefaultBool("enablePendingChange", false)
if EnableStore {
OrderUseNewTable = beego.AppConfig.DefaultBool("orderUseNewTable", false)

View File

@@ -599,6 +599,14 @@ func init() {
MethodParams: param.Make(),
Params: nil})
beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:StoreSkuController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:StoreSkuController"],
beego.ControllerComments{
Method: "GetStoreOpRequests",
Router: `/GetStoreOpRequests`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Params: nil})
beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:StoreSkuController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:StoreSkuController"],
beego.ControllerComments{
Method: "GetStoreSkus",
@@ -615,6 +623,14 @@ func init() {
MethodParams: param.Make(),
Params: nil})
beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:StoreSkuController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:StoreSkuController"],
beego.ControllerComments{
Method: "HandleStoreOpRequest",
Router: `/HandleStoreOpRequest`,
AllowHTTPMethods: []string{"put"},
MethodParams: param.Make(),
Params: nil})
beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:StoreSkuController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:StoreSkuController"],
beego.ControllerComments{
Method: "SyncStoresSkus",