package shunyunbao import ( "errors" "net/http" "net/url" "strconv" "strings" "time" "unicode/utf8" ) const ( CaptchaPath = "/api/p/code1" LoginPath = "/am/auth/login" UserPath = "/am/user/get" StockListTotalPath = "/am/stock/listTotal" StockListPath = "/am/stock/list" StockDetailPath = "/am/stock/detail/listByStock" maxPageSize = 500 maxDetailIDs = 100 maxOrderNumber = 128 maximumDateSpan = 6 ) var errInvalidProtocolInput = errors.New("invalid shunyunbao protocol input") type Column struct { TableName string `json:"tableName"` ColumnName string `json:"colName"` FieldName string `json:"fieldName"` HasAlias int `json:"hasAlias"` TableAlias string `json:"tableAlias"` } type QueryCondition struct { Value string `json:"dvalue"` TableName string `json:"tableName"` ColumnName string `json:"colName"` Operator int `json:"op"` Type int `json:"type"` TableAlias string `json:"tableAlias"` OptionType int `json:"optType"` } var stockColumns = []Column{ {"t_stock", "created", "created", 0, "t"}, {"t_stock", "order_code", "orderCode", 0, "t"}, {"t_stock", "printer", "printer", 0, "t"}, {"t_stock", "weight_time", "weightTime", 0, "t"}, {"t_stock", "weight_inputer", "weightInputer", 0, "t"}, {"t_stock", "pkg_time", "pkgTime", 0, "t"}, {"t_stock", "code", "code", 0, "t"}, {"t_stock", "status", "status", 0, "t"}, {"t_stock", "order_status", "orderStatus", 0, "t"}, {"t_stock", "purchase_status", "purchaseStatus", 0, "t"}, {"t_stock", "exp_code", "expCode", 0, "t"}, {"t_stock", "exp_page_code", "expPageCode", 0, "t"}, {"t_stock", "exp_allow_print", "expAllowPrint", 0, "t"}, {"t_stock", "exp_page_status", "expPageStatus", 0, "t"}, {"t_stock", "page_id", "pageId", 0, "t"}, {"t_stock", "upload_time", "uploadTime", 0, "t"}, {"t_stock", "pay_time", "payTime", 0, "t"}, {"t_stock", "shop_day_to_ship", "shopDayToShip", 0, "t"}, {"t_stock", "remark9", "tsremark9", 1, "t"}, {"t_stock", "remark9", "remark9", 0, "t"}, {"t_stock", "detail_qty", "detailQty", 0, "t"}, {"t_stock", "order_qty", "orderQty", 0, "t"}, {"t_stock_detail", "inner_exp_code", "innerExpCode", 0, "t7"}, {"t_stock_detail", "shelf_code", "shelfCode", 0, "t7"}, {"t_stock", "shelf_code", "tsshelfCode", 1, "t"}, {"t_stock", "store_type", "storeType", 0, "t"}, {"t_stock", "order_bag_code", "orderBagCode", 0, "t"}, {"t_stock", "weight_cust_pkg", "weightCustPkg", 0, "t"}, {"t_stock", "weight_consign", "weightConsign", 0, "t"}, {"t_stock", "amt_order", "amtOrder", 0, "t"}, {"t_stock_append", "offline_amount", "offlineAmount", 0, "t8"}, {"t_stock_append", "escrow_amount", "escrowAmount", 0, "t8"}, {"t_stock", "exp_cod", "expCod", 0, "t"}, {"t_stock", "exp_company", "expCompany", 0, "t"}, {"t_stock", "transport", "transport", 0, "t"}, {"t_stock", "order_origin", "orderOrigin", 0, "t"}, {"t_stock", "exp_out_type", "expOutType", 0, "t"}, {"t_stock", "order_platform", "orderPlatform", 0, "t"}, {"t_stock", "exp_pkg_type", "expPkgType", 0, "t"}, {"t_stock", "exp_pkg_code", "expPkgCode", 0, "t"}, {"t_stock", "exp_batch", "expBatch", 0, "t"}, {"t_stock", "exp_ti_huo", "expTiHuo", 0, "t"}, {"t_stock", "print_time", "printTime", 0, "t"}, {"t_stock", "receiver", "receiver", 0, "t"}, {"t_stock", "receiver_tel", "receiverTel", 0, "t"}, {"t_stock", "receiver_addr", "receiverAddr", 0, "t"}, {"t_stock", "receiver_shop_name", "receiverShopName", 0, "t"}, {"t_stock", "receiver_shop_code", "receiverShopCode", 0, "t"}, {"t_stock", "product_name", "productName", 0, "t"}, {"t_stock", "is_cancel", "isCancel", 0, "t"}, {"t_stock", "err_msg", "errMsg", 0, "t"}, {"t_stock", "remark2", "remark2", 0, "t"}, {"t_stock", "remark1", "remark1", 0, "t"}, {"t_stock", "note", "note", 0, "t"}, {"t_store", "name", "name", 0, "t1"}, {"sys_user", "fullname", "sufullname", 1, "t3"}, {"sys_user", "dept_label_path", "deptLabelPath", 0, "t3"}, {"t_stock", "shop_name", "shopName", 0, "t"}, {"t_shop", "shop_id", "shopId", 0, "t6"}, {"t_stock", "remark4", "remark4", 0, "t"}, {"t_stock", "remark7", "remark7", 0, "t"}, {"t_stock", "package_time", "packageTime", 0, "t"}, {"t_stock", "packer", "packer", 0, "t"}, {"t_stock", "pack_type", "packType", 0, "t"}, {"t_stock", "track_status", "trackStatus", 0, "t"}, {"t_stock", "track_desc", "trackDesc", 0, "t"}, {"t_stock", "err_status", "errStatus", 0, "t"}, {"t_stock", "err_time", "errTime", 0, "t"}, {"t_stock", "err_msg", "tserrMsg", 1, "t"}, {"t_stock", "remark2", "tsremark2", 1, "t"}, {"t_stock", "remark1", "tsremark1", 1, "t"}, {"t_stock", "product_volume_str", "productVolumeStr", 0, "t"}, } func RequestHeaders(baseURL string) (http.Header, error) { origin, err := originFor(baseURL) if err != nil { return nil, errInvalidProtocolInput } return http.Header{ "Accept": {"application/json, text/plain, */*"}, "Origin": {origin}, "Referer": {origin + "/sys/login"}, "User-Agent": {"shunyunbaoerp-client/0.1"}, "X-Requested-With": {"XMLHttpRequest"}, }, nil } func StockColumns() []Column { return append([]Column(nil), stockColumns...) } func OrderNumberQuery(orderNumber string) (QueryCondition, error) { orderNumber = strings.TrimSpace(orderNumber) if orderNumber == "" || len([]byte(orderNumber)) > maxOrderNumber || !utf8.ValidString(orderNumber) || hasControl(orderNumber) { return QueryCondition{}, errInvalidProtocolInput } return QueryCondition{ Value: orderNumber, TableName: "t_stock", ColumnName: "allcode", Operator: 6, Type: 0, TableAlias: "t", OptionType: 1, }, nil } func CreatedRangeQuery(createdFrom, createdTo string) (QueryCondition, error) { from, err := time.Parse(time.DateOnly, createdFrom) if err != nil || from.Format(time.DateOnly) != createdFrom { return QueryCondition{}, errInvalidProtocolInput } to, err := time.Parse(time.DateOnly, createdTo) if err != nil || to.Format(time.DateOnly) != createdTo || to.Before(from) || int(to.Sub(from)/(24*time.Hour)) > maximumDateSpan { return QueryCondition{}, errInvalidProtocolInput } return QueryCondition{ Value: createdFrom + "," + createdTo, TableName: "t_stock", ColumnName: "created", Operator: 0, Type: 3, TableAlias: "t", OptionType: 0, }, nil } func StockListPayload( query QueryCondition, start, pageIndex, pageSize int, ) (map[string]any, error) { if start < 0 || pageIndex < 1 || pageSize < 1 || pageSize > maxPageSize { return nil, errInvalidProtocolInput } return map[string]any{ "history": 0, "length": pageSize, "start": start, "pageTotal": 0, "pageIndex": pageIndex, "store": false, "columns": StockColumns(), "queries": []QueryCondition{query}, }, nil } func StockDetailPayload(externalStockIDs []string) (map[string]any, error) { if len(externalStockIDs) == 0 || len(externalStockIDs) > maxDetailIDs { return nil, errInvalidProtocolInput } ids := make([]uint64, 0, len(externalStockIDs)) seen := make(map[uint64]struct{}, len(externalStockIDs)) for _, value := range externalStockIDs { parsed, err := strconv.ParseUint(strings.TrimSpace(value), 10, 64) if err != nil || parsed == 0 { return nil, errInvalidProtocolInput } if _, exists := seen[parsed]; exists { continue } seen[parsed] = struct{}{} ids = append(ids, parsed) } if len(ids) == 0 { return nil, errInvalidProtocolInput } return map[string]any{"ids": ids}, nil } func originFor(baseURL string) (string, error) { parsed, err := url.Parse(strings.TrimSpace(baseURL)) if err != nil || (parsed.Scheme != "http" && parsed.Scheme != "https") || parsed.Host == "" || parsed.User != nil || parsed.RawQuery != "" || parsed.Fragment != "" || (parsed.Path != "" && parsed.Path != "/") { return "", errInvalidProtocolInput } return parsed.Scheme + "://" + parsed.Host, nil } func hasControl(value string) bool { for _, character := range value { if character < 32 || character == 127 { return true } } return false }