demo_外购监测.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. # -*- codeing = utf-8 -*-
  2. # @Time : 2023/3/12 16:05
  3. # @Author : Clown
  4. # @File : demo_外购监测.py
  5. # @Software : PyCharm
  6. import pandas as pd
  7. import pymysql
  8. import json
  9. from dateutil.parser import parse
  10. from datetime import datetime,timedelta
  11. from demo_id配对工具 import selectCustomerIdForm
  12. import time
  13. def linkTomySql(host, passwd, db_name, port):
  14. '''连接至数据库返回【db】,v2新增local_infile=1 打开文件导入权限'''
  15. try:
  16. # 本地连接为:localhost 服务器连接为:124.222.188.59
  17. db = pymysql.connect (
  18. host=host, user="root",
  19. passwd=passwd,
  20. db=db_name,
  21. port=port,
  22. charset='utf8mb4',
  23. local_infile=1)
  24. print ('\nconnect to mysql server 成功')
  25. print ('---------------------------------------')
  26. except:
  27. print ("\ncould not connect to mysql server")
  28. db = "连接失败"
  29. return db
  30. #按要求筛选门店,当前要求为满足近30天单量大于0的门店
  31. def selectShopsMeetTheRequirements(db,end_date,requ_fate,file_path):
  32. wmplate_dict = {'美团':'meituan',
  33. '饿了么':'eleme'}
  34. cursor = db.cursor()
  35. end_date = parse (end_date).date ()
  36. start_date = (end_date + timedelta (days=-requ_fate+1)).strftime ('%Y-%m-%d')
  37. print(start_date,end_date)
  38. sql = f'''SELECT
  39. `品牌`,
  40. `平台`,
  41. `门店id`,
  42. SUM(`收入`) AS `总收入`,
  43. SUM(`有效单量`) AS `总单量`,
  44. COUNT(`日期`) AS `营业天数`
  45. FROM
  46. shop_daily_operation_data_app
  47. WHERE
  48. `有效单量` > 0
  49. AND `日期` >= '{start_date}'
  50. AND `日期` <= '{end_date}'
  51. GROUP BY
  52. `品牌`,
  53. `平台`,
  54. `门店id`
  55. ORDER BY
  56. `营业天数` DESC'''
  57. cursor.execute(sql)
  58. value_out_list = cursor.fetchall ()
  59. print(len(value_out_list))
  60. df_out = []
  61. for value_out in value_out_list:
  62. row = list(value_out)
  63. out_list = selectCustomerIdForm(db,int(row[2]),wmplate_dict[row[1]])
  64. if len(out_list) == 0:
  65. customer_id = ''
  66. customer_name = ''
  67. yidinghuo_id = ''
  68. else:
  69. customer_id = out_list[0][0]
  70. customer_name = json.loads(out_list[0][1])['customer_name']
  71. yidinghuo_ids = json.loads (out_list[0][1])['yidinghuo']
  72. if len(yidinghuo_ids) == 0:
  73. yidinghuo_id = ''
  74. elif len(yidinghuo_ids) == 1:
  75. yidinghuo_id = yidinghuo_ids[0]
  76. else:
  77. yidinghuo_id = ','.join(yidinghuo_ids)
  78. row.append(customer_id)
  79. row.append(customer_name)
  80. row.append (yidinghuo_id)
  81. df_out.append(row)
  82. df_out = pd.DataFrame(df_out,columns=['品牌','平台','门店id','总收入','总单量','营业天数','店务通编号','门店名称','易订货id'])
  83. file_path = file_path+f'/selectShopsMeetTheRequirements【{start_date}to{end_date}】.xlsx'
  84. df_out.to_excel(file_path)
  85. cursor.close()
  86. return file_path
  87. def selectCustomerInfo(db,customer_id):
  88. cursor = db.cursor ()
  89. sql = f'''SELECT * FROM customer_id_form WHERE customer_id = '{str(customer_id)}';'''
  90. cursor.execute (sql)
  91. value_out_list = cursor.fetchall ()
  92. cursor.close ()
  93. if len(value_out_list)==0:
  94. dict_out = 0
  95. else:
  96. dict_out = json.loads(value_out_list[0][1])
  97. return dict_out
  98. def sku_in_jianguan_dict(db):
  99. cursor = db.cursor ()
  100. sql = '''SELECT * FROM supply_chains_waigoujicha'''
  101. cursor.execute (sql)
  102. value_out_list = cursor.fetchall ()
  103. cursor.close ()
  104. df_in = pd.DataFrame(value_out_list,columns=['品牌','商品关键词','供应链原料id','原料名称','售卖单位','单件数量','考核方式(商品/单量)'])
  105. brand_names = list(set(df_in['品牌']))
  106. dict_out = {}
  107. for brand_name in brand_names:
  108. dict_out[brand_name] = {}
  109. df_in0 = df_in[df_in['品牌']==brand_name]
  110. sku_kinds = list(set(df_in0['考核方式(商品/单量)']))
  111. for sku_kind in sku_kinds:
  112. dict_out[brand_name][sku_kind]=[]
  113. df_in1 = df_in0[df_in0['考核方式(商品/单量)'] == sku_kind]
  114. sku_key_names = list(set(df_in1['商品关键词']))
  115. for sku_key_name in sku_key_names:
  116. df_in2 = df_in1[df_in1['商品关键词'] == sku_key_name]
  117. sku_ids = list(set(df_in2['供应链原料id']))
  118. dict_out0 = {'sku_key_name':sku_key_name,
  119. 'sku_ids':sku_ids}
  120. dict_out[brand_name][sku_kind].append(dict_out0)
  121. # print(dict_out)
  122. return dict_out,df_in
  123. def mapWmOrderData(db,max,min,wm_ids):
  124. cursor = db.cursor ()
  125. if max == min:
  126. sql = f'''SELECT
  127. SUM(`收入`) AS `区间收入`,
  128. SUM(`有效单量`) AS `区间单量`
  129. FROM
  130. shop_daily_operation_data_app
  131. WHERE
  132. `门店id` IN {wm_ids}
  133. AND `日期` >= '{min}'
  134. AND `日期` <= '{max}';'''
  135. else:
  136. sql = f'''SELECT
  137. SUM(`收入`) AS `区间收入`,
  138. SUM(`有效单量`) AS `区间单量`
  139. FROM
  140. shop_daily_operation_data_app
  141. WHERE
  142. `门店id` IN {wm_ids}
  143. AND `日期` > '{min}'
  144. AND `日期` <= '{max}';'''
  145. cursor.execute (sql)
  146. value_out_list = cursor.fetchall ()
  147. cursor.close ()
  148. row_cnt = len (value_out_list)
  149. if row_cnt == 0:
  150. value_out = {'区间收入':0,
  151. '区间单量':0}
  152. else:
  153. value_out = {'区间收入':value_out_list[0][0],
  154. '区间单量':value_out_list[0][1]}
  155. return value_out
  156. def mapOrderData(db,end_date,customer_id,customer_name,wm_ids,dh_ids,sku_ids,sku_key_name):
  157. cursor = db.cursor ()
  158. sql = f'''SELECT t3.`ordersDate`,SUM( t3.`totalNum` ) AS `totalNum` FROM (SELECT t1.`ordersDate`,t1.`skuId`,t1.`skuPcs`,t1.`totalNum` * t2.`PcsNum` AS `totalNum` FROM (SELECT `ordersDate`,`skuId`,`skuPcs`,SUM( `skuOrdNum` ) AS `totalNum` FROM supply_chains_orders_app WHERE `skuId` IN {sku_ids} AND `customersId` IN {dh_ids} AND `ordersDate` <= '{end_date}' GROUP BY `ordersDate`,`skuId`,`skuPcs` ORDER BY `ordersDate` DESC )t1 LEFT JOIN (SELECT * FROM supply_chains_waigoujicha)t2 ON t1.`skuId` = t2.`skuId` AND t1.`skuPcs` = t2.`skuPcs` ) t3 GROUP BY t3.`ordersDate` ORDER BY t3.`ordersDate` DESC;'''
  159. cursor.execute (sql)
  160. value_out_list = cursor.fetchall ()
  161. cursor.close()
  162. row_cnt = len(value_out_list)
  163. #基础参数
  164. max = 0
  165. min = ''
  166. i = -1
  167. result = ''
  168. result_error = ''
  169. result_ok = '订货正常'
  170. result_no = '订货异常'
  171. result_label = ''
  172. totalNum_sum = 0
  173. odr_cnt_sum = 0
  174. totalNum_near1 = 0
  175. odr_cnt_near1 = 0
  176. difference_near1 = 0
  177. min_near = 0
  178. #输出列表
  179. df_out_detail = []
  180. df_out_result = []
  181. if row_cnt == 0:
  182. result = f'门店无【{sku_key_name}】订货记录'
  183. result_error = f'门店过往无【{sku_key_name}】订货记录,请核实门店供应平台的id是否有效或是否完整'
  184. result_label = '无法查询'
  185. else:
  186. for i in range(row_cnt):
  187. if i == 0:
  188. max = end_date
  189. else:
  190. max = value_out_list[i - 1][0]
  191. min = value_out_list[i][0]
  192. totalNum = value_out_list[i][1]
  193. #查询订单逻辑 min< =<max
  194. wmOrder = mapWmOrderData (db, max, min, wm_ids)
  195. income = wmOrder['区间收入']
  196. odr_cnt = wmOrder['区间单量']
  197. if income is None:
  198. result_error = f'此门店可能无订货记录,或平台id可能存在不全,或历史有过平台店铺重开操作,平台数据统计截至{max}【此情况下门店订货数据统计仅统计至该截至日期,截至日期前的【{sku_key_name}】订货数据将不纳入监测范围】'
  199. result_label = '数据不完整'
  200. break
  201. else:
  202. if i == 0:
  203. totalNum_near1 = int (totalNum)
  204. odr_cnt_near1 = int (odr_cnt)
  205. difference_near1 = odr_cnt_near1 - totalNum_near1
  206. min_near = min
  207. totalNum_sum = totalNum_sum + totalNum
  208. odr_cnt_sum = odr_cnt_sum + odr_cnt
  209. detail_dict = {'门店编号':str(customer_id),
  210. '门店名称':customer_name,
  211. '商品关键词':sku_key_name,
  212. '批次(由近至远)':i+1,
  213. '区间开始日期<':min,
  214. '区间结束日期≥':max,
  215. '批次订货量':totalNum,
  216. '区间平台单量':odr_cnt,
  217. '区间平台收入':income}
  218. df_out_detail.append(detail_dict)
  219. # print(detail_dict)
  220. # print (customer_id,i + 1, max, min, totalNum, income, odr_cnt)
  221. if totalNum_sum >= odr_cnt_sum:
  222. result = f'基于门店近{i + 1}次【{sku_key_name}】订货记录及对应周期订单数据判断,截至{min}门店【{result_ok}】'
  223. result_label = result_ok
  224. break
  225. else:
  226. result = f'基于门店近{i + 1}次【{sku_key_name}】订货记录【订货量{totalNum_sum}】及对应周期订单数据【单量{odr_cnt_sum}】判断,截至{min}门店【{result_no}】,按最近一次订货日期【{min_near}】订货量【{totalNum_near1}】至数据起算日【{end_date}】单量【{odr_cnt_near1}】差额计违约数量为【{difference_near1}】'
  227. result_label = result_no
  228. if result_label == '订货正常':
  229. result_dict = {'门店编号':str(customer_id),
  230. '门店名称':customer_name,
  231. '商品关键词':sku_key_name,
  232. '监管数据起算日':end_date,
  233. '监管数据计算截至日':min,
  234. '区间订货批次数':i+1,
  235. '区间订货量':int(totalNum_sum),
  236. '区间单量':int(odr_cnt_sum),
  237. '判定结果':result_label,
  238. '备注':result,
  239. '最近一次订货量':'',
  240. '截至数据起算日单量':'',
  241. '违约数量':'',
  242. '执行日志':result_error}
  243. else:
  244. result_dict = {'门店编号': str (customer_id),
  245. '门店名称': customer_name,
  246. '商品关键词': sku_key_name,
  247. '监管数据起算日': end_date,
  248. '监管数据计算截至日': min,
  249. '区间订货批次数': i + 1,
  250. '区间订货量': int (totalNum_sum),
  251. '区间单量': int (odr_cnt_sum),
  252. '判定结果': result_label,
  253. '备注': result,
  254. '最近一次订货量': totalNum_near1,
  255. '截至数据起算日单量': odr_cnt_near1,
  256. '违约数量': difference_near1,
  257. '执行日志': result_error}
  258. df_out_result.append(result_dict)
  259. # print(result_dict)
  260. # print(customer_id,customer_name,totalNum_sum,odr_cnt_sum,result_label,result,result_error)
  261. return df_out_detail,df_out_result
  262. def waiGouJiCha(db,file_path,file_path_out,sku_in_jianguan_dict,df_sup_goods,end_date):
  263. end_date = parse (end_date).date ()
  264. df_in = pd.read_excel(file_path)
  265. cnt = 0
  266. customer_id_list = list(set(df_in['店务通编号']))
  267. df_out_detail = []
  268. df_out_result = []
  269. for customer_id in customer_id_list:
  270. cnt += 1
  271. dict_out = selectCustomerInfo (db, customer_id)
  272. if dict_out == 0:
  273. print(f'当前店务通门店编码【{customer_id}】在customer_id_form中无返回值')
  274. else:
  275. customer_name = dict_out['customer_name'] #门店名
  276. wm_ids = tuple(dict_out['meituan'] + dict_out['eleme']+['',''])#外卖平台id
  277. dh_id = list(dict_out['yidinghuo'])
  278. dh_id.append(customer_id)
  279. dh_ids = tuple(dh_id+['',''])#订货平台id
  280. brand_name = list(set(df_in[df_in['店务通编号']==customer_id]['品牌']))[0]
  281. sku_jianguan_kinds = ['单量']
  282. for sku_jianguan_kind in sku_jianguan_kinds:
  283. sku_infos = sku_in_jianguan_dict[brand_name][sku_jianguan_kind]
  284. for sku_info in sku_infos:
  285. sku_key_name = sku_info['sku_key_name']
  286. sku_ids = tuple(sku_info['sku_ids']+['',''])#商品id
  287. print (customer_id, customer_name, wm_ids, dh_ids, sku_ids, sku_key_name)
  288. out_detail,out_result = mapOrderData (db, end_date,customer_id,customer_name, wm_ids, dh_ids, sku_ids, sku_key_name)
  289. df_out_detail = df_out_detail + out_detail
  290. df_out_result = df_out_result + out_result
  291. print(f'共计门店数{cnt}')
  292. df_out_detail = pd.DataFrame(df_out_detail)
  293. df_out_result = pd.DataFrame(df_out_result)
  294. writer = pd.ExcelWriter (file_path_out+f'/门店稽查数据报告(数据截至日期{end_date}).xlsx')
  295. df_out_result.to_excel(writer,sheet_name='外购稽查结果',index=False)
  296. df_out_detail.to_excel(writer,sheet_name='稽查数据源',index=False)
  297. df_in.to_excel (writer, sheet_name='门店清单',columns=['品牌', '平台', '门店id', '总收入', '总单量', '营业天数', '店务通编号', '门店名称', '易订货id'], index=False)
  298. df_sup_goods.to_excel(writer,sheet_name='监管产品清单',index=False)
  299. writer.save()
  300. if __name__ == '__main__':
  301. time_start = time.time()
  302. if 1 == 1:
  303. host = 'localhost'
  304. passwd = '111???clown'
  305. db_name = 'hexingxing'
  306. port = 3306
  307. db = linkTomySql (host, passwd, db_name, port)
  308. #end_date必须为有数据的当天
  309. end_date = '20230312'
  310. requ_fate = 30
  311. if 1==0:
  312. wm_ids = [13223788,506074673]
  313. wm_ids = tuple(wm_ids+['',''])
  314. customer_id = '6917635395090128896'
  315. customer_name = '周浦店'
  316. dh_ids = ["6917635395090128896",'20211019003325']
  317. dh_ids = tuple(dh_ids+['',''])
  318. # sku_ids = ["P2953993404",'P5665728343']
  319. # sku_ids = ["P1956508934", 'P5456508916', 'P6958185748', 'P1658181079','P2562679809']
  320. sku_ids = ["P5864140704",'P864145484']
  321. sku_ids = tuple(sku_ids+['',''])
  322. sku_key_name = '四件套'
  323. end_date = parse (end_date).date ()
  324. mapOrderData (db, end_date,customer_id,customer_name, wm_ids, dh_ids, sku_ids, sku_key_name)
  325. if 1== 1:
  326. try:
  327. file_path = r'C:\Users\ClownHe\Desktop\门店外购监测'
  328. customer_df_path = selectShopsMeetTheRequirements (db, end_date, requ_fate, file_path)
  329. sku_in_jianguan_dict,df_sup_goods = sku_in_jianguan_dict (db)
  330. waiGouJiCha(db,customer_df_path,file_path,sku_in_jianguan_dict,df_sup_goods,end_date)
  331. except Exception as e:
  332. print(e)
  333. if 1 == 0:
  334. file_path = r'C:\Users\ClownHe\Desktop\门店外购监测' + '/供应链考核规则表-脚本.xlsx'
  335. sku_in_jianguan_dict (db)
  336. db.close ()
  337. time_end = time.time()
  338. print(f'总执行时间{int(time_end -time_start)/60}min')