获取用户订单列表接口,以及工具方法

This commit is contained in:
Hiven 2023-07-20 14:43:59 +08:00
parent 4a2230d90c
commit 52d47f78ac
13 changed files with 570 additions and 0 deletions

1
.gitignore vendored
View File

@ -30,6 +30,7 @@
*.vsix *.vsix
__debug_bin __debug_bin
__debug_bin*
.idea .idea
.vscode .vscode

View File

@ -121,3 +121,8 @@ const (
// 云仓完成 // 云仓完成
STATUS_FONT_COMPLETED_CLOUD Order = 8 STATUS_FONT_COMPLETED_CLOUD Order = 8
) )
// 订单取消时间
const (
CANCLE_ORDER_EXPIRE int64 = 48 * 3600
)

View File

@ -8,3 +8,6 @@ const DEFAULT_PAGE_SIZE = 20
// 最大每页显示数量 // 最大每页显示数量
const MAX_PAGE_SIZE = 300 const MAX_PAGE_SIZE = 300
// 最大分页
const MAX_PAGE = 100

View File

@ -24,3 +24,7 @@ func (d *FsProductDesignModel) FindOne(ctx context.Context, id int64, userId int
err = d.db.WithContext(ctx).Model(&FsProductDesign{}).Where("`id` = ? and `user_id` = ? and `status` = ?", id, userId, 1).First(&resp).Error err = d.db.WithContext(ctx).Model(&FsProductDesign{}).Where("`id` = ? and `user_id` = ? and `status` = ?", id, userId, 1).First(&resp).Error
return resp, err return resp, err
} }
func (m *FsOrderDetailModel) TableName() string {
return m.name
}

View File

@ -3,8 +3,11 @@ package gmodel
import ( import (
"context" "context"
"fusenapi/constants" "fusenapi/constants"
"reflect"
"time" "time"
"fusenapi/utils/handler"
"gorm.io/gorm" "gorm.io/gorm"
) )
@ -51,3 +54,75 @@ func (o *FsOrderModel) FindLastSuccessOneOrder(ctx context.Context, userId int64
err = o.db.WithContext(ctx).Model(&FsOrder{}).Where("`user_id` = ? and `status` > ?", userId, statusGt).Order("id DESC").Take(&order).Error err = o.db.WithContext(ctx).Model(&FsOrder{}).Where("`user_id` = ? and `status` > ?", userId, statusGt).Order("id DESC").Take(&order).Error
return order, err return order, err
} }
// 分页查询的订单
func (o *FsOrderModel) FindPageListByPage(ctx context.Context, rowBuilder *gorm.DB, page *int64, pageSize *int64, filterMap map[string]string, orderBy string) ([]*FsOrderRel, error) {
var resp []*FsOrderRel
// 过滤
if filterMap != nil {
rowBuilder = rowBuilder.Scopes(handler.FilterData(filterMap))
}
// 排序
if orderBy != "" {
var fieldsMap = make(map[string]struct{})
s := reflect.TypeOf(&FsOrder{}).Elem() //通过反射获取type定义
for i := 0; i < s.NumField(); i++ {
fieldsMap[s.Field(i).Tag.Get("json")] = struct{}{}
}
rowBuilder = rowBuilder.Scopes(handler.OrderCheck(orderBy, fieldsMap))
}
// 分页
rowBuilder = rowBuilder.Scopes(handler.Paginate(page, pageSize))
// 结果
result := rowBuilder.WithContext(ctx).Find(&resp)
if result.Error != nil {
return nil, result.Error
} else {
return resp, nil
}
}
type FsOrderRel struct {
FsOrder
FsOrderDetails []FsOrderDetails `gorm:"foreignKey:order_id;references:id"`
}
type FsOrderDetails struct {
FsOrderDetail
FsOrderDetailTemplateInfo FsOrderDetailTemplate `gorm:"foreignKey:id;references:order_detail_template_id"`
FsProductInfo FsProduct `gorm:"foreignKey:id;references:product_id"`
}
func (m *FsOrderModel) RowSelectBuilder(selectData []string) *gorm.DB {
var rowBuilder = m.db.Table(m.name)
if selectData != nil {
rowBuilder = rowBuilder.Select(selectData)
} else {
rowBuilder = rowBuilder.Select("*")
}
return rowBuilder
}
func (m *FsOrderModel) FindCount(ctx context.Context, countBuilder *gorm.DB, filterMap map[string]string) (int64, error) {
var count int64
// 过滤
if filterMap != nil {
countBuilder = countBuilder.Scopes(handler.FilterData(filterMap))
}
result := countBuilder.WithContext(ctx).Limit(1).Count(&count)
if result.Error != nil {
return 0, result.Error
} else {
return count, nil
}
}
func (m *FsOrderModel) TableName() string {
return m.name
}

