Artifact b08e1014dd30f913aa079ab3d01e8d2087456cb51d8f341dc57ec9c193a8aa00:
- File
shared/ql361api/src/ql361api/goods.gleam
— part of check-in
[5bb18ed4d4]
at
2026-10-23 03:34:10
on branch trunk
— 把来肯对接和本地库落到可切换的后端。
账号从环境读取,商品由人工增量同步,订单按五分钟轮询并缓存登录会话。开发期用 SQLite,同一套结构可切到 PostgreSQL。 (user: pluto size: 3656)
import gleam/dynamic/decode import gleam/json import gleam/result import ql361api/client.{type Client} import ql361api/json_codec import ql361api/models.{type ApiError, type GoodsItem, type Page, GoodsItem, JsonError, Page} pub const list_goods_method = "cc.erp.bll.bas.baseinfomanager.getlist" pub const page_size = 100 pub fn fetch_goods_page( client client: Client, page page: Int, page_size page_size: Int, modified_from modified_from: String, modified_to modified_to: String, ) -> Result(Page(GoodsItem), ApiError) { let condition = [ #("bastype", json.string("goods")), #("showtree", json.bool(False)), #("showline", json.bool(True)), #("showstop", json.bool(True)), #("showstopstatus", json.int(2)), #("keywords", json.string("")), #("pnode", json.string("00000")), #("root", json.string("00000")), #("level", json.int(0)), #("isusecoupon", json.int(-1)), #("isstandard", json.int(-1)), #("goodsclass", json.int(-1)), #("goodstype", json.int(-1)), ] let condition = case modified_from { "" -> condition from -> [ #("modifytimebegin", json.string(from)), #("modifytimeend", json.string(modified_to)), ..condition ] } let payload = json.object([ #( "reportparam", json.object([ #("pagerow", json.int(page_size)), #("pagenumber", json.int(page)), #("parid", json.int(0)), #("displaytype", json.string("list")), #("orders", json.string("")), ]), ), #("conditionparam", json.object(condition)), ]) |> json.to_string use raw_json <- result.try( client.post_rpc(client, list_goods_method, "erp", payload), ) parse_goods_body(raw_json) } pub fn parse_goods_body(body: String) -> Result(Page(GoodsItem), ApiError) { let item_decoder = { use id <- decode.field("gid", json_codec.int()) use code <- decode.optional_field("code", "", decode.string) use name <- decode.optional_field("fullname", "", decode.string) use barcode <- decode.optional_field("commonunitbarcode", "", decode.string) use unit <- decode.optional_field("ufullname", "", decode.string) use spec <- decode.optional_field("spec", "", decode.string) use shelf_life <- decode.optional_field("strshelflife", "", decode.string) use shelf_life_days <- decode.optional_field("shelflife", 0, json_codec.int()) use position_code <- decode.optional_field("gpcode", "", decode.string) use flag <- decode.optional_field("flag", 0, json_codec.int()) use is_parent <- decode.optional_field("isparent", 0, json_codec.int()) use modified_at <- decode.optional_field("modifytime", "", decode.string) use created_at <- decode.optional_field("createtime", "", decode.string) decode.success(GoodsItem( id: id, code: code, name: name, barcode: barcode, unit: unit, spec: spec, shelf_life: shelf_life, shelf_life_days: shelf_life_days, position_code: position_code, disabled: flag != 0, is_category: is_parent != 0, modified_at: modified_at, created_at: created_at, )) } let data_decoder = { use items <- decode.field("datasource", decode.list(of: item_decoder)) use total <- decode.optional_field("rowcount", 0, json_codec.int()) decode.success(Page(items: items, total: total)) } let wrapper_decoder = { use data <- decode.field("data", data_decoder) decode.success(data) } case json.parse(body, wrapper_decoder) { Ok(page) -> Ok(page) Error(_) -> Error(JsonError("商品列表响应无法解析")) } }