fix: datetime->timestamp for audit fields + fix sor.U/sor.I misuse in core.py and API dspy
- All 7 models: created_at/updated_at changed from datetime to timestamp type (DDL template auto-generates DEFAULT CURRENT_TIMESTAMP for timestamp type) - core.py: fix all sor.U() calls passing 3 args (id must be in data dict) - core.py: fix sor.I() misuse for INSERT (should be sor.C()) - API dspy updates: fix sor.U() 3-arg bug in category/product/type_config/resource/subscription/supplier - product_resource_supplier_update.dspy: add missing updated_at field
This commit is contained in:
parent
26d374919c
commit
24605f88e8
@ -121,13 +121,13 @@
|
||||
{
|
||||
"name": "created_at",
|
||||
"title": "创建时间",
|
||||
"type": "datetime",
|
||||
"type": "timestamp",
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "updated_at",
|
||||
"title": "更新时间",
|
||||
"type": "datetime",
|
||||
"type": "timestamp",
|
||||
"nullable": "no"
|
||||
}
|
||||
],
|
||||
@ -202,4 +202,4 @@
|
||||
"cond": "parentid='product_type'"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,13 +90,13 @@
|
||||
{
|
||||
"name": "created_at",
|
||||
"title": "创建时间",
|
||||
"type": "datetime",
|
||||
"type": "timestamp",
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "updated_at",
|
||||
"title": "更新时间",
|
||||
"type": "datetime",
|
||||
"type": "timestamp",
|
||||
"nullable": "no"
|
||||
}
|
||||
],
|
||||
@ -156,4 +156,4 @@
|
||||
"cond": "parentid='product_type'"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,28 +3,115 @@
|
||||
{
|
||||
"name": "product_resource",
|
||||
"title": "产品资源绑定表",
|
||||
"primary": ["id"],
|
||||
"primary": [
|
||||
"id"
|
||||
],
|
||||
"catelog": "relation"
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{"name": "id", "title": "主键ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "product_id", "title": "产品ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "resource_type", "title": "资源类型", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "resource_ref_id", "title": "资源引用ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "resource_ref_name", "title": "资源显示名", "type": "str", "length": 255},
|
||||
{"name": "quota", "title": "配额量", "type": "double", "length": 15, "dec": 4, "default": "0"},
|
||||
{"name": "quota_unit", "title": "配额单位", "type": "str", "length": 32},
|
||||
{"name": "priority", "title": "优先级", "type": "int", "default": "1"},
|
||||
{"name": "overflow_product_id", "title": "超额后转用产品ID", "type": "str", "length": 32},
|
||||
{"name": "status", "title": "状态", "type": "char", "length": 1, "default": "1"},
|
||||
{"name": "created_at", "title": "创建时间", "type": "datetime", "nullable": "no"},
|
||||
{"name": "updated_at", "title": "更新时间", "type": "datetime"}
|
||||
{
|
||||
"name": "id",
|
||||
"title": "主键ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "product_id",
|
||||
"title": "产品ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "resource_type",
|
||||
"title": "资源类型",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "resource_ref_id",
|
||||
"title": "资源引用ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "resource_ref_name",
|
||||
"title": "资源显示名",
|
||||
"type": "str",
|
||||
"length": 255
|
||||
},
|
||||
{
|
||||
"name": "quota",
|
||||
"title": "配额量",
|
||||
"type": "double",
|
||||
"length": 15,
|
||||
"dec": 4,
|
||||
"default": "0"
|
||||
},
|
||||
{
|
||||
"name": "quota_unit",
|
||||
"title": "配额单位",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "priority",
|
||||
"title": "优先级",
|
||||
"type": "int",
|
||||
"default": "1"
|
||||
},
|
||||
{
|
||||
"name": "overflow_product_id",
|
||||
"title": "超额后转用产品ID",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"title": "状态",
|
||||
"type": "char",
|
||||
"length": 1,
|
||||
"default": "1"
|
||||
},
|
||||
{
|
||||
"name": "created_at",
|
||||
"title": "创建时间",
|
||||
"type": "timestamp",
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "updated_at",
|
||||
"title": "更新时间",
|
||||
"type": "datetime"
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_pr_product", "idxtype": "index", "idxfields": ["product_id"]},
|
||||
{"name": "idx_pr_resource", "idxtype": "index", "idxfields": ["resource_type", "resource_ref_id"]},
|
||||
{"name": "idx_pr_status", "idxtype": "index", "idxfields": ["status"]}
|
||||
{
|
||||
"name": "idx_pr_product",
|
||||
"idxtype": "index",
|
||||
"idxfields": [
|
||||
"product_id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "idx_pr_resource",
|
||||
"idxtype": "index",
|
||||
"idxfields": [
|
||||
"resource_type",
|
||||
"resource_ref_id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "idx_pr_status",
|
||||
"idxtype": "index",
|
||||
"idxfields": [
|
||||
"status"
|
||||
]
|
||||
}
|
||||
],
|
||||
"codes": [
|
||||
{
|
||||
|
||||
@ -3,23 +3,83 @@
|
||||
{
|
||||
"name": "product_resource_supplier",
|
||||
"title": "产品资源供应商关联表",
|
||||
"primary": ["id"],
|
||||
"primary": [
|
||||
"id"
|
||||
],
|
||||
"catelog": "relation"
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{"name": "id", "title": "主键ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "product_resource_id", "title": "产品资源绑定ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "supplier_org_id", "title": "供应商机构ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "priority", "title": "优先级", "type": "int", "default": "1"},
|
||||
{"name": "weight", "title": "权重", "type": "int", "default": "100"},
|
||||
{"name": "status", "title": "状态", "type": "char", "length": 1, "default": "1"},
|
||||
{"name": "created_at", "title": "创建时间", "type": "datetime", "nullable": "no"}
|
||||
{
|
||||
"name": "id",
|
||||
"title": "主键ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "product_resource_id",
|
||||
"title": "产品资源绑定ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "supplier_org_id",
|
||||
"title": "供应商机构ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "priority",
|
||||
"title": "优先级",
|
||||
"type": "int",
|
||||
"default": "1"
|
||||
},
|
||||
{
|
||||
"name": "weight",
|
||||
"title": "权重",
|
||||
"type": "int",
|
||||
"default": "100"
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"title": "状态",
|
||||
"type": "char",
|
||||
"length": 1,
|
||||
"default": "1"
|
||||
},
|
||||
{
|
||||
"name": "created_at",
|
||||
"title": "创建时间",
|
||||
"type": "timestamp",
|
||||
"nullable": "no"
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_prs_resource", "idxtype": "index", "idxfields": ["product_resource_id"]},
|
||||
{"name": "idx_prs_supplier", "idxtype": "index", "idxfields": ["supplier_org_id"]},
|
||||
{"name": "idx_prs_unique", "idxtype": "unique", "idxfields": ["product_resource_id", "supplier_org_id"]}
|
||||
{
|
||||
"name": "idx_prs_resource",
|
||||
"idxtype": "index",
|
||||
"idxfields": [
|
||||
"product_resource_id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "idx_prs_supplier",
|
||||
"idxtype": "index",
|
||||
"idxfields": [
|
||||
"supplier_org_id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "idx_prs_unique",
|
||||
"idxtype": "unique",
|
||||
"idxfields": [
|
||||
"product_resource_id",
|
||||
"supplier_org_id"
|
||||
]
|
||||
}
|
||||
],
|
||||
"codes": [
|
||||
{
|
||||
|
||||
@ -3,34 +3,163 @@
|
||||
{
|
||||
"name": "product_subscription",
|
||||
"title": "客户订购表",
|
||||
"primary": ["id"],
|
||||
"primary": [
|
||||
"id"
|
||||
],
|
||||
"catelog": "relation"
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{"name": "id", "title": "主键ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "product_id", "title": "产品ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "user_id", "title": "客户用户ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "user_org_id", "title": "客户机构ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "subscription_type", "title": "订购类型", "type": "char", "length": 1, "nullable": "no"},
|
||||
{"name": "status", "title": "状态", "type": "char", "length": 1, "nullable": "no", "default": "1"},
|
||||
{"name": "start_date", "title": "生效日期", "type": "date", "nullable": "no"},
|
||||
{"name": "end_date", "title": "到期日期", "type": "date", "nullable": "no"},
|
||||
{"name": "quota_total", "title": "总配额", "type": "double", "length": 15, "dec": 4, "default": "0"},
|
||||
{"name": "quota_used", "title": "已使用量", "type": "double", "length": 15, "dec": 4, "default": "0"},
|
||||
{"name": "quota_unit", "title": "配额单位", "type": "str", "length": 32},
|
||||
{"name": "overflow_mode", "title": "超额模式", "type": "char", "length": 1, "default": "1"},
|
||||
{"name": "overflow_rate", "title": "超额单价", "type": "double", "length": 15, "dec": 6, "default": "0"},
|
||||
{"name": "purchase_price", "title": "购买价格", "type": "double", "length": 15, "dec": 2, "default": "0"},
|
||||
{"name": "purchase_currency", "title": "货币", "type": "char", "length": 8, "default": "CNY"},
|
||||
{"name": "created_at", "title": "创建时间", "type": "datetime", "nullable": "no"},
|
||||
{"name": "updated_at", "title": "更新时间", "type": "datetime"}
|
||||
{
|
||||
"name": "id",
|
||||
"title": "主键ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "product_id",
|
||||
"title": "产品ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "user_id",
|
||||
"title": "客户用户ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "user_org_id",
|
||||
"title": "客户机构ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "subscription_type",
|
||||
"title": "订购类型",
|
||||
"type": "char",
|
||||
"length": 1,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"title": "状态",
|
||||
"type": "char",
|
||||
"length": 1,
|
||||
"nullable": "no",
|
||||
"default": "1"
|
||||
},
|
||||
{
|
||||
"name": "start_date",
|
||||
"title": "生效日期",
|
||||
"type": "date",
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "end_date",
|
||||
"title": "到期日期",
|
||||
"type": "date",
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "quota_total",
|
||||
"title": "总配额",
|
||||
"type": "double",
|
||||
"length": 15,
|
||||
"dec": 4,
|
||||
"default": "0"
|
||||
},
|
||||
{
|
||||
"name": "quota_used",
|
||||
"title": "已使用量",
|
||||
"type": "double",
|
||||
"length": 15,
|
||||
"dec": 4,
|
||||
"default": "0"
|
||||
},
|
||||
{
|
||||
"name": "quota_unit",
|
||||
"title": "配额单位",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "overflow_mode",
|
||||
"title": "超额模式",
|
||||
"type": "char",
|
||||
"length": 1,
|
||||
"default": "1"
|
||||
},
|
||||
{
|
||||
"name": "overflow_rate",
|
||||
"title": "超额单价",
|
||||
"type": "double",
|
||||
"length": 15,
|
||||
"dec": 6,
|
||||
"default": "0"
|
||||
},
|
||||
{
|
||||
"name": "purchase_price",
|
||||
"title": "购买价格",
|
||||
"type": "double",
|
||||
"length": 15,
|
||||
"dec": 2,
|
||||
"default": "0"
|
||||
},
|
||||
{
|
||||
"name": "purchase_currency",
|
||||
"title": "货币",
|
||||
"type": "char",
|
||||
"length": 8,
|
||||
"default": "CNY"
|
||||
},
|
||||
{
|
||||
"name": "created_at",
|
||||
"title": "创建时间",
|
||||
"type": "timestamp",
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "updated_at",
|
||||
"title": "更新时间",
|
||||
"type": "datetime"
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_ps_product", "idxtype": "index", "idxfields": ["product_id"]},
|
||||
{"name": "idx_ps_user", "idxtype": "index", "idxfields": ["user_id", "user_org_id"]},
|
||||
{"name": "idx_ps_status", "idxtype": "index", "idxfields": ["status"]},
|
||||
{"name": "idx_ps_dates", "idxtype": "index", "idxfields": ["start_date", "end_date"]}
|
||||
{
|
||||
"name": "idx_ps_product",
|
||||
"idxtype": "index",
|
||||
"idxfields": [
|
||||
"product_id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "idx_ps_user",
|
||||
"idxtype": "index",
|
||||
"idxfields": [
|
||||
"user_id",
|
||||
"user_org_id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "idx_ps_status",
|
||||
"idxtype": "index",
|
||||
"idxfields": [
|
||||
"status"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "idx_ps_dates",
|
||||
"idxtype": "index",
|
||||
"idxfields": [
|
||||
"start_date",
|
||||
"end_date"
|
||||
]
|
||||
}
|
||||
],
|
||||
"codes": [
|
||||
{
|
||||
|
||||
@ -66,13 +66,13 @@
|
||||
{
|
||||
"name": "created_at",
|
||||
"title": "创建时间",
|
||||
"type": "datetime",
|
||||
"type": "timestamp",
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "updated_at",
|
||||
"title": "更新时间",
|
||||
"type": "datetime",
|
||||
"type": "timestamp",
|
||||
"nullable": "no"
|
||||
}
|
||||
],
|
||||
@ -119,4 +119,4 @@
|
||||
"cond": "parentid='enabled_flg'"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,38 +3,186 @@
|
||||
{
|
||||
"name": "product_usage_log",
|
||||
"title": "产品消费记录表",
|
||||
"primary": ["id"],
|
||||
"primary": [
|
||||
"id"
|
||||
],
|
||||
"catelog": "relation"
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{"name": "id", "title": "主键ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "product_id", "title": "产品ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "subscription_id", "title": "订购ID", "type": "str", "length": 32},
|
||||
{"name": "user_id", "title": "消费者用户ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "user_org_id", "title": "消费者机构ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "product_resource_id", "title": "产品资源绑定ID", "type": "str", "length": 32},
|
||||
{"name": "supplier_org_id", "title": "供应商机构ID", "type": "str", "length": 32},
|
||||
{"name": "resource_type", "title": "资源类型", "type": "str", "length": 32},
|
||||
{"name": "resource_ref_id", "title": "资源引用ID", "type": "str", "length": 32},
|
||||
{"name": "used_amount", "title": "消耗量", "type": "double", "length": 15, "dec": 4, "nullable": "no"},
|
||||
{"name": "used_unit", "title": "消耗单位", "type": "str", "length": 32},
|
||||
{"name": "unit_cost", "title": "单位成本", "type": "double", "length": 15, "dec": 8, "default": "0"},
|
||||
{"name": "total_cost", "title": "总成本", "type": "double", "length": 15, "dec": 6, "default": "0"},
|
||||
{"name": "sell_price", "title": "客户售价", "type": "double", "length": 15, "dec": 6, "default": "0"},
|
||||
{"name": "billing_mode", "title": "计费模式", "type": "char", "length": 1, "nullable": "no"},
|
||||
{"name": "source_ref_table", "title": "来源表", "type": "str", "length": 64},
|
||||
{"name": "source_ref_id", "title": "来源记录ID", "type": "str", "length": 32},
|
||||
{"name": "use_time", "title": "消费时间", "type": "datetime", "nullable": "no"},
|
||||
{"name": "created_at", "title": "创建时间", "type": "datetime", "nullable": "no"}
|
||||
{
|
||||
"name": "id",
|
||||
"title": "主键ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "product_id",
|
||||
"title": "产品ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "subscription_id",
|
||||
"title": "订购ID",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "user_id",
|
||||
"title": "消费者用户ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "user_org_id",
|
||||
"title": "消费者机构ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "product_resource_id",
|
||||
"title": "产品资源绑定ID",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "supplier_org_id",
|
||||
"title": "供应商机构ID",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "resource_type",
|
||||
"title": "资源类型",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "resource_ref_id",
|
||||
"title": "资源引用ID",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "used_amount",
|
||||
"title": "消耗量",
|
||||
"type": "double",
|
||||
"length": 15,
|
||||
"dec": 4,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "used_unit",
|
||||
"title": "消耗单位",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "unit_cost",
|
||||
"title": "单位成本",
|
||||
"type": "double",
|
||||
"length": 15,
|
||||
"dec": 8,
|
||||
"default": "0"
|
||||
},
|
||||
{
|
||||
"name": "total_cost",
|
||||
"title": "总成本",
|
||||
"type": "double",
|
||||
"length": 15,
|
||||
"dec": 6,
|
||||
"default": "0"
|
||||
},
|
||||
{
|
||||
"name": "sell_price",
|
||||
"title": "客户售价",
|
||||
"type": "double",
|
||||
"length": 15,
|
||||
"dec": 6,
|
||||
"default": "0"
|
||||
},
|
||||
{
|
||||
"name": "billing_mode",
|
||||
"title": "计费模式",
|
||||
"type": "char",
|
||||
"length": 1,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "source_ref_table",
|
||||
"title": "来源表",
|
||||
"type": "str",
|
||||
"length": 64
|
||||
},
|
||||
{
|
||||
"name": "source_ref_id",
|
||||
"title": "来源记录ID",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "use_time",
|
||||
"title": "消费时间",
|
||||
"type": "datetime",
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "created_at",
|
||||
"title": "创建时间",
|
||||
"type": "timestamp",
|
||||
"nullable": "no"
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_pul_product", "idxtype": "index", "idxfields": ["product_id"]},
|
||||
{"name": "idx_pul_subscription", "idxtype": "index", "idxfields": ["subscription_id"]},
|
||||
{"name": "idx_pul_user", "idxtype": "index", "idxfields": ["user_id", "user_org_id"]},
|
||||
{"name": "idx_pul_supplier", "idxtype": "index", "idxfields": ["supplier_org_id"]},
|
||||
{"name": "idx_pul_time", "idxtype": "index", "idxfields": ["use_time"]},
|
||||
{"name": "idx_pul_source", "idxtype": "index", "idxfields": ["source_ref_table", "source_ref_id"]}
|
||||
{
|
||||
"name": "idx_pul_product",
|
||||
"idxtype": "index",
|
||||
"idxfields": [
|
||||
"product_id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "idx_pul_subscription",
|
||||
"idxtype": "index",
|
||||
"idxfields": [
|
||||
"subscription_id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "idx_pul_user",
|
||||
"idxtype": "index",
|
||||
"idxfields": [
|
||||
"user_id",
|
||||
"user_org_id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "idx_pul_supplier",
|
||||
"idxtype": "index",
|
||||
"idxfields": [
|
||||
"supplier_org_id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "idx_pul_time",
|
||||
"idxtype": "index",
|
||||
"idxfields": [
|
||||
"use_time"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "idx_pul_source",
|
||||
"idxtype": "index",
|
||||
"idxfields": [
|
||||
"source_ref_table",
|
||||
"source_ref_id"
|
||||
]
|
||||
}
|
||||
],
|
||||
"codes": [
|
||||
{
|
||||
|
||||
@ -178,7 +178,6 @@ class ProductManager:
|
||||
"""Get product detail for current org (reseller).
|
||||
|
||||
Returns product_info + category_info + operator_config + extra_parsed.
|
||||
No physical table routing - all data comes from product table + extra_json.
|
||||
"""
|
||||
if not org_id:
|
||||
org_id = self._get_current_org_id()
|
||||
@ -471,9 +470,10 @@ class ProductManager:
|
||||
if existing:
|
||||
config_id = existing[0]['id']
|
||||
await sor.U('product_type_config', {
|
||||
'id': config_id,
|
||||
'config_json': config_json,
|
||||
'updated_at': now
|
||||
}, {'id': config_id, 'org_id': org_id})
|
||||
})
|
||||
return {'success': True, 'id': config_id, 'message': 'Config updated'}
|
||||
else:
|
||||
config_id = getID()
|
||||
@ -511,7 +511,7 @@ class ProductManager:
|
||||
return {'success': False, 'message': 'Product not found'}
|
||||
|
||||
rid = getID()
|
||||
await sor.I('product_resource', {
|
||||
await sor.C('product_resource', {
|
||||
'id': rid,
|
||||
'product_id': product_id,
|
||||
'resource_type': resource_type,
|
||||
@ -598,7 +598,7 @@ class ProductManager:
|
||||
|
||||
sid = getID()
|
||||
try:
|
||||
await sor.I('product_resource_supplier', {
|
||||
await sor.C('product_resource_supplier', {
|
||||
'id': sid,
|
||||
'product_resource_id': product_resource_id,
|
||||
'supplier_org_id': supplier_org_id,
|
||||
@ -636,14 +636,14 @@ class ProductManager:
|
||||
dbname = self._get_dbname()
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
data = {}
|
||||
data = {'id': prs_id}
|
||||
if priority is not None:
|
||||
data['priority'] = int(priority)
|
||||
if weight is not None:
|
||||
data['weight'] = int(weight)
|
||||
if not data:
|
||||
if len(data) <= 1:
|
||||
return {'success': False, 'message': 'No fields to update'}
|
||||
await sor.U('product_resource_supplier', data, {'id': prs_id})
|
||||
await sor.U('product_resource_supplier', data)
|
||||
return {'success': True}
|
||||
|
||||
async def set_overflow_product(self, product_resource_id, overflow_product_id, org_id=None):
|
||||
@ -655,9 +655,10 @@ class ProductManager:
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.U('product_resource', {
|
||||
'id': product_resource_id,
|
||||
'overflow_product_id': overflow_product_id or '',
|
||||
'updated_at': now
|
||||
}, {'id': product_resource_id})
|
||||
})
|
||||
return {'success': True}
|
||||
|
||||
# ─── Subscriptions ───
|
||||
@ -708,7 +709,7 @@ class ProductManager:
|
||||
elif product.get('product_type') in ('llm_model', 'compute'):
|
||||
sub_type = '2'
|
||||
|
||||
await sor.I('product_subscription', {
|
||||
await sor.C('product_subscription', {
|
||||
'id': sub_id,
|
||||
'product_id': product_id,
|
||||
'user_id': user_id,
|
||||
@ -793,8 +794,9 @@ class ProductManager:
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.U('product_subscription', {
|
||||
'id': subscription_id,
|
||||
'status': '3', 'updated_at': now
|
||||
}, {'id': subscription_id})
|
||||
})
|
||||
return {'success': True}
|
||||
|
||||
async def expire_subscriptions(self):
|
||||
@ -816,17 +818,7 @@ class ProductManager:
|
||||
async def product_use(self, product_id, user_id, user_org_id,
|
||||
used_amount, used_unit, resource_ref_id=None,
|
||||
source_ref_table=None, source_ref_id=None):
|
||||
"""Core consumption engine: route supplier, calc cost, log usage.
|
||||
|
||||
Flow:
|
||||
1. Validate product
|
||||
2. Check subscription (for monthly products)
|
||||
3. Route to supplier (by priority/weight)
|
||||
4. Calculate cost from supplier_resource_price
|
||||
5. Calculate sell price
|
||||
6. Write product_usage_log
|
||||
7. Update subscription quota
|
||||
"""
|
||||
"""Core consumption engine: route supplier, calc cost, log usage."""
|
||||
dbname = self._get_dbname()
|
||||
now = time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
today = datetime.date.today().isoformat()
|
||||
@ -873,17 +865,19 @@ class ProductManager:
|
||||
remaining_quota = remaining - used_amount
|
||||
# Update quota
|
||||
await sor.U('product_subscription', {
|
||||
'id': subscription_id,
|
||||
'quota_used': quota_used + used_amount,
|
||||
'updated_at': now
|
||||
}, {'id': subscription_id})
|
||||
})
|
||||
else:
|
||||
# Overflow: use up remaining, rest is overage
|
||||
if remaining > 0:
|
||||
await sor.U('product_subscription', {
|
||||
'id': subscription_id,
|
||||
'quota_used': quota_total,
|
||||
'status': '4',
|
||||
'updated_at': now
|
||||
}, {'id': subscription_id})
|
||||
})
|
||||
billing_mode = '2'
|
||||
overage = used_amount - remaining
|
||||
else:
|
||||
@ -961,7 +955,7 @@ class ProductManager:
|
||||
|
||||
# Step 5: Write usage log
|
||||
log_id = getID()
|
||||
await sor.I('product_usage_log', {
|
||||
await sor.C('product_usage_log', {
|
||||
'id': log_id,
|
||||
'product_id': product_id,
|
||||
'subscription_id': subscription_id or '',
|
||||
|
||||
@ -1,12 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
import json, time
|
||||
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid', 'type': 'error'}}
|
||||
|
||||
try:
|
||||
user_id = await get_user()
|
||||
org_id = (await get_userorgid()) or '0'
|
||||
now = time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
dbname = get_module_dbname('product_management')
|
||||
data = dict(params_kw)
|
||||
@ -23,15 +19,16 @@ try:
|
||||
if not check:
|
||||
raise ValueError('无权操作该记录')
|
||||
|
||||
data['updated_at'] = now
|
||||
data['updated_at'] = timestampstr()
|
||||
data['id'] = record_id
|
||||
if data.get('has_product') == '0':
|
||||
data['product_type'] = ''
|
||||
data['product_type_title'] = ''
|
||||
|
||||
fields = {k: v for k, v in data.items() if v is not None and v != '' or k in ('product_type', 'product_type_title', 'parent_id')}
|
||||
fields = {k: v for k, v in data.items() if v is not None and v != '' or k in ('product_type', 'product_type_title', 'parent_id', 'id')}
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.U('product_category', fields, {'id': record_id, 'org_id': org_id})
|
||||
await sor.U('product_category', fields)
|
||||
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Success', 'message': '类别更新成功', 'type': 'success'}}
|
||||
|
||||
|
||||
@ -6,9 +6,11 @@ try:
|
||||
record_id = data.pop('id', None)
|
||||
if not record_id:
|
||||
raise ValueError('Missing id')
|
||||
data['updated_at'] = timestampstr()
|
||||
data['id'] = record_id
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.U('product_resource_supplier', data, {'id': record_id})
|
||||
await sor.U('product_resource_supplier', data)
|
||||
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Success', 'message': '供应商关联更新成功', 'type': 'success'}}
|
||||
except Exception as e:
|
||||
|
||||
@ -7,9 +7,10 @@ try:
|
||||
if not record_id:
|
||||
raise ValueError('Missing id')
|
||||
data['updated_at'] = timestampstr()
|
||||
data['id'] = record_id
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.U('product_resource', data, {'id': record_id})
|
||||
await sor.U('product_resource', data)
|
||||
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Success', 'message': '资源绑定更新成功', 'type': 'success'}}
|
||||
except Exception as e:
|
||||
|
||||
@ -7,9 +7,10 @@ try:
|
||||
if not record_id:
|
||||
raise ValueError('Missing id')
|
||||
data['updated_at'] = timestampstr()
|
||||
data['id'] = record_id
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.U('product_subscription', data, {'id': record_id})
|
||||
await sor.U('product_subscription', data)
|
||||
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Success', 'message': '订购更新成功', 'type': 'success'}}
|
||||
except Exception as e:
|
||||
|
||||
@ -1,12 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
import json, time
|
||||
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid', 'type': 'error'}}
|
||||
|
||||
try:
|
||||
user_id = await get_user()
|
||||
org_id = (await get_userorgid()) or '0'
|
||||
now = time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
dbname = get_module_dbname('product_management')
|
||||
data = dict(params_kw)
|
||||
@ -22,10 +18,11 @@ try:
|
||||
if not check:
|
||||
raise ValueError('无权操作该记录')
|
||||
|
||||
data['updated_at'] = now
|
||||
data['updated_at'] = timestampstr()
|
||||
data['id'] = record_id
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.U('product_type_config', data, {'id': record_id, 'org_id': org_id})
|
||||
await sor.U('product_type_config', data)
|
||||
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Success', 'message': '配置更新成功', 'type': 'success'}}
|
||||
|
||||
|
||||
@ -1,12 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
import json, time
|
||||
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid', 'type': 'error'}}
|
||||
|
||||
try:
|
||||
user_id = await get_user()
|
||||
org_id = (await get_userorgid()) or '0'
|
||||
now = time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
dbname = get_module_dbname('product_management')
|
||||
data = dict(params_kw)
|
||||
@ -14,7 +10,7 @@ try:
|
||||
if not record_id:
|
||||
raise ValueError('Missing id')
|
||||
|
||||
# Verify belongs to org
|
||||
# Verify belongs to org + auto-fill product_type if category changed
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
check = await sor.sqlExe(
|
||||
"SELECT id FROM product WHERE id = ${id}$ AND org_id = ${org_id}$",
|
||||
@ -23,19 +19,19 @@ try:
|
||||
if not check:
|
||||
raise ValueError('无权操作该记录')
|
||||
|
||||
data['updated_at'] = now
|
||||
if data.get('category_id') and not data.get('product_type'):
|
||||
cat_check = await sor.sqlExe(
|
||||
"SELECT product_type FROM product_category WHERE id = ${category_id}$ AND org_id = ${org_id}$",
|
||||
{'category_id': data['category_id'], 'org_id': org_id}
|
||||
)
|
||||
if cat_check:
|
||||
data['product_type'] = cat_check[0].get('product_type', '')
|
||||
|
||||
# If category changed, update product_type
|
||||
if data.get('category_id') and not data.get('product_type'):
|
||||
cat_check = await sor.sqlExe(
|
||||
"SELECT product_type FROM product_category WHERE id = ${category_id}$ AND org_id = ${org_id}$",
|
||||
{'category_id': data['category_id'], 'org_id': org_id}
|
||||
)
|
||||
if cat_check:
|
||||
data['product_type'] = cat_check[0].get('product_type', '')
|
||||
data['updated_at'] = timestampstr()
|
||||
data['id'] = record_id
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.U('product', data, {'id': record_id, 'org_id': org_id})
|
||||
await sor.U('product', data)
|
||||
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Success', 'message': '产品更新成功', 'type': 'success'}}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user