View File

@ -0,0 +1,78 @@
package handler
import (
"errors"
"net/http"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/rest/httpx"
"fusenapi/utils/auth"
"fusenapi/utils/basic"
"fusenapi/server/orders/internal/logic"
"fusenapi/server/orders/internal/svc"
"fusenapi/server/orders/internal/types"
)
func GetUserOrderListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var (
// 定义错误变量
err error
// 定义用户信息变量
userinfo *auth.UserInfo
)
// 解析JWT token,并对空用户进行判断
claims, err := svcCtx.ParseJwtToken(r)
// 如果解析JWT token出错,则返回未授权的JSON响应并记录错误消息
if err != nil {
httpx.OkJsonCtx(r.Context(), w, &basic.Response{
Code: 401, // 返回401状态码,表示未授权
Message: "unauthorized", // 返回未授权信息
})
logx.Info("unauthorized:", err.Error()) // 记录错误日志
return
}
if claims != nil {
// 从token中获取对应的用户信息
userinfo, err = auth.GetUserInfoFormMapClaims(claims)
// 如果获取用户信息出错,则返回未授权的JSON响应并记录错误消息
if err != nil {
httpx.OkJsonCtx(r.Context(), w, &basic.Response{
Code: 401,
Message: "unauthorized",
})
logx.Info("unauthorized:", err.Error())
return
}
} else {
// 如果claims为nil,则认为用户身份为白板用户
userinfo = &auth.UserInfo{UserId: 0, GuestId: 0}
}
var req types.GetUserOrderListReq
// 如果端点有请求结构体则使用httpx.Parse方法从HTTP请求体中解析请求数据
if err := httpx.Parse(r, &req); err != nil {
httpx.OkJsonCtx(r.Context(), w, &basic.Response{
Code: 510,
Message: "parameter error",
})
logx.Info(err)
return
}
// 创建一个业务逻辑层实例
l := logic.NewGetUserOrderListLogic(r.Context(), svcCtx)
resp := l.GetUserOrderList(&req, userinfo)
// 如果响应不为nil则使用httpx.OkJsonCtx方法返回JSON响应;
if resp != nil {
httpx.OkJsonCtx(r.Context(), w, resp)
} else {
err := errors.New("server logic is error, resp must not be nil")
httpx.ErrorCtx(r.Context(), w, err)
logx.Error(err)
}
}
}

View File

@ -22,6 +22,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
Path: "/api/order/detail", Path: "/api/order/detail",
Handler: GetOrderDetailHandler(serverCtx), Handler: GetOrderDetailHandler(serverCtx),
}, },
{
Method: http.MethodGet,
Path: "/api/user/order-list",
Handler: GetUserOrderListHandler(serverCtx),
},
}, },
) )
} }

View File

