增加订单查询支持
This commit is contained in:
@@ -35,6 +35,8 @@ func FunctionToolSchemas(configs []ToolConfig) []FunctionToolSchema {
|
|||||||
tools = append(tools, opsAIAssistantScheduleQuerySchema())
|
tools = append(tools, opsAIAssistantScheduleQuerySchema())
|
||||||
case "ops_ai_assistant_current_user":
|
case "ops_ai_assistant_current_user":
|
||||||
tools = append(tools, opsAIAssistantCurrentUserSchema())
|
tools = append(tools, opsAIAssistantCurrentUserSchema())
|
||||||
|
case "ops_ai_assistant_purchase_query":
|
||||||
|
tools = append(tools, opsAIAssistantPurchaseQuerySchema())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return tools
|
return tools
|
||||||
@@ -64,6 +66,8 @@ func ExecuteFunctionTool(ctx context.Context, runtime FunctionToolRuntime, name
|
|||||||
return executeOpsAIAssistantScheduleQuery(ctx, runtime, rawArgs)
|
return executeOpsAIAssistantScheduleQuery(ctx, runtime, rawArgs)
|
||||||
case "ops_ai_assistant_current_user":
|
case "ops_ai_assistant_current_user":
|
||||||
return executeOpsAIAssistantCurrentUser(ctx, runtime, rawArgs)
|
return executeOpsAIAssistantCurrentUser(ctx, runtime, rawArgs)
|
||||||
|
case "ops_ai_assistant_purchase_query":
|
||||||
|
return executeOpsAIAssistantPurchaseQuery(ctx, runtime, rawArgs)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown tool: %s", name)
|
return nil, fmt.Errorf("unknown tool: %s", name)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,84 @@ type CurrentUserInfo struct {
|
|||||||
Language string `json:"language,omitempty"`
|
Language string `json:"language,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PurchaseQueryArgs struct {
|
||||||
|
Action string `json:"action"`
|
||||||
|
OrderID uint `json:"order_id,omitempty"`
|
||||||
|
Search string `json:"search,omitempty"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
StartDate string `json:"start_date,omitempty"`
|
||||||
|
EndDate string `json:"end_date,omitempty"`
|
||||||
|
Page int `json:"page,omitempty"`
|
||||||
|
Limit int `json:"limit,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PurchaseQuery struct {
|
||||||
|
Action string
|
||||||
|
OrderID uint
|
||||||
|
Search string
|
||||||
|
Status string
|
||||||
|
StartDate string
|
||||||
|
EndDate string
|
||||||
|
Page int
|
||||||
|
Limit int
|
||||||
|
UserID uint
|
||||||
|
}
|
||||||
|
|
||||||
|
type PurchaseCost struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
OrderID uint `json:"order_id"`
|
||||||
|
UserID uint `json:"user_id"`
|
||||||
|
Price int `json:"price"`
|
||||||
|
Quantity int `json:"quantity"`
|
||||||
|
CurrencyType int `json:"currency_type"`
|
||||||
|
CurrencyName string `json:"currency_name"`
|
||||||
|
CostType int `json:"cost_type"`
|
||||||
|
CostTypeName string `json:"cost_type_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PurchaseCommit struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
OrderID uint `json:"order_id"`
|
||||||
|
UserID uint `json:"user_id"`
|
||||||
|
Action string `json:"action"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
OldStatus string `json:"old_status,omitempty"`
|
||||||
|
Comment string `json:"comment,omitempty"`
|
||||||
|
CreatedAt string `json:"created_at,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PurchaseOrder struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
UserID uint `json:"user_id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Remark string `json:"remark,omitempty"`
|
||||||
|
Link string `json:"link,omitempty"`
|
||||||
|
DetailURL string `json:"detail_url"`
|
||||||
|
Styles string `json:"styles,omitempty"`
|
||||||
|
OrderStatus string `json:"order_status"`
|
||||||
|
OrderStatusName string `json:"order_status_name"`
|
||||||
|
CreatedAt string `json:"created_at,omitempty"`
|
||||||
|
UpdatedAt string `json:"updated_at,omitempty"`
|
||||||
|
CanModify bool `json:"can_modify,omitempty"`
|
||||||
|
Costs []PurchaseCost `json:"costs,omitempty"`
|
||||||
|
Commits []PurchaseCommit `json:"commits,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PurchaseQueryResult struct {
|
||||||
|
Ok bool `json:"ok"`
|
||||||
|
Action string `json:"action"`
|
||||||
|
LoggedIn bool `json:"loggedIn"`
|
||||||
|
Count int `json:"count,omitempty"`
|
||||||
|
Total int64 `json:"total,omitempty"`
|
||||||
|
Page int `json:"page,omitempty"`
|
||||||
|
Limit int `json:"limit,omitempty"`
|
||||||
|
Orders []PurchaseOrder `json:"orders,omitempty"`
|
||||||
|
Order *PurchaseOrder `json:"order,omitempty"`
|
||||||
|
Counts map[string]int64 `json:"counts,omitempty"`
|
||||||
|
Filters map[string]interface{} `json:"filters,omitempty"`
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type ScheduleCalendar struct {
|
type ScheduleCalendar struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@@ -62,12 +140,21 @@ type ScheduleProvider interface {
|
|||||||
QuerySchedules(ctx context.Context, query ScheduleQuery) ([]ScheduleEvent, error)
|
QuerySchedules(ctx context.Context, query ScheduleQuery) ([]ScheduleEvent, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PurchaseProvider interface {
|
||||||
|
QueryPurchases(ctx context.Context, query PurchaseQuery) (*PurchaseQueryResult, error)
|
||||||
|
}
|
||||||
|
|
||||||
var registeredScheduleProvider ScheduleProvider = nil
|
var registeredScheduleProvider ScheduleProvider = nil
|
||||||
|
var registeredPurchaseProvider PurchaseProvider = nil
|
||||||
|
|
||||||
func RegisterScheduleProvider(provider ScheduleProvider) {
|
func RegisterScheduleProvider(provider ScheduleProvider) {
|
||||||
registeredScheduleProvider = provider
|
registeredScheduleProvider = provider
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RegisterPurchaseProvider(provider PurchaseProvider) {
|
||||||
|
registeredPurchaseProvider = provider
|
||||||
|
}
|
||||||
|
|
||||||
func opsAIAssistantScheduleQuerySchema() FunctionToolSchema {
|
func opsAIAssistantScheduleQuerySchema() FunctionToolSchema {
|
||||||
return FunctionToolSchema{
|
return FunctionToolSchema{
|
||||||
Name: "ops_ai_assistant_schedule_query",
|
Name: "ops_ai_assistant_schedule_query",
|
||||||
@@ -109,6 +196,31 @@ func opsAIAssistantCurrentUserSchema() FunctionToolSchema {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func opsAIAssistantPurchaseQuerySchema() FunctionToolSchema {
|
||||||
|
return FunctionToolSchema{
|
||||||
|
Name: "ops_ai_assistant_purchase_query",
|
||||||
|
Description: "只读工具:查询采购模块订单。用户询问采购订单、采购状态、待处理/已下单/已到达/已收件/丢件/退件数量或列表、指定采购订单详情时调用。禁止新增、修改、删除订单或状态。",
|
||||||
|
Parameters: map[string]interface{}{
|
||||||
|
"type": "object",
|
||||||
|
"properties": map[string]interface{}{
|
||||||
|
"action": map[string]interface{}{
|
||||||
|
"type": "string",
|
||||||
|
"enum": []string{"list", "get", "count"},
|
||||||
|
"description": "list 查询订单列表;get 查询单个订单详情;count 统计各状态数量。",
|
||||||
|
},
|
||||||
|
"order_id": map[string]interface{}{"type": "integer", "description": "action=get 时的采购订单 ID。"},
|
||||||
|
"search": map[string]interface{}{"type": "string", "description": "按订单 ID、标题或备注搜索。"},
|
||||||
|
"status": map[string]interface{}{"type": "string", "enum": []string{"", "pending", "ordered", "arrived", "received", "lost", "returned"}, "description": "订单状态过滤:pending 待处理,ordered 已下单,arrived 已到达,received 已收件,lost 丢件,returned 退件。"},
|
||||||
|
"start_date": map[string]interface{}{"type": "string", "description": "可选创建日期开始,格式 YYYY-MM-DD。"},
|
||||||
|
"end_date": map[string]interface{}{"type": "string", "description": "可选创建日期结束,格式 YYYY-MM-DD。"},
|
||||||
|
"page": map[string]interface{}{"type": "integer", "description": "分页页码,默认 1。"},
|
||||||
|
"limit": map[string]interface{}{"type": "integer", "description": "返回上限,默认 20,最大 100。"},
|
||||||
|
},
|
||||||
|
"required": []string{"action"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func executeOpsAIAssistantCurrentUser(ctx context.Context, runtime FunctionToolRuntime, rawArgs []byte) ([]byte, error) {
|
func executeOpsAIAssistantCurrentUser(ctx context.Context, runtime FunctionToolRuntime, rawArgs []byte) ([]byte, error) {
|
||||||
var args CurrentUserArgs
|
var args CurrentUserArgs
|
||||||
if len(rawArgs) > 0 {
|
if len(rawArgs) > 0 {
|
||||||
@@ -144,6 +256,78 @@ func executeOpsAIAssistantCurrentUser(ctx context.Context, runtime FunctionToolR
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func executeOpsAIAssistantPurchaseQuery(ctx context.Context, runtime FunctionToolRuntime, rawArgs []byte) ([]byte, error) {
|
||||||
|
var args PurchaseQueryArgs
|
||||||
|
if err := json.Unmarshal(rawArgs, &args); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if args.Action != "list" && args.Action != "get" && args.Action != "count" {
|
||||||
|
return json.Marshal(map[string]interface{}{
|
||||||
|
"ok": false,
|
||||||
|
"error": "ops_ai_assistant_purchase_query 是只读工具,仅允许 list/get/count 查询操作",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if runtime.UserID <= 0 {
|
||||||
|
return json.Marshal(map[string]interface{}{
|
||||||
|
"ok": true,
|
||||||
|
"action": args.Action,
|
||||||
|
"loggedIn": false,
|
||||||
|
"message": "需要登录才能查询采购模块信息。",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if args.Action == "get" && args.OrderID <= 0 {
|
||||||
|
return nil, fmt.Errorf("order_id is required when action=get")
|
||||||
|
}
|
||||||
|
if args.StartDate != "" {
|
||||||
|
if _, err := time.Parse("2006-01-02", args.StartDate); err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid start_date: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if args.EndDate != "" {
|
||||||
|
if _, err := time.Parse("2006-01-02", args.EndDate); err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid end_date: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if args.StartDate != "" && args.EndDate != "" {
|
||||||
|
startDate, _ := time.Parse("2006-01-02", args.StartDate)
|
||||||
|
endDate, _ := time.Parse("2006-01-02", args.EndDate)
|
||||||
|
if endDate.Before(startDate) {
|
||||||
|
return nil, fmt.Errorf("end_date must be after start_date")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if args.Page <= 0 {
|
||||||
|
args.Page = 1
|
||||||
|
}
|
||||||
|
if args.Limit <= 0 {
|
||||||
|
args.Limit = 20
|
||||||
|
}
|
||||||
|
if args.Limit > 100 {
|
||||||
|
args.Limit = 100
|
||||||
|
}
|
||||||
|
if registeredPurchaseProvider == nil {
|
||||||
|
return json.Marshal(map[string]interface{}{
|
||||||
|
"ok": false,
|
||||||
|
"error": "采购查询服务未注册",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := registeredPurchaseProvider.QueryPurchases(ctx, PurchaseQuery{
|
||||||
|
Action: args.Action,
|
||||||
|
OrderID: args.OrderID,
|
||||||
|
Search: args.Search,
|
||||||
|
Status: args.Status,
|
||||||
|
StartDate: args.StartDate,
|
||||||
|
EndDate: args.EndDate,
|
||||||
|
Page: args.Page,
|
||||||
|
Limit: args.Limit,
|
||||||
|
UserID: runtime.UserID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return json.Marshal(result)
|
||||||
|
}
|
||||||
|
|
||||||
func executeOpsAIAssistantScheduleQuery(ctx context.Context, runtime FunctionToolRuntime, rawArgs []byte) ([]byte, error) {
|
func executeOpsAIAssistantScheduleQuery(ctx context.Context, runtime FunctionToolRuntime, rawArgs []byte) ([]byte, error) {
|
||||||
var args ScheduleQueryArgs
|
var args ScheduleQueryArgs
|
||||||
if err := json.Unmarshal(rawArgs, &args); err != nil {
|
if err := json.Unmarshal(rawArgs, &args); err != nil {
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ func ConfigAllInit() error {
|
|||||||
Tools: []ConfigsAIChatTool_{
|
Tools: []ConfigsAIChatTool_{
|
||||||
{Name: "time", Enabled: true, Description: "解析当前时间、相对日期和日期范围。"},
|
{Name: "time", Enabled: true, Description: "解析当前时间、相对日期和日期范围。"},
|
||||||
{Name: "ops_ai_assistant_current_user", Enabled: true, Description: "返回当前登录用户信息;未登录时提示需要登录才能获取信息。"},
|
{Name: "ops_ai_assistant_current_user", Enabled: true, Description: "返回当前登录用户信息;未登录时提示需要登录才能获取信息。"},
|
||||||
|
{Name: "ops_ai_assistant_purchase_query", Enabled: true, Description: "查询采购订单列表、详情和状态数量统计。"},
|
||||||
{Name: "ops_ai_assistant_schedule_query", Enabled: true, Description: "按日期范围查询当前用户可见的 OPS 日历/日程。"},
|
{Name: "ops_ai_assistant_schedule_query", Enabled: true, Description: "按日期范围查询当前用户可见的 OPS 日历/日程。"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -319,7 +319,7 @@ func handleChat(ctx *gin.Context) {
|
|||||||
toolNames = append(toolNames, tool.Function.Name)
|
toolNames = append(toolNames, tool.Function.Name)
|
||||||
}
|
}
|
||||||
emitTrace("function_tools", "prepare", "success", "已启用 Function Calling 工具", map[string]interface{}{"tools": toolNames})
|
emitTrace("function_tools", "prepare", "success", "已启用 Function Calling 工具", map[string]interface{}{"tools": toolNames})
|
||||||
openaiMsgs = append([]openaiMessage{{Role: "system", Content: "可用工具使用规则:当用户询问“我是谁”“当前登录用户是谁”“我的用户信息”等当前身份问题时,调用 ops_ai_assistant_current_user;工具返回 loggedIn=true 时按工具结果回答当前用户信息,返回 loggedIn=false 时说明不知道并提示需要登录才能获取信息。当用户询问本月、今天、本周、下周等相对日期的日程时,先调用 time 获取明确 start_date/end_date,再调用 ops_ai_assistant_schedule_query 查询日程。不要臆造工具结果中不存在的信息。"}}, openaiMsgs...)
|
openaiMsgs = append([]openaiMessage{{Role: "system", Content: "可用工具使用规则:当用户询问“我是谁”“当前登录用户是谁”“我的用户信息”等当前身份问题时,调用 ops_ai_assistant_current_user;工具返回 loggedIn=true 时按工具结果回答当前用户信息,返回 loggedIn=false 时说明不知道并提示需要登录才能获取信息。当用户询问采购订单列表、采购订单详情、采购状态或数量统计时,调用 ops_ai_assistant_purchase_query;该工具只允许查询,禁止新增、修改、删除采购数据。当用户询问本月、今天、本周、下周等相对日期的日程时,先调用 time 获取明确 start_date/end_date,再调用 ops_ai_assistant_schedule_query 查询日程。不要臆造工具结果中不存在的信息。"}}, openaiMsgs...)
|
||||||
var toolExecuted bool
|
var toolExecuted bool
|
||||||
openaiMsgs, toolExecuted, err = runOpenAIToolLoop(ctx.Request.Context(), profile, openaiMsgs, functionTools, currentUser, tracker, emitTrace)
|
openaiMsgs, toolExecuted, err = runOpenAIToolLoop(ctx.Request.Context(), profile, openaiMsgs, functionTools, currentUser, tracker, emitTrace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ func ensureBuiltinAIChatTools() error {
|
|||||||
builtins := []TabAIChatTool{
|
builtins := []TabAIChatTool{
|
||||||
{Name: "time", Enabled: true, Description: "解析当前时间、相对日期和日期范围。", SortOrder: 0},
|
{Name: "time", Enabled: true, Description: "解析当前时间、相对日期和日期范围。", SortOrder: 0},
|
||||||
{Name: "ops_ai_assistant_current_user", Enabled: true, Description: "返回当前登录用户信息;未登录时提示需要登录才能获取信息。", SortOrder: 5},
|
{Name: "ops_ai_assistant_current_user", Enabled: true, Description: "返回当前登录用户信息;未登录时提示需要登录才能获取信息。", SortOrder: 5},
|
||||||
|
{Name: "ops_ai_assistant_purchase_query", Enabled: true, Description: "查询采购订单列表、详情和状态数量统计。", SortOrder: 8},
|
||||||
{Name: "ops_ai_assistant_schedule_query", Enabled: true, Description: "按日期范围查询当前用户可见的 OPS 日历/日程。", SortOrder: 10},
|
{Name: "ops_ai_assistant_schedule_query", Enabled: true, Description: "按日期范围查询当前用户可见的 OPS 日历/日程。", SortOrder: 10},
|
||||||
}
|
}
|
||||||
for _, builtin := range builtins {
|
for _, builtin := range builtins {
|
||||||
@@ -206,6 +207,7 @@ func seedAIChatConfigFromYAMLIfEmpty() error {
|
|||||||
tools = []models.ConfigsAIChatTool_{
|
tools = []models.ConfigsAIChatTool_{
|
||||||
{Name: "time", Enabled: true, Description: "解析当前时间、相对日期和日期范围。"},
|
{Name: "time", Enabled: true, Description: "解析当前时间、相对日期和日期范围。"},
|
||||||
{Name: "ops_ai_assistant_current_user", Enabled: true, Description: "返回当前登录用户信息;未登录时提示需要登录才能获取信息。"},
|
{Name: "ops_ai_assistant_current_user", Enabled: true, Description: "返回当前登录用户信息;未登录时提示需要登录才能获取信息。"},
|
||||||
|
{Name: "ops_ai_assistant_purchase_query", Enabled: true, Description: "查询采购订单列表、详情和状态数量统计。"},
|
||||||
{Name: "ops_ai_assistant_schedule_query", Enabled: true, Description: "按日期范围查询当前用户可见的 OPS 日历/日程。"},
|
{Name: "ops_ai_assistant_schedule_query", Enabled: true, Description: "按日期范围查询当前用户可见的 OPS 日历/日程。"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package routers
|
package routers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
parsefmt "fmt"
|
parsefmt "fmt"
|
||||||
|
"ops/agents"
|
||||||
"ops/models"
|
"ops/models"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -119,6 +121,269 @@ type TabPurchaseLog struct {
|
|||||||
CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime;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() {
|
func ApiPurchaseInit() {
|
||||||
|
|
||||||
models.DB.AutoMigrate(&TabPurchaseOrder{})
|
models.DB.AutoMigrate(&TabPurchaseOrder{})
|
||||||
@@ -136,6 +401,7 @@ func ApiPurchaseInit() {
|
|||||||
models.DB.Create(&purchaseUserGroup)
|
models.DB.Create(&purchaseUserGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
agents.RegisterPurchaseProvider(purchaseProvider{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func ApiPurchase(r *gin.RouterGroup) {
|
func ApiPurchase(r *gin.RouterGroup) {
|
||||||
|
|||||||
Reference in New Issue
Block a user