Tinger 1 年之前
父节点
当前提交
ae2da6c893

+ 2 - 1
Dockerfile

@@ -1,5 +1,6 @@
 FROM ubuntu:latest
 WORKDIR /srv
 
-RUN apt-get -qq update && apt-get -qq install -y --no-install-recommends ca-certificates curl && \
+RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list && \
+  apt-get -qq update && apt-get -qq install -y --no-install-recommends ca-certificates curl && \
   ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo Asia/Shanghai > /etc/timezone

+ 0 - 13
handlers/manager/admin/charts/handler.go

@@ -1,13 +0,0 @@
-package charts
-
-import (
-	"Wine-Server/utils"
-	"Wine-Server/utils/tables"
-	"github.com/gorilla/websocket"
-)
-
-func Handle(msg utils.WsMsg, conn *websocket.Conn, manager *tables.ManagerTable) {
-	switch msg.Event {
-
-	}
-}

+ 24 - 1
handlers/manager/admin/dashboard/handler.go

@@ -8,6 +8,29 @@ import (
 
 func Handle(msg utils.WsMsg, conn *websocket.Conn, manager *tables.ManagerTable) {
 	switch msg.Event {
-
+	case "adminDashboardRankDevices":
+		rankDevices(conn, manager, msg.Data)
+		break
+	case "adminDashboardRankWines":
+		rankWines(conn, manager, msg.Data)
+		break
+	case "adminDashboardRankWorkers":
+		rankWorkers(conn, manager, msg.Data)
+		break
+	case "adminDashboardQuerySelf":
+		querySelf(conn, manager, msg.Data)
+		break
+	case "adminDashboardQueryDevice":
+		queryDevice(conn, manager, msg.Data)
+		break
+	case "adminDashboardQueryWine":
+		queryWine(conn, manager, msg.Data)
+		break
+	case "adminDashboardQueryWorker":
+		queryWorker(conn, manager, msg.Data)
+		break
+	default:
+		_ = conn.WriteJSON(utils.WsError("unrecognized event"))
+		break
 	}
 }

+ 11 - 0
handlers/manager/admin/dashboard/params.go

@@ -0,0 +1,11 @@
+package dashboard
+
+type rankParam struct {
+	Start string `json:"start"`
+	End   string `json:"end"`
+}
+
+type queryParam struct {
+	Id   string `json:"id"`
+	Type uint8  `json:"type"` // 0:不限、1:一周、2:一月、3:一季、4:半年、5:一年
+}

+ 124 - 0
handlers/manager/admin/dashboard/worker.go

@@ -0,0 +1,124 @@
+package dashboard
+
+import (
+	"Wine-Server/utils"
+	"Wine-Server/utils/tables"
+	"github.com/gorilla/websocket"
+)
+
+func rankDevices(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
+	var param rankParam
+	err := utils.AnyTrans(data, &param)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminDashboardRankDevicesRes", utils.Fail("参数错误")))
+		return
+	}
+	rank, err := tables.TradeRankDevicesForAdmin(manager.Id, param.Start, param.End)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminDashboardRankDevicesRes", utils.Fail("查询设备排行失败")))
+		return
+	}
+	_ = conn.WriteJSON(utils.WsEvent("adminDashboardRankDevicesRes", utils.Success(rank)))
+}
+
+func rankWines(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
+	var param rankParam
+	err := utils.AnyTrans(data, &param)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminDashboardRankWinesRes", utils.Fail("参数错误")))
+		return
+	}
+	rank, err := tables.TradeRankWinesForAdmin(manager.Id, param.Start, param.End)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminDashboardRankWinesRes", utils.Fail("查询酒品排行失败")))
+		return
+	}
+	_ = conn.WriteJSON(utils.WsEvent("adminDashboardRankWinesRes", utils.Success(rank)))
+}
+
+func rankWorkers(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
+	var param rankParam
+	err := utils.AnyTrans(data, &param)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminDashboardRankWorkersRes", utils.Fail("参数错误")))
+		return
+	}
+	rank, err := tables.ChangeRankWorkersForAdmin(manager.Id, param.Start, param.End)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminDashboardRankWorkersRes", utils.Fail("查询上酒工排行失败")))
+		return
+	}
+	_ = conn.WriteJSON(utils.WsEvent("adminDashboardRankWorkersRes", utils.Success(rank)))
+}
+
+func querySelf(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
+	_type, ok := data.(uint8)
+	if !ok {
+		_ = conn.WriteJSON(utils.WsEvent("adminDashboardQuerySelfRes", utils.Fail("参数错误")))
+		return
+	}
+	list, err := tables.TradeQuerySpecificOne("manager", manager.Id, _type)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminDashboardQuerySelfRes", utils.Fail("查询管理员历史记录失败")))
+		return
+	}
+	_ = conn.WriteJSON(utils.WsEvent("adminDashboardQuerySelfRes", utils.Success(list)))
+}
+
+func queryDevice(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
+	var param queryParam
+	err := utils.AnyTrans(data, &param)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminDashboardQueryDeviceRes", utils.Fail("参数错误")))
+		return
+	}
+	list, err := tables.TradeQuerySpecificOneForAdmin(manager.Id, "device", param.Id, param.Type)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminDashboardQueryDeviceRes", utils.Fail("查询设备历史记录失败")))
+		return
+	}
+	_ = conn.WriteJSON(utils.WsEvent("adminDashboardQueryDeviceRes", utils.Success(list)))
+}
+
+func queryWine(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
+	var param queryParam
+	err := utils.AnyTrans(data, &param)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminDashboardQueryWineRes", utils.Fail("参数错误")))
+		return
+	}
+	list, err := tables.TradeQuerySpecificOneForAdmin(manager.Id, "wine", param.Id, param.Type)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminDashboardQueryWineRes", utils.Fail("查询酒品历史记录失败")))
+		return
+	}
+	_ = conn.WriteJSON(utils.WsEvent("adminDashboardQueryWineRes", utils.Success(list)))
+}
+
+func queryWorker(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
+	var param queryParam
+	err := utils.AnyTrans(data, &param)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminDashboardQueryWorkerRes", utils.Fail("参数错误")))
+		return
+	}
+	list, err := tables.ChangeQueryWorkerForAdmin(manager.Id, param.Id, param.Type)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminDashboardQueryWorkerRes", utils.Fail("查询上酒工历史记录失败")))
+		return
+	}
+	_ = conn.WriteJSON(utils.WsEvent("adminDashboardQueryWorkerRes", utils.Success(list)))
+}

+ 2 - 2
handlers/manager/admin/trade/handler.go

@@ -9,10 +9,10 @@ import (
 func Handle(msg utils.WsMsg, conn *websocket.Conn, manager *tables.ManagerTable) {
 	switch msg.Event {
 	case "adminTradeQueryUser":
-		queryUser(conn, msg.Data)
+		queryUser(conn, manager, msg.Data)
 		break
 	case "adminTradeQueryTrade":
-		queryTrade(conn, msg.Data)
+		queryTrade(conn, manager, msg.Data)
 		break
 	case "adminTradeRefund":
 		refund(conn, manager, msg.Data)

+ 10 - 12
handlers/manager/admin/trade/worker.go

@@ -6,8 +6,7 @@ import (
 	"github.com/gorilla/websocket"
 )
 
-// queryUser @super.order.QueryUser
-func queryUser(conn *websocket.Conn, data any) {
+func queryUser(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
 	var param queryUserParam
 	err := utils.AnyTrans(data, &param)
 	if err != nil {
@@ -15,7 +14,7 @@ func queryUser(conn *websocket.Conn, data any) {
 		_ = conn.WriteJSON(utils.WsEvent("adminTradeQueryUserRes", utils.Fail("参数错误")))
 		return
 	}
-	total, users, err := tables.UserQuery(param.Id, param.Vip, param.Limit, param.Page)
+	total, users, err := tables.UserQueryForAdmin(manager.Id, param.Id, param.Vip, param.Limit, param.Page)
 	if err != nil {
 		utils.Logger.Println(err)
 		_ = conn.WriteJSON(utils.WsEvent("adminTradeQueryUserRes", utils.Fail("查询用户失败")))
@@ -27,8 +26,7 @@ func queryUser(conn *websocket.Conn, data any) {
 	))
 }
 
-// queryTrade @super.order.QueryTrade
-func queryTrade(conn *websocket.Conn, data any) {
+func queryTrade(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
 	var param queryTradeParam
 	err := utils.AnyTrans(data, &param)
 	if err != nil {
@@ -36,7 +34,7 @@ func queryTrade(conn *websocket.Conn, data any) {
 		_ = conn.WriteJSON(utils.WsEvent("adminTradeQueryTradeRes", utils.Fail("参数错误")))
 		return
 	}
-	total, trades, err := tables.TradeQuery(param.Cond, param.Uid, param.Limit, param.Page)
+	total, trades, err := tables.TradeQueryForAdmin(manager.Id, param.Cond, param.Uid, param.Limit, param.Page)
 	if err != nil {
 		utils.Logger.Println(err)
 		_ = conn.WriteJSON(utils.WsEvent("adminTradeQueryTradeRes", utils.Fail("查询订单失败")))
@@ -48,7 +46,6 @@ func queryTrade(conn *websocket.Conn, data any) {
 	))
 }
 
-// refund @super.order.Refund
 func refund(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
 	var param refundParam
 	err := utils.AnyTrans(data, &param)
@@ -69,12 +66,12 @@ func refund(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
 		_ = conn.WriteJSON(utils.WsEvent("adminTradeRefundRes", utils.Fail("超出原订单金额")))
 		return
 	}
-	refund := tables.RefundTable{Tid: param.Tid, Manager: manager.Id, Cash: param.Cash, Reason: param.Reason}
-	err = refund.Insert()
+	fundTable := tables.RefundTable{Tid: param.Tid, Manager: manager.Id, Cash: param.Cash, Reason: param.Reason}
+	err = fundTable.Insert()
 	if err != nil {
-		err = refund.Get()
-		refund.Manager, refund.Cash, refund.Reason = manager.Id, param.Cash, param.Reason
-		err = refund.UpdateSelf()
+		err = fundTable.Get()
+		fundTable.Manager, fundTable.Cash, fundTable.Reason = manager.Id, param.Cash, param.Reason
+		err = fundTable.UpdateSelf()
 	}
 	_ = conn.WriteJSON(utils.WsEvent("adminTradeRefundRes", utils.Success(nil)))
 	// update user table
@@ -82,4 +79,5 @@ func refund(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
 	_ = user.Get()
 	user.RefundCount++
 	user.RefundCost += uint64(param.Cash)
+	_ = user.UpdateSelf()
 }

+ 17 - 0
handlers/manager/admin/wine/change/params.go

@@ -0,0 +1,17 @@
+package change
+
+import "Wine-Server/utils/tables"
+
+type queryParam struct {
+	Cond  string `json:"cond"`
+	Deal  int    `json:"deal"`
+	Page  int    `json:"page"`
+	Limit int    `json:"limit"`
+}
+
+type assignParam struct {
+	Id     uint32                     `json:"id"`
+	Worker string                     `json:"worker"`
+	Old    [4]tables.WineWithIdRemain `json:"old"`
+	New    [4]tables.WineWithIdRemain `json:"new"`
+}

+ 118 - 0
handlers/manager/admin/wine/change/worker.go

@@ -0,0 +1,118 @@
+package change
+
+import (
+	"Wine-Server/utils"
+	"Wine-Server/utils/tables"
+	"github.com/gorilla/websocket"
+)
+
+func Query(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
+	var param queryParam
+	err := utils.AnyTrans(data, &param)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineChangeQueryRes", utils.Fail("参数错误")))
+		return
+	}
+	total, warns, err := tables.WarnQuery(manager.Id, param.Cond, param.Deal, param.Limit, param.Page)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineChangeQueryRes", utils.Fail("查询信息失败")))
+		return
+	}
+	_ = conn.WriteJSON(utils.WsEvent(
+		"adminWineChangeQueryRes",
+		utils.Success(utils.JsonType{"total": total, "list": warns}),
+	))
+}
+
+func QueryWine(conn *websocket.Conn, data any) {
+	cond, ok := data.(string)
+	if !ok {
+		_ = conn.WriteJSON(utils.WsEvent("adminWineChangeQueryWineRes", utils.Fail("参数错误")))
+		return
+	}
+	wines, err := tables.WinesQueryByIdOrName(cond)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineChangeQueryWineRes", utils.Fail("查询酒品失败")))
+		return
+	}
+	_ = conn.WriteJSON(utils.WsEvent(
+		"adminWineChangeQueryWineRes",
+		utils.Success(wines),
+	))
+}
+
+func QueryWorker(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
+	cond, ok := data.(string)
+	if !ok {
+		_ = conn.WriteJSON(utils.WsEvent("adminWineChangeQueryWorkerRes", utils.Fail("参数错误")))
+		return
+	}
+	workers, err := tables.WorkersQueryByPhoneOrName(manager.Id, cond)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineChangeQueryWorkerRes", utils.Fail("查询酒品失败")))
+		return
+	}
+	_ = conn.WriteJSON(utils.WsEvent(
+		"adminWineChangeQueryWorkerRes",
+		utils.Success(workers),
+	))
+}
+
+func Assign(conn *websocket.Conn, data any) {
+	var param assignParam
+	err := utils.AnyTrans(data, &param)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineChangeAssignRes", utils.Fail("参数错误")))
+		return
+	}
+	if param.Worker == "" {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineChangeAssignRes", utils.Fail("请选择上酒工")))
+		return
+	}
+	worker := tables.WorkerTable{Id: param.Worker}
+	err = worker.Get()
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineChangeAssignRes", utils.Fail("上酒工不存在")))
+		return
+	}
+	warn := tables.WarnTable{Id: param.Id}
+	err = warn.Get()
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineChangeAssignRes", utils.Fail("报警不存在")))
+		return
+	}
+	change := tables.ChangeTable{
+		Wid: warn.Id, Device: warn.Device, Manager: warn.Manager,
+		Worker: param.Worker, Code: utils.RandomString(6, utils.NumberPatten),
+	}
+	change.Old, change.New = param.Old, param.New
+	err = change.Insert()
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineChangeAssignRes", utils.Fail("安排失败 01")))
+		return
+	}
+	warn.Deal = true
+	err = warn.UpdateSelf()
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineChangeAssignRes", utils.Fail("安排失败 02")))
+		return
+	}
+	worker.Todo++
+	err = worker.UpdateSelf()
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineChangeAssignRes", utils.Fail("安排失败 03")))
+		return
+	}
+	_ = conn.WriteJSON(utils.WsEvent("adminWineChangeAssignRes", utils.Success(nil)))
+}

