@@ -0,0 +1,830 @@
package routers
import (
"encoding/json"
"ops/models"
"strconv"
"time"
"github.com/gin-gonic/gin"
)
// ---------- 数据表结构 ----------
type TabWarehouseContainer struct {
ID uint ` gorm:"primaryKey" `
Title string ` gorm:"size:255;not null;comment:容器名" `
Remark string ` gorm:"type:text;comment:描述" `
CreatedAt string ` gorm:"size:20;comment:创建日期" `
CreatorID uint ` gorm:"not null;index;comment:创建者id" `
ParentID * uint ` gorm:"index;comment:父容器id, nil=顶级" `
ItemCount int ` gorm:"default:0;comment:直接子物品数量" `
ChildCount int ` gorm:"default:0;comment:子容器数量" `
}
type TabWarehouseItem struct {
ID uint ` gorm:"primaryKey" `
Name string ` gorm:"size:255;not null;comment:物品名" `
SerialNumber string ` gorm:"size:255;comment:序列号" `
Remark string ` gorm:"type:text;comment:描述" `
Quantity int ` gorm:"default:1;comment:数量" `
CreatedAt string ` gorm:"size:20;comment:创建日期" `
CreatorID uint ` gorm:"not null;index;comment:创建者id" `
ContainerID * uint ` gorm:"index;comment:所属容器id, nil=未入库" `
}
type TabWarehouseContainerFileBind struct {
ID uint ` gorm:"primaryKey" `
ContainerID uint ` gorm:"not null;index;comment:关联容器id" `
FileID uint ` gorm:"not null;comment:关联文件id" `
CreatorID uint ` gorm:"not null;comment:上传人id" `
CreatedAt time . Time ` gorm:"type:datetime;autoCreateTime" `
}
type TabWarehouseItemFileBind struct {
ID uint ` gorm:"primaryKey" `
ItemID uint ` gorm:"not null;index;comment:关联物品id" `
FileID uint ` gorm:"not null;comment:关联文件id" `
CreatorID uint ` gorm:"not null;comment:上传人id" `
CreatedAt time . Time ` gorm:"type:datetime;autoCreateTime" `
}
type TabWarehouseItemCommit struct {
ID uint ` gorm:"primaryKey" `
ItemID uint ` gorm:"not null;index;comment:关联物品id" `
UserID uint ` gorm:"not null;comment:操作人id" `
OldContainer * uint ` gorm:"index;comment:原容器id" `
NewContainer * uint ` gorm:"index;comment:新容器id" `
Remark string ` gorm:"type:text;comment:备注" `
IP string ` gorm:"size:50;comment:操作IP" `
CreatedAt time . Time ` gorm:"type:datetime;autoCreateTime" `
}
type TabWarehouseLog struct {
ID uint ` gorm:"primaryKey" `
EntityType string ` gorm:"size:50;not null;index;comment:操作对象类型" `
EntityID uint ` gorm:"not null;index;comment:操作对象id" `
UserID uint ` gorm:"not null;index;comment:操作人id" `
ActionType string ` gorm:"size:50;not null;comment:操作类型: create update delete move query" `
OldContent string ` gorm:"type:text;comment:修改前内容(JSON)" `
NewContent string ` gorm:"type:text;comment:修改后内容(JSON)" `
IP string ` gorm:"size:50;comment:操作IP" `
Remark string ` gorm:"size:500;comment:备注" `
CreatedAt time . Time ` gorm:"type:datetime;autoCreateTime" `
}
type TabWarehouseItemWorkOrderBind struct {
ID uint ` gorm:"primaryKey" `
ItemID uint ` gorm:"not null;index;comment:关联物品id" `
WorkOrderID uint ` gorm:"not null;index;comment:关联工单id" `
Remark string ` gorm:"size:500;comment:备注" `
CreatorID uint ` gorm:"not null;comment:绑定人id" `
CreatedAt time . Time ` gorm:"type:datetime;autoCreateTime" `
}
// ---------- 初始化 ----------
func ApiWarehouseInit ( ) {
models . DB . AutoMigrate (
& TabWarehouseContainer { } ,
& TabWarehouseItem { } ,
& TabWarehouseContainerFileBind { } ,
& TabWarehouseItemFileBind { } ,
& TabWarehouseItemCommit { } ,
& TabWarehouseLog { } ,
& TabWarehouseItemWorkOrderBind { } ,
)
}
// ---------- 路由注册 ----------
func ApiWarehouse ( r * gin . RouterGroup ) {
// 新增容器
r . POST ( "/add_container" , func ( ctx * gin . Context ) {
isAuth , user , data := AuthenticationAuthority ( ctx )
if ! isAuth {
ReturnJson ( ctx , "userCookieError" , nil )
return
}
type FromAdd struct {
Title string ` json:"title" `
Remark string ` json:"remark" `
ParentID * uint ` json:"parent_id" `
Photos [ ] string ` json:"photos" `
}
var from FromAdd
if err := decodeJSON ( data , & from ) ; err != nil || from . Title == "" {
ReturnJson ( ctx , "jsonErr" , nil )
return
}
// 校验图片哈希
for _ , hash := range from . Photos {
if models . IsContainsSpecialChar ( hash ) {
ReturnJson ( ctx , "photo_hash_invalid" , nil )
return
}
}
// 检查嵌套层级不超过5层
if from . ParentID != nil {
var parent TabWarehouseContainer
if err := models . DB . First ( & parent , * from . ParentID ) . Error ; err != nil {
ReturnJson ( ctx , "parent_not_found" , nil )
return
}
depth := 0
curID := * from . ParentID
for depth < 100 {
var c TabWarehouseContainer
if err := models . DB . First ( & c , curID ) . Error ; err != nil {
break
}
if c . ParentID == nil {
break
}
curID = * c . ParentID
depth ++
}
if depth >= 4 { // 新容器将落在第5层
ReturnJson ( ctx , "max_depth_exceeded" , nil )
return
}
}
c := TabWarehouseContainer {
Title : from . Title ,
Remark : from . Remark ,
CreatedAt : strconv . FormatInt ( time . Now ( ) . Unix ( ) , 10 ) ,
CreatorID : user . ID ,
ParentID : from . ParentID ,
}
models . DB . Create ( & c )
// 绑定图片
for _ , hash := range from . Photos {
var findFile TabFileInfo_
if models . DB . Where ( & TabFileInfo_ { Sha256 : hash , Type : "image" } ) . First ( & findFile ) . Error == nil {
models . DB . Create ( & TabWarehouseContainerFileBind {
ContainerID : c . ID ,
FileID : findFile . ID ,
CreatorID : user . ID ,
} )
}
}
// 父容器的 ChildCount +1
if from . ParentID != nil {
models . DB . Model ( & TabWarehouseContainer { } ) . Where ( "id = ?" , * from . ParentID ) . Update ( "child_count" , models . DB . Raw ( "child_count + 1" ) )
}
// 写操作日志
newContent , _ := json . Marshal ( from )
models . DB . Create ( & TabWarehouseLog {
EntityType : "container" ,
EntityID : c . ID ,
UserID : user . ID ,
ActionType : "create" ,
NewContent : string ( newContent ) ,
IP : ctx . ClientIP ( ) ,
} )
ReturnJson ( ctx , "apiOK" , gin . H { "id" : c . ID } )
} )
// 编辑容器
r . POST ( "/update_container" , func ( ctx * gin . Context ) {
isAuth , user , data := AuthenticationAuthority ( ctx )
if ! isAuth {
ReturnJson ( ctx , "userCookieError" , nil )
return
}
type FromUpdate struct {
ID uint ` json:"id" `
Title string ` json:"title" `
Remark string ` json:"remark" `
Photos [ ] string ` json:"photos" `
}
var from FromUpdate
if err := decodeJSON ( data , & from ) ; err != nil || from . ID == 0 || from . Title == "" {
ReturnJson ( ctx , "jsonErr" , nil )
return
}
// 校验图片哈希
for _ , hash := range from . Photos {
if models . IsContainsSpecialChar ( hash ) {
ReturnJson ( ctx , "photo_hash_invalid" , nil )
return
}
}
var c TabWarehouseContainer
if err := models . DB . Where ( "id = ?" , from . ID ) . First ( & c ) . Error ; err != nil {
ReturnJson ( ctx , "container_not_found" , nil )
return
}
oldContent , _ := json . Marshal ( c )
models . DB . Model ( & c ) . Updates ( map [ string ] interface { } {
"title" : from . Title ,
"remark" : from . Remark ,
} )
// 重建图片绑定
models . DB . Where ( "container_id = ?" , from . ID ) . Delete ( & TabWarehouseContainerFileBind { } )
for _ , hash := range from . Photos {
var findFile TabFileInfo_
if models . DB . Where ( & TabFileInfo_ { Sha256 : hash , Type : "image" } ) . First ( & findFile ) . Error == nil {
models . DB . Create ( & TabWarehouseContainerFileBind {
ContainerID : from . ID ,
FileID : findFile . ID ,
CreatorID : user . ID ,
} )
}
}
newContent , _ := json . Marshal ( from )
models . DB . Create ( & TabWarehouseLog {
EntityType : "container" ,
EntityID : from . ID ,
UserID : user . ID ,
ActionType : "update" ,
OldContent : string ( oldContent ) ,
NewContent : string ( newContent ) ,
IP : ctx . ClientIP ( ) ,
} )
ReturnJson ( ctx , "apiOK" , nil )
} )
// 删除容器
r . POST ( "/delete_container" , func ( ctx * gin . Context ) {
isAuth , user , data := AuthenticationAuthority ( ctx )
if ! isAuth {
ReturnJson ( ctx , "userCookieError" , nil )
return
}
type FromDelete struct {
ID uint ` json:"id" `
}
var from FromDelete
if err := decodeJSON ( data , & from ) ; err != nil || from . ID == 0 {
ReturnJson ( ctx , "jsonErr" , nil )
return
}
var c TabWarehouseContainer
if err := models . DB . Where ( "id = ?" , from . ID ) . First ( & c ) . Error ; err != nil {
ReturnJson ( ctx , "container_not_found" , nil )
return
}
// 检查是否有子容器或物品
if c . ChildCount > 0 || c . ItemCount > 0 {
ReturnJson ( ctx , "container_not_empty" , nil )
return
}
models . DB . Where ( "container_id = ?" , from . ID ) . Delete ( & TabWarehouseContainerFileBind { } )
// 父容器 ChildCount -1
if c . ParentID != nil {
models . DB . Model ( & TabWarehouseContainer { } ) . Where ( "id = ?" , * c . ParentID ) . Update ( "child_count" , models . DB . Raw ( "child_count - 1" ) )
}
oldContent , _ := json . Marshal ( c )
models . DB . Create ( & TabWarehouseLog {
EntityType : "container" ,
EntityID : from . ID ,
UserID : user . ID ,
ActionType : "delete" ,
OldContent : string ( oldContent ) ,
IP : ctx . ClientIP ( ) ,
} )
models . DB . Delete ( & c )
ReturnJson ( ctx , "apiOK" , nil )
} )
// 获取容器列表
r . POST ( "/list_container" , func ( ctx * gin . Context ) {
isAuth , _ , data := AuthenticationAuthority ( ctx )
if ! isAuth {
ReturnJson ( ctx , "userCookieError" , nil )
return
}
type FromList struct {
Search string ` json:"search" `
ParentID * uint ` json:"parent_id" `
Entries int ` json:"entries" `
Page int ` json:"page" `
}
var from FromList
if err := decodeJSON ( data , & from ) ; err != nil {
ReturnJson ( ctx , "jsonErr" , nil )
return
}
if from . Entries <= 0 || from . Entries > 300 {
from . Entries = 10
}
if from . Page <= 0 {
from . Page = 1
}
var count int64
query := models . DB . Model ( & TabWarehouseContainer { } )
if from . Search != "" {
query = query . Where ( "title LIKE ?" , "%" + from . Search + "%" )
}
if from . ParentID != nil {
query = query . Where ( "parent_id = ?" , * from . ParentID )
} else {
query = query . Where ( "parent_id IS NULL" )
}
query . Count ( & count )
var containers [ ] TabWarehouseContainer
query . Order ( "created_at DESC" ) .
Offset ( from . Entries * ( from . Page - 1 ) ) .
Limit ( from . Entries ) .
Find ( & containers )
ReturnJson ( ctx , "apiOK" , gin . H {
"all_count" : count ,
"containers" : containers ,
} )
} )
// 获取容器详情
r . POST ( "/get_container" , func ( ctx * gin . Context ) {
isAuth , _ , data := AuthenticationAuthority ( ctx )
if ! isAuth {
ReturnJson ( ctx , "userCookieError" , nil )
return
}
type FromGet struct {
ID uint ` json:"id" `
}
var from FromGet
if err := decodeJSON ( data , & from ) ; err != nil || from . ID == 0 {
ReturnJson ( ctx , "jsonErr" , nil )
return
}
var c TabWarehouseContainer
if err := models . DB . Where ( "id = ?" , from . ID ) . First ( & c ) . Error ; err != nil {
ReturnJson ( ctx , "container_not_found" , nil )
return
}
// 关联图片
var binds [ ] TabWarehouseContainerFileBind
models . DB . Where ( "container_id = ?" , from . ID ) . Find ( & binds )
var fileIDs [ ] uint
for _ , b := range binds {
fileIDs = append ( fileIDs , b . FileID )
}
var files [ ] TabFileInfo_
if len ( fileIDs ) > 0 {
models . DB . Where ( "id IN ?" , fileIDs ) . Find ( & files )
}
ReturnJson ( ctx , "apiOK" , gin . H {
"container" : c ,
"photos" : files ,
} )
} )
// 新增物品
r . POST ( "/add_item" , func ( ctx * gin . Context ) {
isAuth , user , data := AuthenticationAuthority ( ctx )
if ! isAuth {
ReturnJson ( ctx , "userCookieError" , nil )
return
}
type FromAdd struct {
Name string ` json:"name" `
SerialNumber string ` json:"serial_number" `
Remark string ` json:"remark" `
Quantity int ` json:"quantity" `
ContainerID * uint ` json:"container_id" `
Photos [ ] string ` json:"photos" `
}
var from FromAdd
if err := decodeJSON ( data , & from ) ; err != nil || from . Name == "" {
ReturnJson ( ctx , "jsonErr" , nil )
return
}
// 校验图片哈希
for _ , hash := range from . Photos {
if models . IsContainsSpecialChar ( hash ) {
ReturnJson ( ctx , "photo_hash_invalid" , nil )
return
}
}
quantity := from . Quantity
if quantity <= 0 {
quantity = 1
}
item := TabWarehouseItem {
Name : from . Name ,
SerialNumber : from . SerialNumber ,
Remark : from . Remark ,
Quantity : quantity ,
CreatedAt : strconv . FormatInt ( time . Now ( ) . Unix ( ) , 10 ) ,
CreatorID : user . ID ,
ContainerID : from . ContainerID ,
}
models . DB . Create ( & item )
// 绑定图片
for _ , hash := range from . Photos {
var findFile TabFileInfo_
if models . DB . Where ( & TabFileInfo_ { Sha256 : hash , Type : "image" } ) . First ( & findFile ) . Error == nil {
models . DB . Create ( & TabWarehouseItemFileBind {
ItemID : item . ID ,
FileID : findFile . ID ,
CreatorID : user . ID ,
} )
}
}
// 所属容器的 ItemCount +1
if from . ContainerID != nil {
models . DB . Model ( & TabWarehouseContainer { } ) . Where ( "id = ?" , * from . ContainerID ) . Update ( "item_count" , models . DB . Raw ( "item_count + 1" ) )
}
// 写操作日志
newContent , _ := json . Marshal ( from )
models . DB . Create ( & TabWarehouseLog {
EntityType : "item" ,
EntityID : item . ID ,
UserID : user . ID ,
ActionType : "create" ,
NewContent : string ( newContent ) ,
IP : ctx . ClientIP ( ) ,
} )
ReturnJson ( ctx , "apiOK" , gin . H { "id" : item . ID } )
} )
// 编辑物品
r . POST ( "/update_item" , func ( ctx * gin . Context ) {
isAuth , user , data := AuthenticationAuthority ( ctx )
if ! isAuth {
ReturnJson ( ctx , "userCookieError" , nil )
return
}
type FromUpdate struct {
ID uint ` json:"id" `
Name string ` json:"name" `
SerialNumber string ` json:"serial_number" `
Remark string ` json:"remark" `
Quantity int ` json:"quantity" `
Photos [ ] string ` json:"photos" `
}
var from FromUpdate
if err := decodeJSON ( data , & from ) ; err != nil || from . ID == 0 || from . Name == "" {
ReturnJson ( ctx , "jsonErr" , nil )
return
}
// 校验图片哈希
for _ , hash := range from . Photos {
if models . IsContainsSpecialChar ( hash ) {
ReturnJson ( ctx , "photo_hash_invalid" , nil )
return
}
}
var item TabWarehouseItem
if err := models . DB . Where ( "id = ?" , from . ID ) . First ( & item ) . Error ; err != nil {
ReturnJson ( ctx , "item_not_found" , nil )
return
}
oldContent , _ := json . Marshal ( item )
models . DB . Model ( & item ) . Updates ( map [ string ] interface { } {
"name" : from . Name ,
"serial_number" : from . SerialNumber ,
"remark" : from . Remark ,
"quantity" : from . Quantity ,
} )
// 重建图片绑定
models . DB . Where ( "item_id = ?" , from . ID ) . Delete ( & TabWarehouseItemFileBind { } )
for _ , hash := range from . Photos {
var findFile TabFileInfo_
if models . DB . Where ( & TabFileInfo_ { Sha256 : hash , Type : "image" } ) . First ( & findFile ) . Error == nil {
models . DB . Create ( & TabWarehouseItemFileBind {
ItemID : from . ID ,
FileID : findFile . ID ,
CreatorID : user . ID ,
} )
}
}
newContent , _ := json . Marshal ( from )
models . DB . Create ( & TabWarehouseLog {
EntityType : "item" ,
EntityID : from . ID ,
UserID : user . ID ,
ActionType : "update" ,
OldContent : string ( oldContent ) ,
NewContent : string ( newContent ) ,
IP : ctx . ClientIP ( ) ,
} )
ReturnJson ( ctx , "apiOK" , nil )
} )
// 删除物品
r . POST ( "/delete_item" , func ( ctx * gin . Context ) {
isAuth , user , data := AuthenticationAuthority ( ctx )
if ! isAuth {
ReturnJson ( ctx , "userCookieError" , nil )
return
}
type FromDelete struct {
ID uint ` json:"id" `
}
var from FromDelete
if err := decodeJSON ( data , & from ) ; err != nil || from . ID == 0 {
ReturnJson ( ctx , "jsonErr" , nil )
return
}
var item TabWarehouseItem
if err := models . DB . Where ( "id = ?" , from . ID ) . First ( & item ) . Error ; err != nil {
ReturnJson ( ctx , "item_not_found" , nil )
return
}
// 所属容器 ItemCount -1
if item . ContainerID != nil {
models . DB . Model ( & TabWarehouseContainer { } ) . Where ( "id = ?" , * item . ContainerID ) . Update ( "item_count" , models . DB . Raw ( "item_count - 1" ) )
}
// 删除关联
models . DB . Where ( "item_id = ?" , from . ID ) . Delete ( & TabWarehouseItemFileBind { } )
models . DB . Where ( "item_id = ?" , from . ID ) . Delete ( & TabWarehouseItemCommit { } )
models . DB . Where ( "item_id = ?" , from . ID ) . Delete ( & TabWarehouseItemWorkOrderBind { } )
oldContent , _ := json . Marshal ( item )
models . DB . Create ( & TabWarehouseLog {
EntityType : "item" ,
EntityID : from . ID ,
UserID : user . ID ,
ActionType : "delete" ,
OldContent : string ( oldContent ) ,
IP : ctx . ClientIP ( ) ,
} )
models . DB . Delete ( & item )
ReturnJson ( ctx , "apiOK" , nil )
} )
// 获取物品列表
r . POST ( "/list_item" , func ( ctx * gin . Context ) {
isAuth , _ , data := AuthenticationAuthority ( ctx )
if ! isAuth {
ReturnJson ( ctx , "userCookieError" , nil )
return
}
type FromList struct {
Search string ` json:"search" `
ContainerID * uint ` json:"container_id" `
Entries int ` json:"entries" `
Page int ` json:"page" `
}
var from FromList
if err := decodeJSON ( data , & from ) ; err != nil {
ReturnJson ( ctx , "jsonErr" , nil )
return
}
if from . Entries <= 0 || from . Entries > 300 {
from . Entries = 10
}
if from . Page <= 0 {
from . Page = 1
}
var count int64
query := models . DB . Model ( & TabWarehouseItem { } )
if from . Search != "" {
query = query . Where ( "name LIKE ? OR serial_number LIKE ? OR remark LIKE ?" ,
"%" + from . Search + "%" , "%" + from . Search + "%" , "%" + from . Search + "%" )
}
if from . ContainerID != nil {
query = query . Where ( "container_id = ?" , * from . ContainerID )
}
query . Count ( & count )
var items [ ] TabWarehouseItem
query . Order ( "created_at DESC" ) .
Offset ( from . Entries * ( from . Page - 1 ) ) .
Limit ( from . Entries ) .
Find ( & items )
ReturnJson ( ctx , "apiOK" , gin . H {
"all_count" : count ,
"items" : items ,
} )
} )
// 获取物品详情
r . POST ( "/get_item" , func ( ctx * gin . Context ) {
isAuth , _ , data := AuthenticationAuthority ( ctx )
if ! isAuth {
ReturnJson ( ctx , "userCookieError" , nil )
return
}
type FromGet struct {
ID uint ` json:"id" `
}
var from FromGet
if err := decodeJSON ( data , & from ) ; err != nil || from . ID == 0 {
ReturnJson ( ctx , "jsonErr" , nil )
return
}
var item TabWarehouseItem
if err := models . DB . Where ( "id = ?" , from . ID ) . First ( & item ) . Error ; err != nil {
ReturnJson ( ctx , "item_not_found" , nil )
return
}
// 关联图片
var binds [ ] TabWarehouseItemFileBind
models . DB . Where ( "item_id = ?" , from . ID ) . Find ( & binds )
var fileIDs [ ] uint
for _ , b := range binds {
fileIDs = append ( fileIDs , b . FileID )
}
var files [ ] TabFileInfo_
if len ( fileIDs ) > 0 {
models . DB . Where ( "id IN ?" , fileIDs ) . Find ( & files )
}
// 移动历史
var commits [ ] TabWarehouseItemCommit
models . DB . Where ( "item_id = ?" , from . ID ) . Order ( "created_at DESC" ) . Find ( & commits )
// 关联工单
var woBinds [ ] TabWarehouseItemWorkOrderBind
models . DB . Where ( "item_id = ?" , from . ID ) . Find ( & woBinds )
type WOInfo struct {
ID uint ` json:"id" `
Title string ` json:"title" `
Status string ` json:"status" `
}
var workOrders [ ] WOInfo
for _ , b := range woBinds {
var wo TabWorkOrder
if models . DB . Where ( "id = ?" , b . WorkOrderID ) . First ( & wo ) . Error == nil {
workOrders = append ( workOrders , WOInfo { ID : wo . ID , Title : wo . Title , Status : wo . CurrentStatus } )
}
}
ReturnJson ( ctx , "apiOK" , gin . H {
"item" : item ,
"photos" : files ,
"commits" : commits ,
"work_orders" : workOrders ,
} )
} )
// 移动物品到其他容器
r . POST ( "/move_item" , func ( ctx * gin . Context ) {
isAuth , user , data := AuthenticationAuthority ( ctx )
if ! isAuth {
ReturnJson ( ctx , "userCookieError" , nil )
return
}
type FromMove struct {
ItemID uint ` json:"item_id" `
NewContainer * uint ` json:"new_container" `
Remark string ` json:"remark" `
}
var from FromMove
if err := decodeJSON ( data , & from ) ; err != nil || from . ItemID == 0 {
ReturnJson ( ctx , "jsonErr" , nil )
return
}
var item TabWarehouseItem
if err := models . DB . Where ( "id = ?" , from . ItemID ) . First ( & item ) . Error ; err != nil {
ReturnJson ( ctx , "item_not_found" , nil )
return
}
oldContainer := item . ContainerID
// 同一容器无需操作
if ptrEqUint ( oldContainer , from . NewContainer ) {
ReturnJson ( ctx , "apiOK" , nil )
return
}
// 旧容器 ItemCount -1
if oldContainer != nil {
models . DB . Model ( & TabWarehouseContainer { } ) . Where ( "id = ?" , * oldContainer ) . Update ( "item_count" , models . DB . Raw ( "item_count - 1" ) )
}
// 新容器 ItemCount +1
if from . NewContainer != nil {
models . DB . Model ( & TabWarehouseContainer { } ) . Where ( "id = ?" , * from . NewContainer ) . Update ( "item_count" , models . DB . Raw ( "item_count + 1" ) )
}
// 更新物品容器
item . ContainerID = from . NewContainer
models . DB . Save ( & item )
// 记录移动日志
models . DB . Create ( & TabWarehouseItemCommit {
ItemID : from . ItemID ,
UserID : user . ID ,
OldContainer : oldContainer ,
NewContainer : from . NewContainer ,
Remark : from . Remark ,
IP : ctx . ClientIP ( ) ,
} )
// 写通用操作日志
models . DB . Create ( & TabWarehouseLog {
EntityType : "item" ,
EntityID : from . ItemID ,
UserID : user . ID ,
ActionType : "move" ,
OldContent : ptrStrUint ( oldContainer ) ,
NewContent : ptrStrUint ( from . NewContainer ) ,
IP : ctx . ClientIP ( ) ,
Remark : from . Remark ,
} )
ReturnJson ( ctx , "apiOK" , nil )
} )
// 获取仓库统计
r . POST ( "/count" , func ( ctx * gin . Context ) {
isAuth , _ , _ := AuthenticationAuthority ( ctx )
if ! isAuth {
ReturnJson ( ctx , "userCookieError" , nil )
return
}
type WCount struct {
ContainerTotal int64 ` json:"container_total" `
ItemTotal int64 ` json:"item_total" `
UnstoredItems int64 ` json:"unstored_items" `
}
var count WCount
models . DB . Model ( & TabWarehouseContainer { } ) . Count ( & count . ContainerTotal )
models . DB . Model ( & TabWarehouseItem { } ) . Count ( & count . ItemTotal )
models . DB . Model ( & TabWarehouseItem { } ) . Where ( "container_id IS NULL" ) . Count ( & count . UnstoredItems )
ReturnJson ( ctx , "apiOK" , gin . H {
"container_total" : count . ContainerTotal ,
"item_total" : count . ItemTotal ,
"unstored_items" : count . UnstoredItems ,
} )
} )
}
// ---------- 辅助函数 ----------
func ptrEqUint ( a , b * uint ) bool {
if a == nil && b == nil {
return true
}
if a == nil || b == nil {
return false
}
return * a == * b
}
func ptrStrUint ( p * uint ) string {
if p == nil {
return "nil"
}
return strconv . FormatUint ( uint64 ( * p ) , 10 )
}