正规的仓储出入库管理系统报价(工厂订单出入库信息管理系统模块2)
工厂订单及出入库业务的趋势是客户要求越来越高,客户需要快捷、方便、简单、一站式的出入库手续。因此,货物出入库管理信息系统要简化出入库手续,减轻人员作业量,提高工作效率,助力企业数字信息化转型。
之前整理过该系统的完整解决方案,有兴趣的可以参考文章:《工厂订单出入库信息管理系统》完整解决方案(含演示账号)
二、整体架构设计开发语言:- Golang:Go 极其地快。其性能与 Java 或 C 相似。在我们的使用中,Go 一般比 Python 要快 30 倍。
- Vue: 轻量级框架, 大小只有几十kb, 国人开发,中文文档,不存在语言障碍,易于理解和学习。运行速度更快,相比较与react而言,同样都是操作虚拟dom,就性能而言,vue存在很大的优势。
- 服务器系统:基于免安装可执行程序:支持Windows、Linux,Centos,Ubuntu操作系统;
- 数据库类型:目前已支持PostgreSQL、MySQL、Oracle、Microsoft sql Server、SQLite等,还可以定制其它类型数据库。
- 热数据缓存服务:基于Key-Value 的Redis 数据库,关键热活数据存储在Redis服务中,提高响应速率。
- 主备双活服务器:确保稳定性,如果主服务器故障,自动切换到备服务器。热数据
- 数据库备份:定时增量备份,定期全量备份。
1、模块截图
2、Go 关键代码
package models
import (
"fmt"
"github.com/astaxie/beego/orm"
"productManage/common"
writelog "productManage/log"
"strconv"
"time"
)
type Sn struct {
Sn string `orm:"column(Sn);size(255);pk" description:"唯一SN"`
BoxNum int `orm:"column(BoxNum)" description:"SN所在的箱号"`
BoxTotal int `orm:"column(BoxTotal)" description:"总箱数,对应order表的box_total"`
CreateTime int64 `orm:"column(CreateTime)" description:"生成时间"`
Ksn string `orm:"column(Ksn);size(255)" description:"唯一KSN"`
OrderID string `orm:"column(OrderID);size(255)" description:"订单号,对应order表的order_id"`
StatusBox int8 `orm:"column(StatusBox)" description:"装箱状态:0 未确认;1正确;2异常;"`
StatusPrepare int8 `orm:"column(StatusPrepare)" description:"备货状态:0 未确认;1正确;2异常;"`
StatusQc int8 `orm:"column(StatusQc)" description:"品管状态"`
DevId string `orm:"column(DevId);size(255)" description:"五码合一用的"`
Sim string `orm:"column(Sim);size(255)" description:"五码合一用的"`
Imei string `orm:"column(Imei);size(255)" description:"五码合一用的"`
RecipientID int64 `orm:"column(RecipientID);" description:"收件编号"`
}
type Sn2 struct {
BoxNumCount int `orm:"column(BoxNumCount)" description:"SN所在的箱号的sn总数"`
BoxNum int `orm:"column(BoxNum)" description:"SN所在的箱号"`
}
func (t *Sn) TableName() string {
return "sn"
}
func (t *Sn) TableIndex() [][]string {
return [][]string{
[]string{"OrderID"},
}
}
func init() {
orm.RegisterModel(new(Sn))
}
// AddSn insert a new Sn into database and returns
// last inserted Id on success.
func AddSn(m *Sn) (id int64, err error) {
o := orm.NewOrm()
id, err = o.Insert(m)
//success id == 0, err == nil
return
}
func AddSnList(sn_list []Sn) (err error, error_sn string) {
o := orm.NewOrm()
o.Begin()
writelog.WriteDebug("AddSnList o.Begin() count%d", len(sn_list))
_, err = o.InsertMulti(100, sn_list)
if err != nil {
writelog.WriteError("AddSnList 事务执行错误:err=%s", err.Error())
o.Rollback()
return err, "InsertMulti"
}
err = o.Commit()
if err != nil {
writelog.WriteError("AddSnList 事务提交错误:err=%s", err.Error())
return err, "Commit"
}
writelog.WriteDebug("AddSnList End")
return nil, ""
}
func DelSn(sn string) (num int64, err error) {
o := orm.NewOrm()
num, err = o.Delete(&Sn{Sn: sn})
return num, err
}
func DelSnByOrderID(OrderID string) (num int64, err error) {
o := orm.NewOrm()
sql := fmt.Sprintf(`DELETE FROM sn WHERE OrderID='%s'`, OrderID)
res, err := o.Raw(sql).Exec()
if err == nil {
num, _ = res.RowsAffected()
}
return num, err
}
func GetSnList(StartTime, EndTime int64, OrderID string, Sn string, BoxNum, BoxTotal, StatusBox, Page, RowNum int) (recordcount int64, num int64, sn []Sn, err error, sql string) {
if RowNum <= 0 {
RowNum = common.Row_Num_Default
}
sql = fmt.Sprintf(`SELECT * FROM sn WHERE 1=1`)
if StartTime > 0 {
sql = fmt.Sprintf(` %s AND CreateTime >= %d`, sql, StartTime)
}
if EndTime > 0 {
sql = fmt.Sprintf(` %s AND CreateTime <= %d`, sql, EndTime)
}
if len(OrderID) > 0 {
sql = fmt.Sprintf(` %s AND OrderID like '%%%s%%'`, sql, OrderID)
}
if len(Sn) > 0 {
sql = fmt.Sprintf(` %s AND Sn like '%%%s%%'`, sql, Sn)
}
if BoxNum > 0 {
sql = fmt.Sprintf(` %s AND BoxNum = %d`, sql, BoxNum)
}
if BoxTotal > 0 {
sql = fmt.Sprintf(` %s AND BoxTotal = %d`, sql, BoxTotal)
}
if StatusBox > 0 {
sql = fmt.Sprintf(` %s AND StatusBox = %d`, sql, StatusBox)
}
o := orm.NewOrm()
sql_num := "SELECT COUNT(*) FROM ( " sql " ) AS a"
err = o.Raw(sql_num).QueryRow(&recordcount)
if err != nil {
//writelog.WriteDebug(sql_num)
return 0, 0, sn, err, sql
}
if Page >= 0 {
sql = sql " limit " strconv.Itoa(Page*RowNum) "," strconv.Itoa(RowNum)
}
writelog.WriteDebug(sql)
num, err = o.Raw(sql).QueryRows(&sn)
return recordcount, num, sn, err, sql
}
func GetSnBySn(strSn string) (v *Sn, err error) {
o := orm.NewOrm()
var ArraySn []Sn
num, err := o.QueryTable("sn").Filter("Sn", strSn).All(&ArraySn)
if err == nil {
if num <= 0 {
writelog.WriteDebug("没找到%s", strSn)
return nil, orm.ErrNoRows
} else if num > 1 {
writelog.WriteDebug("多条报错%s", strSn)
return nil, orm.ErrMultiRows
} else { //num==1
return &ArraySn[0], nil
}
}
return nil, err
}
func GetSnByKsn(strKsn string) (sn string, err error, _type string) {
o := orm.NewOrm()
var ArraySn []Sn
num, err := o.QueryTable("sn").Filter("Ksn", strKsn).All(&ArraySn)
if err == nil {
if num <= 0 {
writelog.WriteDebug("没找到%s", strKsn)
return "", orm.ErrNoRows, ""
} else if num > 1 {
writelog.WriteDebug("多条报错%s", strKsn)
return "", orm.ErrMultiRows, ""
} else { //num==1
return ArraySn[0].Sn, nil, "ksn"//通过Ksn找到的
}
}
return "", err, ""
}
//不知道输入的是Sn还是Ksn,通过还原成Sn
func GetSnBySnOrKsn(strSn string) (sn string, err error, _type string) {
v, err := GetSnBySn(strSn)
if err == nil && v != nil {
return v.Sn, nil, "sn"//通过sn找到的
}
return GetSnByKsn(strSn)
}
//导入sn的时候,计算每个sn对应的BoxNum
func SetBoxNumInSn(OrderID string, BoxTotal, NumberPerBox int) {
writelog.WriteDebug("--------%s,%d,%d", OrderID, BoxTotal, NumberPerBox)
sql := fmt.Sprintf(`SELECT * FROM sn WHERE OrderID='%s'`, OrderID)
o := orm.NewOrm()
sql_num := "SELECT COUNT(*) FROM ( " sql " ) AS a"
var recordcount int64
err := o.Raw(sql_num).QueryRow(&recordcount)
if err != nil {
return
}
Page := 0
for 1 == 1 {
BoxNum := Page 1
sql_update := fmt.Sprintf(`UPDATE sn AS A INNER JOIN (SELECT Sn FROM sn WHERE OrderID='%s' LIMIT %s,%s) As B ON A.Sn=B.Sn SET A.BoxNum=%d`,
OrderID, strconv.Itoa(Page*NumberPerBox), strconv.Itoa(NumberPerBox), BoxNum)
writelog.WriteDebug("sn分箱 start %v", sql_update)
o := orm.NewOrm()
_, err = o.Raw(sql_update).Exec()
if err != nil {
writelog.WriteDebug("%s %s", sql_update, err.Error())
}
writelog.WriteDebug("sn分箱 end")
//创建box表
sql = fmt.Sprintf(`SELECT Sn FROM sn WHERE OrderID='%s' AND BoxNum=%d ORDER BY Sn`, OrderID, BoxNum)
var s []Sn
writelog.WriteDebug("box分箱 start")
n, err := o.Raw(sql).QueryRows(&s)
if err != nil {
writelog.WriteDebug("%s %s", sql, err.Error())
}
if n <= 0 {
writelog.WriteDebug("n %d error ", n)
} else {
var box Box
box.BoxNum = BoxNum
box.OrderID = OrderID
box.BoxTotal = BoxTotal
box.SnStart = s[0].Sn
box.SnEnd = s[n-1].Sn
box.CreateTime = time.Now().Unix()
_, err = AddBox(&box)
if err != nil {
writelog.WriteDebug("AddBox BoxNum:%s OrderID:%s, err:%s", BoxNum, OrderID, err.Error())
}
writelog.WriteDebug("box分箱 end, %v, %v" , BoxNum, box.SnStart )
}
Page = Page 1
time.Sleep(time.Nanosecond)
if int64(Page*NumberPerBox) >= recordcount {
break
}
}
}
func GetSnListByOrderIDAndBoxNum(OrderID string, BoxNum int) (num int64, sn []Sn, err error, sql string) {
sql = fmt.Sprintf(`SELECT * FROM sn WHERE 1=1`)
if len(OrderID) > 0 {
sql = fmt.Sprintf(` %s AND OrderID='%s'`, sql, OrderID)
}
if BoxNum > 0 {
sql = fmt.Sprintf(` %s AND BoxNum=%d`, sql, BoxNum)
}
//writelog.WriteDebug(sql)
o := orm.NewOrm()
num, err = o.Raw(sql).QueryRows(&sn)
return num, sn, err, sql
}
func UpdateSnStatusBox( SnList []string, StatusBox int) {
writelog.WriteDebug("UpdateSnStatusBox begin")
sql_update := fmt.Sprintf(`UPDATE sn SET StatusBox=%d WHERE 1 = 1`, StatusBox)
Len := len(SnList)
if Len > 0 {
sql_update = sql_update " and Sn in ("
for k, v := range SnList {
if k < Len-1 {
sql_update = sql_update fmt.Sprintf("'%s' ,", v)
} else {
sql_update = sql_update fmt.Sprintf("'%s' ", v)
}
}
sql_update = sql_update " )"
}
o := orm.NewOrm()
_, err := o.Raw(sql_update).Exec()
if err != nil {
writelog.WriteDebug("%s %s", sql_update, err.Error())
}
writelog.WriteDebug("UpdateSnStatusBox %s ok", sql_update)
}
func UpdateSnBoxStatusBySn(BoxNum int, OrderID string, Sn string, StatusBox int) {
sql_update := fmt.Sprintf(`UPDATE sn SET StatusBox=%d WHERE OrderID='%s' and BoxNum=%d and Sn='%s'`, StatusBox, OrderID, BoxNum, Sn)
o := orm.NewOrm()
_, err := o.Raw(sql_update).Exec()
if err != nil {
writelog.WriteDebug("%s %s", sql_update, err.Error())
}
writelog.WriteDebug("UpdateSnBoxStatusBySn %s ok", sql_update)
}
// 查询OrderId, BoxNum的 StatusBox 为OK的个数是否等于 NumPerBox
func CountSnByBoxNumAndOrderID(BoxNum int, OrderID string) (recordcount int, err error) {
o := orm.NewOrm()
sql_num := fmt.Sprintf(`SELECT COUNT(*) FROM sn WHERE OrderID='%s' and BoxNum=%d `, OrderID, BoxNum)
err = o.Raw(sql_num).QueryRow(&recordcount)
return recordcount, err
}
// 查询OrderId, BoxNum的 StatusBox 为OK的个数是否等于 NumPerBox
func CountSnByStatusBox(BoxNum int, OrderID string, StatusBox int) (recordcount int, err error) {
o := orm.NewOrm()
sql_num := fmt.Sprintf(`SELECT COUNT(*) FROM sn WHERE StatusBox=%d and OrderID='%s' and BoxNum=%d `, StatusBox, OrderID, BoxNum)
err = o.Raw(sql_num).QueryRow(&recordcount)
return recordcount, err
}
func CountSnBySnStartToSnEnd(SnStart, SnEnd string) (recordcount int, err error) {
o := orm.NewOrm()
sql_num := fmt.Sprintf(`SELECT COUNT(*) FROM sn WHERE Sn>='%s' AND Sn<='%s'`, SnStart, SnEnd)
err = o.Raw(sql_num).QueryRow(&recordcount)
writelog.WriteDebug("CountSnBySnStartToSnEnd %s %d", sql_num, recordcount)
return recordcount, err
}
func GetKsnBySn(sn string) (s Sn, err error) {
sql := fmt.Sprintf(`select Sn,Ksn from sn where Sn='%s'`, sn)
o := orm.NewOrm()
err = o.Raw(sql).QueryRow(&s)
writelog.WriteDebug("GetKsnBySn % v" ,s)
return s, err
}
//更新五码合一数据
func Update5Code(sn_s []Sn) {
writelog.WriteDebug("% v", sn_s)
for _, s := range sn_s {
o := orm.NewOrm()
sql := fmt.Sprintf(`Update sn set DevId='%s',Sim='%s',Imei='%s' WHERE Sn='%s'`, s.DevId, s.Sim, s.Sim, s.Sn)
//writelog.WriteDebug(sql)
o.Raw(sql).Exec()
}
}
func GetSnListByRecipientID(OrderID string , RecipientID int64) (num int64, sn []Sn2, err error) {
sql := fmt.Sprintf(`SELECT count(BoxNum) as BoxNumCount, BoxNum FROM sn WHERE RecipientID=%d and OrderID = '%s' group by BoxNum `, RecipientID, OrderID )
o := orm.NewOrm()
num, err = o.Raw(sql).QueryRows(&sn)
writelog.WriteDebug("%v, %v", sql, sn)
return num, sn, err
}
func EditRecipientIDByOrderIDAndBox(RecipientID int64, OrderID string, BoxNum int , SnStart, SnEnd string) (num int64, err error) {
o := orm.NewOrm()
sql := fmt.Sprintf(`Update sn set RecipientID=%d WHERE BoxNum=%d And OrderID = '%s' And Sn >='%s' And Sn <='%s' `, RecipientID, BoxNum, OrderID, SnStart, SnEnd)
writelog.WriteDebug(sql)
res, err := o.Raw(sql).Exec()
if err == nil {
num, _ = res.RowsAffected()
writelog.WriteDebug("EditRecipientIDByOrderIDAndBox ok, rows=%d", num)
} else {
writelog.WriteDebug("EditRecipientIDByOrderIDAndBox err:%s", err.Error())
}
return num, err
}
func GetSnListByOrderIDAndBoxNumAndRecipientId(OrderID string, BoxNum int) (num int64, sn []Sn, err error, sql string) {
sql = fmt.Sprintf(`SELECT * FROM sn WHERE RecipientId=0`)
if len(OrderID) > 0 {
sql = fmt.Sprintf(` %s AND OrderID='%s'`, sql, OrderID)
}
if BoxNum > 0 {
sql = fmt.Sprintf(` %s AND BoxNum=%d`, sql, BoxNum)
}
//writelog.WriteDebug(sql)
o := orm.NewOrm()
num, err = o.Raw(sql).QueryRows(&sn)
return num, sn, err, sql
}
func ResetSnRecipientId(RecipientID int64) (num int64, err error) {
o := orm.NewOrm()
sql := fmt.Sprintf(`Update sn set RecipientID=0 WHERE RecipientID=%d`, RecipientID)
writelog.WriteDebug(sql)
res, err := o.Raw(sql).Exec()
if err == nil {
num, _ = res.RowsAffected()
writelog.WriteDebug("ResetSnRecipientId ok, rows=%d", num)
} else {
writelog.WriteDebug("ResetSnRecipientId err:%s", err.Error())
}
return num, err
}
3、vue代码
<template>
<div class="page-container">
<div class="search-wrapper">
<div class="block">
<span class="label">开始时间</span>
<el-date-picker
v-model="searchForm.startTime"
type="date"
:clearable="true"
format="yyyy-MM-dd 00:00:00"
placeholder="选择日期">
</el-date-picker>
</div>
<div class="block">
<span class="label">结束时间</span>
<el-date-picker
v-model="searchForm.endTime"
type="date"
:clearable="true"
format="yyyy-MM-dd 23:59:59"
placeholder="选择日期">
</el-date-picker>
</div>
<div class="block">
<div class="label">订单号</div>
<el-input v-model="searchForm.orderId"></el-input>
</div>
<div class="block">
<div class="label">箱号</div>
<el-input v-model="searchForm.boxNum"></el-input>
</div>
<div class="block">
<div class="label">扫描装箱状态</div>
<el-select v-model="searchForm.status" placeholder="请选择">
<el-option
v-for="item in status"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</div>
<div class="block">
<div class="label">SN</div>
<el-input v-model="searchForm.sn"></el-input>
</div>
<div class="operates">
<el-button type="primary" @click="searchClick">查询</el-button>
<el-button type="primary" @click="resetClick">重置</el-button>
</div>
</div>
<el-table
:data="tableData"
style="width:100%">
<el-table-column
prop="OrderID"
label="订单号"
>
</el-table-column>
<el-table-column
prop="Sn"
label="SN"
>
</el-table-column>
<el-table-column
prop="Ksn"
label="KSN">
</el-table-column>
<el-table-column
prop="BoxNum"
label="箱号"
>
</el-table-column>
<el-table-column
prop="CreateTime"
label="创建时间"
>
</el-table-column>
<el-table-column
prop="StatusBox"
label="扫描装箱状态"
>
<template slot-scope="scope">
<div v-if="scope.row.StatusBox==0">无</div>
<div v-if="scope.row.StatusBox==1">正确</div>
<div v-if="scope.row.StatusBox==2">异常</div>
<div v-if="scope.row.StatusBox==3">进行中</div>
</template>
</el-table-column>
</el-table>
<el-pagination
@current-change="pageChange"
:current-page.sync="pageData.Page"
:page-size="pageData.RowNum"
layout="total, prev, pager, next"
:total="RowNum">
</el-pagination>
</div>
</template>
<script>
import {SN_API_PATH} from "../../service/api"
let StatusDefinite = [{
id:0,
name:"全部"
},{
id:1,
name:"正确"
},{
id:2,
name:"异常"
}]
export default{
data(){
return{
orderId:"",
searchForm:{
startTime:new Date(new Date().toLocaleDateString()),
endTime:new Date(new Date().toLocaleDateString()),
boxNum:"",
status:0,
orderId:"",
sn:""
},
tableData:[],
status:StatusDefinite,
RowNum:0,
pageData:{
Page:1,
RowNum:20
}
}
},
created(){
this.searchForm.orderId = this.$route.query.orderId?this.$route.query.orderId:""
this.searchForm.boxNum = this.$route.query.boxNum?this.$route.query.boxNum:""
if(this.$route.query.startTime){
this.searchForm.startTime = new Date(this.$route.query.startTime)
}
if(this.$route.query.endTime){
this.searchForm.endTime = new Date(this.$route.query.endTime)
}
this.getSnList()
},
methods:{
pageChange(){
this.getSnList()
},
getSnList(){
let formData = new FormData()
formData.append("Token",sessionStorage.getItem("token"))
formData.append("Act","GetSnList")
formData.append("Box",this.searchForm.boxNum)
formData.append("StatusBox",this.searchForm.status)
formData.append("Page",this.pageData.Page-1)
formData.append("RowNum",this.pageData.RowNum)
let startTime = this.moment(this.searchForm.startTime).format("YYYY-MM-DD 00:00:00")
let endTime = this.moment(this.searchForm.endTime).format("YYYY-MM-DD 23:59:59")
formData.append("StartTime",new Date(startTime).getTime()/1000)
formData.append("EndTime",new Date(endTime).getTime()/1000)
formData.append("OrderID",this.searchForm.orderId)
formData.append("Sn",this.searchForm.sn)
this.$axios.post(SN_API_PATH,formData).then(res=>{
if(res.data.Ret == 0){
if(res.data.Data){
res.data.Data.map(v=>{
v.CreateTime = this.moment((v.CreateTime*1000)).format("YYYY-MM-DD HH:mm:ss")
})
}else{
res.data.Data = []
}
this.tableData = res.data.Data
this.RowNum = res.data.Recordcount
}else{
this.$message({
message:res.data.Msg,
type:"error",
duration:3000
});
}
})
},
resetClick(){
this.searchForm = {
orderId:"",
startTime:new Date(new Date().toLocaleDateString()),
endTime:new Date(new Date().toLocaleDateString()),
sn:""
}
},
searchClick(){
this.getSnList()
},
repairClick(scope){
this.$prompt('SN号', '', {
confirmButtonText: '确定',
cancelButtonText: '取消',
}).then(res=>{
})
}
}
}
</script>
<style scoped>
.search-wrapper{
display:flex;
justy-content:flex-start;
flex-wrap:wrap;
}
</style>
四、结语
本次分享结束,欢迎来撩!
完整方案介绍:《工厂订单出入库信息管理系统》完整解决方案(含演示账号)
系统演示网址:factory http://47.113.115.218:81 演示密码:123456
,免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com