+ 31 - 1
handlers/manager/admin/wine/handler.go

@@ -1,6 +1,8 @@
 package wine
 
 import (
+	"Wine-Server/handlers/manager/admin/wine/change"
+	"Wine-Server/handlers/manager/admin/wine/history"
 	"Wine-Server/utils"
 	"Wine-Server/utils/tables"
 	"github.com/gorilla/websocket"
@@ -8,6 +10,34 @@ import (
 
 func Handle(msg utils.WsMsg, conn *websocket.Conn, manager *tables.ManagerTable) {
 	switch msg.Event {
-
+	// change
+	case "adminWineChangeQuery":
+		change.Query(conn, manager, msg.Data)
+		break
+	case "adminWineChangeQueryWine":
+		change.QueryWine(conn, msg.Data)
+		break
+	case "adminWineChangeQueryWorker":
+		change.QueryWorker(conn, manager, msg.Data)
+		break
+	case "adminWineChangeAssign":
+		change.Assign(conn, msg.Data)
+		break
+	// history
+	case "adminWineHistoryQueryWorker":
+		history.QueryWorker(conn, manager, msg.Data)
+		break
+	case "adminWineHistoryQueryOrder":
+		history.QueryOrder(conn, manager, msg.Data)
+		break
+	case "adminWineHistoryOrderUpdate":
+		history.OrderUpdate(conn, msg.Data)
+		break
+	case "adminWineHistoryOrderDelete":
+		history.OrderDelete(conn, msg.Data)
+		break
+	default:
+		_ = conn.WriteJSON(utils.WsError("unrecognized event"))
+		break
 	}
 }

+ 22 - 0
handlers/manager/admin/wine/history/params.go

@@ -0,0 +1,22 @@
+package history
+
+import "Wine-Server/utils/tables"
+
+type queryWorkerParam struct {
+	Cond  string `json:"cond"`
+	Page  int    `json:"page"`
+	Limit int    `json:"limit"`
+}
+
+type queryOrderParam struct {
+	Worker string `json:"worker"`
+	Cond   string `json:"cond"`
+	Deal   int    `json:"deal"`
+	Page   int    `json:"page"`
+	Limit  int    `json:"limit"`
+}
+
+type updateParam struct {
+	Id  uint32                     `json:"id"`
+	New [4]tables.WineWithIdRemain `json:"new"`
+}

+ 118 - 0
handlers/manager/admin/wine/history/worker.go

@@ -0,0 +1,118 @@
+package history
+
+import (
+	"Wine-Server/utils"
+	"Wine-Server/utils/tables"
+	"github.com/gorilla/websocket"
+)
+
+func QueryWorker(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
+	var param queryWorkerParam
+	err := utils.AnyTrans(data, &param)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineHistoryQueryWorkerRes", utils.Fail("参数错误")))
+		return
+	}
+	total, workers, err := tables.WorkerQueryForHistory(manager.Id, param.Cond, param.Limit, param.Page)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineHistoryQueryWorkerRes", utils.Fail("查询信息失败")))
+		return
+	}
+	_ = conn.WriteJSON(utils.WsEvent(
+		"adminWineHistoryQueryWorkerRes",
+		utils.Success(utils.JsonType{"total": total, "list": workers}),
+	))
+}
+
+func QueryOrder(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
+	var param queryOrderParam
+	err := utils.AnyTrans(data, &param)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineHistoryQueryOrderRes", utils.Fail("参数错误")))
+		return
+	}
+	total, orders, err := tables.ChangesQuery(manager.Id, param.Worker, param.Cond, param.Deal, param.Limit, param.Page)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineHistoryQueryOrderRes", utils.Fail("查询信息失败")))
+		return
+	}
+	_ = conn.WriteJSON(utils.WsEvent(
+		"adminWineHistoryQueryOrderRes",
+		utils.Success(utils.JsonType{"total": total, "list": orders}),
+	))
+}
+
+func OrderUpdate(conn *websocket.Conn, data any) {
+	var param updateParam
+	err := utils.AnyTrans(data, &param)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineHistoryOrderUpdateRes", utils.Fail("参数错误")))
+		return
+	}
+	change := tables.ChangeTable{Id: param.Id}
+	err = change.Get()
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineHistoryOrderUpdateRes", utils.Fail("订单不存在")))
+		return
+	}
+	if change.Deal {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineHistoryOrderUpdateRes", utils.Fail("不可修改已完成的订单")))
+		return
+	}
+	change.New, change.Publish = param.New, utils.TimeNow()
+	err = change.UpdateSelf()
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineHistoryOrderUpdateRes", utils.Fail("更新失败")))
+		return
+	}
+	_ = conn.WriteJSON(utils.WsEvent("adminWineHistoryOrderUpdateRes", utils.Success(nil)))
+}
+
+func OrderDelete(conn *websocket.Conn, data any) {
+	id, ok := data.(uint32)
+	if !ok {
+		_ = conn.WriteJSON(utils.WsEvent("adminWineHistoryOrderDeleteRes", utils.Fail("参数错误")))
+		return
+	}
+	change := tables.ChangeTable{Id: id}
+	err := change.Get()
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineHistoryOrderDeleteRes", utils.Fail("订单不存在")))
+		return
+	}
+	if change.Deal {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineHistoryOrderDeleteRes", utils.Fail("不可删除已完成的订单")))
+		return
+	}
+	warn := tables.WarnTable{Id: change.Wid}
+	err = warn.Get()
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineHistoryOrderDeleteRes", utils.Fail("报警信息错误")))
+		return
+	}
+	warn.Deal = false
+	err = warn.UpdateSelf()
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineHistoryOrderDeleteRes", utils.Fail("重置报警信息失败")))
+		return
+	}
+	err = change.Delete()
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("adminWineHistoryOrderDeleteRes", utils.Fail("删除失败")))
+		return
+	}
+	_ = conn.WriteJSON(utils.WsEvent("adminWineHistoryOrderDeleteRes", utils.Fail("删除成功")))
+}

+ 0 - 6
handlers/manager/spliter.go