@ -0,0 +1,146 @@
package logic
import (
"context"
"errors"
"fusenapi/constants"
"fusenapi/model/gmodel"
"fusenapi/utils/auth"
"fusenapi/utils/basic"
"fusenapi/utils/format"
"math"
"fusenapi/server/orders/internal/svc"
"fusenapi/server/orders/internal/types"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
)
type GetUserOrderListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetUserOrderListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserOrderListLogic {
return &GetUserOrderListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetUserOrderListLogic) GetUserOrderList(req *types.GetUserOrderListReq, userinfo *auth.UserInfo) (resp *basic.Response) {
// 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data)
// userinfo 传入值时, 一定不为null
orderDetailModel := gmodel.NewFsOrderDetailModel(l.svcCtx.MysqlConn)
orderModel := gmodel.NewFsOrderModel(l.svcCtx.MysqlConn)
rowBuilder := orderModel.RowSelectBuilder(nil)
if userinfo == nil || userinfo.UserId == 0 {
return resp.SetStatusWithMessage(basic.CodeDbRecordNotFoundErr, "order not found")
}
// 查询条件
var page = req.Page
var pageSize = req.PageSize
var listRes []*gmodel.FsOrderRel
rowBuilder = rowBuilder.Where("user_id =?", userinfo.UserId).Where("status =?", req.Status)
// 查询总数
total, err := orderModel.FindCount(l.ctx, rowBuilder, nil)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return resp.SetStatusWithMessage(basic.CodeDbRecordNotFoundErr, "order not found")
}
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to get order info")
}
// 查询数据
if total > 0 {
rowBuilder = rowBuilder.Preload("FsOrderDetails", func(dbPreload *gorm.DB) *gorm.DB {
return dbPreload.Table(orderDetailModel.TableName()).Preload("FsOrderDetailTemplateInfo").Preload("FsProductInfo")
})
listRes, err = orderModel.FindPageListByPage(l.ctx, rowBuilder, &page, &pageSize, nil, "")
}
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return resp.SetStatusWithMessage(basic.CodeDbRecordNotFoundErr, "order not found")
}
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to get order info")
}
listResLen := len(listRes)
var respList []types.Items
if listResLen > 0 {
// 数据处理
for _, item := range listRes {
var pbData types.Items
pbData.ID = item.Id
pbData.Sn = *item.Sn
pbData.UserID = *item.UserId
pbData.TotalAmount = *item.TotalAmount
pbData.Ctime = format.TimeIntToFormat(*item.Ctime)
pbData.Status = *item.Status
pbData.DeliveryMethod = *item.DeliveryMethod
pbData.TsTime = format.TimeToFormat(*item.TsTime)
pbData.IsPayCompleted = *item.IsPayCompleted
pbData.DeliverSn = *item.DeliverSn
var pcsBox int64
var pcs int64
var productList []*types.Product
if len(item.FsOrderDetails) > 0 {
for _, fsOrderDetailItem := range item.FsOrderDetails {
fsOrderDetailBuyNum := *fsOrderDetailItem.FsOrderDetail.BuyNum
fsOrderDetailEachBoxNum := *fsOrderDetailItem.FsOrderDetailTemplateInfo.EachBoxNum
pcs = pcs + fsOrderDetailBuyNum
pcsBoxNum := fsOrderDetailBuyNum / fsOrderDetailEachBoxNum
var csBoxNumF int64
if (fsOrderDetailBuyNum % fsOrderDetailEachBoxNum) > 0 {
csBoxNumF = 1
}
pcsBox = pcsBox + pcsBoxNum + csBoxNumF
var product types.Product
product.Cover = *fsOrderDetailItem.Cover
product.Fitting = *fsOrderDetailItem.OptionalTitle
product.OptionPrice = *fsOrderDetailItem.OptionPrice
product.OrderDetailTemplateId = *fsOrderDetailItem.OrderDetailTemplateId
product.OrderId = *fsOrderDetailItem.OrderId
product.Pcs = fsOrderDetailBuyNum
product.PcsBox = pcsBox
product.Price = *fsOrderDetailItem.FsOrderDetail.Amount
product.ProductId = *fsOrderDetailItem.OptionPrice
//product.Size = *fsOrderDetailItem.FsProductInfo.s
product.Title = *fsOrderDetailItem.FsProductInfo.Title
productList = append(productList, &product)
}
pbData.ProductList = productList
}
pbData.PcsBox = pcsBox
pbData.Pcs = pcs
pbData.SurplusAt = *item.Ctime + constants.CANCLE_ORDER_EXPIRE
pbData.LogisticsStatus = 1
pbData.Deposit = *item.TotalAmount / 2
pbData.Remaining = pbData.Deposit
respList = append(respList, pbData)
}
}
return resp.SetStatusWithMessage(basic.CodeOK, "success", types.GetUserOrderListRsp{
Items: respList,
Meta: types.Meta{
TotalCount: total,
PageCount: int64(math.Ceil(float64(total) / float64(pageSize))),
CurrentPage: int(page),
PerPage: int(pageSize),
},
})
}

