Files
2026-06-10 20:11:39 +08:00

1151 lines
31 KiB
Go

package routers
import (
"context"
"encoding/json"
parsefmt "fmt"
"ops/agents"
"ops/models"
"slices"
"strings"
"time"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
var (
purchaseUserGroup TabUserGroups
purchaseAdmins []uint
)
// 更新管理员成员缓存
func PurchaseUpdateAdminsCash() {
purchaseAdmins = nil
// id 1 是系统管理员
purchaseAdmins = append(purchaseAdmins, 1)
var binds []TabUserGroupBinds
models.DB.Where("group_id = ?", purchaseUserGroup.ID).Find(&binds)
for _, item := range binds {
if !slices.Contains(purchaseAdmins, item.UserID) {
purchaseAdmins = append(purchaseAdmins, item.UserID)
}
}
}
// 判断用户是否有权限修改/删除订单(创建者或管理员)
func canModifyPurchase(userID, creatorUserID uint) bool {
if slices.Contains(purchaseAdmins, userID) {
return true
}
if userID == creatorUserID {
return true
}
return false
}
// decodeJSON 将 map 通过 JSON 中转解码到目标结构体,绕过 mapstructure 的字段名匹配问题
func decodeJSON(data map[string]interface{}, out interface{}) error {
jsonBytes, err := json.Marshal(data)
if err != nil {
return err
}
return json.Unmarshal(jsonBytes, out)
}
type CostItem struct {
Cost int `json:"cost"` // 费用(分)
CostT int `json:"costt"` // 总价
CurrencyType int `json:"currencytype"` // 货币类型: 1-CNY 2-MOP 3-HKD 4-USD
Int int `json:"int"` // 数量
Type int `json:"type"` // 费用类型: 1-单价 2-运费
}
type From_purchase_addorder struct {
Costs []CostItem `json:"costs"` // 成本
Link string `json:"link"` // 链接
OrderStatus string `json:"order_status"` // 订单状态
Photos []string `json:"photos"` // 图片备注
Remark string `json:"remark"` // 备注
Styles string `json:"styles"` // 样式备注
Title string `json:"title"` // 标题
}
type TabPurchaseOrder struct {
ID uint `gorm:"primarykey"`
UserID uint `gorm:"not null"`
Title string `gorm:"size:200;comment:标题"`
Remark string `gorm:"type:text;comment:备注"`
Link string `gorm:"size:1000;comment:链接"`
Styles string `gorm:"type:text;comment:样式数组"`
OrderStatus string `gorm:"size:50;default:pending;comment:订单状态: pending-待处理 ordered-已下单 arrived-已到达 received-已收件 lost-丢件 returned-退件"`
CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime"`
UpdatedAt *time.Time `gorm:"type:datetime;autoUpdateTime"`
DeletedAt gorm.DeletedAt `gorm:"index"`
}
type TabPurchaseCosts struct {
ID uint `gorm:"primarykey"`
OrderID uint `gorm:"not null"`
UserID uint `gorm:"not null"`
Price int `gorm:"not null"`
Quantity int `gorm:"not null"`
CurrencyType int `gorm:"default:1;comment:货币类型: 1-CNY 2-MOP 3-HKD 4-USD"`
CostType int `gorm:"default:1;comment:费用类型: 1-单价 2-运费"`
CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime"`
}
// TabPurchaseCommit 记录订单状态变更及评论
type TabPurchaseCommit struct {
ID uint `gorm:"primarykey"`
OrderID uint `gorm:"not null;index;comment:关联订单ID"`
UserID uint `gorm:"not null;comment:操作人ID"`
Action string `gorm:"size:50;not null;comment:操作类型: create-创建 create_status-状态变更"`
Status string `gorm:"size:50;comment:变更后的状态"`
OldStatus string `gorm:"size:50;comment:变更前的状态"`
Comment string `gorm:"type:text;comment:评论/备注"`
Photos string `gorm:"type:text;comment:变更图片(JSON数组,存放sha256哈希)"`
IP string `gorm:"size:50;comment:操作IP"`
CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime"`
}
type TabPurchaseLog struct {
ID uint `gorm:"primarykey"`
OrderID uint `gorm:"not null;index;comment:关联OrderID"`
UserID uint `gorm:"not null;comment:操作人ID"`
ActionType string `gorm:"size:50;not null;comment:操作类型: create-创建 update-修改 delete-删除 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;comment:操作时间"`
}
type purchaseProvider struct{}
func (purchaseProvider) QueryPurchases(ctx context.Context, query agents.PurchaseQuery) (*agents.PurchaseQueryResult, error) {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
result := &agents.PurchaseQueryResult{
Ok: true,
Action: query.Action,
LoggedIn: query.UserID > 0,
}
if !result.LoggedIn {
result.Message = "需要登录才能查询采购模块信息。"
return result, nil
}
switch query.Action {
case "count":
counts, err := queryPurchaseCounts(query)
if err != nil {
return nil, err
}
result.Counts = counts
return result, nil
case "get":
order, err := queryPurchaseOrderDetail(query.OrderID, query.UserID)
if err != nil {
return nil, err
}
result.Order = order
if order != nil {
result.Count = 1
}
return result, nil
default:
orders, total, err := queryPurchaseOrderList(query)
if err != nil {
return nil, err
}
result.Orders = orders
result.Count = len(orders)
result.Total = total
result.Page = query.Page
result.Limit = query.Limit
result.Filters = map[string]interface{}{
"search": query.Search,
"status": query.Status,
"start_date": query.StartDate,
"end_date": query.EndDate,
}
return result, nil
}
}
func applyPurchaseQueryFilters(db *gorm.DB, query agents.PurchaseQuery) (*gorm.DB, error) {
if query.Search != "" {
var id uint
if _, err := parsefmt.Sscanf(query.Search, "%d", &id); err == nil && id > 0 {
db = db.Where("id = ?", id)
} else {
db = db.Where("title LIKE ? OR remark LIKE ?", "%"+query.Search+"%", "%"+query.Search+"%")
}
}
if query.Status != "" {
db = db.Where("order_status = ?", query.Status)
}
if query.StartDate != "" {
startDate, err := time.Parse("2006-01-02", query.StartDate)
if err != nil {
return nil, err
}
db = db.Where("created_at >= ?", startDate)
}
if query.EndDate != "" {
endDate, err := time.Parse("2006-01-02", query.EndDate)
if err != nil {
return nil, err
}
db = db.Where("created_at < ?", endDate.AddDate(0, 0, 1))
}
return db, nil
}
func queryPurchaseOrderList(query agents.PurchaseQuery) ([]agents.PurchaseOrder, int64, error) {
db, err := applyPurchaseQueryFilters(models.DB.Model(&TabPurchaseOrder{}), query)
if err != nil {
return nil, 0, err
}
var total int64
if err := db.Count(&total).Error; err != nil {
return nil, 0, err
}
var rows []TabPurchaseOrder
if err := db.Order("updated_at DESC, id DESC").Offset(query.Limit * (query.Page - 1)).Limit(query.Limit).Find(&rows).Error; err != nil {
return nil, 0, err
}
orders := make([]agents.PurchaseOrder, 0, len(rows))
for _, row := range rows {
order, err := queryPurchaseOrderDetail(row.ID, query.UserID)
if err != nil {
return nil, 0, err
}
if order != nil {
orders = append(orders, *order)
}
}
return orders, total, nil
}
func queryPurchaseOrderDetail(orderID uint, userID uint) (*agents.PurchaseOrder, error) {
var row TabPurchaseOrder
if err := models.DB.Where("id = ?", orderID).First(&row).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
order := buildPurchaseOrder(row, userID)
var costs []TabPurchaseCosts
if err := models.DB.Where("order_id = ?", orderID).Order("id asc").Find(&costs).Error; err != nil {
return nil, err
}
order.Costs = make([]agents.PurchaseCost, 0, len(costs))
for _, cost := range costs {
order.Costs = append(order.Costs, agents.PurchaseCost{
ID: cost.ID,
OrderID: cost.OrderID,
UserID: cost.UserID,
Price: cost.Price,
Quantity: cost.Quantity,
CurrencyType: cost.CurrencyType,
CurrencyName: purchaseCurrencyName(cost.CurrencyType),
CostType: cost.CostType,
CostTypeName: purchaseCostTypeName(cost.CostType),
})
}
var commits []TabPurchaseCommit
if err := models.DB.Where("order_id = ?", orderID).Order("created_at DESC").Find(&commits).Error; err != nil {
return nil, err
}
order.Commits = make([]agents.PurchaseCommit, 0, len(commits))
for _, commit := range commits {
order.Commits = append(order.Commits, agents.PurchaseCommit{
ID: commit.ID,
OrderID: commit.OrderID,
UserID: commit.UserID,
Action: commit.Action,
Status: commit.Status,
OldStatus: commit.OldStatus,
Comment: commit.Comment,
CreatedAt: formatTimePtr(commit.CreatedAt),
})
}
return &order, nil
}
func queryPurchaseCounts(query agents.PurchaseQuery) (map[string]int64, error) {
counts := map[string]int64{}
statuses := []string{"pending", "ordered", "arrived", "received", "lost", "returned"}
var total int64
base, err := applyPurchaseQueryFilters(models.DB.Model(&TabPurchaseOrder{}), agents.PurchaseQuery{Search: query.Search, StartDate: query.StartDate, EndDate: query.EndDate})
if err != nil {
return nil, err
}
if err := base.Count(&total).Error; err != nil {
return nil, err
}
counts["total"] = total
for _, status := range statuses {
statusQuery := agents.PurchaseQuery{Search: query.Search, Status: status, StartDate: query.StartDate, EndDate: query.EndDate}
db, err := applyPurchaseQueryFilters(models.DB.Model(&TabPurchaseOrder{}), statusQuery)
if err != nil {
return nil, err
}
var count int64
if err := db.Count(&count).Error; err != nil {
return nil, err
}
counts[status] = count
}
return counts, nil
}
func buildPurchaseOrder(row TabPurchaseOrder, currentUserID uint) agents.PurchaseOrder {
return agents.PurchaseOrder{
ID: row.ID,
UserID: row.UserID,
Title: row.Title,
Remark: row.Remark,
Link: row.Link,
DetailURL: purchaseOrderDetailURL(row.ID),
Styles: row.Styles,
OrderStatus: row.OrderStatus,
OrderStatusName: purchaseStatusName(row.OrderStatus),
CreatedAt: formatTimePtr(row.CreatedAt),
UpdatedAt: formatTimePtr(row.UpdatedAt),
CanModify: canModifyPurchase(currentUserID, row.UserID),
}
}
func purchaseOrderDetailURL(orderID uint) string {
return parsefmt.Sprintf("/purchase/showorder/%d", orderID)
}
func formatTimePtr(t *time.Time) string {
if t == nil {
return ""
}
return t.Format("2006-01-02 15:04:05")
}
func purchaseStatusName(status string) string {
switch status {
case "pending":
return "待处理"
case "ordered":
return "已下单"
case "arrived":
return "已到达"
case "received":
return "已收件"
case "lost":
return "丢件"
case "returned":
return "退件"
default:
return status
}
}
func purchaseCurrencyName(currencyType int) string {
switch currencyType {
case 1:
return "CNY"
case 2:
return "MOP"
case 3:
return "HKD"
case 4:
return "USD"
default:
return "未知"
}
}
func purchaseCostTypeName(costType int) string {
switch costType {
case 1:
return "单价"
case 2:
return "运费"
default:
return "未知"
}
}
func ApiPurchaseInit() {
models.DB.AutoMigrate(&TabPurchaseOrder{})
models.DB.AutoMigrate(&TabPurchaseCosts{})
models.DB.AutoMigrate(&TabPurchaseLog{})
models.DB.AutoMigrate(&TabPurchaseCommit{})
//先检查用户组有没有这个key purchase
purchaseUserGroup.Name = "purchase_admin"
if models.DB.Where(&purchaseUserGroup).First(&purchaseUserGroup).Error == nil {
PurchaseUpdateAdminsCash()
} else {
purchaseUserGroup.Type = "usergroup"
models.DB.Create(&purchaseUserGroup)
}
agents.RegisterPurchaseProvider(purchaseProvider{})
}
func ApiPurchase(r *gin.RouterGroup) {
r.POST("/getorder", func(ctx *gin.Context) {
isAuth, user, data := AuthenticationAuthority(ctx)
if !isAuth {
ReturnJson(ctx, "userCookieError", nil)
return
}
type FromGetOrder struct {
ID uint `json:"id"`
}
var from FromGetOrder
if err := decodeJSON(data, &from); err != nil || from.ID == 0 {
ReturnJson(ctx, "jsonErr", nil)
return
}
var order TabPurchaseOrder
if err := models.DB.Where("id = ?", from.ID).First(&order).Error; err != nil {
ReturnJson(ctx, "order_not_found", nil)
return
}
// 查询关联费用
var costs []TabPurchaseCosts
models.DB.Where("order_id = ?", from.ID).Find(&costs)
// 查询关联图片
var binds []TabPurchaseFileBind
models.DB.Where("order_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 []TabPurchaseCommit
models.DB.Where("order_id = ?", from.ID).Order("created_at DESC").Find(&commits)
// 解析每条 commit 的 Photos JSON 字段为数组
type CommitResponse struct {
ID uint `json:"id"`
OrderID uint `json:"orderId"`
UserID uint `json:"userId"`
Action string `json:"action"`
Status string `json:"status"`
OldStatus string `json:"oldStatus"`
Comment string `json:"comment"`
IP string `json:"ip"`
CreatedAt time.Time `json:"createdAt"`
Photos []string `json:"photos"`
}
var commitResps []CommitResponse
for _, c := range commits {
// Status 优先用数据库字段;若为空(历史旧数据),从 Comment 备注中截取状态
status := c.Status
if status == "" {
status = strings.TrimPrefix(c.Comment, "状态变更为: ")
status = strings.TrimPrefix(status, "变更状态为: ")
// 如果截取后跟原文一样,说明不是"状态变更为"格式,取原文作为展示
if status == c.Comment {
status = ""
}
}
resp := CommitResponse{
ID: c.ID,
OrderID: c.OrderID,
UserID: c.UserID,
Action: c.Action,
Status: status,
OldStatus: c.OldStatus,
Comment: c.Comment,
IP: c.IP,
CreatedAt: time.Time{},
}
if c.CreatedAt != nil {
resp.CreatedAt = *c.CreatedAt
}
if c.Photos != "" {
json.Unmarshal([]byte(c.Photos), &resp.Photos)
}
commitResps = append(commitResps, resp)
}
// 查询关联工单(通过 TabWorkOrderPurchaseOrderBind 表)
type WorkOrderInfo struct {
ID uint `json:"id"`
Title string `json:"title"`
CurrentStatus string `json:"status"`
}
var linkedWorkOrders []WorkOrderInfo
var woBinds []TabWorkOrderPurchaseOrderBind
models.DB.Where("purchase_order_id = ?", from.ID).Find(&woBinds)
if len(woBinds) > 0 {
woIDSet := make(map[uint]bool)
var woIDs []uint
for _, wb := range woBinds {
if !woIDSet[wb.WorkOrderID] {
woIDSet[wb.WorkOrderID] = true
woIDs = append(woIDs, wb.WorkOrderID)
}
}
var workOrders []TabWorkOrder
models.DB.Where("id IN ?", woIDs).Find(&workOrders)
for _, wo := range workOrders {
linkedWorkOrders = append(linkedWorkOrders, WorkOrderInfo{
ID: wo.ID,
Title: wo.Title,
CurrentStatus: wo.CurrentStatus,
})
}
}
if linkedWorkOrders == nil {
linkedWorkOrders = []WorkOrderInfo{}
}
// 判断当前用户是否可以修改
canModify := canModifyPurchase(user.ID, order.UserID)
ReturnJson(ctx, "apiOK", gin.H{
"order": order,
"canModify": canModify,
"costs": costs,
"photos": files,
"commits": commitResps,
"workOrders": linkedWorkOrders,
})
})
// 更新订单状态(可附带评论)
r.POST("/updatestatus", func(ctx *gin.Context) {
isAuth, user, data := AuthenticationAuthority(ctx)
if !isAuth {
ReturnJson(ctx, "userCookieError", nil)
return
}
type FromUpdateStatus struct {
ID uint `json:"id"`
Status string `json:"status" binding:"required"`
Comment string `json:"comment"`
Photos []string `json:"photos"` // 变更附带的图片哈希
}
var from FromUpdateStatus
if err := decodeJSON(data, &from); err != nil || from.ID == 0 {
ReturnJson(ctx, "jsonErr", nil)
return
}
// 校验图片哈希(不包含标点符号的哈希值)
for _, hash := range from.Photos {
if models.IsContainsSpecialChar(hash) {
ReturnJson(ctx, "photo_hash_invalid", nil)
return
}
}
// 校验状态值
validStatuses := map[string]bool{
"pending": true,
"ordered": true,
"arrived": true,
"received": true,
"lost": true,
"returned": true,
}
if !validStatuses[from.Status] {
ReturnJson(ctx, "invalid_status", nil)
return
}
var order TabPurchaseOrder
if err := models.DB.Where("id = ?", from.ID).First(&order).Error; err != nil {
ReturnJson(ctx, "order_not_found", nil)
return
}
oldStatus := order.OrderStatus
// 更新状态
updates := map[string]interface{}{
"order_status": from.Status,
}
if err := models.DB.Model(&order).Updates(updates).Error; err != nil {
ReturnJson(ctx, "apiErr", nil)
return
}
// 写状态变更 commit
comment := from.Comment
if comment == "" && len(from.Photos) == 0 {
comment = "状态变更为: " + from.Status
}
photosJSON := ""
if len(from.Photos) > 0 {
if pj, err := json.Marshal(from.Photos); err == nil {
photosJSON = string(pj)
}
}
commit := TabPurchaseCommit{
OrderID: order.ID,
UserID: user.ID,
Action: "create_status",
Status: from.Status,
OldStatus: oldStatus,
Comment: comment,
Photos: photosJSON,
IP: ctx.ClientIP(),
}
models.DB.Create(&commit)
// 写操作日志
newContent, _ := json.Marshal(map[string]string{
"status": from.Status,
"comment": comment,
})
oldContent, _ := json.Marshal(map[string]string{
"status": oldStatus,
})
tosqllog := TabPurchaseLog{
UserID: user.ID,
OrderID: order.ID,
ActionType: "update_status",
NewContent: string(newContent),
OldContent: string(oldContent),
IP: ctx.ClientIP(),
Remark: comment,
}
models.DB.Create(&tosqllog)
ReturnJson(ctx, "apiOK", nil)
})
// 删除状态记录
r.POST("/delete_commit", func(ctx *gin.Context) {
isAuth, user, data := AuthenticationAuthority(ctx)
if !isAuth {
ReturnJson(ctx, "userCookieError", nil)
return
}
type FromDeleteCommit struct {
OrderID uint `json:"orderId"`
CommitID uint `json:"commitId"`
}
var from FromDeleteCommit
if err := decodeJSON(data, &from); err != nil || from.OrderID == 0 || from.CommitID == 0 {
ReturnJson(ctx, "jsonErr", nil)
return
}
// 获取订单信息
var order TabPurchaseOrder
if err := models.DB.Where("id = ?", from.OrderID).First(&order).Error; err != nil {
ReturnJson(ctx, "order_not_found", nil)
return
}
// 获取进度信息
var commit TabPurchaseCommit
if err := models.DB.Where("id = ? AND order_id = ?", from.CommitID, from.OrderID).First(&commit).Error; err != nil {
ReturnJson(ctx, "commit_not_found", nil)
return
}
// 权限判断:订单创建者 或 进度创建者 或 管理员
isOrderCreator := user.ID == order.UserID
isCommitCreator := user.ID == commit.UserID
isAdmin := slices.Contains(purchaseAdmins, user.ID)
if !isOrderCreator && !isCommitCreator && !isAdmin {
ReturnJson(ctx, "no_permission", nil)
return
}
// 删除进度
models.DB.Where("id = ?", from.CommitID).Delete(&TabPurchaseCommit{})
ReturnJson(ctx, "apiOK", nil)
})
r.POST("/getorders", func(ctx *gin.Context) {
isAuth, _, data := AuthenticationAuthority(ctx)
if isAuth {
//fmt.Println(user)
// DebugPrintJson(data)
type From_purchase_getorders struct {
Search string
Status string
Entries int
Page int
}
var jsondata From_purchase_getorders
if err := decodeJSON(data, &jsondata); err == nil {
//fmt.Println(jsondata)
is_data_ok := true
if jsondata.Entries <= 0 || jsondata.Entries > 300 {
is_data_ok = false
}
if jsondata.Page <= 0 {
is_data_ok = false
}
if is_data_ok {
//读取有多少条目
var count int64
query := models.DB.Model(TabPurchaseOrder{})
if jsondata.Search != "" {
// 精确匹配订单 ID
var id uint
if _, err := parsefmt.Sscanf(jsondata.Search, "%d", &id); err == nil && id > 0 {
query = query.Where("id = ?", id)
} else {
// 模糊匹配标题和用途(Remark)
query = query.Where("title LIKE ? OR remark LIKE ?",
"%"+jsondata.Search+"%", "%"+jsondata.Search+"%")
}
}
if jsondata.Status != "" {
query = query.Where("order_status = ?", jsondata.Status)
}
query.Count(&count)
//读取条目
var getorders []TabPurchaseOrder
query.Order("updated_at DESC, id DESC").Offset(jsondata.Entries * (jsondata.Page - 1)).Limit(jsondata.Entries).Find(&getorders)
ReturnJson(ctx, "apiOK", map[string]interface{}{
"all_count": count,
"all_orders": getorders,
})
} else {
ReturnJson(ctx, "jsonErr_1", nil)
}
} else {
ReturnJson(ctx, "jsonErr", nil)
}
} else {
ReturnJson(ctx, "userCookieError", nil)
}
})
r.POST("/addorder", func(ctx *gin.Context) {
isAuth, user, data := AuthenticationAuthority(ctx)
if isAuth {
//需要处理提交的数据,接口有固定的数据格式,不允许乱搞
//fmt.Println(isAuth)
//fmt.Println(user)
//DebugPrintJson(data)
var jsondata From_purchase_addorder
//fmt.Println(data)
if err := decodeJSON(data, &jsondata); err == nil {
//jsonStr, _ := json.MarshalIndent(jsondata, "", " ")
//fmt.Println("转换后数据:\n", string(jsonStr))
//数据比较混乱 在这里校验
//判断标题不为空
is_data_ok := true
if jsondata.Title == "" {
is_data_ok = false
}
//判断数量与价格是否为负数
for i := 0; i < len(jsondata.Costs); i++ {
if jsondata.Costs[i].Cost <= 0 {
is_data_ok = false
}
if jsondata.Costs[i].Int <= 0 {
is_data_ok = false
}
}
//判断图片是否为哈希值
for i := 0; i < len(jsondata.Photos); i++ {
//判断字符串是否包含标点符号
if models.IsContainsSpecialChar(jsondata.Photos[i]) {
is_data_ok = false
//fmt.Println("err4")
}
}
//判断时间字符串是否合法
// uptime, e := models.StringToTimePtr(jsondata.UpdateTime)
// if e != nil {
// is_data_ok = false
// fmt.Println("err5")
// }
if is_data_ok {
//校验通过
//photos, _ := json.Marshal(jsondata.Photos)
new_data := TabPurchaseOrder{
UserID: user.ID,
Title: jsondata.Title,
Remark: jsondata.Remark,
Link: jsondata.Link,
Styles: jsondata.Styles,
OrderStatus: "pending", // 默认待处理
}
models.DB.Create(&new_data)
for i := 0; i < len(jsondata.Costs); i++ {
currencyType := jsondata.Costs[i].CurrencyType
if currencyType <= 0 {
currencyType = 1 // 默认 CNY
}
costType := jsondata.Costs[i].Type
if costType <= 0 {
costType = 1 // 默认单价
}
new_cost_data := TabPurchaseCosts{
Price: jsondata.Costs[i].Cost,
Quantity: jsondata.Costs[i].Int,
UserID: user.ID,
OrderID: new_data.ID,
CurrencyType: currencyType,
CostType: costType,
}
models.DB.Create(&new_cost_data)
}
//绑定文件
for i := 0; i < len(jsondata.Photos); i++ {
findFile := TabFileInfo{
Sha256: jsondata.Photos[i],
Type: "image",
}
if models.DB.Where(&findFile).First(&findFile).Error == nil {
bind := TabPurchaseFileBind{
OrderID: new_data.ID,
FileID: findFile.ID,
}
models.DB.Create(&bind)
}
}
// 写创建日志
newContent, _ := json.Marshal(jsondata)
tosqllog := TabPurchaseLog{
UserID: user.ID,
OrderID: new_data.ID,
ActionType: "create",
NewContent: string(newContent),
OldContent: "",
IP: ctx.ClientIP(),
}
models.DB.Create(&tosqllog)
// 写状态创建 commit
commitLog := TabPurchaseCommit{
OrderID: new_data.ID,
UserID: user.ID,
Action: "create",
Status: "pending",
OldStatus: "",
Comment: "订单创建",
IP: ctx.ClientIP(),
}
models.DB.Create(&commitLog)
ReturnJson(ctx, "apiOK", nil)
} else {
ReturnJson(ctx, "jsonErr_1", nil)
}
} else {
ReturnJson(ctx, "jsonErr", nil)
}
} else {
ReturnJson(ctx, "userCookieError", nil)
}
//ReturnJson(ctx, "apiErr", nil)
})
// 编辑订单基本信息
r.POST("/updateorder", func(ctx *gin.Context) {
isAuth, user, data := AuthenticationAuthority(ctx)
if !isAuth {
ReturnJson(ctx, "userCookieError", nil)
return
}
type FromUpdateOrder struct {
ID uint `json:"id"`
Title string `json:"title"`
Remark string `json:"remark"`
Link string `json:"link"`
Styles string `json:"styles"`
Photos []string `json:"photos"`
Costs []CostItem `json:"costs"`
}
var from FromUpdateOrder
if err := decodeJSON(data, &from); err != nil || from.ID == 0 {
ReturnJson(ctx, "jsonErr", nil)
return
}
if from.Title == "" {
ReturnJson(ctx, "jsonErr_1", nil)
return
}
// 校验图片哈希
for _, hash := range from.Photos {
if models.IsContainsSpecialChar(hash) {
ReturnJson(ctx, "photo_hash_invalid", nil)
return
}
}
// 校验费用
for _, c := range from.Costs {
if c.Cost <= 0 || c.Int <= 0 {
ReturnJson(ctx, "jsonErr_1", nil)
return
}
}
var order TabPurchaseOrder
if err := models.DB.Where("id = ?", from.ID).First(&order).Error; err != nil {
ReturnJson(ctx, "order_not_found", nil)
return
}
// 权限校验:只有创建者或管理员可以修改
if !canModifyPurchase(user.ID, order.UserID) {
ReturnJson(ctx, "no_permission", nil)
return
}
// 记录旧数据
oldContent, _ := json.Marshal(order)
// 更新基本字段
if err := models.DB.Model(&order).Updates(map[string]interface{}{
"title": from.Title,
"remark": from.Remark,
"link": from.Link,
"styles": from.Styles,
}).Error; err != nil {
ReturnJson(ctx, "apiErr", nil)
return
}
// 重建费用明细:先删旧,再插新
models.DB.Where("order_id = ?", from.ID).Delete(&TabPurchaseCosts{})
for _, c := range from.Costs {
ct := c.CurrencyType
if ct <= 0 {
ct = 1
}
ctype := c.Type
if ctype <= 0 {
ctype = 1
}
models.DB.Create(&TabPurchaseCosts{
Price: c.Cost,
Quantity: c.Int,
UserID: user.ID,
OrderID: from.ID,
CurrencyType: ct,
CostType: ctype,
})
}
// 重建图片绑定:先删旧,再插新
models.DB.Where("order_id = ?", from.ID).Delete(&TabPurchaseFileBind{})
for _, hash := range from.Photos {
findFile := TabFileInfo{Sha256: hash, Type: "image"}
if models.DB.Where(&findFile).First(&findFile).Error == nil {
models.DB.Create(&TabPurchaseFileBind{
OrderID: from.ID,
FileID: findFile.ID,
})
}
}
// 写操作日志
newContent, _ := json.Marshal(from)
models.DB.Create(&TabPurchaseLog{
UserID: user.ID,
OrderID: from.ID,
ActionType: "update",
OldContent: string(oldContent),
NewContent: string(newContent),
IP: ctx.ClientIP(),
})
ReturnJson(ctx, "apiOK", nil)
})
// 删除订单
r.POST("/deleteorder", func(ctx *gin.Context) {
isAuth, user, data := AuthenticationAuthority(ctx)
if !isAuth {
ReturnJson(ctx, "userCookieError", nil)
return
}
type FromDeleteOrder struct {
ID uint `json:"id"`
}
var from FromDeleteOrder
if err := decodeJSON(data, &from); err != nil || from.ID == 0 {
ReturnJson(ctx, "jsonErr", nil)
return
}
var order TabPurchaseOrder
if err := models.DB.Where("id = ?", from.ID).First(&order).Error; err != nil {
ReturnJson(ctx, "order_not_found", nil)
return
}
// 权限校验:只有创建者或管理员可以删除
if !canModifyPurchase(user.ID, order.UserID) {
ReturnJson(ctx, "no_permission", nil)
return
}
// 关联删除(硬删,不保留)
models.DB.Where("order_id = ?", from.ID).Delete(&TabPurchaseCosts{})
models.DB.Where("order_id = ?", from.ID).Delete(&TabPurchaseFileBind{})
models.DB.Where("order_id = ?", from.ID).Delete(&TabPurchaseCommit{})
models.DB.Where("order_id = ?", from.ID).Delete(&TabPurchaseLog{})
// 软删除订单本身
models.DB.Delete(&order)
ReturnJson(ctx, "apiOK", nil)
})
// 获取订单数量统计
r.POST("/getordercount", func(ctx *gin.Context) {
isAuth, _, _ := AuthenticationAuthority(ctx)
if !isAuth {
ReturnJson(ctx, "userCookieError", nil)
return
}
type OrderCount struct {
Pending int64 `json:"pending"` // 待处理
Ordered int64 `json:"ordered"` // 已下单
Arrived int64 `json:"arrived"` // 已到达
Received int64 `json:"received"` // 已收件
Lost int64 `json:"lost"` // 丢件
Returned int64 `json:"returned"` // 退件
Total int64 `json:"total"` // 总数
}
var count OrderCount
models.DB.Model(&TabPurchaseOrder{}).Count(&count.Total)
models.DB.Model(&TabPurchaseOrder{}).Where("order_status = ?", "pending").Count(&count.Pending)
models.DB.Model(&TabPurchaseOrder{}).Where("order_status = ?", "ordered").Count(&count.Ordered)
models.DB.Model(&TabPurchaseOrder{}).Where("order_status = ?", "arrived").Count(&count.Arrived)
models.DB.Model(&TabPurchaseOrder{}).Where("order_status = ?", "received").Count(&count.Received)
models.DB.Model(&TabPurchaseOrder{}).Where("order_status = ?", "lost").Count(&count.Lost)
models.DB.Model(&TabPurchaseOrder{}).Where("order_status = ?", "returned").Count(&count.Returned)
ReturnJson(ctx, "apiOK", map[string]interface{}{
"pending": count.Pending,
"ordered": count.Ordered,
"arrived": count.Arrived,
"received": count.Received,
"lost": count.Lost,
"returned": count.Returned,
"total": count.Total,
})
})
// 搜索工单(用于采购订单关联)
r.POST("/search_work_orders", func(ctx *gin.Context) {
isAuth, _, data := AuthenticationAuthority(ctx)
if !isAuth {
ReturnJson(ctx, "userCookieError", nil)
return
}
type FromSearch struct {
Search string `json:"search"`
Limit int `json:"limit"`
}
var from FromSearch
if err := decodeJSON(data, &from); err != nil {
ReturnJson(ctx, "jsonErr", nil)
return
}
if from.Limit <= 0 || from.Limit > 20 {
from.Limit = 5
}
query := models.DB.Model(&TabWorkOrder{})
if from.Search != "" {
var id uint
if _, err := parsefmt.Sscanf(from.Search, "%d", &id); err == nil && id > 0 {
query = query.Where("id = ?", id)
} else {
query = query.Where("title LIKE ? OR description LIKE ?",
"%"+from.Search+"%", "%"+from.Search+"%")
}
}
var orders []TabWorkOrder
query.Order("updated_at DESC, id DESC").Limit(from.Limit).Find(&orders)
type WorkOrderInfo struct {
ID uint `json:"id"`
Title string `json:"title"`
Status string `json:"status"`
}
var result []WorkOrderInfo
for _, o := range orders {
result = append(result, WorkOrderInfo{
ID: o.ID,
Title: o.Title,
Status: o.CurrentStatus,
})
}
if result == nil {
result = []WorkOrderInfo{}
}
ReturnJson(ctx, "apiOK", gin.H{"orders": result})
})
}