@@ -1,12 +1,10 @@
 package manager
 
 import (
-	adminCharts "Wine-Server/handlers/manager/admin/charts"
 	adminDashboard "Wine-Server/handlers/manager/admin/dashboard"
 	adminTrade "Wine-Server/handlers/manager/admin/trade"
 	adminWine "Wine-Server/handlers/manager/admin/wine"
 	adminWorker "Wine-Server/handlers/manager/admin/worker"
-	superCharts "Wine-Server/handlers/manager/super/charts"
 	superConfig "Wine-Server/handlers/manager/super/config"
 	superDashboard "Wine-Server/handlers/manager/super/dashboard"
 	superOrder "Wine-Server/handlers/manager/super/order"
@@ -137,8 +135,6 @@ func SocketHandler(ctx *gin.Context) {
 			superConfig.Handle(msg, conn, manager)
 		} else if strings.HasPrefix(msg.Event, "superRecord") {
 			superRecord.Handle(msg, conn, manager)
-		} else if strings.HasPrefix(msg.Event, "superCharts") {
-			superCharts.Handle(msg, conn, manager)
 		} else if strings.HasPrefix(msg.Event, "adminDashboard") { // admin:
 			adminDashboard.Handle(msg, conn, manager)
 		} else if strings.HasPrefix(msg.Event, "adminWorker") {
@@ -147,8 +143,6 @@ func SocketHandler(ctx *gin.Context) {
 			adminTrade.Handle(msg, conn, manager)
 		} else if strings.HasPrefix(msg.Event, "adminWine") {
 			adminWine.Handle(msg, conn, manager)
-		} else if strings.HasPrefix(msg.Event, "adminCharts") {
-			adminCharts.Handle(msg, conn, manager)
 		} else {
 			_ = conn.WriteJSON(utils.WsError("unrecognized event"))
 		}

+ 0 - 13
handlers/manager/super/charts/handler.go

@@ -1,13 +0,0 @@
-package charts
-
-import (
-	"Wine-Server/utils"
-	"Wine-Server/utils/tables"
-	"github.com/gorilla/websocket"
-)
-
-func Handle(msg utils.WsMsg, conn *websocket.Conn, manager *tables.ManagerTable) {
-	switch msg.Event {
-
-	}
-}

+ 27 - 1
handlers/manager/super/dashboard/handler.go

@@ -8,6 +8,32 @@ import (
 
 func Handle(msg utils.WsMsg, conn *websocket.Conn, manager *tables.ManagerTable) {
 	switch msg.Event {
-
+	case "superDashboardRankAdmins":
+		rankAdmins(conn, manager, msg.Data)
+		break
+	case "superDashboardRankDevices":
+		rankDevices(conn, manager, msg.Data)
+		break
+	case "superDashboardRankWines":
+		rankWines(conn, manager, msg.Data)
+		break
+	case "superDashboardRankWorkers":
+		rankWorkers(conn, manager, msg.Data)
+		break
+	case "superDashboardQueryAdmin":
+		queryAdmin(conn, manager, msg.Data)
+		break
+	case "superDashboardQueryDevice":
+		queryDevice(conn, manager, msg.Data)
+		break
+	case "superDashboardQueryWine":
+		queryWine(conn, manager, msg.Data)
+		break
+	case "superDashboardQueryWorker":
+		queryWorker(conn, manager, msg.Data)
+		break
+	default:
+		_ = conn.WriteJSON(utils.WsError("unrecognized event"))
+		break
 	}
 }

+ 11 - 0
handlers/manager/super/dashboard/params.go

@@ -0,0 +1,11 @@
+package dashboard
+
+type rankParam struct {
+	Start string `json:"start"`
+	End   string `json:"end"`
+}
+
+type queryParam struct {
+	Id   string `json:"id"`
+	Type uint8  `json:"type"` // 0:不限、1:一周、2:一月、3:一季、4:半年、5:一年
+}

+ 175 - 0
handlers/manager/super/dashboard/worker.go

@@ -0,0 +1,175 @@
+package dashboard
+
+import (
+	"Wine-Server/utils"
+	"Wine-Server/utils/tables"
+	"github.com/gorilla/websocket"
+)
+
+func rankAdmins(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
+	if !manager.Super {
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardRankAdminsRes", utils.Fail("权限不足")))
+		return
+	}
+	var param rankParam
+	err := utils.AnyTrans(data, &param)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardRankAdminsRes", utils.Fail("参数错误")))
+		return
+	}
+	rank, err := tables.TradeRankAdmins(param.Start, param.End)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardRankAdminsRes", utils.Fail("查询管理员排行失败")))
+		return
+	}
+	_ = conn.WriteJSON(utils.WsEvent("superDashboardRankAdminsRes", utils.Success(rank)))
+}
+
+func rankDevices(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
+	if !manager.Super {
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardRankDevicesRes", utils.Fail("权限不足")))
+		return
+	}
+	var param rankParam
+	err := utils.AnyTrans(data, &param)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardRankDevicesRes", utils.Fail("参数错误")))
+		return
+	}
+	rank, err := tables.TradeRankDevices(param.Start, param.End)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardRankDevicesRes", utils.Fail("查询设备排行失败")))
+		return
+	}
+	_ = conn.WriteJSON(utils.WsEvent("superDashboardRankDevicesRes", utils.Success(rank)))
+}
+
+func rankWines(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
+	if !manager.Super {
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardRankWinesRes", utils.Fail("权限不足")))
+		return
+	}
+	var param rankParam
+	err := utils.AnyTrans(data, &param)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardRankWinesRes", utils.Fail("参数错误")))
+		return
+	}
+	rank, err := tables.TradeRankWines(param.Start, param.End)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardRankWinesRes", utils.Fail("查询酒品排行失败")))
+		return
+	}
+	_ = conn.WriteJSON(utils.WsEvent("superDashboardRankWinesRes", utils.Success(rank)))
+}
+
+func rankWorkers(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
+	if !manager.Super {
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardRankWorkersRes", utils.Fail("权限不足")))
+		return
+	}
+	var param rankParam
+	err := utils.AnyTrans(data, &param)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardRankWorkersRes", utils.Fail("参数错误")))
+		return
+	}
+	rank, err := tables.ChangeRankWorkers(param.Start, param.End)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardRankWorkersRes", utils.Fail("查询上酒工排行失败")))
+		return
+	}
+	_ = conn.WriteJSON(utils.WsEvent("superDashboardRankWorkersRes", utils.Success(rank)))
+}
+
+func queryAdmin(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
+	if !manager.Super {
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardQueryAdminRes", utils.Fail("权限不足")))
+		return
+	}
+	var param queryParam
+	err := utils.AnyTrans(data, &param)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardQueryAdminRes", utils.Fail("参数错误")))
+		return
+	}
+	list, err := tables.TradeQuerySpecificOne("manager", param.Id, param.Type)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardQueryAdminRes", utils.Fail("查询管理员历史记录失败")))
+		return
+	}
+	_ = conn.WriteJSON(utils.WsEvent("superDashboardQueryAdminRes", utils.Success(list)))
+}
+
+func queryDevice(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
+	if !manager.Super {
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardQueryDeviceRes", utils.Fail("权限不足")))
+		return
+	}
+	var param queryParam
+	err := utils.AnyTrans(data, &param)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardQueryDeviceRes", utils.Fail("参数错误")))
+		return
+	}
+	list, err := tables.TradeQuerySpecificOne("device", param.Id, param.Type)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardQueryDeviceRes", utils.Fail("查询设备历史记录失败")))
+		return
+	}
+	_ = conn.WriteJSON(utils.WsEvent("superDashboardQueryDeviceRes", utils.Success(list)))
+}
+
+func queryWine(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
+	if !manager.Super {
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardQueryWineRes", utils.Fail("权限不足")))
+		return
+	}
+	var param queryParam
+	err := utils.AnyTrans(data, &param)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardQueryWineRes", utils.Fail("参数错误")))
+		return
+	}
+	list, err := tables.TradeQuerySpecificOne("wine", param.Id, param.Type)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardQueryWineRes", utils.Fail("查询酒品历史记录失败")))
+		return
+	}
+	_ = conn.WriteJSON(utils.WsEvent("superDashboardQueryWineRes", utils.Success(list)))
+}
+
+func queryWorker(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
+	if !manager.Super {
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardQueryWorkerRes", utils.Fail("权限不足")))
+		return
+	}
+	var param queryParam
+	err := utils.AnyTrans(data, &param)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardQueryWorkerRes", utils.Fail("参数错误")))
+		return
+	}
+	list, err := tables.ChangeQueryWorker(param.Id, param.Type)
+	if err != nil {
+		utils.Logger.Println(err)
+		_ = conn.WriteJSON(utils.WsEvent("superDashboardQueryWorkerRes", utils.Fail("查询上酒工历史记录失败")))
+		return
+	}
+	_ = conn.WriteJSON(utils.WsEvent("superDashboardQueryWorkerRes", utils.Success(list)))
+}

+ 1 - 7
handlers/manager/super/order/trade/worker.go

@@ -6,7 +6,6 @@ import (
 	"github.com/gorilla/websocket"
 )
 
-// QueryUser @admin.trade.queryUser
 func QueryUser(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
 	if !manager.Super {
 		_ = conn.WriteJSON(utils.WsEvent("superOrderTradeQueryUserRes", utils.Fail("权限不足")))
@@ -31,7 +30,6 @@ func QueryUser(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
 	))
 }
 
-// QueryTrade @admin.trade.queryTrade
 func QueryTrade(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
 	if !manager.Super {
 		_ = conn.WriteJSON(utils.WsEvent("superOrderTradeQueryTradeRes", utils.Fail("权限不足")))
@@ -56,7 +54,6 @@ func QueryTrade(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
 	))
 }
 
