qrpa 1.1.33__py3-none-any.whl → 1.1.34__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of qrpa might be problematic. Click here for more details.
- qrpa/RateLimitedSender.py +45 -45
- qrpa/__init__.py +31 -31
- qrpa/db_migrator.py +600 -600
- qrpa/feishu_bot_app.py +267 -267
- qrpa/fun_base.py +339 -339
- qrpa/fun_excel.py +3059 -3059
- qrpa/fun_file.py +318 -318
- qrpa/fun_web.py +258 -258
- qrpa/fun_win.py +198 -198
- qrpa/mysql_module/new_product_analysis_model.py +429 -0
- qrpa/mysql_module/shein_ledger_model.py +468 -468
- qrpa/mysql_module/shein_product_model.py +484 -484
- qrpa/mysql_module/shein_return_order_model.py +569 -569
- qrpa/shein_daily_report_model.py +375 -375
- qrpa/shein_excel.py +3125 -3125
- qrpa/shein_lib.py +3932 -3607
- qrpa/shein_mysql.py +22 -0
- qrpa/shein_sqlite.py +153 -153
- qrpa/shein_ziniao.py +529 -529
- qrpa/temu_chrome.py +56 -56
- qrpa/temu_excel.py +139 -139
- qrpa/temu_lib.py +154 -154
- qrpa/time_utils.py +882 -882
- qrpa/time_utils_example.py +243 -243
- qrpa/wxwork.py +318 -318
- {qrpa-1.1.33.dist-info → qrpa-1.1.34.dist-info}/METADATA +1 -1
- qrpa-1.1.34.dist-info/RECORD +32 -0
- qrpa-1.1.33.dist-info/RECORD +0 -31
- {qrpa-1.1.33.dist-info → qrpa-1.1.34.dist-info}/WHEEL +0 -0
- {qrpa-1.1.33.dist-info → qrpa-1.1.34.dist-info}/top_level.txt +0 -0
|
@@ -1,468 +1,468 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
SHEIN账本记录数据模型
|
|
5
|
-
使用SQLAlchemy定义账本记录表结构
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from sqlalchemy import create_engine, Column, String, Text, DateTime, Integer, DECIMAL, Index
|
|
9
|
-
from sqlalchemy.ext.declarative import declarative_base
|
|
10
|
-
from sqlalchemy.orm import sessionmaker
|
|
11
|
-
from datetime import datetime
|
|
12
|
-
import json
|
|
13
|
-
|
|
14
|
-
# 创建基类
|
|
15
|
-
Base = declarative_base()
|
|
16
|
-
|
|
17
|
-
class SheinLedgerRecord(Base):
|
|
18
|
-
"""
|
|
19
|
-
SHEIN账本记录表
|
|
20
|
-
存储来自接口的账本记录数据
|
|
21
|
-
"""
|
|
22
|
-
__tablename__ = 'shein_ledger_records'
|
|
23
|
-
|
|
24
|
-
# 主键ID (自增)
|
|
25
|
-
id = Column(Integer, primary_key=True, autoincrement=True, comment='主键ID')
|
|
26
|
-
|
|
27
|
-
# 原始数据中的ID作为ledger_id
|
|
28
|
-
ledger_id = Column(String(50), nullable=False, unique=True, comment='账本记录ID(原始数据中的id字段)')
|
|
29
|
-
|
|
30
|
-
# 变更类型相关字段
|
|
31
|
-
display_change_type = Column(Integer, nullable=True, comment='显示变更类型编码')
|
|
32
|
-
display_change_type_name = Column(String(100), nullable=True, comment='显示变更类型名称')
|
|
33
|
-
change_type = Column(Integer, nullable=True, comment='变更类型编码')
|
|
34
|
-
|
|
35
|
-
# 结算类型相关字段
|
|
36
|
-
settle_type = Column(Integer, nullable=True, comment='结算类型编码')
|
|
37
|
-
settle_type_name = Column(String(100), nullable=True, comment='结算类型名称')
|
|
38
|
-
|
|
39
|
-
# 业务单号相关字段
|
|
40
|
-
business_no = Column(String(100), nullable=True, comment='业务单号')
|
|
41
|
-
bill_no = Column(String(100), nullable=True, comment='账单编号')
|
|
42
|
-
uniq_key = Column(String(100), nullable=True, comment='唯一键')
|
|
43
|
-
|
|
44
|
-
# 时间相关字段
|
|
45
|
-
happen_time = Column(DateTime, nullable=True, comment='发生时间')
|
|
46
|
-
add_time = Column(DateTime, nullable=True, comment='添加时间')
|
|
47
|
-
|
|
48
|
-
# 供应商相关字段
|
|
49
|
-
supplier_id = Column(Integer, nullable=True, comment='供应商ID')
|
|
50
|
-
supplier_name = Column(String(200), nullable=True, comment='供应商名称')
|
|
51
|
-
supplier_code = Column(String(100), nullable=True, comment='供应商编码')
|
|
52
|
-
|
|
53
|
-
# 商品相关字段
|
|
54
|
-
skc = Column(String(100), nullable=True, comment='SKC编码')
|
|
55
|
-
sku = Column(String(100), nullable=True, comment='SKU编码')
|
|
56
|
-
supplier_sku = Column(String(200), nullable=True, comment='供应商SKU')
|
|
57
|
-
suffix_zh = Column(String(100), nullable=True, comment='商品属性(中文)')
|
|
58
|
-
|
|
59
|
-
# 数量和金额相关字段
|
|
60
|
-
quantity = Column(Integer, nullable=True, comment='数量')
|
|
61
|
-
cost = Column(DECIMAL(10, 2), nullable=True, comment='成本')
|
|
62
|
-
amount = Column(DECIMAL(10, 2), nullable=True, comment='金额')
|
|
63
|
-
currency = Column(String(10), nullable=True, comment='货币类型')
|
|
64
|
-
|
|
65
|
-
# 备注
|
|
66
|
-
remark = Column(Text, nullable=True, comment='备注')
|
|
67
|
-
|
|
68
|
-
# 前后关联字段
|
|
69
|
-
before_inventory_id = Column(String(50), nullable=True, comment='前库存ID')
|
|
70
|
-
after_inventory_id = Column(String(50), nullable=True, comment='后库存ID')
|
|
71
|
-
before_business_no = Column(String(100), nullable=True, comment='前业务单号')
|
|
72
|
-
after_business_no = Column(String(100), nullable=True, comment='后业务单号')
|
|
73
|
-
before_bill_no = Column(String(100), nullable=True, comment='前账单号')
|
|
74
|
-
after_bill_no = Column(String(100), nullable=True, comment='后账单号')
|
|
75
|
-
after_change_type_name = Column(String(100), nullable=True, comment='后变更类型名称')
|
|
76
|
-
|
|
77
|
-
# 业务标签
|
|
78
|
-
business_no_tags = Column(String(500), nullable=True, comment='业务单号标签')
|
|
79
|
-
|
|
80
|
-
# 来源系统
|
|
81
|
-
source_system = Column(String(50), nullable=True, comment='来源系统')
|
|
82
|
-
|
|
83
|
-
# 促销相关
|
|
84
|
-
show_promotion = Column(Integer, nullable=True, comment='显示促销')
|
|
85
|
-
|
|
86
|
-
# 销售相关字段
|
|
87
|
-
sales_seller_id = Column(Integer, nullable=True, comment='销售卖家ID')
|
|
88
|
-
sales_seller_name = Column(String(200), nullable=True, comment='销售卖家名称')
|
|
89
|
-
|
|
90
|
-
# 店铺相关字段
|
|
91
|
-
store_username = Column(String(100), nullable=True, comment='店铺用户名')
|
|
92
|
-
store_name = Column(String(200), nullable=True, comment='店铺名称')
|
|
93
|
-
store_manager = Column(String(100), nullable=True, comment='店铺经理')
|
|
94
|
-
|
|
95
|
-
# 成本价格和SKU图片
|
|
96
|
-
cost_price = Column(DECIMAL(10, 2), nullable=True, comment='成本价格')
|
|
97
|
-
sku_img = Column(Text, nullable=True, comment='SKU图片URL')
|
|
98
|
-
|
|
99
|
-
# 时间戳
|
|
100
|
-
created_at = Column(DateTime, default=datetime.now, comment='创建时间')
|
|
101
|
-
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, comment='更新时间')
|
|
102
|
-
|
|
103
|
-
# 定义索引
|
|
104
|
-
__table_args__ = (
|
|
105
|
-
Index('ix_ledger_id', 'ledger_id'),
|
|
106
|
-
Index('ix_business_no', 'business_no'),
|
|
107
|
-
Index('ix_bill_no', 'bill_no'),
|
|
108
|
-
Index('ix_supplier_id', 'supplier_id'),
|
|
109
|
-
Index('ix_skc', 'skc'),
|
|
110
|
-
Index('ix_sku', 'sku'),
|
|
111
|
-
Index('ix_store_username', 'store_username'),
|
|
112
|
-
Index('ix_happen_time', 'happen_time'),
|
|
113
|
-
Index('ix_add_time', 'add_time'),
|
|
114
|
-
Index('ix_change_type', 'change_type'),
|
|
115
|
-
Index('ix_display_change_type', 'display_change_type'),
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
def __repr__(self):
|
|
119
|
-
return f"<SheinLedgerRecord(id={self.id}, ledger_id='{self.ledger_id}', business_no='{self.business_no}')>"
|
|
120
|
-
|
|
121
|
-
class SheinLedgerManager:
|
|
122
|
-
"""
|
|
123
|
-
SHEIN账本记录数据管理器
|
|
124
|
-
提供数据库操作相关方法
|
|
125
|
-
"""
|
|
126
|
-
|
|
127
|
-
def __init__(self, database_url):
|
|
128
|
-
"""
|
|
129
|
-
初始化数据库连接
|
|
130
|
-
|
|
131
|
-
Args:
|
|
132
|
-
database_url (str): 数据库连接URL,例如:
|
|
133
|
-
mysql+pymysql://username:password@localhost:3306/database_name
|
|
134
|
-
"""
|
|
135
|
-
self.engine = create_engine(database_url, echo=False)
|
|
136
|
-
self.Session = sessionmaker(bind=self.engine)
|
|
137
|
-
|
|
138
|
-
def create_tables(self):
|
|
139
|
-
"""
|
|
140
|
-
创建数据表
|
|
141
|
-
"""
|
|
142
|
-
Base.metadata.create_all(self.engine)
|
|
143
|
-
print("账本记录数据表创建成功!")
|
|
144
|
-
|
|
145
|
-
def drop_tables(self):
|
|
146
|
-
"""
|
|
147
|
-
删除数据表
|
|
148
|
-
"""
|
|
149
|
-
Base.metadata.drop_all(self.engine)
|
|
150
|
-
print("账本记录数据表删除成功!")
|
|
151
|
-
|
|
152
|
-
def _parse_datetime(self, datetime_str):
|
|
153
|
-
"""
|
|
154
|
-
解析日期时间字符串
|
|
155
|
-
"""
|
|
156
|
-
if not datetime_str:
|
|
157
|
-
return None
|
|
158
|
-
try:
|
|
159
|
-
return datetime.strptime(datetime_str, '%Y-%m-%d %H:%M:%S')
|
|
160
|
-
except:
|
|
161
|
-
return None
|
|
162
|
-
|
|
163
|
-
def _parse_decimal(self, value):
|
|
164
|
-
"""
|
|
165
|
-
解析decimal值
|
|
166
|
-
"""
|
|
167
|
-
if value is None:
|
|
168
|
-
return None
|
|
169
|
-
try:
|
|
170
|
-
return float(value)
|
|
171
|
-
except:
|
|
172
|
-
return None
|
|
173
|
-
|
|
174
|
-
def _parse_cost_price(self, value):
|
|
175
|
-
"""
|
|
176
|
-
解析成本价格,如果是"-"或"未匹配到"等非数字值则返回0
|
|
177
|
-
"""
|
|
178
|
-
if value is None:
|
|
179
|
-
return 0.0
|
|
180
|
-
|
|
181
|
-
# 转换为字符串处理
|
|
182
|
-
str_value = str(value).strip()
|
|
183
|
-
|
|
184
|
-
# 如果是 "-" 或包含 "未匹配到" 等文本,则返回0
|
|
185
|
-
if str_value == "-" or "未匹配到" in str_value or str_value == "":
|
|
186
|
-
return 0.0
|
|
187
|
-
|
|
188
|
-
try:
|
|
189
|
-
return float(str_value)
|
|
190
|
-
except:
|
|
191
|
-
return 0.0
|
|
192
|
-
|
|
193
|
-
def upsert_ledger_data(self, data_list):
|
|
194
|
-
"""
|
|
195
|
-
从JSON数据中执行upsert操作(插入或更新)
|
|
196
|
-
|
|
197
|
-
Args:
|
|
198
|
-
data_list (list): 账本记录数据列表
|
|
199
|
-
"""
|
|
200
|
-
session = self.Session()
|
|
201
|
-
try:
|
|
202
|
-
insert_count = 0
|
|
203
|
-
update_count = 0
|
|
204
|
-
|
|
205
|
-
for data in data_list:
|
|
206
|
-
ledger_id = str(data.get('id'))
|
|
207
|
-
existing_record = session.query(SheinLedgerRecord).filter_by(ledger_id=ledger_id).first()
|
|
208
|
-
|
|
209
|
-
if existing_record:
|
|
210
|
-
# 更新现有记录
|
|
211
|
-
self._update_record_from_data(existing_record, data)
|
|
212
|
-
update_count += 1
|
|
213
|
-
else:
|
|
214
|
-
# 插入新记录
|
|
215
|
-
new_record = self._create_record_from_data(data)
|
|
216
|
-
session.add(new_record)
|
|
217
|
-
insert_count += 1
|
|
218
|
-
|
|
219
|
-
session.commit()
|
|
220
|
-
print(f"成功处理 {len(data_list)} 条账本记录数据")
|
|
221
|
-
print(f"新增记录: {insert_count} 条,更新记录: {update_count} 条")
|
|
222
|
-
|
|
223
|
-
except Exception as e:
|
|
224
|
-
session.rollback()
|
|
225
|
-
print(f"处理数据失败: {e}")
|
|
226
|
-
raise
|
|
227
|
-
finally:
|
|
228
|
-
session.close()
|
|
229
|
-
|
|
230
|
-
def _create_record_from_data(self, data):
|
|
231
|
-
"""
|
|
232
|
-
从JSON数据创建新的记录对象
|
|
233
|
-
"""
|
|
234
|
-
return SheinLedgerRecord(
|
|
235
|
-
ledger_id=str(data.get('id')),
|
|
236
|
-
display_change_type=data.get('displayChangeType'),
|
|
237
|
-
display_change_type_name=data.get('displayChangeTypeName'),
|
|
238
|
-
change_type=data.get('changeType'),
|
|
239
|
-
settle_type=data.get('settleType'),
|
|
240
|
-
settle_type_name=data.get('settleTypeName'),
|
|
241
|
-
business_no=data.get('businessNo'),
|
|
242
|
-
bill_no=data.get('billNo'),
|
|
243
|
-
uniq_key=data.get('uniqKey'),
|
|
244
|
-
happen_time=self._parse_datetime(data.get('happenTime')),
|
|
245
|
-
add_time=self._parse_datetime(data.get('addTime')),
|
|
246
|
-
supplier_id=data.get('supplierId'),
|
|
247
|
-
supplier_name=data.get('supplierName'),
|
|
248
|
-
supplier_code=data.get('supplierCode'),
|
|
249
|
-
skc=data.get('skc'),
|
|
250
|
-
sku=data.get('sku'),
|
|
251
|
-
supplier_sku=data.get('supplierSku'),
|
|
252
|
-
suffix_zh=data.get('suffixZh'),
|
|
253
|
-
quantity=data.get('quantity'),
|
|
254
|
-
cost=self._parse_decimal(data.get('cost')),
|
|
255
|
-
amount=self._parse_decimal(data.get('amount')),
|
|
256
|
-
currency=data.get('currency'),
|
|
257
|
-
remark=data.get('remark'),
|
|
258
|
-
before_inventory_id=data.get('beforeInventoryId'),
|
|
259
|
-
after_inventory_id=data.get('afterInventoryId'),
|
|
260
|
-
before_business_no=data.get('beforeBusinessNo'),
|
|
261
|
-
after_business_no=data.get('afterBusinessNo'),
|
|
262
|
-
before_bill_no=data.get('beforeBillNo'),
|
|
263
|
-
after_bill_no=data.get('afterBillNo'),
|
|
264
|
-
after_change_type_name=data.get('afterChangeTypeName'),
|
|
265
|
-
business_no_tags=data.get('businessNoTags'),
|
|
266
|
-
source_system=data.get('sourceSystem'),
|
|
267
|
-
show_promotion=data.get('showPromotion'),
|
|
268
|
-
sales_seller_id=data.get('salesSellerId'),
|
|
269
|
-
sales_seller_name=data.get('salesSellerName'),
|
|
270
|
-
store_username=data.get('store_username'),
|
|
271
|
-
store_name=data.get('store_name'),
|
|
272
|
-
store_manager=data.get('store_manager'),
|
|
273
|
-
cost_price=self._parse_cost_price(data.get('cost_price')),
|
|
274
|
-
sku_img=data.get('sku_img')
|
|
275
|
-
)
|
|
276
|
-
|
|
277
|
-
def _update_record_from_data(self, record, data):
|
|
278
|
-
"""
|
|
279
|
-
使用JSON数据更新现有记录
|
|
280
|
-
"""
|
|
281
|
-
record.display_change_type = data.get('displayChangeType')
|
|
282
|
-
record.display_change_type_name = data.get('displayChangeTypeName')
|
|
283
|
-
record.change_type = data.get('changeType')
|
|
284
|
-
record.settle_type = data.get('settleType')
|
|
285
|
-
record.settle_type_name = data.get('settleTypeName')
|
|
286
|
-
record.business_no = data.get('businessNo')
|
|
287
|
-
record.bill_no = data.get('billNo')
|
|
288
|
-
record.uniq_key = data.get('uniqKey')
|
|
289
|
-
record.happen_time = self._parse_datetime(data.get('happenTime'))
|
|
290
|
-
record.add_time = self._parse_datetime(data.get('addTime'))
|
|
291
|
-
record.supplier_id = data.get('supplierId')
|
|
292
|
-
record.supplier_name = data.get('supplierName')
|
|
293
|
-
record.supplier_code = data.get('supplierCode')
|
|
294
|
-
record.skc = data.get('skc')
|
|
295
|
-
record.sku = data.get('sku')
|
|
296
|
-
record.supplier_sku = data.get('supplierSku')
|
|
297
|
-
record.suffix_zh = data.get('suffixZh')
|
|
298
|
-
record.quantity = data.get('quantity')
|
|
299
|
-
record.cost = self._parse_decimal(data.get('cost'))
|
|
300
|
-
record.amount = self._parse_decimal(data.get('amount'))
|
|
301
|
-
record.currency = data.get('currency')
|
|
302
|
-
record.remark = data.get('remark')
|
|
303
|
-
record.before_inventory_id = data.get('beforeInventoryId')
|
|
304
|
-
record.after_inventory_id = data.get('afterInventoryId')
|
|
305
|
-
record.before_business_no = data.get('beforeBusinessNo')
|
|
306
|
-
record.after_business_no = data.get('afterBusinessNo')
|
|
307
|
-
record.before_bill_no = data.get('beforeBillNo')
|
|
308
|
-
record.after_bill_no = data.get('afterBillNo')
|
|
309
|
-
record.after_change_type_name = data.get('afterChangeTypeName')
|
|
310
|
-
record.business_no_tags = data.get('businessNoTags')
|
|
311
|
-
record.source_system = data.get('sourceSystem')
|
|
312
|
-
record.show_promotion = data.get('showPromotion')
|
|
313
|
-
record.sales_seller_id = data.get('salesSellerId')
|
|
314
|
-
record.sales_seller_name = data.get('salesSellerName')
|
|
315
|
-
record.store_username = data.get('store_username')
|
|
316
|
-
record.store_name = data.get('store_name')
|
|
317
|
-
record.store_manager = data.get('store_manager')
|
|
318
|
-
record.cost_price = self._parse_cost_price(data.get('cost_price'))
|
|
319
|
-
record.sku_img = data.get('sku_img')
|
|
320
|
-
record.updated_at = datetime.now()
|
|
321
|
-
|
|
322
|
-
def get_ledger_records(self, limit=None, offset=None, order_by=None):
|
|
323
|
-
"""
|
|
324
|
-
查询账本记录列表
|
|
325
|
-
|
|
326
|
-
Args:
|
|
327
|
-
limit (int): 限制返回数量
|
|
328
|
-
offset (int): 偏移量
|
|
329
|
-
order_by (str): 排序字段,默认按happen_time降序
|
|
330
|
-
|
|
331
|
-
Returns:
|
|
332
|
-
list: 账本记录列表
|
|
333
|
-
"""
|
|
334
|
-
session = self.Session()
|
|
335
|
-
try:
|
|
336
|
-
query = session.query(SheinLedgerRecord)
|
|
337
|
-
|
|
338
|
-
# 默认按发生时间降序排列
|
|
339
|
-
if order_by:
|
|
340
|
-
if hasattr(SheinLedgerRecord, order_by):
|
|
341
|
-
query = query.order_by(getattr(SheinLedgerRecord, order_by).desc())
|
|
342
|
-
else:
|
|
343
|
-
query = query.order_by(SheinLedgerRecord.happen_time.desc())
|
|
344
|
-
|
|
345
|
-
if offset:
|
|
346
|
-
query = query.offset(offset)
|
|
347
|
-
if limit:
|
|
348
|
-
query = query.limit(limit)
|
|
349
|
-
return query.all()
|
|
350
|
-
finally:
|
|
351
|
-
session.close()
|
|
352
|
-
|
|
353
|
-
def search_records(self, **kwargs):
|
|
354
|
-
"""
|
|
355
|
-
根据条件搜索账本记录
|
|
356
|
-
|
|
357
|
-
Args:
|
|
358
|
-
**kwargs: 搜索条件,如store_username, business_no, sku等
|
|
359
|
-
|
|
360
|
-
Returns:
|
|
361
|
-
list: 符合条件的记录列表
|
|
362
|
-
"""
|
|
363
|
-
session = self.Session()
|
|
364
|
-
try:
|
|
365
|
-
query = session.query(SheinLedgerRecord)
|
|
366
|
-
|
|
367
|
-
# 根据传入的条件进行过滤
|
|
368
|
-
for key, value in kwargs.items():
|
|
369
|
-
if hasattr(SheinLedgerRecord, key) and value is not None:
|
|
370
|
-
if isinstance(value, str) and key in ['business_no', 'bill_no', 'skc', 'sku', 'supplier_name', 'store_name']:
|
|
371
|
-
# 字符串字段支持模糊搜索
|
|
372
|
-
query = query.filter(getattr(SheinLedgerRecord, key).like(f'%{value}%'))
|
|
373
|
-
else:
|
|
374
|
-
query = query.filter(getattr(SheinLedgerRecord, key) == value)
|
|
375
|
-
|
|
376
|
-
return query.order_by(SheinLedgerRecord.happen_time.desc()).all()
|
|
377
|
-
finally:
|
|
378
|
-
session.close()
|
|
379
|
-
|
|
380
|
-
def get_statistics_by_store(self, store_username=None):
|
|
381
|
-
"""
|
|
382
|
-
按店铺统计数据
|
|
383
|
-
|
|
384
|
-
Args:
|
|
385
|
-
store_username (str): 店铺用户名,如果不提供则统计所有店铺
|
|
386
|
-
|
|
387
|
-
Returns:
|
|
388
|
-
dict: 统计结果
|
|
389
|
-
"""
|
|
390
|
-
session = self.Session()
|
|
391
|
-
try:
|
|
392
|
-
query = session.query(SheinLedgerRecord)
|
|
393
|
-
|
|
394
|
-
if store_username:
|
|
395
|
-
query = query.filter(SheinLedgerRecord.store_username == store_username)
|
|
396
|
-
|
|
397
|
-
records = query.all()
|
|
398
|
-
|
|
399
|
-
# 统计信息
|
|
400
|
-
total_count = len(records)
|
|
401
|
-
total_amount = sum([r.amount for r in records if r.amount])
|
|
402
|
-
total_cost = sum([r.cost for r in records if r.cost])
|
|
403
|
-
total_quantity = sum([r.quantity for r in records if r.quantity])
|
|
404
|
-
|
|
405
|
-
# 按变更类型统计
|
|
406
|
-
change_type_stats = {}
|
|
407
|
-
for record in records:
|
|
408
|
-
change_type = record.display_change_type_name or '未知'
|
|
409
|
-
if change_type not in change_type_stats:
|
|
410
|
-
change_type_stats[change_type] = {'count': 0, 'amount': 0, 'quantity': 0}
|
|
411
|
-
change_type_stats[change_type]['count'] += 1
|
|
412
|
-
change_type_stats[change_type]['amount'] += record.amount or 0
|
|
413
|
-
change_type_stats[change_type]['quantity'] += record.quantity or 0
|
|
414
|
-
|
|
415
|
-
return {
|
|
416
|
-
'total_count' : total_count,
|
|
417
|
-
'total_amount' : float(total_amount) if total_amount else 0,
|
|
418
|
-
'total_cost' : float(total_cost) if total_cost else 0,
|
|
419
|
-
'total_quantity' : total_quantity,
|
|
420
|
-
'change_type_stats': change_type_stats
|
|
421
|
-
}
|
|
422
|
-
finally:
|
|
423
|
-
session.close()
|
|
424
|
-
|
|
425
|
-
def import_from_json_file(self, json_file_path):
|
|
426
|
-
"""
|
|
427
|
-
从JSON文件导入数据
|
|
428
|
-
|
|
429
|
-
Args:
|
|
430
|
-
json_file_path (str): JSON文件路径
|
|
431
|
-
"""
|
|
432
|
-
with open(json_file_path, 'r', encoding='utf-8') as f:
|
|
433
|
-
data_list = json.load(f)
|
|
434
|
-
self.upsert_ledger_data(data_list)
|
|
435
|
-
|
|
436
|
-
def example_usage():
|
|
437
|
-
"""
|
|
438
|
-
使用示例
|
|
439
|
-
"""
|
|
440
|
-
# 数据库连接URL(请根据实际情况修改)
|
|
441
|
-
database_url = "mysql+pymysql://root:123wyk@localhost:3306/lz"
|
|
442
|
-
|
|
443
|
-
# 创建管理器实例
|
|
444
|
-
manager = SheinLedgerManager(database_url)
|
|
445
|
-
|
|
446
|
-
# 创建数据表
|
|
447
|
-
manager.create_tables()
|
|
448
|
-
|
|
449
|
-
# 从JSON文件导入数据
|
|
450
|
-
json_file = "ledger_record_GS0365305_2025-09-24_2025-09-24.json"
|
|
451
|
-
manager.import_from_json_file(json_file)
|
|
452
|
-
|
|
453
|
-
# 查询示例
|
|
454
|
-
records = manager.get_ledger_records(limit=10)
|
|
455
|
-
for record in records:
|
|
456
|
-
print(f"记录ID: {record.ledger_id}, 业务单号: {record.business_no}, 金额: {record.amount}")
|
|
457
|
-
|
|
458
|
-
# 搜索示例
|
|
459
|
-
search_results = manager.search_records(store_username="GS0365305")
|
|
460
|
-
print(f"店铺 GS0365305 的记录数量: {len(search_results)}")
|
|
461
|
-
|
|
462
|
-
# 统计示例
|
|
463
|
-
stats = manager.get_statistics_by_store("GS0365305")
|
|
464
|
-
print(f"统计结果: {stats}")
|
|
465
|
-
|
|
466
|
-
if __name__ == "__main__":
|
|
467
|
-
pass
|
|
468
|
-
# example_usage()
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
SHEIN账本记录数据模型
|
|
5
|
+
使用SQLAlchemy定义账本记录表结构
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from sqlalchemy import create_engine, Column, String, Text, DateTime, Integer, DECIMAL, Index
|
|
9
|
+
from sqlalchemy.ext.declarative import declarative_base
|
|
10
|
+
from sqlalchemy.orm import sessionmaker
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
import json
|
|
13
|
+
|
|
14
|
+
# 创建基类
|
|
15
|
+
Base = declarative_base()
|
|
16
|
+
|
|
17
|
+
class SheinLedgerRecord(Base):
|
|
18
|
+
"""
|
|
19
|
+
SHEIN账本记录表
|
|
20
|
+
存储来自接口的账本记录数据
|
|
21
|
+
"""
|
|
22
|
+
__tablename__ = 'shein_ledger_records'
|
|
23
|
+
|
|
24
|
+
# 主键ID (自增)
|
|
25
|
+
id = Column(Integer, primary_key=True, autoincrement=True, comment='主键ID')
|
|
26
|
+
|
|
27
|
+
# 原始数据中的ID作为ledger_id
|
|
28
|
+
ledger_id = Column(String(50), nullable=False, unique=True, comment='账本记录ID(原始数据中的id字段)')
|
|
29
|
+
|
|
30
|
+
# 变更类型相关字段
|
|
31
|
+
display_change_type = Column(Integer, nullable=True, comment='显示变更类型编码')
|
|
32
|
+
display_change_type_name = Column(String(100), nullable=True, comment='显示变更类型名称')
|
|
33
|
+
change_type = Column(Integer, nullable=True, comment='变更类型编码')
|
|
34
|
+
|
|
35
|
+
# 结算类型相关字段
|
|
36
|
+
settle_type = Column(Integer, nullable=True, comment='结算类型编码')
|
|
37
|
+
settle_type_name = Column(String(100), nullable=True, comment='结算类型名称')
|
|
38
|
+
|
|
39
|
+
# 业务单号相关字段
|
|
40
|
+
business_no = Column(String(100), nullable=True, comment='业务单号')
|
|
41
|
+
bill_no = Column(String(100), nullable=True, comment='账单编号')
|
|
42
|
+
uniq_key = Column(String(100), nullable=True, comment='唯一键')
|
|
43
|
+
|
|
44
|
+
# 时间相关字段
|
|
45
|
+
happen_time = Column(DateTime, nullable=True, comment='发生时间')
|
|
46
|
+
add_time = Column(DateTime, nullable=True, comment='添加时间')
|
|
47
|
+
|
|
48
|
+
# 供应商相关字段
|
|
49
|
+
supplier_id = Column(Integer, nullable=True, comment='供应商ID')
|
|
50
|
+
supplier_name = Column(String(200), nullable=True, comment='供应商名称')
|
|
51
|
+
supplier_code = Column(String(100), nullable=True, comment='供应商编码')
|
|
52
|
+
|
|
53
|
+
# 商品相关字段
|
|
54
|
+
skc = Column(String(100), nullable=True, comment='SKC编码')
|
|
55
|
+
sku = Column(String(100), nullable=True, comment='SKU编码')
|
|
56
|
+
supplier_sku = Column(String(200), nullable=True, comment='供应商SKU')
|
|
57
|
+
suffix_zh = Column(String(100), nullable=True, comment='商品属性(中文)')
|
|
58
|
+
|
|
59
|
+
# 数量和金额相关字段
|
|
60
|
+
quantity = Column(Integer, nullable=True, comment='数量')
|
|
61
|
+
cost = Column(DECIMAL(10, 2), nullable=True, comment='成本')
|
|
62
|
+
amount = Column(DECIMAL(10, 2), nullable=True, comment='金额')
|
|
63
|
+
currency = Column(String(10), nullable=True, comment='货币类型')
|
|
64
|
+
|
|
65
|
+
# 备注
|
|
66
|
+
remark = Column(Text, nullable=True, comment='备注')
|
|
67
|
+
|
|
68
|
+
# 前后关联字段
|
|
69
|
+
before_inventory_id = Column(String(50), nullable=True, comment='前库存ID')
|
|
70
|
+
after_inventory_id = Column(String(50), nullable=True, comment='后库存ID')
|
|
71
|
+
before_business_no = Column(String(100), nullable=True, comment='前业务单号')
|
|
72
|
+
after_business_no = Column(String(100), nullable=True, comment='后业务单号')
|
|
73
|
+
before_bill_no = Column(String(100), nullable=True, comment='前账单号')
|
|
74
|
+
after_bill_no = Column(String(100), nullable=True, comment='后账单号')
|
|
75
|
+
after_change_type_name = Column(String(100), nullable=True, comment='后变更类型名称')
|
|
76
|
+
|
|
77
|
+
# 业务标签
|
|
78
|
+
business_no_tags = Column(String(500), nullable=True, comment='业务单号标签')
|
|
79
|
+
|
|
80
|
+
# 来源系统
|
|
81
|
+
source_system = Column(String(50), nullable=True, comment='来源系统')
|
|
82
|
+
|
|
83
|
+
# 促销相关
|
|
84
|
+
show_promotion = Column(Integer, nullable=True, comment='显示促销')
|
|
85
|
+
|
|
86
|
+
# 销售相关字段
|
|
87
|
+
sales_seller_id = Column(Integer, nullable=True, comment='销售卖家ID')
|
|
88
|
+
sales_seller_name = Column(String(200), nullable=True, comment='销售卖家名称')
|
|
89
|
+
|
|
90
|
+
# 店铺相关字段
|
|
91
|
+
store_username = Column(String(100), nullable=True, comment='店铺用户名')
|
|
92
|
+
store_name = Column(String(200), nullable=True, comment='店铺名称')
|
|
93
|
+
store_manager = Column(String(100), nullable=True, comment='店铺经理')
|
|
94
|
+
|
|
95
|
+
# 成本价格和SKU图片
|
|
96
|
+
cost_price = Column(DECIMAL(10, 2), nullable=True, comment='成本价格')
|
|
97
|
+
sku_img = Column(Text, nullable=True, comment='SKU图片URL')
|
|
98
|
+
|
|
99
|
+
# 时间戳
|
|
100
|
+
created_at = Column(DateTime, default=datetime.now, comment='创建时间')
|
|
101
|
+
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, comment='更新时间')
|
|
102
|
+
|
|
103
|
+
# 定义索引
|
|
104
|
+
__table_args__ = (
|
|
105
|
+
Index('ix_ledger_id', 'ledger_id'),
|
|
106
|
+
Index('ix_business_no', 'business_no'),
|
|
107
|
+
Index('ix_bill_no', 'bill_no'),
|
|
108
|
+
Index('ix_supplier_id', 'supplier_id'),
|
|
109
|
+
Index('ix_skc', 'skc'),
|
|
110
|
+
Index('ix_sku', 'sku'),
|
|
111
|
+
Index('ix_store_username', 'store_username'),
|
|
112
|
+
Index('ix_happen_time', 'happen_time'),
|
|
113
|
+
Index('ix_add_time', 'add_time'),
|
|
114
|
+
Index('ix_change_type', 'change_type'),
|
|
115
|
+
Index('ix_display_change_type', 'display_change_type'),
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
def __repr__(self):
|
|
119
|
+
return f"<SheinLedgerRecord(id={self.id}, ledger_id='{self.ledger_id}', business_no='{self.business_no}')>"
|
|
120
|
+
|
|
121
|
+
class SheinLedgerManager:
|
|
122
|
+
"""
|
|
123
|
+
SHEIN账本记录数据管理器
|
|
124
|
+
提供数据库操作相关方法
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
def __init__(self, database_url):
|
|
128
|
+
"""
|
|
129
|
+
初始化数据库连接
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
database_url (str): 数据库连接URL,例如:
|
|
133
|
+
mysql+pymysql://username:password@localhost:3306/database_name
|
|
134
|
+
"""
|
|
135
|
+
self.engine = create_engine(database_url, echo=False)
|
|
136
|
+
self.Session = sessionmaker(bind=self.engine)
|
|
137
|
+
|
|
138
|
+
def create_tables(self):
|
|
139
|
+
"""
|
|
140
|
+
创建数据表
|
|
141
|
+
"""
|
|
142
|
+
Base.metadata.create_all(self.engine)
|
|
143
|
+
print("账本记录数据表创建成功!")
|
|
144
|
+
|
|
145
|
+
def drop_tables(self):
|
|
146
|
+
"""
|
|
147
|
+
删除数据表
|
|
148
|
+
"""
|
|
149
|
+
Base.metadata.drop_all(self.engine)
|
|
150
|
+
print("账本记录数据表删除成功!")
|
|
151
|
+
|
|
152
|
+
def _parse_datetime(self, datetime_str):
|
|
153
|
+
"""
|
|
154
|
+
解析日期时间字符串
|
|
155
|
+
"""
|
|
156
|
+
if not datetime_str:
|
|
157
|
+
return None
|
|
158
|
+
try:
|
|
159
|
+
return datetime.strptime(datetime_str, '%Y-%m-%d %H:%M:%S')
|
|
160
|
+
except:
|
|
161
|
+
return None
|
|
162
|
+
|
|
163
|
+
def _parse_decimal(self, value):
|
|
164
|
+
"""
|
|
165
|
+
解析decimal值
|
|
166
|
+
"""
|
|
167
|
+
if value is None:
|
|
168
|
+
return None
|
|
169
|
+
try:
|
|
170
|
+
return float(value)
|
|
171
|
+
except:
|
|
172
|
+
return None
|
|
173
|
+
|
|
174
|
+
def _parse_cost_price(self, value):
|
|
175
|
+
"""
|
|
176
|
+
解析成本价格,如果是"-"或"未匹配到"等非数字值则返回0
|
|
177
|
+
"""
|
|
178
|
+
if value is None:
|
|
179
|
+
return 0.0
|
|
180
|
+
|
|
181
|
+
# 转换为字符串处理
|
|
182
|
+
str_value = str(value).strip()
|
|
183
|
+
|
|
184
|
+
# 如果是 "-" 或包含 "未匹配到" 等文本,则返回0
|
|
185
|
+
if str_value == "-" or "未匹配到" in str_value or str_value == "":
|
|
186
|
+
return 0.0
|
|
187
|
+
|
|
188
|
+
try:
|
|
189
|
+
return float(str_value)
|
|
190
|
+
except:
|
|
191
|
+
return 0.0
|
|
192
|
+
|
|
193
|
+
def upsert_ledger_data(self, data_list):
|
|
194
|
+
"""
|
|
195
|
+
从JSON数据中执行upsert操作(插入或更新)
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
data_list (list): 账本记录数据列表
|
|
199
|
+
"""
|
|
200
|
+
session = self.Session()
|
|
201
|
+
try:
|
|
202
|
+
insert_count = 0
|
|
203
|
+
update_count = 0
|
|
204
|
+
|
|
205
|
+
for data in data_list:
|
|
206
|
+
ledger_id = str(data.get('id'))
|
|
207
|
+
existing_record = session.query(SheinLedgerRecord).filter_by(ledger_id=ledger_id).first()
|
|
208
|
+
|
|
209
|
+
if existing_record:
|
|
210
|
+
# 更新现有记录
|
|
211
|
+
self._update_record_from_data(existing_record, data)
|
|
212
|
+
update_count += 1
|
|
213
|
+
else:
|
|
214
|
+
# 插入新记录
|
|
215
|
+
new_record = self._create_record_from_data(data)
|
|
216
|
+
session.add(new_record)
|
|
217
|
+
insert_count += 1
|
|
218
|
+
|
|
219
|
+
session.commit()
|
|
220
|
+
print(f"成功处理 {len(data_list)} 条账本记录数据")
|
|
221
|
+
print(f"新增记录: {insert_count} 条,更新记录: {update_count} 条")
|
|
222
|
+
|
|
223
|
+
except Exception as e:
|
|
224
|
+
session.rollback()
|
|
225
|
+
print(f"处理数据失败: {e}")
|
|
226
|
+
raise
|
|
227
|
+
finally:
|
|
228
|
+
session.close()
|
|
229
|
+
|
|
230
|
+
def _create_record_from_data(self, data):
|
|
231
|
+
"""
|
|
232
|
+
从JSON数据创建新的记录对象
|
|
233
|
+
"""
|
|
234
|
+
return SheinLedgerRecord(
|
|
235
|
+
ledger_id=str(data.get('id')),
|
|
236
|
+
display_change_type=data.get('displayChangeType'),
|
|
237
|
+
display_change_type_name=data.get('displayChangeTypeName'),
|
|
238
|
+
change_type=data.get('changeType'),
|
|
239
|
+
settle_type=data.get('settleType'),
|
|
240
|
+
settle_type_name=data.get('settleTypeName'),
|
|
241
|
+
business_no=data.get('businessNo'),
|
|
242
|
+
bill_no=data.get('billNo'),
|
|
243
|
+
uniq_key=data.get('uniqKey'),
|
|
244
|
+
happen_time=self._parse_datetime(data.get('happenTime')),
|
|
245
|
+
add_time=self._parse_datetime(data.get('addTime')),
|
|
246
|
+
supplier_id=data.get('supplierId'),
|
|
247
|
+
supplier_name=data.get('supplierName'),
|
|
248
|
+
supplier_code=data.get('supplierCode'),
|
|
249
|
+
skc=data.get('skc'),
|
|
250
|
+
sku=data.get('sku'),
|
|
251
|
+
supplier_sku=data.get('supplierSku'),
|
|
252
|
+
suffix_zh=data.get('suffixZh'),
|
|
253
|
+
quantity=data.get('quantity'),
|
|
254
|
+
cost=self._parse_decimal(data.get('cost')),
|
|
255
|
+
amount=self._parse_decimal(data.get('amount')),
|
|
256
|
+
currency=data.get('currency'),
|
|
257
|
+
remark=data.get('remark'),
|
|
258
|
+
before_inventory_id=data.get('beforeInventoryId'),
|
|
259
|
+
after_inventory_id=data.get('afterInventoryId'),
|
|
260
|
+
before_business_no=data.get('beforeBusinessNo'),
|
|
261
|
+
after_business_no=data.get('afterBusinessNo'),
|
|
262
|
+
before_bill_no=data.get('beforeBillNo'),
|
|
263
|
+
after_bill_no=data.get('afterBillNo'),
|
|
264
|
+
after_change_type_name=data.get('afterChangeTypeName'),
|
|
265
|
+
business_no_tags=data.get('businessNoTags'),
|
|
266
|
+
source_system=data.get('sourceSystem'),
|
|
267
|
+
show_promotion=data.get('showPromotion'),
|
|
268
|
+
sales_seller_id=data.get('salesSellerId'),
|
|
269
|
+
sales_seller_name=data.get('salesSellerName'),
|
|
270
|
+
store_username=data.get('store_username'),
|
|
271
|
+
store_name=data.get('store_name'),
|
|
272
|
+
store_manager=data.get('store_manager'),
|
|
273
|
+
cost_price=self._parse_cost_price(data.get('cost_price')),
|
|
274
|
+
sku_img=data.get('sku_img')
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
def _update_record_from_data(self, record, data):
|
|
278
|
+
"""
|
|
279
|
+
使用JSON数据更新现有记录
|
|
280
|
+
"""
|
|
281
|
+
record.display_change_type = data.get('displayChangeType')
|
|
282
|
+
record.display_change_type_name = data.get('displayChangeTypeName')
|
|
283
|
+
record.change_type = data.get('changeType')
|
|
284
|
+
record.settle_type = data.get('settleType')
|
|
285
|
+
record.settle_type_name = data.get('settleTypeName')
|
|
286
|
+
record.business_no = data.get('businessNo')
|
|
287
|
+
record.bill_no = data.get('billNo')
|
|
288
|
+
record.uniq_key = data.get('uniqKey')
|
|
289
|
+
record.happen_time = self._parse_datetime(data.get('happenTime'))
|
|
290
|
+
record.add_time = self._parse_datetime(data.get('addTime'))
|
|
291
|
+
record.supplier_id = data.get('supplierId')
|
|
292
|
+
record.supplier_name = data.get('supplierName')
|
|
293
|
+
record.supplier_code = data.get('supplierCode')
|
|
294
|
+
record.skc = data.get('skc')
|
|
295
|
+
record.sku = data.get('sku')
|
|
296
|
+
record.supplier_sku = data.get('supplierSku')
|
|
297
|
+
record.suffix_zh = data.get('suffixZh')
|
|
298
|
+
record.quantity = data.get('quantity')
|
|
299
|
+
record.cost = self._parse_decimal(data.get('cost'))
|
|
300
|
+
record.amount = self._parse_decimal(data.get('amount'))
|
|
301
|
+
record.currency = data.get('currency')
|
|
302
|
+
record.remark = data.get('remark')
|
|
303
|
+
record.before_inventory_id = data.get('beforeInventoryId')
|
|
304
|
+
record.after_inventory_id = data.get('afterInventoryId')
|
|
305
|
+
record.before_business_no = data.get('beforeBusinessNo')
|
|
306
|
+
record.after_business_no = data.get('afterBusinessNo')
|
|
307
|
+
record.before_bill_no = data.get('beforeBillNo')
|
|
308
|
+
record.after_bill_no = data.get('afterBillNo')
|
|
309
|
+
record.after_change_type_name = data.get('afterChangeTypeName')
|
|
310
|
+
record.business_no_tags = data.get('businessNoTags')
|
|
311
|
+
record.source_system = data.get('sourceSystem')
|
|
312
|
+
record.show_promotion = data.get('showPromotion')
|
|
313
|
+
record.sales_seller_id = data.get('salesSellerId')
|
|
314
|
+
record.sales_seller_name = data.get('salesSellerName')
|
|
315
|
+
record.store_username = data.get('store_username')
|
|
316
|
+
record.store_name = data.get('store_name')
|
|
317
|
+
record.store_manager = data.get('store_manager')
|
|
318
|
+
record.cost_price = self._parse_cost_price(data.get('cost_price'))
|
|
319
|
+
record.sku_img = data.get('sku_img')
|
|
320
|
+
record.updated_at = datetime.now()
|
|
321
|
+
|
|
322
|
+
def get_ledger_records(self, limit=None, offset=None, order_by=None):
|
|
323
|
+
"""
|
|
324
|
+
查询账本记录列表
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
limit (int): 限制返回数量
|
|
328
|
+
offset (int): 偏移量
|
|
329
|
+
order_by (str): 排序字段,默认按happen_time降序
|
|
330
|
+
|
|
331
|
+
Returns:
|
|
332
|
+
list: 账本记录列表
|
|
333
|
+
"""
|
|
334
|
+
session = self.Session()
|
|
335
|
+
try:
|
|
336
|
+
query = session.query(SheinLedgerRecord)
|
|
337
|
+
|
|
338
|
+
# 默认按发生时间降序排列
|
|
339
|
+
if order_by:
|
|
340
|
+
if hasattr(SheinLedgerRecord, order_by):
|
|
341
|
+
query = query.order_by(getattr(SheinLedgerRecord, order_by).desc())
|
|
342
|
+
else:
|
|
343
|
+
query = query.order_by(SheinLedgerRecord.happen_time.desc())
|
|
344
|
+
|
|
345
|
+
if offset:
|
|
346
|
+
query = query.offset(offset)
|
|
347
|
+
if limit:
|
|
348
|
+
query = query.limit(limit)
|
|
349
|
+
return query.all()
|
|
350
|
+
finally:
|
|
351
|
+
session.close()
|
|
352
|
+
|
|
353
|
+
def search_records(self, **kwargs):
|
|
354
|
+
"""
|
|
355
|
+
根据条件搜索账本记录
|
|
356
|
+
|
|
357
|
+
Args:
|
|
358
|
+
**kwargs: 搜索条件,如store_username, business_no, sku等
|
|
359
|
+
|
|
360
|
+
Returns:
|
|
361
|
+
list: 符合条件的记录列表
|
|
362
|
+
"""
|
|
363
|
+
session = self.Session()
|
|
364
|
+
try:
|
|
365
|
+
query = session.query(SheinLedgerRecord)
|
|
366
|
+
|
|
367
|
+
# 根据传入的条件进行过滤
|
|
368
|
+
for key, value in kwargs.items():
|
|
369
|
+
if hasattr(SheinLedgerRecord, key) and value is not None:
|
|
370
|
+
if isinstance(value, str) and key in ['business_no', 'bill_no', 'skc', 'sku', 'supplier_name', 'store_name']:
|
|
371
|
+
# 字符串字段支持模糊搜索
|
|
372
|
+
query = query.filter(getattr(SheinLedgerRecord, key).like(f'%{value}%'))
|
|
373
|
+
else:
|
|
374
|
+
query = query.filter(getattr(SheinLedgerRecord, key) == value)
|
|
375
|
+
|
|
376
|
+
return query.order_by(SheinLedgerRecord.happen_time.desc()).all()
|
|
377
|
+
finally:
|
|
378
|
+
session.close()
|
|
379
|
+
|
|
380
|
+
def get_statistics_by_store(self, store_username=None):
|
|
381
|
+
"""
|
|
382
|
+
按店铺统计数据
|
|
383
|
+
|
|
384
|
+
Args:
|
|
385
|
+
store_username (str): 店铺用户名,如果不提供则统计所有店铺
|
|
386
|
+
|
|
387
|
+
Returns:
|
|
388
|
+
dict: 统计结果
|
|
389
|
+
"""
|
|
390
|
+
session = self.Session()
|
|
391
|
+
try:
|
|
392
|
+
query = session.query(SheinLedgerRecord)
|
|
393
|
+
|
|
394
|
+
if store_username:
|
|
395
|
+
query = query.filter(SheinLedgerRecord.store_username == store_username)
|
|
396
|
+
|
|
397
|
+
records = query.all()
|
|
398
|
+
|
|
399
|
+
# 统计信息
|
|
400
|
+
total_count = len(records)
|
|
401
|
+
total_amount = sum([r.amount for r in records if r.amount])
|
|
402
|
+
total_cost = sum([r.cost for r in records if r.cost])
|
|
403
|
+
total_quantity = sum([r.quantity for r in records if r.quantity])
|
|
404
|
+
|
|
405
|
+
# 按变更类型统计
|
|
406
|
+
change_type_stats = {}
|
|
407
|
+
for record in records:
|
|
408
|
+
change_type = record.display_change_type_name or '未知'
|
|
409
|
+
if change_type not in change_type_stats:
|
|
410
|
+
change_type_stats[change_type] = {'count': 0, 'amount': 0, 'quantity': 0}
|
|
411
|
+
change_type_stats[change_type]['count'] += 1
|
|
412
|
+
change_type_stats[change_type]['amount'] += record.amount or 0
|
|
413
|
+
change_type_stats[change_type]['quantity'] += record.quantity or 0
|
|
414
|
+
|
|
415
|
+
return {
|
|
416
|
+
'total_count' : total_count,
|
|
417
|
+
'total_amount' : float(total_amount) if total_amount else 0,
|
|
418
|
+
'total_cost' : float(total_cost) if total_cost else 0,
|
|
419
|
+
'total_quantity' : total_quantity,
|
|
420
|
+
'change_type_stats': change_type_stats
|
|
421
|
+
}
|
|
422
|
+
finally:
|
|
423
|
+
session.close()
|
|
424
|
+
|
|
425
|
+
def import_from_json_file(self, json_file_path):
|
|
426
|
+
"""
|
|
427
|
+
从JSON文件导入数据
|
|
428
|
+
|
|
429
|
+
Args:
|
|
430
|
+
json_file_path (str): JSON文件路径
|
|
431
|
+
"""
|
|
432
|
+
with open(json_file_path, 'r', encoding='utf-8') as f:
|
|
433
|
+
data_list = json.load(f)
|
|
434
|
+
self.upsert_ledger_data(data_list)
|
|
435
|
+
|
|
436
|
+
def example_usage():
|
|
437
|
+
"""
|
|
438
|
+
使用示例
|
|
439
|
+
"""
|
|
440
|
+
# 数据库连接URL(请根据实际情况修改)
|
|
441
|
+
database_url = "mysql+pymysql://root:123wyk@localhost:3306/lz"
|
|
442
|
+
|
|
443
|
+
# 创建管理器实例
|
|
444
|
+
manager = SheinLedgerManager(database_url)
|
|
445
|
+
|
|
446
|
+
# 创建数据表
|
|
447
|
+
manager.create_tables()
|
|
448
|
+
|
|
449
|
+
# 从JSON文件导入数据
|
|
450
|
+
json_file = "ledger_record_GS0365305_2025-09-24_2025-09-24.json"
|
|
451
|
+
manager.import_from_json_file(json_file)
|
|
452
|
+
|
|
453
|
+
# 查询示例
|
|
454
|
+
records = manager.get_ledger_records(limit=10)
|
|
455
|
+
for record in records:
|
|
456
|
+
print(f"记录ID: {record.ledger_id}, 业务单号: {record.business_no}, 金额: {record.amount}")
|
|
457
|
+
|
|
458
|
+
# 搜索示例
|
|
459
|
+
search_results = manager.search_records(store_username="GS0365305")
|
|
460
|
+
print(f"店铺 GS0365305 的记录数量: {len(search_results)}")
|
|
461
|
+
|
|
462
|
+
# 统计示例
|
|
463
|
+
stats = manager.get_statistics_by_store("GS0365305")
|
|
464
|
+
print(f"统计结果: {stats}")
|
|
465
|
+
|
|
466
|
+
if __name__ == "__main__":
|
|
467
|
+
pass
|
|
468
|
+
# example_usage()
|