View File

@ -76,6 +76,47 @@ type Deposit struct {
TransNo string `json:"trans_no"` TransNo string `json:"trans_no"`
} }
type GetUserOrderListReq struct {
Page int64 `form:"page"` // 分页
PageSize int64 `form:"page_size"` // 每页数量
Status int64 `form:"status"` // 状态筛选
Time int64 `form:"time"` // 时间筛选
Total int64 `form:"total"` // 总数
Size int64 `form:"size"` // 图片尺寸
}
type GetUserOrderListRsp struct {
Items []Items `json:"items"`
Meta Meta `json:"_meta"`
}
type StatusTimes struct {
Key int `json:"key"`
Time string `json:"time"`
}
type Items struct {
ID int64 `json:"id"`
Sn string `json:"sn"`
UserID int64 `json:"user_id"`
TotalAmount int64 `json:"total_amount"`
Ctime string `json:"ctime"`
Status int64 `json:"status"`
DeliveryMethod int64 `json:"delivery_method"`
TsTime string `json:"ts_time"`
IsPayCompleted int64 `json:"is_pay_completed"`
DeliverSn string `json:"deliver_sn"`
PcsBox int64 `json:"pcs_box"`
Pcs int64 `json:"pcs"`
SurplusAt int64 `json:"surplus_at"`
LogisticsStatus int64 `json:"logistics_status"`
StatusTimes []*StatusTimes `json:"status_times"`
Deposit int64 `json:"deposit"`
Remaining int64 `json:"remaining"`
ProductList []*Product `json:"productList"`
IsStop int64 `json:"is_stop"`
}
type Request struct { type Request struct {
} }

View File

@ -0,0 +1,7 @@
package main
import "testing"
func TestMain(t *testing.T) {
main()
}

View File