-// Refund @admin.trade.refund
 func Refund(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
 	if !manager.Super {
 		_ = conn.WriteJSON(utils.WsEvent("superOrderTradeRefundRes", utils.Fail("权限不足")))
@@ -91,10 +88,7 @@ func Refund(conn *websocket.Conn, manager *tables.ManagerTable, data any) {
 	_ = conn.WriteJSON(utils.WsEvent("superOrderTradeRefundRes", utils.Success(nil)))
 	// update user table
 	user := tables.UserTable{Id: trade.Payer}
-	err = user.Get()
-	if err != nil {
-		utils.Logger.Println(err)
-	}
+	_ = user.Get()
 	user.RefundCount++
 	user.RefundCost += uint64(param.Cash)
 	_ = user.UpdateSelf()

+ 2 - 1
handlers/setup.go

@@ -130,7 +130,8 @@ func initDatabase(config *utils.Config) {
 		tables.CreateAdvertiseTable, tables.CreateDeviceTable, tables.CreateManagerTable,
 		tables.CreateParamsTable, tables.CreateVersionTable, tables.CreateWineTable,
 		tables.CreateTradeTable, tables.CreateWorkerTable, tables.CreateUserTable,
-		tables.CreateRefundTable, tables.CreateOperationTable,
+		tables.CreateRefundTable, tables.CreateOperationTable, tables.CreateWarnTable,
+		tables.CreateChangeTable,
 	}
 	for _, Fn := range toCreate {
 		err = Fn()

二进制
main


+ 295 - 0
utils/tables/change.go

@@ -0,0 +1,295 @@
+package tables
+
+import (
+	"Wine-Server/utils"
+	"database/sql"
+)
+
+type ChangeTable struct {
+	Id      uint32
+	Wid     uint32
+	Device  string
+	Manager string
+	Worker  string
+	Code    string
+	Deal    bool
+	Publish utils.TimeType
+	Finish  utils.TimeType
+	Old     [4]WineWithIdRemain
+	New     [4]WineWithIdRemain
+}
+
+func CreateChangeTable() error {
+	SQL := "CREATE TABLE IF NOT EXISTS `change`(" +
+		"`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL," +
+		"`wid` INT UNSIGNED NOT NULL," +
+		"`device` VARCHAR(32) NOT NULL,`manager` VARCHAR(16) NOT NULL," +
+		"`worker` VARCHAR(16) NOT NULL,`code` VARCHAR(6) NOT NULL," +
+		"`deal` BOOL DEFAULT FALSE," +
+		"`publish` DATETIME DEFAULT CURRENT_TIMESTAMP," +
+		"`finish` DATETIME DEFAULT CURRENT_TIMESTAMP," +
+		"`wine1o` SMALLINT UNSIGNED DEFAULT 0," +
+		"`remain1o` SMALLINT UNSIGNED DEFAULT 0," +
+		"`wine2o` SMALLINT UNSIGNED DEFAULT 0," +
+		"`remain2o` SMALLINT UNSIGNED DEFAULT 0," +
+		"`wine3o` SMALLINT UNSIGNED DEFAULT 0," +
+		"`remain3o` SMALLINT UNSIGNED DEFAULT 0," +
+		"`wine4o` SMALLINT UNSIGNED DEFAULT 0," +
+		"`remain4o` SMALLINT UNSIGNED DEFAULT 0," +
+		"`wine1n` SMALLINT UNSIGNED DEFAULT 0," +
+		"`remain1n` SMALLINT UNSIGNED DEFAULT 0," +
+		"`wine2n` SMALLINT UNSIGNED DEFAULT 0," +
+		"`remain2n` SMALLINT UNSIGNED DEFAULT 0," +
+		"`wine3n` SMALLINT UNSIGNED DEFAULT 0," +
+		"`remain3n` SMALLINT UNSIGNED DEFAULT 0," +
+		"`wine4n` SMALLINT UNSIGNED DEFAULT 0," +
+		"`remain4n` SMALLINT UNSIGNED DEFAULT 0);"
+	_, err := utils.Mysql.Exec(SQL)
+	return err
+}
+
+func (row *ChangeTable) Insert() error {
+	SQL := "INSERT INTO `change` (``wid,`device`,`manager`,`worker`,`code`,`wine1o`,`remain1o`,`wine2o`,`remain2o`," +
+		"`wine3o`,`remain3o`,`wine4o`,`remain4o`,`wine1n`,`remain1n`,`wine2n`,`remain2n`,`wine3n`,`remain3n`," +
+		"`wine4n`,`remain4n`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);"
+	pre, err := utils.Mysql.Prepare(SQL)
+	if err != nil {
+		return err
+	}
+	_, err = pre.Exec(
+		row.Wid, row.Device, row.Manager, row.Worker, row.Code,
+		row.Old[0].Id, row.Old[0].Remain, row.Old[1].Id, row.Old[1].Remain,
+		row.Old[2].Id, row.Old[2].Remain, row.Old[3].Id, row.Old[3].Remain,
+		row.New[0].Id, row.New[0].Remain, row.New[1].Id, row.New[1].Remain,
+		row.New[2].Id, row.New[2].Remain, row.New[3].Id, row.New[3].Remain,
+	)
+	return err
+}
+
+func (row *ChangeTable) Delete() error {
+	_, err := utils.Mysql.Exec("DELETE FROM `change` WHERE `id`=?;", row.Id)
+	return err
+}
+
+func (row *ChangeTable) Update(args utils.JsonType) error {
+	keys, values := utils.UnZip(args)
+	SQL := utils.Format("UPDATE `change` SET %s WHERE `id`=%d;", utils.SqlFields(keys), row.Id)
+	pre, err := utils.Mysql.Prepare(SQL)
+	if err != nil {
+		return err
+	}
+	_, err = pre.Exec(values...)
+	return err
+}
+
+func (row *ChangeTable) UpdateSelf() error {
+	SQL := "UPDATE `change` SET `wid`=?,`device`=?,`manager`=?,`worker`=?,`code`=?,`deal`=?,`publish`=?,`finish`=?," +
+		"`wine1o`=?,`remain1o`=?,`wine2o`=?,`remain2o`=?,`wine3o`=?,`remain3o`=?,`wine4o`=?,`remain4o`=?," +
+		"`wine1n`=?,`remain1n`=?,`wine2n`=?,`remain2n`=?,`wine3n`=?,`remain3n`=?,`wine4n`=?,`remain4n`=? WHERE `id`=?;"
+	_, err := utils.Mysql.Exec(
+		SQL, row.Wid, row.Device, row.Manager, row.Worker,
+		row.Code, row.Deal, row.Publish, row.Finish,
+		row.Old[0].Id, row.Old[0].Remain, row.Old[1].Id, row.Old[1].Remain,
+		row.Old[2].Id, row.Old[2].Remain, row.Old[3].Id, row.Old[3].Remain,
+		row.New[0].Id, row.New[0].Remain, row.New[1].Id, row.New[1].Remain,
+		row.New[2].Id, row.New[2].Remain, row.New[3].Id, row.New[3].Remain, row.Id,
+	)
+	return err
+}
+
+func (row *ChangeTable) Get() error {
+	SQL := "SELECT `wid`,`device`,`manager`,`worker`,`code`,`deal`,`publish`,`finish`," +
+		"`wine1o`,`remain1o`,`wine2o`,`remain2o`,`wine3o`,`remain3o`,`wine4o`,`remain4o`," +
+		"`wine1n`,`remain1n`,`wine2n`,`remain2n`,`wine3n`,`remain3n`,`wine4n`,`remain4n` FROM `change` WHERE `id`=?;"
+	err := utils.Mysql.QueryRow(SQL, row.Id).Scan(
+		&row.Wid, &row.Device, &row.Manager, &row.Worker,
+		&row.Code, &row.Deal, &row.Publish, &row.Finish,
+		&row.Old[0].Id, &row.Old[0].Remain, &row.Old[1].Id, &row.Old[1].Remain,
+		&row.Old[2].Id, &row.Old[2].Remain, &row.Old[3].Id, &row.Old[3].Remain,
+		&row.New[0].Id, &row.New[0].Remain, &row.New[1].Id, &row.New[1].Remain,
+		&row.New[2].Id, &row.New[2].Remain, &row.New[3].Id, &row.New[3].Remain,
+	)
+	return err
+}
+
+type changeQueryRes struct {
+	Id     uint32 `json:"id"`
+	Device struct {
+		Id   string `json:"id"`
+		Addr string `json:"addr"`
+	} `json:"device"`
+	Code    string         `json:"code"`
+	Deal    bool           `json:"deal"`
+	Publish utils.TimeType `json:"publish"`
+	Finish  utils.TimeType `json:"finish"`
+
+	Old [4]WineWithIdNameRemain `json:"old"`
+	New [4]WineWithIdNameRemain `json:"new"`
+}
+
+func ChangesQuery(manager, worker, cond string, deal, limit, page int) (int, []changeQueryRes, error) {
+	SQL, like := "none", utils.Format("%%%s%%", cond)
+	if deal == -1 {
+		SQL = utils.Format(
+			"SELECT COUNT(c.`id`) FROM `change` AS c LEFT JOIN `device` AS d ON c.`device`=d.`id`"+
+				"WHERE c.`manager`='%s' AND c.`worker`='%s' AND (d.`id` LIKE '%s' OR d.`addr` LIKE '%s')",
+			manager, worker, like, like,
+		)
+	} else {
+		SQL = utils.Format(
+			"SELECT COUNT(c.`id`) FROM `change` AS c LEFT JOIN `device` AS d ON c.`device`=d.`id`"+
+				"WHERE c.`deal`=%d AND c.`manager`='%s' AND c.`worker`='%s' AND (d.`id` LIKE '%s' OR d.`addr` LIKE '%s')",
+			deal, manager, worker, like, like,
+		)
+	}
+	total := 0
+	err := utils.Mysql.QueryRow(SQL).Scan(&total)
+	if err != nil {
+		return 0, nil, err
+	}
+	if total == 0 {
+		return 0, []changeQueryRes{}, err
+	}
+	if deal == -1 {
+		SQL = utils.Format(
+			"SELECT c.`id`,d.`id`,d.`addr`,c.`code`,c.`deal`,c.`publish`,c.`finish`,"+
+				"w1.`id`,w1.`name`,c.`remain1o`,w2.`id`,w2.`name`,c.`remain2o`,"+
+				"w3.`id`,w3.`name`,c.`remain3o`,w4.`id`,w4.`name`,c.`remain4o`,"+
+				"w5.`id`,w5.`name`,c.`remain1n`,w6.`id`,w6.`name`,c.`remain2n`,"+
+				"w7.`id`,w7.`name`,c.`remain3n`,w8.`id`,w8.`name`,c.`remain4n` "+
+				"FROM `change` AS c LEFT JOIN `device` AS d ON c.`device`=d.`id`"+
+				"INNER JOIN `wine` AS w1 ON w1.`id`=c.`wine1o` INNER JOIN `wine` AS w2 ON w2.`id`=c.`wine2o` "+
+				"INNER JOIN `wine` AS w3 ON w3.`id`=c.`wine3o` INNER JOIN `wine` AS w4 ON w4.`id`=c.`wine4o` "+
+				"INNER JOIN `wine` AS w5 ON w5.`id`=c.`wine1n` INNER JOIN `wine` AS w6 ON w6.`id`=c.`wine2n` "+
+				"INNER JOIN `wine` AS w7 ON w7.`id`=c.`wine3n` INNER JOIN `wine` AS w8 ON w8.`id`=c.`wine4n` "+
+				"WHERE c.`manager`='%s' AND c.`worker`='%s' AND (d.`id` LIKE '%s' OR d.`addr` LIKE '%s') "+
+				"LIMIT %d OFFSET %d;", manager, worker, like, like, limit, (page-1)*limit,
+		)
+	} else {
+		SQL = utils.Format(
+			"SELECT c.`id`,d.`id`,d.`addr`,c.`code`,c.`deal`,c.`publish`,c.`finish`,"+
+				"w1.`id`,w1.`name`,c.`remain1o`,w2.`id`,w2.`name`,c.`remain2o`,"+
+				"w3.`id`,w3.`name`,c.`remain3o`,w4.`id`,w4.`name`,c.`remain4o`,"+
+				"w5.`id`,w5.`name`,c.`remain1n`,w6.`id`,w6.`name`,c.`remain2n`,"+
+				"w7.`id`,w7.`name`,c.`remain3n`,w8.`id`,w8.`name`,c.`remain4n` "+
+				"FROM `change` AS c LEFT JOIN `device` AS d ON c.`device`=d.`id`"+
+				"INNER JOIN `wine` AS w1 ON w1.`id`=c.`wine1o` INNER JOIN `wine` AS w2 ON w2.`id`=c.`wine2o` "+
+				"INNER JOIN `wine` AS w3 ON w3.`id`=c.`wine3o` INNER JOIN `wine` AS w4 ON w4.`id`=c.`wine4o` "+
+				"INNER JOIN `wine` AS w5 ON w5.`id`=c.`wine1n` INNER JOIN `wine` AS w6 ON w6.`id`=c.`wine2n` "+
+				"INNER JOIN `wine` AS w7 ON w7.`id`=c.`wine3n` INNER JOIN `wine` AS w8 ON w8.`id`=c.`wine4n` "+
+				"WHERE c.`deal`=%d AND c.`manager`='%s' AND c.`worker`='%s' AND (d.`id` LIKE '%s' OR d.`addr` LIKE '%s') "+
+				"LIMIT %d OFFSET %d;", deal, manager, worker, like, like, limit, (page-1)*limit,
+		)
+	}
+	var rows *sql.Rows
+	res := make([]changeQueryRes, 0)
+	rows, err = utils.Mysql.Query(SQL)
+	if err != nil {
+		return 0, nil, err
+	}
+	for rows.Next() {
+		var one changeQueryRes
+		_ = rows.Scan(
+			&one.Id, &one.Device.Id, &one.Device.Addr, &one.Code, &one.Deal, &one.Publish, &one.Finish,
+			&one.Old[0].Id, &one.Old[0].Name, &one.Old[0].Remain,
+			&one.Old[1].Id, &one.Old[1].Name, &one.Old[1].Remain,
+			&one.Old[2].Id, &one.Old[2].Name, &one.Old[2].Remain,
+			&one.Old[3].Id, &one.Old[3].Name, &one.Old[3].Remain,
+			&one.New[0].Id, &one.New[0].Name, &one.New[0].Remain,
+			&one.New[1].Id, &one.New[1].Name, &one.New[1].Remain,
+			&one.New[2].Id, &one.New[2].Name, &one.New[2].Remain,
+			&one.New[3].Id, &one.New[3].Name, &one.New[3].Remain,
+		)
+		res = append(res, one)
+	}
+	_ = rows.Close()
+	return total, res, nil
+}
+
+func ChangeRankWorkers(start, end string) ([]idNameTotal, error) {
+	SQL := "SELECT w.`id` AS `person`,w.`name`,SUM(c.`id`) AS `count` FROM `change` AS c " +
+		"LEFT JOIN `worker` AS w ON c.`worker`=w.`id`  WHERE c.`deal`=TRUE " +
+		"AND (c.`finish` BETWEEN TIMESTAMP(?) AND TIMESTAMP(?)) GROUP BY `person` ORDER BY `count` DESC;"
+	rows, err := utils.Mysql.Query(SQL, start, end)
+	if err != nil {
+		return nil, err
+	}
+	res := make([]idNameTotal, 0)
+	for rows.Next() {
+		var one idNameTotal
+		_ = rows.Scan(&one.Id, &one.Name, &one.Total)
+		res = append(res, one)
+	}
+	_ = rows.Close()
+	return res, nil
+}
+
+func ChangeRankWorkersForAdmin(manager, start, end string) ([]idNameTotal, error) {
+	SQL := "SELECT w.`id` AS `person`,w.`name`,SUM(c.`id`) AS `count` FROM `change` AS c " +
+		"LEFT JOIN `worker` AS w ON c.`worker`=w.`id`  WHERE c.`deal`=TRUE AND c.`manager`=? " +
+		"AND (c.`finish` BETWEEN TIMESTAMP(?) AND TIMESTAMP(?)) GROUP BY `person` ORDER BY `count` DESC;"
+	rows, err := utils.Mysql.Query(SQL, manager, start, end)
+	if err != nil {
+		return nil, err
+	}
+	res := make([]idNameTotal, 0)
+	for rows.Next() {
+		var one idNameTotal
+		_ = rows.Scan(&one.Id, &one.Name, &one.Total)
+		res = append(res, one)
+	}
+	_ = rows.Close()
+	return res, nil
+}
+
+func ChangeQueryWorker(id string, kind uint8) ([]timeTotal, error) {
+	SQL := "none"
+	if kind == 0 {
+		SQL = "SELECT DATE_FORMAT(`time`,'%Y-%m') as `tt`,SUM(`id`) FROM `change` WHERE `deal`=TRUE AND `worker`=? GROUP BY `tt`;"
+	} else {
+		align, days := getAlignDays(kind)
+		SQL = utils.Format(
+			"SELECT DATE_FORMAT(`time`,'%s') as `tt`,SUM(`tt`) FROM `change` WHERE `deal`=TRUE AND `worker`=? AND (`time` BETWEEN "+
+				"DATE_SUB(CURRENT_TIMESTAMP,INTERVAL %d DAY) AND CURRENT_TIMESTAMP) GROUP BY `tt`;", align, days,
+		)
+	}
+	rows, err := utils.Mysql.Query(SQL, id)
+	if err != nil {
+		return nil, err
+	}
+	res := make([]timeTotal, 0)
+	for rows.Next() {
+		var one timeTotal
+		_ = rows.Scan(&one.Time, &one.Total)
+		res = append(res, one)
+	}
+	_ = rows.Close()
+	return res, nil
+}
+
+func ChangeQueryWorkerForAdmin(manager, id string, kind uint8) ([]timeTotal, error) {
+	SQL := "none"
+	if kind == 0 {
+		SQL = "SELECT DATE_FORMAT(`time`,'%Y-%m') as `tt`,SUM(`id`) FROM `change` " +
+			"WHERE `deal`=TRUE AND `manager`=? AND `worker`=? GROUP BY `tt`;"
+	} else {
+		align, days := getAlignDays(kind)
+		SQL = utils.Format(
+			"SELECT DATE_FORMAT(`time`,'%s') as `tt`,SUM(`tt`) FROM `change` WHERE `deal`=TRUE AND `manager`=? "+
+				"AND `worker`=? AND (`time` BETWEEN DATE_SUB(CURRENT_TIMESTAMP,INTERVAL %d DAY) AND CURRENT_TIMESTAMP) "+
+				"GROUP BY `tt`;", align, days,
+		)
+	}
+	rows, err := utils.Mysql.Query(SQL, manager, id)
+	if err != nil {
+		return nil, err
+	}
+	res := make([]timeTotal, 0)
+	for rows.Next() {
+		var one timeTotal
+		_ = rows.Scan(&one.Time, &one.Total)
+		res = append(res, one)
+	}
+	_ = rows.Close()
+	return res, nil
+}

+ 42 - 0
utils/tables/com.go

@@ -0,0 +1,42 @@
+package tables
+
+type WineWithIdRemain struct {
+	Id     uint16 `json:"id"`
+	Remain uint16 `json:"remain"`
+}
+type WineWithIdNameRemain struct {
+	Id     uint16 `json:"id"`
+	Name   string `json:"name"`
+	Remain uint16 `json:"remain"`
+}
+type idTotal struct {
+	Id    string `json:"id"`
+	Total uint64 `json:"total"`
+}
+type idNameTotal struct {
+	Id    string `json:"id"`
+	Name  string `json:"name"`
+	Total uint64 `json:"total"`
+}
+type timeTotal struct {
+	Time  string `json:"time"`
+	Total uint64 `json:"total"`
+}
+
+// 0:不限、1:一周、2:一月、3:一季、4:半年、5:一年
+func getAlignDays(kind uint8) (string, uint16) {
+	switch kind {
+	case 1:
+		return "%Y-%m-%d", 7
+	case 2:
+		return "%Y-%m-%d", 30
+	case 3:
+		return "%Y-%m", 120
+	case 4:
+		return "%Y-%m", 180
+	case 5:
+		return "%Y-%m", 360
+	default:
+		return "%Y-%m-%d", 7
+	}
+}

+ 1 - 0
utils/tables/manager.go

@@ -88,6 +88,7 @@ func (row *ManagerTable) Get() error {
 	return err
 }
 
+// GetByAcc change to `Login(acc, pwd) error`
 func (row *ManagerTable) GetByAcc() error {
 	SQL := "SELECT `id`,`super`,`first`,`last`,`name`,`password`,`order`,`income` FROM `manager` WHERE `account`=? LIMIT 1;"
 	pre, err := utils.Mysql.Prepare(SQL)

+ 187 - 0
utils/tables/trade.go

@@ -128,3 +128,190 @@ func TradeQuery(cond, uid string, limit, page int) (int, []utils.JsonType, error
 	_ = rows.Close()
 	return total, res, nil
 }
+
+func TradeQueryForAdmin(manager, cond, uid string, limit, page int) (int, []utils.JsonType, error) {
+	like := utils.Format("%%%s%%", cond)
+	SQL := "SELECT COUNT(t.`id`) FROM `trade` AS t LEFT JOIN `wine` AS w ON t.`wine`=w.`id` " +
+		"WHERE t.`manager`=? AND t.`payer`=? AND (t.`id` LIKE ? OR w.`name` LIKE ?);"
+	pre, err := utils.Mysql.Prepare(SQL)
+	if err != nil {
+		return 0, nil, err
+	}
+	total := 0
+	err = pre.QueryRow(manager, uid, like, like).Scan(&total)
+	if err != nil {
+		return 0, nil, err
+	}
+	if total == 0 {
+		return 0, []utils.JsonType{}, nil
+	}
+	SQL = "SELECT t.`id`,t.`device`,t.`time`,w.`name`,t.`price`,t.`weight`,t.`cash`,r.`cash`,r.`reason` " +
+		"FROM `trade` AS t LEFT JOIN `wine` AS w ON t.`wine`=w.`id` LEFT JOIN `refund` AS r ON t.`id`=r.`tid` " +
+		"WHERE t.`manager`=? AND t.`payer`=? AND (t.`id` LIKE ? OR w.`name` LIKE ?) ORDER BY t.`time` DESC LIMIT ? OFFSET ?;"
+	pre, err = utils.Mysql.Prepare(SQL)
+	if err != nil {
+		return 0, nil, err
+	}
+	var rows *sql.Rows
+	rows, err = pre.Query(manager, uid, like, like, limit, (page-1)*limit)
+	if err != nil {
+		return 0, nil, err
+	}
+	res := make([]utils.JsonType, 0)
+	for rows.Next() {
+		var one TradeTable
+		refund := 0
+		wineName, reason := "", ""
+		_ = rows.Scan(&one.Id, &one.Device, &one.Time, &wineName, &one.Price, &one.Weight, &one.Cash, &refund, &reason)
+		res = append(res, utils.JsonType{
+			"id": one.Id, "device": one.Device, "time": one.Time, "wine": wineName, "price": one.Price,
+			"weight": one.Weight, "cash": one.Cash, "refund": refund, "reason": reason,
+		})
+	}
+	_ = rows.Close()
+	return total, res, nil
+}
+
+func TradeRankAdmins(start, end string) ([]idNameTotal, error) {
+	SQL := "SELECT m.`id`,m.`name`,SUM(t.`cash`) AS `income` FROM `trade` AS t " +
+		"LEFT JOIN `manager` AS m ON t.`manager`=m.`id` " +
+		"WHERE t.`time` BETWEEN TIMESTAMP(?) AND TIMESTAMP(?) GROUP BY t.`manager` ORDER BY `income` DESC;"
+	rows, err := utils.Mysql.Query(SQL, start, end)
+	if err != nil {
+		return nil, err
+	}
+	res := make([]idNameTotal, 0)
+	for rows.Next() {
+		var one idNameTotal
+		_ = rows.Scan(&one.Id, &one.Name, &one.Total)
+		res = append(res, one)
+	}
+	_ = rows.Close()
+	return res, nil
+}
+
+func TradeRankDevices(start, end string) ([]idTotal, error) {
+	SQL := "SELECT `device`,SUM(`cash`) AS `income` FROM `trade` " +
+		"WHERE `time` BETWEEN TIMESTAMP(?) AND TIMESTAMP(?) GROUP BY `device` ORDER BY `income` DESC;"
+	rows, err := utils.Mysql.Query(SQL, start, end)
+	if err != nil {
+		return nil, err
+	}
+	res := make([]idTotal, 0)
+	for rows.Next() {
+		var one idTotal
+		_ = rows.Scan(&one.Id, &one.Total)
+		res = append(res, one)
+	}
+	_ = rows.Close()
+	return res, nil
+}
+
+func TradeRankDevicesForAdmin(manager, start, end string) ([]idTotal, error) {
+	SQL := "SELECT `device`,SUM(`cash`) AS `income` FROM `trade` WHERE `manager`=? " +
+		"AND (`time` BETWEEN TIMESTAMP(?) AND TIMESTAMP(?)) GROUP BY `device` ORDER BY `income` DESC;"
+	rows, err := utils.Mysql.Query(SQL, manager, start, end)
+	if err != nil {
+		return nil, err
+	}
+	res := make([]idTotal, 0)
+	for rows.Next() {
+		var one idTotal
+		_ = rows.Scan(&one.Id, &one.Total)
+		res = append(res, one)
+	}
+	_ = rows.Close()
+	return res, nil
+}
+
+func TradeRankWines(start, end string) ([]idNameTotal, error) {
+	SQL := "SELECT w.`id`,w.`name`,SUM(t.`cash`) AS `income` FROM `trade` AS t " +
+		"LEFT JOIN `wine` AS w ON t.`wine`=w.`id` " +
+		"WHERE t.`time` BETWEEN TIMESTAMP(?) AND TIMESTAMP(?) GROUP BY t.`wine` ORDER BY `income` DESC;"
+	rows, err := utils.Mysql.Query(SQL, start, end)
+	if err != nil {
+		return nil, err
+	}
+	res := make([]idNameTotal, 0)
+	for rows.Next() {
+		var one idNameTotal
+		_ = rows.Scan(&one.Id, &one.Name, &one.Total)
+		res = append(res, one)
+	}
+	_ = rows.Close()
+	return res, nil
+}
+
+func TradeRankWinesForAdmin(manager, start, end string) ([]idNameTotal, error) {
+	SQL := "SELECT w.`id`,w.`name`,SUM(t.`cash`) AS `income` FROM `trade` AS t " +
+		"LEFT JOIN `wine` AS w ON t.`wine`=w.`id` WHERE t.`manager`=? " +
+		"AND (t.`time` BETWEEN TIMESTAMP(?) AND TIMESTAMP(?)) GROUP BY t.`wine` ORDER BY `income` DESC;"
+	rows, err := utils.Mysql.Query(SQL, manager, start, end)
+	if err != nil {
+		return nil, err
+	}
+	res := make([]idNameTotal, 0)
+	for rows.Next() {
+		var one idNameTotal
+		_ = rows.Scan(&one.Id, &one.Name, &one.Total)
+		res = append(res, one)
+	}
+	_ = rows.Close()
+	return res, nil
+}
+
+func TradeQuerySpecificOne(filed, value string, kind uint8) ([]timeTotal, error) {
+	SQL := "none"
+	if kind == 0 {
+		SQL = utils.Format(
+			"SELECT DATE_FORMAT(`time`,'%%Y-%%m') as `tt`,SUM(`cash`) FROM `trade` WHERE `%s`=? GROUP BY `tt`;", filed,
+		)
+	} else {
+		align, days := getAlignDays(kind)
+		SQL = utils.Format(
+			"SELECT DATE_FORMAT(`time`,'%s') as `tt`,SUM(`cash`) FROM `trade` WHERE `%s`=? AND (`time` BETWEEN "+
+				"DATE_SUB(CURRENT_TIMESTAMP,INTERVAL %d DAY) AND CURRENT_TIMESTAMP) GROUP BY `tt`;", align, filed, days,
+		)
+	}
+	rows, err := utils.Mysql.Query(SQL, value)
+	if err != nil {
+		return nil, err
+	}
+	res := make([]timeTotal, 0)
+	for rows.Next() {
+		var one timeTotal
+		_ = rows.Scan(&one.Time, &one.Total)
+		res = append(res, one)
+	}
+	_ = rows.Close()
+	return res, nil
+}
+
+func TradeQuerySpecificOneForAdmin(manager, filed, value string, kind uint8) ([]timeTotal, error) {
+	SQL := "none"
+	if kind == 0 {
+		SQL = utils.Format(
+			"SELECT DATE_FORMAT(`time`,'%%Y-%%m') as `tt`,SUM(`cash`) FROM `trade` "+
+				"WHERE `manager`=? AND `%s`=? GROUP BY `tt`;", filed,
+		)
+	} else {
+		align, days := getAlignDays(kind)
+		SQL = utils.Format(
+			"SELECT DATE_FORMAT(`time`,'%s') as `tt`,SUM(`cash`) FROM `trade` WHERE `manager`=? AND "+
+				"`%s`=? AND (`time` BETWEEN DATE_SUB(CURRENT_TIMESTAMP,INTERVAL %d DAY) AND CURRENT_TIMESTAMP) "+
+				"GROUP BY `tt`;", align, filed, days,
+		)
+	}
+	rows, err := utils.Mysql.Query(SQL, manager, value)
+	if err != nil {
+		return nil, err
+	}
+	res := make([]timeTotal, 0)
+	for rows.Next() {
+		var one timeTotal
+		_ = rows.Scan(&one.Time, &one.Total)
+		res = append(res, one)
+	}
+	_ = rows.Close()
+	return res, nil
+}

+ 60 - 0
utils/tables/user.go

@@ -135,3 +135,63 @@ func UserQuery(id string, vip, limit, page int) (int, []utils.JsonType, error) {
 	_ = rows.Close()
 	return total, res, nil
 }
+
+func UserQueryForAdmin(manager, id string, vip, limit, page int) (int, []utils.JsonType, error) {
+	SQL := "none"
+	if vip == -1 {
+		SQL = utils.Format(
+			"SELECT COUNT(u.`id`) FROM `user` AS u WHERE u.`id` LIKE '%%%s%%' AND u.`id` IN "+
+				"(SELECT DISTINCT t.`payer` FROM `trade` AS t WHERE t.`manager`='%s');",
+			id, manager,
+		)
+	} else {
+		SQL = utils.Format(
+			"SELECT COUNT(u.`id`) FROM `user` AS u WHERE u.`vip`=%d AND u.`id` LIKE '%%%s%%' "+
+				"AND u.`id` IN (SELECT DISTINCT t.`payer` FROM `trade` AS t WHERE t.`manager`='%s'); ",
+			vip, id, manager,
+		)
+	}
+	total := 0
+	err := utils.Mysql.QueryRow(SQL).Scan(&total)
+	if err != nil {
+		return 0, nil, err
+	}
+	if total == 0 {
+		return 0, []utils.JsonType{}, err
+	}
+	if vip == -1 {
+		SQL = utils.Format(
+			"SELECT u.`id`,u.`first`,u.`last`,u.`vip`,u.`cash`,u.`max_cost`,u.`buy_count`,u.`buy_cost`,"+
+				"u.`refund_count`,u.`refund_cost` FROM `user` AS u WHERE u.`id` LIKE '%%%s%%' AND u.`id` "+
+				"IN (SELECT DISTINCT t.`payer` FROM `trade` AS t WHERE t.`manager`='%s') LIMIT %d OFFSET %d;",
+			id, manager, limit, (page-1)*limit,
+		)
+	} else {
+		SQL = utils.Format(
+			"SELECT u.`id`,u.`first`,u.`last`,u.`vip`,u.`cash`,u.`max_cost`,u.`buy_count`,u.`buy_cost`,"+
+				"u.`refund_count`,u.`refund_cost` FROM `user` AS u WHERE u.`vip`=%d AND u.`id` LIKE '%%%s%%' AND "+
+				"u.`id` IN (SELECT t.`payer` FROM `trade` AS t WHERE t.`manager`='%s') LIMIT %d OFFSET %d;",
+			vip, id, manager, limit, (page-1)*limit,
+		)
+	}
+	var rows *sql.Rows
+	res := make([]utils.JsonType, 0)
+	rows, err = utils.Mysql.Query(SQL)
+	if err != nil {
+		return 0, nil, err
+	}
+	for rows.Next() {
+		var tmp UserTable
+		err = rows.Scan(
+			&tmp.Id, &tmp.First, &tmp.Last, &tmp.Vip, &tmp.Cash, &tmp.MaxCost,
+			&tmp.BuyCount, &tmp.BuyCost, &tmp.RefundCount, &tmp.RefundCost,
+		)
+		res = append(res, utils.JsonType{
+			"id": tmp.Id, "first": tmp.First, "last": tmp.Last, "vip": tmp.Vip, "cash": tmp.Cash,
+			"max_cost": tmp.MaxCost, "buy_count": tmp.BuyCount, "buy_cost": tmp.BuyCost,
+			"refund_count": tmp.RefundCount, "refund_cost": tmp.RefundCost,
+		})
+	}
+	_ = rows.Close()
+	return total, res, nil
+}

+ 145 - 0
utils/tables/warn.go

@@ -0,0 +1,145 @@
+package tables
+
+import (
+	"Wine-Server/utils"
+	"database/sql"
+)
+
+type WarnTable struct {
+	Id      uint32
+	Device  string
+	Manager string
+	Time    utils.TimeType
+	Deal    bool
+}
+
+func CreateWarnTable() error {
+	SQL := "CREATE TABLE IF NOT EXISTS `warn`(" +
+		"`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL," +
+		"`device` VARCHAR(32) NOT NULL," +
+		"`manager` VARCHAR(16) NOT NULL," +
+		"`time` DATETIME DEFAULT CURRENT_TIMESTAMP," +
+		"`deal` BOOL DEFAULT FALSE);"
+	_, err := utils.Mysql.Exec(SQL)
+	return err
+}
+
+func (row *WarnTable) Insert() error {
+	SQL := "INSERT INTO `warn` (`device`,`manager`) VALUES (?,?);"
+	pre, err := utils.Mysql.Prepare(SQL)
+	if err != nil {
+		return err
+	}
+	_, err = pre.Exec(row.Device, row.Manager)
+	return err
+}
+
+// Delete deprecated
+func (row *WarnTable) Delete() error {
+	_, err := utils.Mysql.Exec("DELETE FROM `warn` WHERE `id`=?;", row.Id)
+	return err
+}
+
+func (row *WarnTable) Update(args utils.JsonType) error {
+	keys, values := utils.UnZip(args)
+	SQL := utils.Format("UPDATE `warn` SET %s WHERE `id`=%d;", utils.SqlFields(keys), row.Id)
+	pre, err := utils.Mysql.Prepare(SQL)
+	if err != nil {
+		return err
+	}
+	_, err = pre.Exec(values...)
+	return err
+}
+
+func (row *WarnTable) UpdateSelf() error {
+	SQL := "UPDATE `warn` SET `device`=?,`manager`=?,`time`=?,`deal`=? WHERE `id`=?;"
+	row.Time = utils.TimeNow()
+	_, err := utils.Mysql.Exec(SQL, row.Device, row.Manager, row.Time, row.Deal, row.Id)
+	return err
+}
+
+func (row *WarnTable) Get() error {
+	SQL := "SELECT `device`,`manager`,`time`,`deal` FROM `warn` WHERE `id`=?;"
+	err := utils.Mysql.QueryRow(SQL, row.Id).Scan(&row.Device, &row.Manager, &row.Time, &row.Deal)
+	return err
+}
+
+func (row *WarnTable) GetByDeviceAndManager() error {
+	SQL := "SELECT `id`,`time`,`deal` FROM `warn` WHERE `device`=? AND `manager`=?;"
+	err := utils.Mysql.QueryRow(SQL, row.Device, row.Manager).Scan(&row.Id, &row.Time, &row.Deal)
+	return err
+}
+
+type warnQueryRes struct {
+	Id     uint32         `json:"id"`
+	Device string         `json:"device"`
+	Time   utils.TimeType `json:"time"`
+	Deal   bool           `json:"deal"`
+	Addr   string         `json:"addr"`
+
+	Old [4]WineWithIdNameRemain `json:"old"`
+}
+
+func WarnQuery(manager, cond string, deal, limit, page int) (int, []warnQueryRes, error) {
+	SQL := "none"
+	if deal == -1 {
+		SQL = utils.Format(
+			"SELECT COUNT(w.`id`) AS `total` FROM `warn` AS w LEFT JOIN `device` AS d ON w.`device`=d.`id` "+
+				"WHERE w.`manager`='%s' AND (w.`device` LIKE '%%%s%%' OR d.`addr` LIKE '%%%s%%');", manager, cond, cond,
+		)
+	} else {
+		SQL = utils.Format(
+			"SELECT COUNT(w.`id`) AS `total` FROM `warn` AS w LEFT JOIN `device` AS d ON w.`device`=d.`id` "+
+				"WHERE w.`manager`='%s' AND w.`deal`=%d AND (w.`device` LIKE '%%%s%%' OR d.`addr` LIKE '%%%s%%');",
+			manager, deal, cond, cond,
+		)
+	}
+	total := 0
+	err := utils.Mysql.QueryRow(SQL).Scan(&total)
+	if err != nil {
+		return 0, nil, err
+	}
+	if total == 0 {
+		return 0, []warnQueryRes{}, err
+	}
+	if deal == -1 {
+		SQL = utils.Format(
+			"SELECT w.`id`,w.`device`,w.`time`,w.`deal`,d.`addr`,d.`wine1`,w1.`name`,d.`remain1`,"+
+				"d.`wine2`,w2.`name`,d.`remain2`,d.`wine3`,w3.`name`,d.`remain3`,d.`wine4`,w4.`name`,d.`remain4` "+
+				"FROM `warn` AS w LEFT JOIN `device` AS d ON w.`device`=d.`id` "+
+				"INNER JOIN `wine` AS w1 ON d.`wine1`=w1.`id` INNER JOIN `wine` AS w2 ON d.`wine1`=w2.`id` "+
+				"INNER JOIN `wine` AS w3 ON d.`wine1`=w3.`id` INNER JOIN `wine` AS w4 ON d.`wine1`=w4.`id` "+
+				"WHERE w.`manager`='%s' AND (w.`device` LIKE '%%%s%%' OR d.`addr` LIKE '%%%s%%') LIMIT %d OFFSET %d;",
+			manager, cond, cond, limit, (page-1)*limit,
+		)
+	} else {
+		SQL = utils.Format(
+			"SELECT w.`id`,w.`device`,w.`time`,w.`deal`,d.`addr`,d.`wine1`,w1.`name`,d.`remain1`,"+
+				"d.`wine2`,w2.`name`,d.`remain2`,d.`wine3`,w3.`name`,d.`remain3`,d.`wine4`,w4.`name`,d.`remain4` "+
+				"FROM `warn` AS w LEFT JOIN `device` AS d ON w.`device`=d.`id` "+
+				"INNER JOIN `wine` AS w1 ON d.`wine1`=w1.`id` INNER JOIN `wine` AS w2 ON d.`wine1`=w2.`id` "+
+				"INNER JOIN `wine` AS w3 ON d.`wine1`=w3.`id` INNER JOIN `wine` AS w4 ON d.`wine1`=w4.`id` "+
+				"WHERE w.`manager`='%s' AND w.`deal`=%d AND (w.`device` LIKE '%%%s%%' OR d.`addr` LIKE '%%%s%%') "+
+				"LIMIT %d OFFSET %d;", manager, deal, cond, cond, limit, (page-1)*limit,
+		)
+	}
+	var rows *sql.Rows
+	res := make([]warnQueryRes, 0)
+	rows, err = utils.Mysql.Query(SQL)
+	if err != nil {
+		return 0, nil, err
+	}
+	for rows.Next() {
+		var tmp warnQueryRes
+		_ = rows.Scan(
+			&tmp.Id, &tmp.Device, &tmp.Time, &tmp.Deal, &tmp.Addr,
+			&tmp.Old[0].Id, &tmp.Old[0].Name, &tmp.Old[0].Remain,
+			&tmp.Old[1].Id, &tmp.Old[1].Name, &tmp.Old[1].Remain,
+			&tmp.Old[2].Id, &tmp.Old[2].Name, &tmp.Old[2].Remain,
+			&tmp.Old[3].Id, &tmp.Old[3].Name, &tmp.Old[3].Remain,
+		)
+		res = append(res, tmp)
+	}
+	_ = rows.Close()
+	return total, res, nil
+}

+ 17 - 0
utils/tables/wine.go

@@ -151,3 +151,20 @@ func WineQueryMaxId() (uint16, error) {
 	}
 	return max, nil
 }
+
+func WinesQueryByIdOrName(cond string) ([]utils.JsonType, error) {
+	like := utils.Format("%%%s%%", cond)
+	SQL := "SELECT `id`,`name` FROM `wine` WHERE `id`!=10100 AND (`id` LIKE ? OR `name` LIKE ?) LIMIT 10;"
+	rows, err := utils.Mysql.Query(SQL, like, like)
+	if err != nil {
+		return nil, err
+	}
+	res := make([]utils.JsonType, 0)
+	for rows.Next() {
+		tid, tna := "", ""
+		_ = rows.Scan(&tid, &tna)
+		res = append(res, utils.JsonType{"id": tid, "name": tna})
+	}
+	_ = rows.Close()
+	return res, nil
+}

+ 138 - 54
utils/tables/worker.go

@@ -16,7 +16,9 @@ type WorkerTable struct {
 	Manager  string
 
 	Count uint32
-	Cost  uint32
+	Todo  uint32
+	Avg   uint32
+	Max   uint32
 }
 
 func CreateWorkerTable() error {
@@ -29,18 +31,20 @@ func CreateWorkerTable() error {
 		"`password` VARCHAR(128) NOT NULL," +
 		"`manager` VARCHAR(16) DEFAULT ''," +
 		"`count` INT UNSIGNED DEFAULT 0," +
-		"`cost` INT UNSIGNED DEFAULT 0);"
+		"`todo` INT UNSIGNED DEFAULT 0," +
+		"`avg_cost` INT UNSIGNED DEFAULT 0," +
+		"`max_cost` INT UNSIGNED DEFAULT 0);"
 	_, err := utils.Mysql.Exec(SQL)
 	return err
 }
 
 func (row *WorkerTable) Insert() error {
-	SQL := "INSERT INTO `worker`(`id`,`name`,`phone`,`password`) VALUES(?,?,?,?);"
+	SQL := "INSERT INTO `worker`(`id`,`name`,`phone`,`password`,`manager`) VALUES(?,?,?,?,?);"
 	pre, err := utils.Mysql.Prepare(SQL)
 	if err != nil {
 		return err
 	}
-	_, err = pre.Exec(row.Id, row.Name, row.Phone, row.Password)
+	_, err = pre.Exec(row.Id, row.Name, row.Phone, row.Password, row.Manager)
 	return err
 }
 
@@ -65,43 +69,51 @@ func (row *WorkerTable) Update(args utils.JsonType) error {
 }
 
 func (row *WorkerTable) UpdateSelf() error {
-	SQL := "UPDATE `worker` SET `last`=?,`name`=?,`phone`=?,`password`=?,`manager`=?,`count`=?,`cost`=? WHERE `id`=?;"
-	pre, err := utils.Mysql.Prepare(SQL)
-	if err != nil {
-		return err
-	}
+	SQL := "UPDATE `worker` SET `last`=?,`name`=?,`phone`=?,`password`=?,`manager`=?," +
+		"`count`=?,`todo`=?,`avg_cost`=?,`max_cost`=? WHERE `id`=?;"
 	row.Last = utils.TimeNow()
-	_, err = pre.Exec(row.Last, row.Name, row.Phone, row.Password, row.Manager, row.Count, row.Cost, row.Id)
+	_, err := utils.Mysql.Exec(
+		SQL, row.Last, row.Name, row.Phone, row.Password, row.Manager,
+		row.Count, row.Todo, row.Avg, row.Max, row.Id,
+	)
 	return err
 }
 
 func (row *WorkerTable) Get() error {
-	SQL := "SELECT `first`,`last`,`name`,`phone`,`password`,`manager`,`count`,`cost` FROM `worker` WHERE `id`=?;"
-	pre, err := utils.Mysql.Prepare(SQL)
-	if err != nil {
-		return err
-	}
-	err = pre.QueryRow(row.Id).Scan(
-		&row.First, &row.Last, &row.Name, &row.Phone,
-		&row.Password, &row.Count, &row.Cost,
+	SQL := "SELECT `first`,`last`,`name`,`phone`,`password`,`manager`,`count`,`todo`," +
+		"`avg_cost`,`max_cost` FROM `worker` WHERE `id`=?;"
+	return utils.Mysql.QueryRow(SQL, row.Id).Scan(
+		&row.First, &row.Last, &row.Name, &row.Phone, &row.Password,
+		&row.Manager, &row.Count, &row.Todo, &row.Avg, &row.Max,
 	)
-	return err
 }
 
 func (row *WorkerTable) GetByPhone() error {
-	SQL := "SELECT `id`,`first`,`last`,`name`,`password`,`manager`,`count`,`cost` FROM `worker` WHERE `phone`=? LIMIT 1;"
-	pre, err := utils.Mysql.Prepare(SQL)
-	if err != nil {
-		return err
-	}
-	err = pre.QueryRow(row.Phone).Scan(
-		&row.Id, &row.First, &row.Last, &row.Name,
-		&row.Password, &row.Manager, &row.Count, &row.Cost,
+	SQL := "SELECT `id`,`first`,`last`,`name`,`password`,`manager`,`count`,`todo`," +
+		"`avg_cost`,`max_cost` FROM `worker` WHERE `phone`=?;"
+	return utils.Mysql.QueryRow(SQL, row.Phone).Scan(
+		&row.Id, &row.First, &row.Last, &row.Name, &row.Password,
+		&row.Manager, &row.Count, &row.Todo, &row.Avg, &row.Max,
 	)
-	return err
 }
 
-func WorkerQuery(manager, cond string, limit, page int) (int, []utils.JsonType, error) {
+type workerQueryRes struct {
+	Id    string `json:"id"`
+	Name  string `json:"name"`
+	Phone string `json:"phone"`
+	Count uint32 `json:"count"`
+	Cost  uint32 `json:"cost"`
+
+	First utils.TimeType `json:"first"`
+	Last  utils.TimeType `json:"last"`
+
+	Manager struct {
+		Id   string `json:"id"`
+		Name string `json:"name"`
+	} `json:"manager"`
+}
+
+func WorkerQuery(manager, cond string, limit, page int) (int, []workerQueryRes, error) {
 	SQL := "none"
 	if manager == "" { // any
 		SQL = utils.Format(
@@ -120,20 +132,20 @@ func WorkerQuery(manager, cond string, limit, page int) (int, []utils.JsonType,
 		return 0, nil, err
 	}
 	if total == 0 {
-		return 0, []utils.JsonType{}, err
+		return 0, []workerQueryRes{}, err
 	}
 	if manager == "" { // any
 		SQL = utils.Format(
-			"SELECT w.`id`,w.`first`,w.`last`,w.`name`,w.`phone`,w.`count`,w.`cost`,m.`id`,m.`name` "+
-				"FROM `worker` AS w LEFT JOIN `manager` AS m "+
-				"ON w.`manager`=m.`id` WHERE w.`name` LIKE '%%%s%%' OR w.`phone` LIKE '%%%s%%' LIMIT %d OFFSET %d;",
+			"SELECT w.`id`,w.`first`,w.`last`,w.`name`,w.`phone`,w.`count`,w.`avg_cost`,m.`id`,m.`name` "+
+				"FROM `worker` AS w LEFT JOIN `manager` AS m ON w.`manager`=m.`id` "+
+				"WHERE w.`name` LIKE '%%%s%%' OR w.`phone` LIKE '%%%s%%' LIMIT %d OFFSET %d;",
 			cond, cond, limit, (page-1)*limit,
 		)
 	} else { // one
 		SQL = utils.Format(
-			"SELECT w.`id`,w.`first`,w.`last`,w.`name`,w.`phone`,w.`count`,w.`cost`,m.`id`,m.`name` "+
-				"FROM `worker` AS w LEFT JOIN `manager` AS m "+
-				"ON w.`manager`=m.`id` WHERE w.`manager`='%s' AND (w.`name` LIKE '%%%s%%' OR w.`phone` LIKE '%%%s%%') LIMIT %d OFFSET %d;",
+			"SELECT w.`id`,w.`first`,w.`last`,w.`name`,w.`phone`,w.`count`,w.`avg_cost`,m.`id`,m.`name` "+
+				"FROM `worker` AS w LEFT JOIN `manager` AS m ON w.`manager`=m.`id` "+
+				"WHERE w.`manager`='%s' AND (w.`name` LIKE '%%%s%%' OR w.`phone` LIKE '%%%s%%') LIMIT %d OFFSET %d;",
 			manager, cond, cond, limit, (page-1)*limit,
 		)
 	}
@@ -143,21 +155,31 @@ func WorkerQuery(manager, cond string, limit, page int) (int, []utils.JsonType,
 	if err != nil {
 		return 0, nil, err
 	}
-	res := make([]utils.JsonType, 0)
+	res := make([]workerQueryRes, 0)
 	for rows.Next() {
-		var worker WorkerTable
-		name := ""
-		_ = rows.Scan(&worker.Id, &worker.First, &worker.Last, &worker.Name, &worker.Phone, &worker.Count, &worker.Cost, &worker.Manager, &name)
-		res = append(res, utils.JsonType{
-			"id": worker.Id, "first": worker.First, "last": worker.Last, "name": worker.Name, "phone": worker.Phone,
-			"count": worker.Count, "cost": worker.Cost, "manager": utils.JsonType{"id": worker.Manager, "name": name},
-		})
+		var one workerQueryRes
+		_ = rows.Scan(
+			&one.Id, &one.First, &one.Last, &one.Name, &one.Phone,
+			&one.Count, &one.Cost, &one.Manager.Id, &one.Manager.Name,
+		)
+		res = append(res, one)
 	}
 	_ = rows.Close()
 	return total, res, nil
 }
 
-func WorkerQueryForAdmin(manager, cond string, limit, page int) (int, []utils.JsonType, error) {
+type workerQueryForAdminRes struct {
+	Id    string `json:"id"`
+	Name  string `json:"name"`
+	Phone string `json:"phone"`
+	Count uint32 `json:"count"`
+	Cost  uint32 `json:"cost"`
+
+	First utils.TimeType `json:"first"`
+	Last  utils.TimeType `json:"last"`
+}
+
+func WorkerQueryForAdmin(manager, cond string, limit, page int) (int, []workerQueryForAdminRes, error) {
 	SQL := utils.Format(
 		"SELECT COUNT(`id`) AS `total` FROM `worker` WHERE `manager`='%s' AND (`name` LIKE '%%%s%%' OR `phone` LIKE '%%%s%%');",
 		manager, cond, cond,
@@ -168,10 +190,10 @@ func WorkerQueryForAdmin(manager, cond string, limit, page int) (int, []utils.Js
 		return 0, nil, err
 	}
 	if total == 0 {
-		return 0, []utils.JsonType{}, err
+		return 0, []workerQueryForAdminRes{}, err
 	}
 	SQL = utils.Format(
-		"SELECT `id`,`first`,`last`,`name`,`phone`,`count`,`cost` FROM `worker` "+
+		"SELECT `id`,`first`,`last`,`name`,`phone`,`count`,`avg_cost` FROM `worker` "+
 			"WHERE `manager`='%s' AND (`name` LIKE '%%%s%%' OR `phone` LIKE '%%%s%%') LIMIT %d OFFSET %d;",
 		manager, cond, cond, limit, (page-1)*limit,
 	)
@@ -180,14 +202,11 @@ func WorkerQueryForAdmin(manager, cond string, limit, page int) (int, []utils.Js
 	if err != nil {
 		return 0, nil, err
 	}
-	res := make([]utils.JsonType, 0)
+	res := make([]workerQueryForAdminRes, 0)
 	for rows.Next() {
-		var worker WorkerTable
-		_ = rows.Scan(&worker.Id, &worker.First, &worker.Last, &worker.Name, &worker.Phone, &worker.Count, &worker.Cost)
-		res = append(res, utils.JsonType{
-			"id": worker.Id, "first": worker.First, "last": worker.Last, "name": worker.Name,
-			"phone": worker.Phone, "count": worker.Count, "cost": worker.Cost,
-		})
+		var one workerQueryForAdminRes
+		_ = rows.Scan(&one.Id, &one.First, &one.Last, &one.Name, &one.Phone, &one.Count, &one.Cost)
+		res = append(res, one)
 	}
 	_ = rows.Close()
 	return total, res, nil
@@ -198,3 +217,68 @@ func WorkersDelete(ids []string) error {
 	_, err := utils.Mysql.Exec(SQL)
 	return err
 }
+
+func WorkersQueryByPhoneOrName(manager, cond string) ([]utils.JsonType, error) {
+	like := utils.Format("%%%s%%", cond)
+	SQL := "SELECT `id`,`phone`,`name` FROM `worker` WHERE `manager`=? AND (`phone` LIKE ? OR `name` LIKE ?) LIMIT 10;"
+	rows, err := utils.Mysql.Query(SQL, manager, like, like)
+	if err != nil {
+		return nil, err
+	}
+	res := make([]utils.JsonType, 0)
+	for rows.Next() {
+		tid, tph, tna := "", "", ""
+		_ = rows.Scan(&tid, &tph, &tna)
+		res = append(res, utils.JsonType{"id": tid, "phone": tph, "name": tna})
+	}
+	_ = rows.Close()
+	return res, nil
+}
+
+type workerQueryForHistoryRes struct {
+	Id    string `json:"id"`
+	Name  string `json:"name"`
+	Phone string `json:"phone"`
+	Count uint32 `json:"count"`
+	Todo  uint32 `json:"todo"`
+	Avg   uint32 `json:"avg"`
+	Max   uint32 `json:"max"`
+
+	Last utils.TimeType `json:"last"`
+}
+
+func WorkerQueryForHistory(manager, cond string, limit, page int) (int, []workerQueryForHistoryRes, error) {
+	SQL := utils.Format(
+		"SELECT COUNT(`id`) AS `total` FROM `worker` WHERE `manager`='%s' AND (`name` LIKE '%%%s%%' OR `phone` LIKE '%%%s%%');",
+		manager, cond, cond,
+	)
+	total := 0
+	err := utils.Mysql.QueryRow(SQL).Scan(&total)
+	if err != nil {
+		return 0, nil, err
+	}
+	if total == 0 {
+		return 0, []workerQueryForHistoryRes{}, err
+	}
+	SQL = utils.Format(
+		"SELECT `id`,`last`,`name`,`phone`,`count`,`todo`,`avg_cost`,`max_cost` FROM `worker` "+
+			"WHERE `manager`='%s' AND (`name` LIKE '%%%s%%' OR `phone` LIKE '%%%s%%') LIMIT %d OFFSET %d;",
+		manager, cond, cond, limit, (page-1)*limit,
+	)
+	var rows *sql.Rows
+	rows, err = utils.Mysql.Query(SQL)
+	if err != nil {
+		return 0, nil, err
+	}
+	res := make([]workerQueryForHistoryRes, 0)
+	for rows.Next() {
+		var one workerQueryForHistoryRes
+		_ = rows.Scan(
+			&one.Id, &one.Last, &one.Name, &one.Phone,
+			&one.Count, &one.Todo, &one.Avg, &one.Max,
+		)
+		res = append(res, one)
+	}
+	_ = rows.Close()
+	return total, res, nil
+}