@ -16,6 +16,10 @@ service orders {
//获取订单详情 //获取订单详情
@handler GetOrderDetailHandler @handler GetOrderDetailHandler
get /api/order/detail (GetOrderDetailReq) returns (response); get /api/order/detail (GetOrderDetailReq) returns (response);
//获取订单列表
@handler GetUserOrderListHandler
get /api/user/order-list (GetUserOrderListReq) returns (response);
} }
//获取订单发票 //获取订单发票
@ -82,4 +86,48 @@ type PayInfo {
type Deposit { type Deposit {
Method string `json:"method"` Method string `json:"method"`
TransNo string `json:"trans_no"` TransNo string `json:"trans_no"`
}
// 获取订单列表
type (
GetUserOrderListReq {
Page int64 `form:"page"` // 分页
PageSize int64 `form:"page_size"` // 每页数量
Status int64 `form:"status"` // 状态筛选
Time int64 `form:"time"` // 时间筛选
Total int64 `form:"total"` // 总数
Size int64 `form:"size"` // 图片尺寸
}
GetUserOrderListRsp {
Items []Items `json:"items"`
Meta Meta `json:"_meta"`
}
)
type StatusTimes {
Key int `json:"key"`
Time string `json:"time"`
}
type Items {
ID int64 `json:"id"`
Sn string `json:"sn"`
UserID int64 `json:"user_id"`
TotalAmount int64 `json:"total_amount"`
Ctime string `json:"ctime"`
Status int64 `json:"status"`
DeliveryMethod int64 `json:"delivery_method"`
TsTime string `json:"ts_time"`
IsPayCompleted int64 `json:"is_pay_completed"`
DeliverSn string `json:"deliver_sn"`
PcsBox int64 `json:"pcs_box"`
Pcs int64 `json:"pcs"`
SurplusAt int64 `json:"surplus_at"`
LogisticsStatus int64 `json:"logistics_status"`
StatusTimes []*StatusTimes `json:"status_times"`
Deposit int64 `json:"deposit"`
Remaining int64 `json:"remaining"`
ProductList []*Product `json:"productList"`
IsStop int64 `json:"is_stop"`
} }

11
utils/format/time.go Normal file
View File

@ -0,0 +1,11 @@
package format
import "time"
func TimeToFormat(t time.Time) string {
return time.Time(t).Format("2006-01-02 15:04:05")
}
func TimeIntToFormat(unixTime int64) string {
return time.Unix(unixTime, 0).Format("2006-01-02 15:04:05")
}

View File

@ -0,0 +1,146 @@
package handler
import (
"encoding/json"
"fmt"
"fusenapi/constants"
"strconv"
"strings"
"unicode"
"gorm.io/gorm"
)
// FilterData 条件过滤
func FilterData(filterMap map[string]string) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
// 组合索引
db = db.Where("id <> ?", 0)
for fieldName, item := range filterMap {
options := strings.Split(item, "|")
conditionKey := options[1]
var conditionValueType string
if len(options) == 3 {
conditionValueType = options[2]
}
var conditionValue interface{}
if conditionKey == "=" || conditionKey == "<>" || conditionKey == ">=" || conditionKey == "<=" {
conditionStr := options[0]
if conditionValueType == "string" {
conditionValue = options[0]
} else {
if ss := CheckIsDigit(conditionStr); ss != 0 {
conditionValue = ss
} else {
conditionValue = options[0]
}
}
} else {
var conditionArrNew []int64
conditionArr := strings.Split(options[0], ",")
for _, s := range conditionArr {
if ss := CheckIsDigit(s); ss != 0 {
conditionArrNew = append(conditionArrNew, ss)
}
}
if len(conditionArrNew) > 0 {
conditionValue = conditionArrNew
} else {
conditionValue = conditionArr
}
}
switch conditionKey {
case "LIKE":
db = db.Where(fmt.Sprintf("%v LIKE ?", fieldName), fmt.Sprintf("%%%v%%", conditionValue))
case "BETWEEN":
db = db.Where(fmt.Sprintf("%v BETWEEN ? AND ?", fieldName), conditionValue.([]interface{})[0], conditionValue.([]interface{})[1])
default:
db = db.Where(fmt.Sprintf("%v %v ?", fieldName, conditionKey), conditionValue)
}
}
return db
}
}
// OrderCheck 公共排序--检测
func OrderCheck(orderData string, fields map[string]struct{}) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
var orderType = "desc"
if orderData != "" {
var sortData []map[string]interface{}
_ = json.Unmarshal([]byte(orderData), &sortData)
sortCount := len(sortData)
for i := 0; i < sortCount; i++ {
data := sortData[i]
prop, existProp := data["prop"]
sort, existSort := data["order"]
if existProp && existSort {
propData := strings.TrimSpace(prop.(string))
sortData := strings.TrimSpace(sort.(string))
if propData != "" && sortData != "" {
// 判断数据库字段
_, existFields := fields[propData]
if existFields {
if sortData == "descending" || sortData == "desc" {
orderType = "desc"
} else {
orderType = "asc"
}
db.Order(fmt.Sprintf("%v %v", prop, orderType))
}
}
}
}
return db
} else {
return db.Order("id asc")
}
}
}
// Paginate 公共分页
func Paginate(page *int64, pageSize *int64) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
page, _ := strconv.Atoi(strconv.FormatInt(*page, 10))
switch {
case page == 0:
page = constants.DEFAULT_PAGE
case page > constants.MAX_PAGE:
page = constants.MAX_PAGE
}
pageSize, _ := strconv.Atoi(strconv.FormatInt(*pageSize, 10))
switch {
case pageSize > constants.MAX_PAGE_SIZE:
pageSize = constants.MAX_PAGE_SIZE
case pageSize <= 0:
pageSize = constants.DEFAULT_PAGE_SIZE
}
offset := (page - 1) * pageSize
return db.Offset(offset).Limit(pageSize)
}
}
// CheckIsDigit 判断int
func CheckIsDigit(s string) int64 {
isDigit := true
for _, r := range s {
if !unicode.IsDigit(r) {
isDigit = false
break
}
}
if isDigit {
ss, _ := strconv.ParseInt(s, 10, 64)
return ss
} else {
return 0
}
}