addproject

This commit is contained in:
qiaoxinjiu
2026-01-22 19:10:37 +08:00
commit 6994b185a3
184 changed files with 21039 additions and 0 deletions

0
zhyy/__init__.py Normal file
View File

View File

@@ -0,0 +1,97 @@
# -*- coding:utf-8 -*-
"""
Author: qiaoxinjiu
Email: qiaoxinjiu@sparkedu.com
Create Date: 2026/01/22 5:58 下午
"""
import logging
import os
import sys
# 添加项目根目录到 Python 路径,以便导入 base_framework 模块
current_file_path = os.path.abspath(__file__)
project_root = os.path.abspath(os.path.join(os.path.dirname(current_file_path), '../../../../'))
if project_root not in sys.path:
sys.path.insert(0, project_root)
from base_framework.public_tools import log
from base_framework.public_tools.my_faker import MyFaker
from base_framework.public_tools.runner import Runner
from base_framework.public_tools.pgsqlhelper import PgSqlHelper
from base_framework.public_tools import read_config
from base_framework.public_tools import utils
from base_framework.public_tools import mg_keyword
from zhyy.library.ZZYY_interface import ZhyyInterface
obj_get_log = log.get_logger()
obj_my_faker = MyFaker()
obj_runner = Runner()
obj_pgsql_helper = PgSqlHelper()
obj_get_way = utils.Tools()
obj_mg_keyword = mg_keyword.ManageKeyWord()
class PurchaseOrder(ZhyyInterface):
def __init__(self):
'''
这个是针对于读取配置文件的初始化函数,用于读取默认参数
'''
super().__init__()
self.config_index_path = os.path.dirname(os.path.abspath(__file__))
self.config_index_filePath = os.path.join(self.config_index_path, "purchase.ini")
self.config_index_content = read_config.ReadConfig(filename=self.config_index_filePath)
def kw_zhyy_get_purchase_page_post(self, note, user, **kwargs):
"""
| 功能说明: | 返回采购工作台采购单列表数据 |
| 输入参数: | note | 注释 |
|user | 用户信息,传入 'purchase' 默认读取配置文件里面 'purchase' 对应的默认账号信息|
|supplier_company_ids | 供应商id | 非必填
|payment_status | 付款状态 | 非必填
|status | 采购单状态 | 非必填
|order_sn | 采购单号 | 非必填
|page_no | 页码 | 必填
|page_size | 每页条数 | 必填
| 返回参数: | {"success":true,"message":"success","code":200,"data":
{'todoTask':['PO260116003','PO260115010'],'inProcessTask':['PO260116003','PO260115010']}} | |
| 作者信息: | 谯新久 | 修改时间 | 2022-8-20 |
"""
logging.info("==========={0}===========".format(note))
# 获取所有参数
supplier_company_ids = kwargs.get("supplier_company_ids")
payment_status = kwargs.get("payment_status")
status = kwargs.get("status")
order_sn = kwargs.get("order_sn")
page_no = kwargs.get("page_no")
page_size = kwargs.get("page_size")
# 检查必填参数
if not page_no or not page_size:
raise Exception("页码和每页条数不能为空")
# 组装参数字典,只包含非空字段,参数名使用 pageNo 和 pageSize
request_params = {
"pageNo": page_no,
"pageSize": page_size
}
# 如果字段不为空,才添加到参数字典中
if supplier_company_ids is not None and supplier_company_ids != "":
request_params["supplier_company_ids"] = supplier_company_ids
if payment_status is not None and payment_status != "":
request_params["payment_status"] = payment_status
if status is not None and status != "":
request_params["status"] = status
if order_sn is not None and order_sn != "":
request_params["order_sn"] = order_sn
# 使用 ** 方式解包字典传递参数
get_todo_info = self.kw_in_zhyy_purchase_order_page_post(user=user, **request_params)
print(get_todo_info if get_todo_info else "查询失败")
return get_todo_info
if __name__ == '__main__':
test = PurchaseOrder()
a = test.kw_zhyy_get_purchase_page_post(user='purchase', note="测试", page_no=1, page_size=10)
print(a)

View File

@@ -0,0 +1,88 @@
# -*- coding:utf-8 -*-
"""
Author: qiaoxinjiu
Email: qiaoxinjiu@sparkedu.com
Create Date: 2022/08/20 5:58 下午
"""
import logging
import os
import sys
# 添加项目根目录到 Python 路径,以便导入 base_framework 模块
current_file_path = os.path.abspath(__file__)
project_root = os.path.abspath(os.path.join(os.path.dirname(current_file_path), '../../../../'))
if project_root not in sys.path:
sys.path.insert(0, project_root)
from base_framework.public_tools import log
from base_framework.public_tools.my_faker import MyFaker
from base_framework.public_tools.runner import Runner
from base_framework.public_tools.pgsqlhelper import PgSqlHelper
from base_framework.public_tools import read_config
from base_framework.public_tools import utils
from base_framework.public_tools import mg_keyword
from zhyy.library.ZZYY_interface import ZhyyInterface
obj_get_log = log.get_logger()
obj_my_faker = MyFaker()
obj_runner = Runner()
obj_pgsql_helper = PgSqlHelper()
obj_get_way = utils.Tools()
obj_mg_keyword = mg_keyword.ManageKeyWord()
class PurchaseIndex(ZhyyInterface):
def __init__(self):
'''
这个是针对于读取配置文件的初始化函数,用于读取默认参数
'''
super().__init__()
self.config_index_path = os.path.dirname(os.path.abspath(__file__))
self.config_index_filePath = os.path.join(self.config_index_path, "purchase.ini")
self.config_index_content = read_config.ReadConfig(filename=self.config_index_filePath)
def kw_zhyy_get_todo(self, note, user):
"""
| 功能说明: | 返回采购工作台首页待办任务的PO与在办任务PO |
| 输入参数: | note | 注释 |
|user | 用户信息,传入 'purchase' 默认读取配置文件里面 'purchase' 对应的默认账号信息|
| 返回参数: | {"success":true,"message":"success","code":200,"data":
{'todoTask':['PO260116003','PO260115010'],'inProcessTask':['PO260116003','PO260115010']}} | |
| 作者信息: | 谯新久 | 修改时间 | 2022-8-20 |
"""
logging.info("==========={0}===========".format(note))
get_todo_info = self.kw_in_zhyy_purchase_todo_get(user=user)
if get_todo_info['code'] != 0:
raise Exception("查询采购待办任务失败: {}".format(get_todo_info))
get_todo_info["message"] = "查询采购待办任务成功"
data = get_todo_info.get("data")
if data is None:
raise Exception("返回数据为空data字段不存在")
list_get_todo_task = data.get("todoTask") or []
list_get_process_task = data.get("inProcessTask") or []
list_todo_task_po = []
list_process_task_po = []
for todoTask in list_get_todo_task:
if isinstance(todoTask, dict):
list_todo_task_po.append(todoTask.get("businessSn"))
for processTask in list_get_process_task:
if isinstance(processTask, dict):
list_process_task_po.append(processTask.get("businessSn"))
get_todo_info["data"]["todoTask"] = list_todo_task_po
get_todo_info["data"]["inProcessTask"] = list_process_task_po
if list_todo_task_po:
test_purchase = list_todo_task_po[0]
# 表在public schema中使用public.erp_purchase_order格式
sql = "SELECT * FROM public.erp_purchase_order WHERE order_sn = '{}'".format(test_purchase)
try:
obj_pgsql_helper.select_one(sql)
except Exception as e:
# 如果查询失败,记录日志但不影响主流程
obj_get_log.warning("查询采购订单表失败,订单号:{},错误:{}".format(test_purchase, str(e)))
return get_todo_info
if __name__ == '__main__':
test = PurchaseIndex()
a = test.kw_zhyy_get_todo(user='purchase',note="测试")
print(a)

View File

@@ -0,0 +1,2 @@
[qa-user]
user_info ={"studentId":21797349,"sex":0,"nickName":"auto st test","birthday":1640966400000,"avatar":"https://stalegacy.huohua.cn/image/huohua/avatar/default/default_avatar1.png"}

View File

View File

View File

@@ -0,0 +1,57 @@
# -*- coding:utf-8 -*-
"""
Author: 陈江
Email: chenjiang@huohua.cn
Create Date: 2022/03/03 11:25 下午
"""
from base_framework.public_tools import log
from base_framework.public_tools.sqlhelper import MySqLHelper
obj_log = log.get_logger()
obj_my_sql_helper = MySqLHelper()
class AssetCommon:
def __init__(self):
pass
def get_course_package_info_by_name(self, course_package_name):
"""
功能:根据套餐名称获取套餐基本信息
| 输入参数: | course_package_name string | 套餐名称 |
"""
sql = "SELECT id,code,name,price FROM `peppa`.`course_package` WHERE `name`='{}';".format(course_package_name)
return obj_my_sql_helper.select_one(sql)
def get_order_info_V2(self, **where_condition):
# user_id = None, course_package_id = None, course_id = None, business_line_type = None, order_id = None, order_code = None
ocp = ['user_id', 'business_line_type', 'order_id', 'order_code', 'status']
sprl = ['course_package_id', 'course_id']
where_str = None
for k in where_condition.keys():
t = ''
if k in sprl:
t = 'sprl'
else:
t = 'ocp'
if not where_str:
where_str = '{}.{}=\'{}\''.format(t, k, where_condition.get(k))
else:
where_str = '{} AND {}.{}=\'{}\''.format(where_str, t, k, where_condition.get(k))
if not where_str:
obj_log.error('参数必填')
return False
sql = "SELECT ocp.id as order_id,ocp.code,ocp.audit_status,ocp.status as order_status,ocp.user_id,ocp.user_address_id,ocp.business_line_type,sprl.course_package_id,sprl.course_id,sprl.subscribe_id,sprl.valid as subscribe_pre_valid,sprl.id as subscribe_pre_id FROM order_center.order_course_package ocp LEFT JOIN order_center.subscribe_pre_create_log sprl ON ocp.id=sprl.order_id WHERE {} ORDER BY ocp.id DESC LIMIT 1".format(
where_str.replace('order_id', 'id'))
obj_log.info(sql)
return obj_my_sql_helper.select_one(sql)
if __name__ == '__main__':
user_common_obj = AssetCommon()
print(user_common_obj.get_order_info(**{'order_id': 1193182, 'business_line_type': '1', 'status': 100}))

View File

@@ -0,0 +1,240 @@
# -*- coding:utf-8 -*-
# @Time : 2023/3/7 13:28
# @Author: luozhipeng
# @File : contract_pair_check.py
import os
import sys
input_team_name = sys.argv
BASIC_PATH = os.path.dirname(os.path.abspath(__file__))
TEAM_PATH = os.path.abspath(os.path.join(BASIC_PATH, '../../../{}'.format("base_framework")))
sys.path.append(TEAM_PATH)
PROJECT_PATH = os.path.abspath(os.path.join(BASIC_PATH, '../../..'))
sys.path.append(PROJECT_PATH)
from base_framework.public_tools.sqlhelper import MySqLHelper
from base_framework.public_tools.utils import Tools
import requests
import re
import json
import pymysql
obj_my_sql_helper = MySqLHelper()
class ContractPairCheck():
def __init__(self):
pass
def get_unfinished_interface(self):
sql_get_interface = "SELECT distinct (x.con_url) 提供方接口,x.pro_url 消费方接口,x.con_method 提供方请求方式,x.con_server 提供方服务,x.pro_server 消费方服务,x.at_num 自动化接口数 FROM sparkatp.contract_pair x WHERE x.at_num =0 and x.status =1 and x.con_server in (select si.server_name from sparkatp.swagger_info si where si.team in ('ubrd','GUE') and access_type IN (2)) and x.pro_server not in (select si.server_name from sparkatp.swagger_info si where si.team ='ubrd' and access_type IN (2)) and x.roles = 0 order by x.pro_server,x.con_url"
unfinished_interface = obj_my_sql_helper.select_all(sql_get_interface)
return unfinished_interface
def send_reshult(self):
headers = {"Content-Type": "application/json;charset=UTF-8"}
web_hook = "https://open.feishu.cn/open-apis/bot/v2/hook/9f3556b7-cb60-44bf-adbf-24b5b2552014"
contract_pair_intf = self.get_unfinished_interface()
if len(contract_pair_intf) != 0:
message_data = {"msg_type": "text", "content": {'text': '未完成自动化的契约对接口{}'.format(contract_pair_intf)}}
rsp = requests.post(url=web_hook, json=message_data, headers=headers)
class AutoInterfaceCheck():
def __init__(self):
pass
def get_unfinished_interface(self):
sql_get_interface = """select id,in_url from sparkatp.interface_info a WHERE( a.swagger_id in (SELECT id FROM sparkatp.swagger_info WHERE team in ("UBRD","GUE") and access_type not IN (0)) ) AND a.created_time > "2023-01-01 00:00:50" AND is_used = 1 and (case_numbers IS NULL or case_numbers='') and offline=0 and jira_id is null order by created_time """
unfinished_interface_list = obj_my_sql_helper.select_all(sql_get_interface)
return unfinished_interface_list
def get_interface_jira(self,interface):
sql_req_time = "select max(created_time) created_time from sparkatp.request_parameters rp where rp.interface_id ={}".format(
interface["id"])
req_max_time = obj_my_sql_helper.select_one(sql_req_time)['created_time']
sql_rep_time = "select max(created_time) created_time from sparkatp.response_parameters rp where rp.interface_id ={}".format(
interface["id"])
rep_max_time = obj_my_sql_helper.select_one(sql_rep_time)['created_time']
if rep_max_time and req_max_time:
if rep_max_time <= req_max_time:
sql_req_jira = "select distinct (jira_id) from sparkatp.request_parameters rp where rp.interface_id ={0} and created_time = '{1}'".format(
interface["id"], req_max_time)
interface_jira = obj_my_sql_helper.select_one(sql_req_jira)["jira_id"]
else:
sql_rep_jira = "select distinct (jira_id) from sparkatp.response_parameters rp where rp.interface_id ={0} and created_time = '{1}'".format(
interface["id"], rep_max_time)
interface_jira = obj_my_sql_helper.select_one(sql_rep_jira)["jira_id"]
elif rep_max_time and not req_max_time:
sql_rep_jira = "select distinct (jira_id) from sparkatp.response_parameters rp where rp.interface_id ={0} and created_time = '{1}'".format(
interface["id"], rep_max_time)
interface_jira = obj_my_sql_helper.select_one(sql_rep_jira)["jira_id"]
elif not rep_max_time and req_max_time:
sql_req_jira = "select distinct (jira_id) from sparkatp.request_parameters rp where rp.interface_id ={0} and created_time = '{1}'".format(
interface["id"], req_max_time)
interface_jira = obj_my_sql_helper.select_one(sql_req_jira)["jira_id"]
return interface_jira
def send_result(self):
unfinished_interface_list = self.get_unfinished_interface()
for interface in unfinished_interface_list:
interface['jira'] = self.get_interface_jira(interface)
if not interface['jira']:
interface['qa'] = None
else:
interface['qa'] = self.get_jira_qa(interface['jira'])
headers = {"Content-Type": "application/json;charset=UTF-8"}
web_hook = "https://open.feishu.cn/open-apis/bot/v2/hook/9f3556b7-cb60-44bf-adbf-24b5b2552014"
if len(unfinished_interface_list) != 0:
message_data = {"msg_type": "text", "content": {'text': 'QA公共环境未完成自动化的接口{}'.format(unfinished_interface_list)}}
rsp = requests.post(url=web_hook, json=message_data, headers=headers)
def get_jira_qa(self,jira):
try:
conn = pymysql.connect(host='10.250.200.53',user='root',password='peppa@test',database='tools',charset="utf8",port=3306)
except :
raise pymysql.OperationalError("连接数据库失败")
cn =conn.cursor()
sql = "SELECT tester FROM tools.tm_project where jira_number = '{}'".format(jira)
cn.execute(sql)
qa = cn.fetchall()
cn.close()
conn.close()
if qa:
return qa[0][0]
else:
return None
class CoverageCheck():
# 连接数据库
@staticmethod
def get_select(sql):
try:
conn = pymysql.connect(host='10.250.200.53', user='root', password='peppa@test', database='tools',
charset="utf8", port=3306)
except:
raise pymysql.OperationalError("连接数据库失败")
cn = conn.cursor()
cn.execute(sql)
res = cn.fetchall()
cn.close()
conn.close()
return res
# 获取未搜集覆盖率的项目
@staticmethod
def get_unfinished_coverage():
start_time = Tools().get_format_date(r_type=15, add_days=-2)
end_time = Tools().get_format_date(r_type=16, add_days=-1)
# 查询最近一天上线有后端代码变动,需要搜集覆盖率项目
need_sql = """SELECT env FROM tools.project_plan WHERE ID IN
(SELECT project_id FROM tools.project_tester WHERE tester IN ("陈洁","陈江","罗志鹏","谯新久","刘涛婷"))
AND status IN (14) AND rd_code_add_line>0 AND (it_start_date IS NOT NULL OR qa_start_date IS NOT null)
AND online_date BETWEEN '{}' AND '{}'""".format(start_time, end_time)
need_coverage_list = CoverageCheck().get_select(need_sql)
# 查询已搜集覆盖率项目
sql_implemented_sql = '''SELECT `env_name` FROM `sparkatp`.`build_jacoco` WHERE `team` = 'UBRD' AND `status` = '1'
AND `is_delete` = '0' AND `is_pass` = '1' '''
completed_coverage_list = obj_my_sql_helper.select_all(sql_implemented_sql)
unfinished_coverage_list = []
for item1 in range(0, len(need_coverage_list)):
unfinished_coverage_list.append(need_coverage_list[item1][0])
# 返回未搜集覆盖率的项目
for item in completed_coverage_list:
completed_jira = item["env_name"]
if completed_jira in unfinished_coverage_list:
unfinished_coverage_list.remove(completed_jira)
# 根据unfinished_coverage_list 查jira_name
jira_name_list = []
for item in unfinished_coverage_list:
env_name = item
sql = """SELECT jira_number FROM tools.project_plan WHERE env='{}'""".format(env_name)
jira_name = CoverageCheck().get_select(sql)
jira_name_list.append(jira_name[0][0])
return jira_name_list
# 获取未构建基线用例项目
@staticmethod
def get_not_bulid_jira():
start_time = Tools().get_format_date(r_type=15, add_days=-2)
end_time = Tools().get_format_date(r_type=16, add_days=-1)
# 返回有后端变动的jira (有后端代码变更&上线时间在3天内 + jira存在服务变更项目状态为sim测试
need_sql = """SELECT env FROM tools.project_plan WHERE
ID IN (SELECT project_id FROM tools.project_tester WHERE tester IN ("陈洁","陈江","罗志鹏","谯新久","刘涛婷"))
AND status IN (14) AND rd_code_add_line>0 AND online_date BETWEEN '{}' AND '{}'
AND qa_start_date IS NOT NULL UNION SELECT env FROM tools.project_plan WHERE ID IN (SELECT project_plan_id
FROM tools.project_plan_server) AND jira_number IN (SELECT jira_number FROM tools.tm_project WHERE status
IN (12) AND `tester` IN ("陈洁","陈江","罗志鹏","谯新久","刘涛婷") AND test_qa_time_consume>0)""".format(start_time,
end_time)
need_build_list = CoverageCheck().get_select(need_sql)
# 返回有构建基线用例jira
built_sql = """SELECT DISTINCT special_env FROM sparkatp.build_info WHERE run_type=2 AND team='UBRD' AND STATUS=2"""
built_list = obj_my_sql_helper.select_all(built_sql)
not_bulit_list = []
# 取出有后端改动得jira
for item1 in range(0, len(need_build_list)):
not_bulit_list.append(need_build_list[item1][0])
# 返回未构建基线用例jira
for item in built_list:
completed_jira = item["special_env"]
if completed_jira in not_bulit_list:
not_bulit_list.remove(completed_jira)
jira_name_list = []
for item in not_bulit_list:
env_name = item
sql = """SELECT jira_number FROM tools.project_plan WHERE env='{}'""".format(env_name)
jira_name = CoverageCheck().get_select(sql)
jira_name_list.append(jira_name[0][0])
return jira_name_list
# 发送消息
def send_result(self, type=1):
if type == 1:
jira_list = self.get_unfinished_coverage()
elif type == 2:
jira_list = self.get_not_bulid_jira()
send_list = []
for item in jira_list:
send_dict = {}
send_dict["jira"] = item
qa_list = AutoInterfaceCheck().get_jira_qa(item)
send_dict["qa"] = qa_list
send_list.append(send_dict)
headers = {"Content-Type": "application/json;charset=UTF-8"}
web_hook = "https://open.feishu.cn/open-apis/bot/v2/hook/b6bf33ae-4239-4bef-a8a8-21896e0d1ba1"
if len(jira_list) != 0:
if type == 1:
message_data = {"msg_type": "text",
"content": {'text': '未搜集覆盖率已上线项目{}'.format(send_list)}}
rsp = requests.post(url=web_hook, json=message_data, headers=headers)
elif type == 2:
message_data = {"msg_type": "text",
"content": {'text': '未构建基线用例已上线项目{}'.format(send_list)}}
rsp = requests.post(url=web_hook, json=message_data, headers=headers)
if __name__ == '__main__':
A = ContractPairCheck()
A.send_reshult()
B = AutoInterfaceCheck()
# B.get_unfinished_interface()
# B.get_jira_qa(jira = 'PLATFORM-31791')
B.send_result()
C = CoverageCheck()
C.send_result(type=1)
C.send_result(type=2)

View File

@@ -0,0 +1,31 @@
# -*- coding:utf-8 -*-
"""
Author: 罗志鹏
Email: luozhipeng@huohua.cn
Create Date: 2022/03/03 11:25 下午
"""
from base_framework.public_tools import log
from base_framework.public_tools.sqlhelper import MySqLHelper
obj_log = log.get_logger()
obj_my_sql_helper = MySqLHelper()
class CoursePackageCommon:
def __init__(self):
pass
def get_course_package_info_by_name(self, course_package_name):
"""
功能:根据套餐名称获取套餐基本信息
| 输入参数: | course_package_name string | 套餐名称 |
"""
sql = "SELECT id,code,name,price FROM `peppa`.`course_package` WHERE `name`='{}';".format(course_package_name)
return obj_my_sql_helper.select_one(sql)
if __name__ == '__main__':
user_common_obj = CoursePackageCommon()
user_common_obj.get_course_package_info_by_name('测试杰拉德0325-火花-直播逻辑思维')

View File

@@ -0,0 +1,31 @@
import matplotlib.pyplot as plt
import matplotlib
# 设置字体为支持中文的字体如SimHei
matplotlib.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统
# matplotlib.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # macOS系统
matplotlib.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# 数据
categories = ['供应链', '天窗', 'cc', 'la', '题库', '练测', '教务', '教师', '基础', '学生端', '家长端', '活字', '转介绍']
values = [8, 4, 13, 20, 11, 2, 22, 10, 1, 1, 14, 1, 17]
# 创建柱状图
plt.figure(figsize=(10, 6))
bars = plt.bar(categories, values, color='skyblue')
# 添加标题和标签
plt.title('各类别数量统计', fontsize=16)
plt.xlabel('类别', fontsize=12)
plt.ylabel('数量', fontsize=12)
plt.xticks(rotation=45, ha='right') # 旋转X轴标签
# 在每个柱子上方显示数量
for bar in bars:
height = bar.get_height()
plt.text(bar.get_x() + bar.get_width() / 2, height, str(height),
ha='center', va='bottom', fontsize=10)
# 显示图表
plt.tight_layout()
plt.show()

View File

@@ -0,0 +1,7 @@
[QA]
check_list = ['PEPPA-STUDENT-API','PEPPA-LEARNING-PLAN-LISTENER','PEPPA-LEARNING-PLAN-SERVER','PEPPA-TEACH-BIZ','PEPPA-TEACH-LISTENER','PEPPA-TEACH-TIMETABLE-SERVER','PEPPA-USER-AUTH-API','PEPPA-USER-CENTER-SERVER']
[SIM]
check_list = ['PEPPA-STUDENT-API','PEPPA-LEARNING-PLAN-LISTENER','PEPPA-LEARNING-PLAN-SERVER','PEPPA-TEACH-BIZ','PEPPA-TEACH-LISTENER','PEPPA-TEACH-TIMETABLE-SERVER','PEPPA-USER-AUTH-API','PEPPA-USER-CENTER-SERVER']

View File

@@ -0,0 +1,141 @@
# -*- coding:utf-8 -*-
"""
Author: 罗志鹏
Email: luozhipeng@huohua.cn
Create Date: 2022/07/25 11:25 下午
"""
import requests
import re
import json
class EnvCheck():
def __init__(self):
self.container_ip_list = []
def get_env_container_ip(self,env):
dis_server =[]
dis_ip = []
headers = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9','Accept-Encoding':'gzip, deflate, br'}
if env =='QA':
req = requests.get(url="http://eureka.qa.huohua.cn/",headers=headers)
elif env == 'SIM':
req = requests.get(url="http://eureka.sim.huohua.cn/", headers=headers)
else:
raise RuntimeError("环境选择异常")
req_list =req.text.split('tbody')
# print(req_list[1])
server_str_list = req_list[1].split('<tr>')
check_list = self.get_check_lsit(env=env)
for server in check_list:
if req.text.find(server) ==-1:
dis_server.append(server)
for i in check_list:
for str_ser in server_str_list:
if str_ser.find(i) != -1:
dict_sever = {}
# ip_list = []
str_sername_start = '<td><b>'
str_sername_end = '</b></td>'
server_name = str_ser[str_ser.find(str_sername_start) + 7:str_ser.find(str_sername_end)]
ip_list = re.findall(r'[a-z]://(.*?):8080', str_ser)
dict_sever[server_name] = ip_list
# print(dict_sever)
for ip_ch in ip_list:
url_check = "http://"+ip_ch+':8080/actuator/health'
resp = requests.get(url=url_check)
# print(resp.json())
if resp.json()['status'] != 'UP':
dict_ip = {server_name:ip_ch}
dis_ip.append(dict_ip)
return dis_ip,dis_server
# re_list = []
# server_list = []
# for str_ser in server_check_list:
# print(str_ser)
# print(server_name)
# pattern = re.compile(r'')
# str_server = pattern.search(str_ser)
# server_name = str_ser[str_ser.start():str_ser.end()]
# print(server_name)
# for server in check_list :
# for string_server in server_list:
# if string_server.find(server)!= -1:
# # print(string_server.find(server))
# re_list.append(string_server)
# print(re_list)
# ip_list = []
# for string_server in re_list :
# if string_server.find('http://'):
# pattern = re.compile(r'[a-zA-z]+://[^\s]*:8080')
# str_ip = pattern.search(string_server)
# # print(str_ip.start())
# url = string_server[str_ip.start():str_ip.end()]
# print(url)
def send_result(self):
dis_ip,dis_server = self.get_env_container_ip(env='SIM')
at_user_list = [{"tag": "at", "user_id": "{}".format("7020366259502153730")}]
message_data_SIM_server = {"msg_type": "text", "content": {'text':'SIM服务未找到部署IP{}'.format(dis_server)}}
message_data_SIM_IP = {"msg_type": "text", "content": {'text':'SIM服务部署IP不可用{}'.format(dis_ip)}}
headers = {"Content-Type": "application/json;charset=UTF-8"}
web_hook = "https://open.feishu.cn/open-apis/bot/v2/hook/28b775e7-a863-4807-b280-c82e09be0a80"
if len(dis_server) != 0:
json_data = json.dumps(message_data_SIM_server)
rsp = requests.post(url=web_hook, json=message_data_SIM_server, headers=headers)
if len(dis_ip) != 0:
json_data = json.dumps(message_data_SIM_IP)
rsp = requests.post(url=web_hook, json=message_data_SIM_IP, headers=headers)
dis_ip,dis_server = self.get_env_container_ip(env='QA')
message_data_QA_server = {"msg_type": "text", "content": {'text':'QA服务未找到部署IP{}'.format(dis_server)}}
message_data_QA_IP = {"msg_type": "text", "content": {'text':'QA服务部署IP不可用{}'.format(dis_ip)}}
if len(dis_server) != 0:
json_data = json.dumps(message_data_QA_server)
rsp = requests.post(url=web_hook, data=json_data, headers=headers)
if len(dis_ip) != 0:
json_data = json.dumps(message_data_QA_IP)
rsp = requests.post(url=web_hook, json=message_data_QA_IP, headers=headers)
def get_check_lsit(self,env):
if env == 'QA':
check_list = ['PEPPA-STUDENT-API','PEPPA-LEARNING-PLAN-LISTENER','PEPPA-LEARNING-PLAN-SERVER','PEPPA-TEACH-BIZ','PEPPA-TEACH-LISTENER','PEPPA-TEACH-TIMETABLE-SERVER','PEPPA-USER-AUTH-API','PEPPA-USER-CENTER-SERVER']
return check_list
if env == 'SIM':
check_list = ['PEPPA-STUDENT-API','PEPPA-LEARNING-PLAN-LISTENER','PEPPA-LEARNING-PLAN-SERVER','PEPPA-TEACH-BIZ','PEPPA-TEACH-LISTENER','PEPPA-TEACH-TIMETABLE-SERVER','PEPPA-USER-AUTH-API','PEPPA-USER-CENTER-SERVER']
return check_list
if __name__ == '__main__':
# o ='"http://10.251.187.248:8080/actuator/info" "http://10.251.187.248:8080/actuator/info"'
# i =re.findall(r'[a-z]://(.*?):8080',o)
# print(a)
A=EnvCheck()
A.send_result()

View File

@@ -0,0 +1,141 @@
# encoding: UTF-8
import time
import subprocess
from subprocess import PIPE,Popen
import os
class OSType:
WIN, LINUX, UNKNOWN = range(3)
def __init__(self):
pass
@staticmethod
def get_type():
import platform
system_name = platform.system()
if system_name.lower() == 'windows':
return OSType.WIN
elif system_name.lower() == 'linux':
return OSType.LINUX
else:
return OSType.UNKNOWN
class tool(object):
def __init__(self):
self.env_port = 5011
def run_process(self, cmd_str, out_p=False):
"""
run command
cmd_str unicode string.
"""
if OSType.WIN == OSType.get_type():
# cmd_str = cmd_str.encode('gbk')
cmd_str = cmd_str
elif OSType.LINUX == OSType.get_type():
cmd_str = cmd_str.encode('utf-8')
else:
raise RuntimeError("your os is not support.")
close_fds = False if OSType.WIN == OSType.get_type() else True
if out_p:
p = subprocess.Popen(cmd_str, shell=True, close_fds=close_fds, stdout=PIPE)
p.wait()
return p.returncode, p.stdout.read()
else:
c = self.get_devnull()
p = subprocess.Popen(cmd_str, shell=True, close_fds=close_fds, stdout=c)
# for line in p.stdout.readline():
# print(line)
# p.stdout.close()
p.communicate()
return p.returncode, None
def adb_cmd(self,cmd):
process = Popen(cmd, shell=True, stderr=PIPE, stdout=PIPE)
(stdout, stdrr) = process.communicate()
stdout = stdout.decode('gbk') # 返回字段中存在中文使用gbk
return stdout, stdrr
def get_devnull(self):
try:
return subprocess.DEVNULL
except AttributeError:
# Python 2.x or older
return open(os.devnull, 'r+')
def _kills_pid(self):
if OSType.WIN == OSType.get_type():
kill_pid_cmd = "taskkill /f /pid {}".format(self.pid)
elif OSType.LINUX == OSType.get_type():
kill_pid_cmd = "kill -9 {}".format(self.pid)
else:
raise RuntimeError("your os is not support.")
res_code, res_context = self.run_process(kill_pid_cmd)
if res_code:
raise RuntimeError("kill pid: {} failed. error: {}".format(self.pid, res_context))
def check_port(self):
if OSType.WIN == OSType.get_type():
find_pid_win_cmd = 'netstat -ano | findstr {} | findstr LISTENING'.format(self.env_port)
print(find_pid_win_cmd)
res_code, res_context = self.run_process(find_pid_win_cmd, out_p=True)
if res_code == 0:
if len(res_context) > 0:
try:
self.pid = str(res_context).split()[-1].replace("\\r\\n'", "")
self._kills_pid()
except IndexError:
pass
elif OSType.LINUX == OSType.get_type():
find_pid_linux_cmd = "lsof -i:{}".format(self.env_port)
res_code, res_context = self.run_process(find_pid_linux_cmd, out_p=True)
if res_code == 0:
# 获取pid
if len(res_context) > 0:
try:
self.pid = str(res_context).split("\n")[1].split()[1]
self._kills_pid()
except IndexError:
pass
else:
raise RuntimeError("your os is not support.")
def run_manage(self):
count = 3
while count > 0:
self.run_manages()
find_pid_linux_cmd = "lsof -i:{}".format(self.env_port)
res_code, res_context = self.run_process(find_pid_linux_cmd, out_p=True)
print(res_code, "---res_code---", res_context, "---res_context---")
if len(res_context) > 0:
time.sleep(2)
pid = str(res_context).split("\n")[1].split()[1]
print(pid, "pid####")
count -= 1
if pid:
break
else:
continue
else:
break
def run_manages(self):
lod = "nohup python3 platform_tools/aida/manage.py &."
sh_lod = "./5011.sh"
self.run_process(sh_lod)
# subprocess.call(["./5011.sh"])
# self.run_process(lod)
if __name__ == '__main__':
test = tool()
test.check_port()
time.sleep(3)
test.run_manage()

View File

@@ -0,0 +1,105 @@
import os
import subprocess
from urllib.parse import urlparse
import argparse
class HarmonyAppInstaller:
def __init__(self) -> None:
self.project_path = os.getcwd()
self.temp_dir = os.path.join(self.project_path, "temp")
def _run_command(self, command: str) -> str:
result = subprocess.run(command, shell=True, capture_output=True, text=True)
if result.returncode != 0:
raise Exception(f"Command failed: {command}\nError: {result.stderr}")
return result.stdout
def install(self, appUniqId, packageId):
import requests
import os
import shutil
# Create or clean temp directory
if os.path.exists(self.temp_dir):
shutil.rmtree(self.temp_dir)
os.makedirs(self.temp_dir)
url = "https://api.qa.huohua.cn/api/versions"
headers = {
'Content-Type': 'application/json',
'huohua-podenv': 'HHC-111781'
}
params = {
'version': '25.1.2.1',
'platform': 'harmonyos',
'appUniqId': appUniqId,
'packageId': packageId
}
response = requests.get(url, headers=headers, params=params)
response_data = response.json()
if not response_data.get('success'):
raise Exception(f"API request failed: {response_data.get('message')}")
app_version = response_data.get('data', {}).get('app_version', {})
download_url = app_version.get('url')
if not download_url:
raise Exception("No download URL found in response")
# Get the filename from the URL
hap_filename = os.path.basename(urlparse(download_url).path)
if not hap_filename.endswith('.hap'):
hap_filename = 'eduparent.hap' # fallback name if URL doesn't end with .hap
# Download the HAP file
hap_file_path = os.path.join(self.temp_dir, hap_filename)
download_response = requests.get(download_url, stream=True)
download_response.raise_for_status()
with open(hap_file_path, 'wb') as f:
for chunk in download_response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
# Execute HDC commands
device_path = f"data/local/tmp/{hap_filename}"
print(f'hdc file send "{hap_file_path}" "{device_path}"')
# Push HAP file to device
self._run_command(f'hdc file send "{hap_file_path}" "{device_path}"')
try:
# Install HAP package
print(f'hdc shell bm install -p "{device_path}"')
self._run_command(f'hdc shell bm install -p "{device_path}"')
finally:
# Clean up: Remove HAP file from device
print(f'hdc shell rm -rf "{device_path}"')
self._run_command(f'hdc shell rm -rf "{device_path}"')
return hap_file_path
def main():
parser = argparse.ArgumentParser(description='Install HarmonyOS application')
parser.add_argument('--app-uniq-id', required=True, help='Unique ID of the application')
parser.add_argument('--package-id', required=True, help='Package ID for the application')
args = parser.parse_args()
try:
installer = HarmonyAppInstaller()
hap_path = installer.install(args.app_uniq_id, args.package_id)
print(f"Successfully installed HAP from: {hap_path}")
except Exception as e:
print(f"Error: {str(e)}")
exit(1)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,257 @@
# -*- coding:utf-8 -*-
import sys
import xml.etree.ElementTree as ET
WORKSPACE = sys.argv[1]
sys.path.append(WORKSPACE)
import json
import requests
import os
import re
from bs4 import BeautifulSoup
from html.parser import HTMLParser
handle_parser = HTMLParser()
BUILD_INFO = {}
class handle_to_jenkins():
'''
处理关于jenkins的执行相关方法
'''
def __init__(self):
self.dict_info = {}
self.list_info = []
def GetJenkinsVar(self, key):
'''
获取jenkins上获得的参数信息
'''
try:
value = os.environ.get(key)
except Exception:
value = os.environ.get(key.upper())
if (not value):
value = ''
return value
def kw_to_get_dd_token(self, environment):
"""
功能: 根据传入环境获取不同的token信息
"""
sim = "40696c86-264a-4222-a40c-cfd64a05dffd"
product = "40696c86-264a-4222-a40c-cfd64a05dffd"
if environment.lower() == "sim" or environment.lower() == "qa":
return sim
else:
return product
def send_message_by_dingding(self, data, environment="qa"):
'''
发送钉钉消息
'''
token = self.kw_to_get_dd_token(environment=environment)
# web_hook = "https://oapi.dingtalk.com/robot/send?access_token={}".format(token)
web_hook = "https://open.feishu.cn/open-apis/bot/v2/hook/{}".format(token)
headers = {"Content-Type": "application/json"}
json_data = json.dumps(data)
rsp = requests.post(url=web_hook, data=json_data, headers=headers)
return rsp
# print(data)
def send_feishu(self,inferfaces):
'''
发送飞书
:return:
'''
at_user_list = [{"tag": "at", "user_id": "{}".format("7020366259502153730")}]
message_data = {"msg_type": "post", "content": {
"post": {"zh_cn": {"title": "有存在的接口未实现自动化哦",
"content": [[{"tag": "text", "text": "问题数据:"}],
[{"tag": "text", "text": "{}".format(inferfaces)}],
at_user_list]}}}}
web_hook = "https://open.feishu.cn/open-apis/bot/v2/hook/40696c86-264a-4222-a40c-cfd64a05dffd"
headers = {"Content-Type": "application/json"}
json_data = json.dumps(message_data)
print(json_data)
rsp = requests.post(url=web_hook, data=json_data, headers=headers)
return rsp
def handle_send_message(self,root):
'''
处理对应所有人根据job名称匹配来发送@的消息
'''
list_name_phone = {"陈江": "13458500234", "张楠": "", "罗志鹏": "",
"蒲思宇": "", "陈洁": "15328118883", "刘涛婷": "18328504751", "谯新久": "18202810506"}
job_name = self.GetJenkinsVar("JOB_TO_NAME")
job_url = self.GetJenkinsVar("JOB_TO_URL")
enviroment = self.GetJenkinsVar("JOB_ENVIRONMENT")
# job_name = "123"
# job_url = "http://10.250.200.1:8080/jenkins/job/QA%E7%8E%AF%E5%A2%83%E5%B7%A1%E6%A3%80/2387/robot/report/report.html"
# enviroment = "PRODUCT"
dict_info = self.get_fail_test_case(son_node=root)
BUILD_INFO = dict_info.get("BUILD_INFO")
fail_info = dict_info.get("INFO")
list_phone = []
if BUILD_INFO:
for key, value in BUILD_INFO.items():
list_phone.append(list_name_phone.get(key))
print(list_phone)
print("+++++++{0}+++++{1}".format(job_name, job_url))
if re.search("PRODUCT", enviroment):
# message_data = {"msg_type": "text", "content": {
# "text": "线上巡检:线上环境出现了问题,请点击进行查看{0},以下是错误日志:{1}".format(job_url + "robot/report/report.html",fail_info)},
# "at": {"atMobiles": list_phone}}
at_user_list = [{"tag": "at", "user_id": "{}".format("7020366258071715842")}]
message_data = {"msg_type": "post", "content": {
"post": {"zh_cn": {"title": "线上环境巡检",
"content": [[{"tag": "text", "text": "线上环境出现了问题:"}],
[{"tag": "a", "text": "点击查看","href":"".format(job_url + "robot/report/report.html")}],
at_user_list]}}}}
print(message_data)
self.send_message_by_dingding(message_data,environment="product")
return enviroment
if re.search("QA", enviroment) or re.search("SIM", enviroment):
message_data = {"msg_type": "text", "content": {
"text": "{0}环境构建出问题了哦,请点击进行查看{1},以下是错误日志:{2}".format(enviroment, job_url + "robot/report/report.html",fail_info)},
"at": {"atMobiles": list_phone}}
print(message_data)
self.send_message_by_dingding(message_data)
return enviroment
for key, value in list_name_phone.items():
if re.search(key, job_name):
message_data = {"msg_type": "text", "content": {
"text": "亲爱的{0}同学,你的独立环境构建出问题了哦,请点击进行查看{1}".format(key, job_url + "robot/report/report.html")},
"at": {"atMobiles": [value]}}
self.send_message_by_dingding(message_data)
return value
else:
return "这个job:{}没有找到人".format(job_name)
def get_fail_test_case(self,son_node, father_node=None, grandpa_node=None):
"""
功能遍历xml文件的所有节点搜索构建失败的用例信息
"""
children_node = son_node.getchildren()
if len(children_node) == 0:
# if son_node.tag == 'doc':
# print(son_node.text)
# if son_node.tag == 'msg' and son_node.attrib['level'] == 'FAIL':
# # print(son_node.text)
# # print(son_node.attrib['timestamp'])
# print(children_node)
if son_node.tag == 'status' and son_node.attrib['status'] == 'FAIL' and 'critical' in son_node.attrib:
# print(father_node.attrib["name"])
tag = self.find_tags(father_node)
# a = self.find_error(father_node)
# print(a)
info_error = self.check_log_info(son_node.text)
INFO = "用例名称:" + father_node.attrib["name"] +"。人员:" + tag + "。失败日志:" + str(info_error)
run_time = son_node.attrib['endtime']
# print(info_error)
# print(son_node.text)
# self.check_log_info(text=son_node.text)
run_time = "{}-{}-{} {}".format(run_time[0:4], run_time[4:6], run_time[6:8], run_time[9:17])
reason = son_node.text.replace('"', '\\"')
# 满足构建时间,同时数据库中没有数据,才入库
# print(run_time)
if tag in BUILD_INFO:
BUILD_INFO[tag] += 1
else:
BUILD_INFO[tag] = 1
# print(BUILD_INFO)
# print(tag)
self.list_info.append(INFO)
self.dict_info["BUILD_INFO"] = BUILD_INFO
self.dict_info["INFO"] = self.list_info
# print(tag)
run_time = run_time
author = tag
case_name = father_node.attrib['name']
file_path = grandpa_node.attrib['source']
reason = reason[0:4500]
return self.dict_info
for child in children_node:
self.dict_info = self.get_fail_test_case(child, son_node, father_node)
return self.dict_info
def check_log_info(self,text):
'''
过滤精确日志信息
'''
soup = BeautifulSoup(text, features="lxml")
list_info = []
for a in soup.find_all(name='span'):
dict_info = {}
if a.text == "Old message:":
dict_info["old_message"] = a.nextSibling
list_info.append(dict_info)
elif a.text == "New message:":
dict_info["new_message"] = a.nextSibling
list_info.append(dict_info)
# print(a.nextSibling)
if list_info:
return list_info
else:
return text
def find_tags(self,root):
"""
功能在xml文件中搜索失败用对应的作者
"""
children_node = root.getchildren()
for child in children_node:
if child.tag == 'tags':
tags = child.getchildren()
for tag in tags:
if 'qa-' in tag.text.lower():
return tag.text[3:len(tag.text)]
return "无作者标签"
def find_error(self,root):
"""
功能在xml文件中查询失败日志
"""
children_node = root.getchildren()
for child in children_node:
if child.tag == 'kw':
msgs = child.getchildren()
for msg in msgs:
a = msg.tag
# t = msg.attrib['level']
l = msg.text
# print(msg.attrib['timestamp'])
if msg.tag == 'msg' and msg.attrib['level'] == 'FAIL':
print(msg.attrib)
s = msg.text
return msg.text
return ""
def run(self):
'''
运行入口
'''
# FILE_PATH = "D:/output2.xml"
FILE_PATH = os.path.abspath(os.path.join(WORKSPACE, 'Report/out/output.xml'))
root = ET.parse(FILE_PATH).getroot()
# self.get_fail_test_case(son_node=root)
self.handle_send_message(root=root)
if __name__ == '__main__':
test = handle_to_jenkins()
# FILE_PATH = "D:/output1.xml"
# FILE_PATH = os.path.abspath(os.path.join(WORKSPACE, 'Report/out/output.xml'))
# root = ET.parse(FILE_PATH).getroot()
# print(test.get_fail_test_case(son_node=root))
test.run()
# test.handle_send_message(root=root)

View File

@@ -0,0 +1,331 @@
# -*- coding:utf-8 -*-
"""
人员当日jira任务、故事状态流转
"""
import os, sys
file_dir = os.path.dirname(__file__)
project_dir = os.path.abspath(os.path.join(file_dir, "..", "..", ".."))
sys.path.append(project_dir)
from jira import JIRA
from base_framework.public_tools.utils import Tools
from base_framework.public_tools.read_config import ReadConfig
from base_framework.platform_tools.Message_service.Feishu_api import FeiShuMessage,get_user_name_by_email_prefix,get_feishu_config_value
from configparser import ConfigParser
from base_framework.base_config.current_pth import *
import datetime
tools = Tools()
ReadConfig = ReadConfig(filename=la_config_path)
FS_INFO = [{"team": "TO", "users": "wuyonggang,xuwenjun,luohong"},
{"team": "TO-RD", "users": "fengtian,guosongchao,zhaoxiaofang,"
"zhuliang,majincheng,gaozhijun,liuxuegang,"
"zhangxiong"},
{"team": "USER-FE", "users": "zhaofei,liuxinlin,qingchen,yefei,xiangming,jianghao,jianglingmin,xuchangle,"
"libaicheng,gouyuheng,jixiang.dong,baiyang01"}
]
class JiraApi:
def __init__(self):
self.jira = JIRA(server='https://jira.bg.huohua.cn/',
basic_auth=("wuyonggang", "Mima@123"))
def close_jira_subtask(self, user_name):
"""
功能: 关闭指定人员名下当天结束的子任务
请求参数 user_name 指定人员jira登录名type: list
"""
bad_name = []
right_name = []
for j in user_name:
if j not in ReadConfig.get_sections():
bad_name.append(j)
elif j in ReadConfig.get_sections():
right_name.append(j)
success = []
false = []
today_time = tools.get_format_date(r_type=1)
work_start_time = datetime.datetime.strptime('{0} 10:00:00'.format(today_time), '%Y-%m-%d %H:%M:%S')
for name in right_name:
jira = JIRA(server='https://jira.bg.huohua.cn/', basic_auth=(name, ReadConfig.get_value(name, 'password')))
jql = 'project = HHC AND issuetype = 子任务 AND status in (重新打开, 启动, 需求池, 暂停, 计划, 产品需求设计,' \
' 产品需求内审, 需求评审, 排期, 执行, 开发, 待测试, QA测试, SIM验证, 验证中, 待办, 处理中, 关闭) AND resolution = Unresolved' \
' AND (QA in ({0}) OR assignee in ({1})) ORDER BY priority DESC, updated DESC'.format(name,name)
issues = jira.search_issues(jql, fields='')
# if len(issues) == 0: break
not_need_closed = 0
for i in issues:
s = list(jira.transitions(i))
close_status_id = [x['id'] for x in s if x['name'] == '关闭']
if str(i.fields.status) == '处理中':
if i.fields.aggregatetimeestimate != None:
if i.fields.aggregatetimeestimate > 28800:
jira.add_worklog(i, timeSpent='7', started=work_start_time)
elif 0 < i.fields.aggregatetimeestimate <= 28800:
jira.add_worklog(i, timeSpent='{0}'.format(i.fields.aggregatetimeestimate / 3600),
started=work_start_time)
else:
jira.add_worklog(i, timeSpent='8', started=work_start_time)
if str(i.fields.status) == '处理中' and str(i.fields.customfield_13406) == today_time:
jira.transition_issue(i, int(close_status_id[0]))
else:
not_need_closed += 1
issues2 = jira.search_issues(jql, fields='')
if not_need_closed == len(issues2):
success.append(name)
else:
false.append(name)
return "{0}今日结束的子任务已关闭成功; {1}今日结束的子任务关闭失败; {2}没有进行账号配置".format(success, false, bad_name)
def start_jira_subtask(self, user_name):
"""
功能: 将待开始为今天的子任务状态流转为“处理中”
请求参数 user_name 指定人员jira登录名type: list
"""
bad_name = []
right_name = []
for j in user_name:
if j not in ReadConfig.get_sections():
bad_name.append(j)
elif j in ReadConfig.get_sections():
right_name.append(j)
success = []
false = []
for name in right_name:
jira = JIRA(server='https://jira.bg.huohua.cn/', basic_auth=(name, ReadConfig.get_value(name, 'password')))
jql = 'project = HHC AND issuetype = 子任务 AND status in (重新打开, 启动, 需求池, 暂停, 计划, 产品需求设计, 产品需求内审, 需求评审, ' \
'排期, 执行, 开发, 待测试, QA测试, SIM验证, 验证中, 待办, 关闭) AND resolution = Unresolved' \
' AND (QA in ({0}) OR assignee in ({1})) ORDER BY priority DESC, updated DESC'.format(name,name)
today_time = tools.get_format_date(r_type=1)
issues = jira.search_issues(jql, fields='')
# if len(issues) == 0: break
not_need_update = 0
for i in issues:
s = list(jira.transitions(i))
start_status_id = [x['id'] for x in s if x['name'] == '处理中']
if str(i.fields.status) == '待办' and str(i.fields.customfield_12700) == today_time:
jira.transition_issue(i, int(start_status_id[0]))
else:
not_need_update += 1
issues2 = jira.search_issues(jql, fields='')
if not_need_update == len(issues2):
success.append(name)
else:
false.append(name)
return "{0}今日开始的子任务已将状态修改为“处理中”‘; {1}今日开始的子任务状态修改失败; {2}没有进行账号配置".format(success, false, bad_name)
def change_story(self, user_name, type):
"""
功能: 将提测时间为今天的指定人员名下的故事状态修改至“QA测试”
请求参数 user_name 指定人员jira登录名type: list
type (故事需要修改至的状态 1提测日为今天状态修改至【QA测试】 2上线日为今天状态修改至【关闭】)
"""
bad_name = []
right_name = []
for j in user_name:
if j not in ReadConfig.get_sections():
bad_name.append(j)
elif j in ReadConfig.get_sections():
right_name.append(j)
success = []
false = []
for name in right_name:
jira = JIRA(server='https://jira.bg.huohua.cn/', basic_auth=(name, ReadConfig.get_value(name, 'password')))
jql = "project = HHC AND issuetype = 故事 AND " \
"status in (重新打开, 启动, 需求池, 暂停, 计划, 产品需求设计, 产品需求内审, 需求评审, 排期, 执行, 开发, 待测试, 验证中, 待办, QA测试, SIM验证, 处理中, 关闭)" \
" AND resolution = Unresolved AND QA in ({0}) ORDER BY priority DESC, updated DESC".format(name)
today_time = tools.get_format_date(r_type=1)
issues = jira.search_issues(jql, fields='')
# if len(issues) == 0 : break
x = 0
need_update = 0
if type == 1: # 将故事状态一直流转至“QA测试”
for i in issues:
if str(i.fields.status) == 'QA测试' or str(i.fields.status) == 'SIM验证': need_update += 1
if str(i.fields.customfield_10504) == today_time:
if str(i.fields.status) != 'QA测试' and str(i.fields.status) != 'SIM验证':
need_update += 1
for j in range(8):
jira.transition_issue(i, int(jira.transitions(i)[2]['id']))
issues_2 = jira.search_issues(jql, fields='') # 重新获取所有故事
status_now = issues_2[x].fields.status
print(status_now)
if str(status_now) == 'QA测试' or str(status_now) == 'SIM验证':
break
x += 1
jql2 = "project = HHC AND issuetype = 故事 AND " \
"status in (重新打开, 启动, 需求池, 暂停, 计划, 产品需求设计, 产品需求内审, 需求评审, 排期, 执行, 开发, 待测试, 验证中, 待办, 处理中, 关闭)" \
" AND resolution = Unresolved AND QA in ({0}) ORDER BY priority DESC, updated DESC".format(name)
issues2 = jira.search_issues(jql2, fields='')
if len(issues2) == len(issues) - need_update:
success.append(name)
else:
false.append(name)
elif type == 2: # 直接将故事关闭
for i in issues:
if str(i.fields.customfield_10606) == today_time:
jira.transition_issue(i, int(jira.transitions(i)[1]['id']), comment="已上线") # 将故事修改为关闭状态
need_update += 1
issues2 = jira.search_issues(jql, fields='')
if len(issues2) + need_update == len(issues):
success.append(name)
else:
false.append(name)
return "{0}今日的故事已将状态修改成功!; {1}今日的故事状态修改失败; {2}没有进行账号配置".format(success, false, bad_name)
def change_jira_status(self, user_name):
"""
功能: 判断当前时间是否在下午18:00点以前来选择打开或关闭【子任务】和【故事】
请求参数 user_name 指定人员jira登录名type: list
"""
time_now = tools.get_format_date(r_type=4)
time_now2 = '{0} 18:00:00'.format(time_now[0:10:1])
if time_now < '{0} 18:00:00'.format(time_now2):
a = self.start_jira_subtask(user_name)
b = self.change_story(user_name, type=1)
return a + '\n' + b
elif time_now >= '{0} 18:00:00'.format(time_now2):
a = self.close_jira_subtask(user_name)
b = self.change_story(user_name, type=2)
return a + '\n' + b
def query_jira_subtask(self, user_name, begin_date=0, end_date=0):
"""
| 功能 | 查询用户对应的jira子任务 |
| 入参 | user_name | 用户名字 |
| | begin_date | 开始时间0-今天X-未来X天-X-过去的X天 |
| | end_date | 开始时间0-今天X-未来X天-X-过去的X天 |
"""
b_date = tools.get_format_date(r_type=1, add_days=int(begin_date))
e_date = tools.get_format_date(r_type=1, add_days=int(end_date))
jql = "issuetype = 子任务 AND 计划开始时间 <= {} AND 计划结束时间 >= {} AND assignee in ({}) ORDER BY cf[12700] ASC"\
.format(b_date, e_date, user_name)
issues = self.jira.search_issues(jql, fields='')
# print(jql)
return issues
# for item in issues:
# print("{0}:{1}, 当前状态:{2}".format(item.key, item.fields.summary, item.fields.status))
def query_jira_by_id(self, jira_id):
"""
| 功能 | 跟进jira的id查询具体详情 |
| 入参 | jira_id | jira的id |
"""
jql = "id={}".format(jira_id)
issues = self.jira.search_issues(jql, fields='')
if len(issues) == 0:
raise Exception("根据jira_id未查询到对应的jira信息请检查....")
rd = issues[0].fields.assignee.displayName
qa = issues[0].fields.reporter.displayName
title = issues[0].fields.summary
status = issues[0].fields.status
j_time = issues[0].fields.created
j_time = j_time[0:10] + ' ' + j_time[11:19]
return {"jira_rd": rd,
"jira_qa": qa,
"jira_title": title,
"jira_time": j_time,
"jira_status": str(status)}
def query_overdue_issues(self, user_name):
"""
| 功能 | 查询逾期的子任务和故事 |
| 入参 | 无 |
"""
today = tools.get_format_date()
check_time = tools.get_format_date(r_type=4)
if check_time < "{} 17:00:00".format(today):
jql = "issuetype in (故事,子任务) AND (计划结束时间<now() OR 计划上线时间<now()) AND status!=关闭 AND " \
"(assignee={0} OR QA={0})".format(user_name)
else:
jql = "issuetype in (故事,子任务) AND (计划结束时间<=now() OR 计划上线时间<=now()) AND status!=关闭 AND " \
"(assignee={0} OR QA={0})".format(user_name)
issues = self.jira.search_issues(jql, fields='')
return issues
def check_overdue_issues(self):
"""检查预期未关闭的故事和子任务"""
today = tools.get_format_date()
fs = FS_INFO[0]
user_list = fs['users'].split(',')
remind_info = []
for user in user_list:
issues = self.query_overdue_issues(user_name=user)
if issues:
for issue in issues:
issue_info = [user, issue.key]
remind_info.append(issue_info)
if remind_info:
msg = "【JIRA任务延期提醒{}】:\n".format(today)
for info in remind_info:
user_name = get_user_name_by_email_prefix(email_prefix=info[0])
open_id = get_feishu_config_value(option_key=user_name)
at_msg = " |--<at user_id='{}'>{}</at>".format(open_id, user_name)
msg = msg + at_msg + ": https://jira.bg.huohua.cn/browse/{}\n".format(info[1])
msg = msg + " |--请及时关闭以上逾期任务..."
print(msg)
fs = FeiShuMessage(team=fs['team'])
fs.send_text(msg=msg)
else:
print("++++++++++++++++++++++")
print("| 所有任务或故事均已关闭 |")
print("++++++++++++++++++++++")
def check_pdp_standard(self):
"""查找当天有无子任务,没有则发送飞书消息提醒"""
# 非工作日构建
today = tools.get_format_date()
if not tools.check_the_date_is_a_working_day(today):
print("### 非工作日,不检查....")
return False
for fs in FS_INFO:
user_list = fs['users'].split(',')
need_remind_user = []
for user in user_list:
issues = self.query_jira_subtask(user_name=user)
if not issues:
need_remind_user.append(user)
if need_remind_user:
msg = "【JIRA子任务提醒{}】:\n |--".format(today)
for user in need_remind_user:
user_name = get_user_name_by_email_prefix(email_prefix=user)
open_id = get_feishu_config_value(option_key=user_name)
at_msg = "<at user_id='{}'>{}</at>".format(open_id, user_name)
msg = msg + at_msg
msg = msg + "\n |--请及时添加与今日工作对应的子任务哦..."
print(msg)
fs = FeiShuMessage(team=fs['team'])
fs.send_text(msg=msg)
else:
print("++++++++++++++++++++++++++++++++")
print("| {} 满足pdp规范无须提示 |".format(fs['team']))
print("++++++++++++++++++++++++++++++++")
if __name__ == '__main__':
# 关闭自动修改jira的代码不要再打开了...
# lp = Jira_About()
# print(lp.change_jira_status(['liupeng', 'zhourenhua', 'xuwenjun', 'lichao04', 'wanggang02', 'baoli']))
# 以下内容若有修改请勿上传否则会影响jira监控的每日构建
# 1.获取命令行参数
cmd_params = sys.argv
try:
index = cmd_params.index('-t')
except ValueError:
raise Exception("类型参数必填pdp-检查PDP规范overdue-检查预期未关闭的故事和子任务....")
else:
check_type = cmd_params[index + 1]
if check_type.lower() not in ("pdp", "overdue"):
raise Exception("目前仅支持pdp和overdue但你输入的是{}".format(check_type))
jira = JiraApi()
if check_type.lower() == "pdp":
jira.check_pdp_standard()
elif check_type.lower() == "overdue":
jira.check_overdue_issues()

View File

@@ -0,0 +1,45 @@
import requests
def fetch_kibana_logs(query, time_range, index_pattern):
kibana_url = "https://logstashlog-kibana.qc.huohua.cn/login" # 替换为实际的Kibana URL
search_endpoint = f"{kibana_url}/app/kibana" # Kibana搜索API的URL
headers = {
"Content-Type": "application/json",
}
user = "elastic"
pwd = "s3dr40O,&j"
payload = {
"query": query,
"timeRange": time_range,
"index": index_pattern
}
try:
response = requests.post(kibana_url, headers=headers,auth=(user, pwd), json=payload)
response.raise_for_status()
logs = response.json()
# 处理日志数据
# ...
return logs
except requests.exceptions.RequestException as e:
print(f"Error fetching Kibana logs: {str(e)}")
return None
query = "error" # 搜索关键字
time_range = "now-1d/d" # 过去一天内的日志
index_pattern = "my-logs-*" # 匹配以 "my-logs-" 开头的索引
logs = fetch_kibana_logs(query, time_range, index_pattern)
if logs:
# 处理日志数据
for log in logs:
# 对每条日志进行操作
print(log)
else:
# 处理错误情况
print("Failed to fetch Kibana logs.")

View File

@@ -0,0 +1,204 @@
# -*- coding:utf-8 -*-
"""
Author: qiaoxinjiu
Email: qiaoxinjiu@sparkedu.com
Create Date: 2022/05/08 5:58 下午
"""
import os
from urllib import parse
from base_framework.public_tools import read_config
import requests
import sys
import json
class auth_login():
def __init__(self):
self.config_login_path = os.path.dirname(os.path.abspath(__file__))
self.config_login_filePath = os.path.join(self.config_login_path, "../Config/team_config.ini")
self.config_login_content = read_config.ReadConfig(filename=self.config_login_filePath)
def get_json_result(self, url, request_data=None, headers=None, send_way="GET"):
"""
# url: 测试的url
# request_data: url请求时发送的数据
# headers: url请求时发送的消息头
# 功能获取json格式的数据
"""
if send_way == "GET":
response = requests.request(send_way, url, headers=headers, params=json.dumps(request_data))
else:
response = requests.request(send_way, url, headers=headers, data=json.dumps(request_data))
try:
response_data = json.loads(response.text)
except:
info = sys.exc_info()
response_data = info[1], info[2]
return response_data
def get_m_online_token(self):
'''
获取家长端线上登录的token
:param username:
:param password:
:return:
'''
headers = {
"Content-Type": "application/json;charset=UTF-8",
"sso-client-id": "gmp"
}
get_login_info = eval(
self.config_login_content.get_value(sections="m-userinfo", options="login_parent_info")) # 获取配置文件中的默认请求参数值
get_url_info = eval(self.config_login_content.get_value(sections="m-userinfo", options="parent_url"))
m_token = requests.post(url=get_url_info.get("login_url"), json=get_login_info, verify=False, headers=headers)
if m_token.status_code == 200:
token = m_token.json()
token = token['data']['token']
return token
else:
raise KeyError('获取 parent token失败')
def get_student_online_token(self):
'''
获取学生端线上登录的token
:param username:
:param password:
:return:
'''
headers = {
"Content-Type": "application/json;charset=UTF-8",
"sso-client-id": "gmp"
}
get_login_info = eval(
self.config_login_content.get_value(sections="m-userinfo", options="login_student_info")) # 获取配置文件中的默认请求参数值
get_url_info = eval(self.config_login_content.get_value(sections="m-userinfo", options="student_url"))
m_token = requests.post(url=get_url_info.get("login_url"), json=get_login_info, verify=False, headers=headers)
if m_token.status_code == 200:
token = m_token.json()
token = token['data']['token']
return token
else:
raise KeyError('获取 parent token失败')
def get_m_headers(self, m_token):
'''
获取请求的header
:return:
'''
headers = {
"Content-Type": "application/json;charset=UTF-8",
"sso-client-id": "gmp",
"user-token": m_token,
}
return headers
def get_parent_config_info(self, url_name):
'''
获取家长端配置文件中的url信息
:return:
'''
get_parent_url_info = eval(self.config_login_content.get_value(sections="m-userinfo", options="parent_url"))
get_realm_name = eval(self.config_login_content.get_value(sections="m-userinfo", options="realm_name"))
get_url = get_realm_name.get("parent_api") + get_parent_url_info.get(url_name)
return get_url
def get_visaprk_parent_config_info(self, url_name):
'''
获取海外家长端配置文件中的url信息
:return:
'''
get_parent_url_info = eval(self.config_login_content.get_value(sections="m-userinfo", options="parent_course_url"))
get_realm_name = eval(self.config_login_content.get_value(sections="m-userinfo", options="realm_name"))
get_url = get_realm_name.get("parent_vispark_api") + get_parent_url_info.get(url_name)
return get_url
def get_student_config_info(self, url_name):
'''
获取学生端配置文件中的url信息
:return:
'''
get_student_url_info = eval(self.config_login_content.get_value(sections="m-userinfo", options="student_url"))
get_realm_name = eval(self.config_login_content.get_value(sections="m-userinfo", options="realm_name"))
get_url = get_realm_name.get("student_api") + get_student_url_info.get(url_name)
return get_url
class m_parent():
def __init__(self):
self.get_url = auth_login()
def get_parentPayEditionPage(self):
'''
首页轮播页面展示
:return:
'''
m_token = self.get_url.get_m_online_token()
headers = self.get_url.get_m_headers(m_token=m_token)
url = self.get_url.get_parent_config_info(url_name="parentPayEditionPage_url")
get_response_data = self.get_url.get_json_result(url=url, headers=headers, request_data={"terminalType": 5})
return get_response_data
def get_recommendedCourse(self):
'''
家长端获取课程信息
:return:
'''
m_token = self.get_url.get_m_online_token()
headers = self.get_url.get_m_headers(m_token=m_token)
url = self.get_url.get_parent_config_info(url_name="recommendedCourse_url")
get_response_data = self.get_url.get_json_result(url=url, headers=headers)
return get_response_data
def get_trial_level(self,courseSubtype):
'''
获取试听课级别
ap为21cp为20
:return:
'''
m_token = self.get_url.get_m_online_token()
headers = self.get_url.get_m_headers(m_token=m_token)
url = self.get_url.get_visaprk_parent_config_info(url_name="get_trial_url")
request_url = url +"?courseSubtype={0}&subjectType=1".format(courseSubtype)
get_response_data = self.get_url.get_json_result(url=request_url, headers=headers)
# 针对ap或者cp获取级别查看是否存在重复的level
list_level = []
for trial_data in get_response_data.get("data"):
list_level.append(trial_data.get("level"))
set_level = set(list_level)
dict_response = {}
for level in set_level:
if list_level.count(level) > 1:
dict_response[level] = list_level.count(level)
if dict_response:
return {"code":500,"data":dict_response}
else:
return {"code":200,"data":dict_response}
class student_verity():
def __init__(self):
self.get_url = auth_login()
def get_schedule_count(self):
'''
:return:
'''
m_token = self.get_url.get_m_online_token()
headers = self.get_url.get_m_headers(m_token=m_token)
url = self.get_url.get_student_config_info(url_name="scheduleCount_url")
print(url)
get_response_data = self.get_url.get_json_result(url=url, headers=headers, request_data={"subjectType": 4})
return get_response_data
if __name__ == '__main__':
test = auth_login()
test1 = m_parent()
test2 = student_verity()
# print(test.get_student_online_token())
# print(test1.get_recommendedCourse())
print(test1.get_trial_level(courseSubtype=22))
# print(test2.get_schedule_count())

View File

@@ -0,0 +1,19 @@
import pika
# 设置RabbitMQ服务器的连接参数使用自定义端口9876
connection_params = pika.ConnectionParameters('rocketmq.qa.huohua.cn', 9876)
connection = pika.BlockingConnection(connection_params)
channel = connection.channel()
# 声明一个队列,如果队列不存在则会创建
queue_name = 'my_queue'
channel.queue_declare(queue=queue_name)
# 发布消息到指定的队列
message = 'Hello, World!'
channel.basic_publish(exchange='',
routing_key=queue_name,
body=message)
print(f" [x] Sent '{message}'")
# 关闭连接
connection.close()

View File

@@ -0,0 +1,56 @@
# -*- coding:utf-8 -*-
"""
Author: qiaoxinjiu
Email: qiaoxinjiu@sparkedu.com
Create Date: 2022/05/08 5:58 下午
"""
import os
from PIL import Image
def image_gray(img):
# 打开图片
img = Image.open(img)
# 计算平均灰度值
gray_sum = 0
count = 0
for x in range(img.width):
for y in range(img.height):
if img.mode == "RGB":
r, g, b = img.getpixel((x, y))
gray_sum += (r + g + b) / 3
elif img.mode == "L":
gray_value = img.getpixel((x, y))
gray_sum += gray_value
count += 1
avg_gray = gray_sum / count
return avg_gray
def find_image(folder_path):
# 定义一个列表存储图片路径
images = []
# 遍历文件夹下的所有文件
for root, dirs, files in os.walk(folder_path):
for file in files:
file_path = os.path.join(root, file)
# 处理每个文件,将其添加到列表中
images.append(file_path)
return images
def assert_run(folder_path):
images = find_image(folder_path)
for img in images:
gray = image_gray(img)
# 灰度值小于50将认为是黑图
if gray < 50:
print(img, "", gray)
if __name__ == "__main__":
# image_gray()
# find_image()
folder_path = r'D:\picture'
assert_run(folder_path)

View File

@@ -0,0 +1,211 @@
# -*- coding:utf-8 -*-
"""
Author: qiaoxinjiu
Email: xinjiu.qiao@allschool.com
Create Date: 2022/04/26 5:58 下午
"""
import os
import sys
input_team_name = sys.argv
BASIC_PATH = os.path.dirname(os.path.abspath(__file__))
TEAM_PATH = os.path.abspath(os.path.join(BASIC_PATH, '../../../{}'.format("base_framework")))
sys.path.append(TEAM_PATH)
PROJECT_PATH = os.path.abspath(os.path.join(BASIC_PATH, '../../..'))
sys.path.append(PROJECT_PATH)
from base_framework.public_tools.sqlhelper import MySqLHelper
import json
import requests
obj_mysql_helper = MySqLHelper()
class Handle_tools:
def __init__(self):
pass
def query_interface_sql(self):
'''
查询qa的余量接口信息
:return:
'''
query_sql = ''' SELECT in_url,controller_name
FROM
sparkatp.interface_info
WHERE
id IN (
SELECT
a.id
FROM
(SELECT * from sparkatp.interface_info) a
WHERE
( a.swagger_id in (SELECT id FROM sparkatp.swagger_info WHERE team="UBRD") )
AND a.created_time > "2022-01-01 00:00:50"
AND is_used = 1 and at_numbers = 0 and offline=0 and jira_id is null) ORDER BY created_time'''
query_sql_r = """SELECT
si.id AS interface_id,
rpgm.jira_id,
si.in_url
FROM
(
SELECT
ii.id,
ii.in_url,
ii.jira_id
FROM
sparkatp.interface_info ii
WHERE
ii.swagger_id IN ( SELECT id FROM sparkatp.swagger_info WHERE team = "UBRD" )
AND ii.created_time > "2022-01-01 00:00:50"
AND ii.is_used = 1
AND ii.at_numbers = 0
AND ii.offline = 0
AND ii.jira_id IS NULL
) si
INNER JOIN (
SELECT
rp.interface_id,
rp.jira_id,
MAX( modified_time )
FROM
request_parameters rp
WHERE
rp.jira_id IS NOT NULL
AND rp.jira_id <> 'None'
AND rp.jira_id <> ''
GROUP BY
rp.interface_id
) rpgm ON rpgm.interface_id = si.id;"""
# query_sql = " SELECT * FROM sparkatp.interface_info WHERE id IN (SELECT a.id FROM(SELECT * from sparkatp.interface_info) a WHERE( a.swagger_id in (SELECT id FROM sparkatp.swagger_info WHERE team='UBRD') ) AND a.created_time > '2022-01-01 00:00:50' AND is_used = 1 and at_numbers = 0 and offline=0 and jira_id is null) ORDER BY created_time"
query_sql_r_result = obj_mysql_helper.select_all(query_sql_r)
query_sql_dict = {}
query_sql_dict_temp = {}
# <class 'dict'>: {'interface_id': 924646, 'jira_id': 'HHC-48875', 'in_url': 'http://peppa-parent-api.qa.huohua.cn/classes/audition/enter'}
for qsrr in query_sql_r_result:
in_url = "{}_{}".format(str(qsrr.get('interface_id')).strip(' '), qsrr.get('in_url').strip(' '))
if qsrr.get('jira_id') in query_sql_dict.keys():
if in_url in query_sql_dict.get(qsrr.get('jira_id')):
pass
else:
query_sql_dict[qsrr.get('jira_id')].append(in_url)
else:
query_sql_dict[qsrr.get('jira_id')] = [in_url]
query_sql_s = """SELECT
si.id AS interface_id,
rpgm.jira_id,
si.in_url
FROM
(
SELECT
ii.id,
ii.in_url,
ii.jira_id
FROM
sparkatp.interface_info ii
WHERE
ii.swagger_id IN ( SELECT id FROM sparkatp.swagger_info WHERE team = "UBRD" )
AND ii.created_time > "2022-01-01 00:00:50"
AND ii.is_used = 1
AND ii.at_numbers = 0
AND ii.offline = 0
AND ii.jira_id IS NULL
) si
INNER JOIN (
SELECT
rp.interface_id,
rp.jira_id,
MAX( modified_time )
FROM
response_parameters rp
WHERE
rp.jira_id IS NOT NULL
AND rp.jira_id <> 'None'
AND rp.jira_id <> ''
GROUP BY
rp.interface_id
) rpgm ON rpgm.interface_id = si.id;"""
query_sql_s_result = obj_mysql_helper.select_all(query_sql_s)
for qsrs in query_sql_s_result:
in_url = "{}_{}".format(str(qsrs.get('interface_id')).strip(' '), qsrs.get('in_url').strip(' '))
if qsrs.get('jira_id') in query_sql_dict.keys():
if in_url in query_sql_dict[qsrs.get('jira_id')]:
pass
else:
query_sql_dict[qsrs.get('jira_id')].append(in_url)
else:
query_sql_dict[qsrs.get('jira_id')] = [in_url]
return query_sql_dict
def send_log_info(self):
'''
每天进行日志提醒
:return:
'''
feishu_name_id = {"罗志鹏": "7020366259502153730", "谯新久": "7020366258071715842",
"陈洁": "7020370251997069314", "蒲思宇": "7076270364313108481", "张楠": "7076270369128349697",
"刘涛婷": "7020366262240854017"}
order_list = ["谯新久", "陈洁", "刘涛婷", "罗志鹏", "蒲思宇", "张楠"]
# for name in order_list:
def send_feishu(self, inferfaces):
'''
发送飞书
:return:
'''
at_user_list = [{"tag": "at", "user_id": "{}".format("7020366259502153730")}]
message_data = {"msg_type": "post", "content": {
"post": {"zh_cn": {"title": "有存在的接口未实现自动化哦",
"content": [[{"tag": "text", "text": "问题数据:"}],
[{"tag": "text", "text": "{}".format(inferfaces)}],
at_user_list]}}}}
web_hook = "https://open.feishu.cn/open-apis/bot/v2/hook/40696c86-264a-4222-a40c-cfd64a05dffd"
headers = {"Content-Type": "application/json"}
json_data = json.dumps(message_data)
print(json_data)
rsp = requests.post(url=web_hook, data=json_data, headers=headers)
return rsp
def send_message_by_feishu(self, web_hook, data):
headers = {"Content-Type": "application/json"}
json_data = json.dumps(data)
rsp = requests.post(url=web_hook, data=json_data, headers=headers)
return rsp
def compare_txt(self, su_t, st_t):
'''
对比两个文档,返回不同的信息
:param su_t:
:param st_t:
:return:
'''
file_object1 = open(su_t, 'r', encoding='utf-8')
file_object2 = open(st_t, 'r', encoding='utf-8')
try:
while True:
line = file_object1.readline()
lines = file_object2.readline()
if line.strip() != lines.strip():
print("pc :", line)
print("right:", lines)
else:
continue
finally:
file_object1.close()
file_object2.close()
if __name__ == '__main__':
test = Handle_tools()
# get_interfaces = test.query_interface_sql()
# if get_interfaces:
# test.send_feishu(json.dumps(get_interfaces))
# else:
# print("都已经完成")
su_t = r"D:\UiVispark\spark1.txt"
st_t = r"D:\UiVispark\spark2.txt"
test.compare_txt(st_t=st_t,su_t=su_t)

View File

@@ -0,0 +1 @@
host_list = [{"ip":'127.0.0.1',"host_names":"ts.sim.huohua.cn"},{"ip":'127.0.0.1',"host_names":"student-api.sim.huohua.cn"}]

View File

@@ -0,0 +1,263 @@
# -*- coding:utf-8 -*-
# @Time : 2023/7/5 10:27
# @Author: luozhipeng
# @File : host_update.py
import os
from python_hosts import Hosts, HostsEntry
import sys
LOCAL_PATH = os.path.dirname(os.path.abspath(__file__))
BASE_PROJECT_PATH = os.path.abspath(os.path.join(LOCAL_PATH, '../../../{}'.format("UBRD")))
BASIC_PATH = os.path.abspath(os.path.join(LOCAL_PATH, '../../../'))
sys.path.append(BASE_PROJECT_PATH)
sys.path.append(BASIC_PATH)
from base_framework.public_tools.utils import Tools
from base_framework.public_tools.apollo import Apollo
import requests, time
import os
import sys
import socket
import subprocess
import requests
import urllib.parse
obj_apollo = Apollo()
obj_tools = Tools()
class HostUpdate:
def __init__(self):
if sys.platform.startswith('win'):
self.hosts_location = Hosts(path='C:\Windows\System32\drivers\etc\hosts')
elif sys.platform.startswith('darwin'):
self.hosts_location = Hosts(path='\etc\hosts')
def host_update_by_host_dict(self, host_dict_list: list):
entry_list = []
for host in host_dict_list:
new_entry = HostsEntry(entry_type='ipv4', address=host['ip'], names=[host['host_name'], ''])
entry_list.append(new_entry)
self.hosts_location.add(entry_list)
self.hosts_location.write()
def host_remove_intercept(self,address,name):
self.hosts_location.remove_all_matching(address,name)
self.hosts_location.write()
def get_ip_by_host_domain(self,domain: str):
ip=socket.gethostbyname(domain)
return ip
def wait_dns_flush(self,ip,domain):
count=1
while True:
cmd1="ipconfig /flushdns"
result1=subprocess.run(cmd1,capture_output=True,text=True)
output1=result1.stdout
print(output1)
cmd2="ping {}".format(domain)
result2=subprocess.run(cmd2,capture_output=True,text=True)
output2=result2.stdout
print(output2)
cmd="ipconfig /displaydns"
result=subprocess.run(cmd,capture_output=True,text=True)
output=result.stdout
print(output)
if output is None:
output=''
if ip in output:
break
if count >= 30:
break
count=count + 1
host_list=[{"ip": ip,"host_name": domain}]
self.host_remove_intercept(ip,domain)
self.host_update_by_host_dict(host_list)
time.sleep(2)
class CoreApollo:
def __init__(self,show_username=None,password=None,current_evn=None):
if not show_username:
self.show_username="liuruiquan"
if not password:
self.password="lrq5823LRQ"
self.apollo_url="http://apollo.qa.huohua.cn/signin"
if not current_evn:
self.current_evn="SIM"
self.apollo_host="http://apollo.qa.huohua.cn"
self.session=self.core_apollo_login()
def core_apollo_login(self):
post_data=dict()
post_data['login-submit']='登录'
post_data['username']=self.show_username
post_data['password']=self.password
req_session=requests.Session()
resp=req_session.post(url=self.apollo_url,data=post_data)
return req_session
def get_key(self,server,cluster,key,project):
req_url="%s/apps/%s/envs/%s/clusters/%s/namespaces" % (
self.apollo_host,server,self.current_evn,cluster)
resp=self.session.get(url=req_url).json()
for item in resp:
base_info=item.get('baseInfo')
items=item.get('items')
if not base_info['namespaceName'].lower() == project.lower():
continue
for key_info in items:
item_detail=key_info["item"]
if item_detail["key"] == key:
return key_info["item"]
else:
return None
def set_key(self,key,value,cluster='default',server="",project=None):
if server:
self.server=server
req_url="%s/apps/%s/envs/%s/clusters/%s/namespaces/%s/item" % (
self.apollo_host,self.server,self.current_evn,cluster,project)
items=self.get_key(server,cluster,key,project)
if items is None:
items=dict()
items["value"]=value
items["key"]=key
items["tableViewOperType"]='create'
items["addItemBtnDisabled"]=True
else:
items["value"]=value
items["tableViewOperType"]='update'
update_resp=self.session.put(url=req_url,json=items)
assert update_resp.status_code == 200
release_body=dict()
release_time_stamp=time.localtime()
release_time='%s-release' % time.strftime("%Y%m%d%H%M%S",release_time_stamp)
release_body["isEmergencyPublish"]=False
release_body["releaseComment"]=""
release_body["releaseTitle"]=release_time
release_url="%s/apps/%s/envs/%s/clusters/%s/namespaces/%s/releases" % (
self.apollo_host,self.server,self.current_evn,cluster,project)
resp=self.session.post(url=release_url,json=release_body)
assert resp.status_code == 200
time.sleep(5)
def set_user_force_true(self,vlaue="true"):
self.set_key(value=vlaue,key="user-token.forceFallback",server="peppa-core-api",cluster="check"
,project="application")
def set_teacher_force_true(self,vlaue="true"):
self.set_key(value=vlaue,key="sso-token.forceFallback",server="peppa-core-api",cluster="check"
,project="application")
class EduClassroom:
def kw_ubrd_get_online_test_classroom(self,post_data_input=None):
post_data = dict()
host = "sso.huohua.cn"
api_url = "https://{}/authentication/form".format(host)
show_username = "liuruiquan"
username = "liuruiquan"
password = "lrq5823LRQ"
post_data['showUsername'] = show_username
post_data['username'] = username
post_data['password'] = password
user_agent = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"}
deviceid = {"SSO_DEVICE_ID":"8dfbecf2-064a-4e3f-ac2b-fc09a2401416"}
req_session = requests.session()
req_session.cookies.update(deviceid)
req_session.headers.update(user_agent)
resp = req_session.post(
url=api_url,
data=post_data,
allow_redirects=False)
# token_header = {"accesstoken": token}
# req_session.headers.update(token_header)
# print(token_header)
client_id = "tic-payment-admin"
origin_url="aHR0cHM6Ly9zc28uaHVvaHVhLmNuLw=="
# origin_url="aHR0cHM6Ly9zc28uc2ltLmh1b2h1YS5jbi8="
redirect_uri = "https://{}/uim/authorize_proxy?client_id={}&debug_mode=1&origin_url={}".format(host,client_id,origin_url)
print(redirect_uri)
token_code_url = "https://{}/oauth/authorize?client_id={}&response_type=code&".format(host,client_id,redirect_uri)
authorize_data = {"redirect_uri": redirect_uri}
authorize_data_uri =urllib.parse.urlencode(authorize_data)
resp = req_session.get(
url=token_code_url+authorize_data_uri,
data=authorize_data,
allow_redirects=False)
print(resp.url)
print(resp.headers)
print(resp)
# token_url = "https://{}/uim/authorize_proxy?client_id={}&debug_mode=1&origin_url={}&code={}".format(host,client_id,origin_url,code)
token_url= resp.headers["Location"]
# authorize_code_data = {"client_id":"tic-payment-admin","debug_mode": 1,"origin_url": origin_url,"code":code}
print(token_url,"token_url")
# print(authorize_code_data)
resp = req_session.get(
url=token_url,
# data=authorize_code_data,
allow_redirects=False)
print(resp)
print(resp.url)
for key, value in req_session.cookies.items():
if key == 'peppa_sso_token':
token = value
token_header = {"accesstoken": token}
req_session.headers.update(token_header)
print(token_header)
print(value)
break
op_time = obj_tools.get_format_date(r_type=4,add_minutes=10)
print(op_time)
create_classroom_json = {"courseId":1898,"unionFlag":0,"lessonId":49264,"planStudentCount":4,"timezoneName":"Asia/Shanghai","openTime":op_time,"overseasTag":0,"secondOpenTime":op_time,"teacherId":36573,"type":100}
api_url_created_classroom = "https://teach-api.huohua.cn/peppa-teach-api/classroom"
create_classroom_resp = req_session.post(
url=api_url_created_classroom,
json=create_classroom_json)
# print(req_session.headers)
# print(req_session.cookies)
# print(resp.content)
# print(resp.url)
print(create_classroom_resp.json())
classroom_id = create_classroom_resp.json()['data']["classroomId"]
print(create_classroom_resp.json())
#
# return req_session
add_student_json = {"classroomId":classroom_id,"studentId":274886,"userId":275774,"joinType":0,"ignoreClassHour":1}
api_url_add_student = "https://teach-api.huohua.cn/peppa-teach-api/classroom_student/add"
add_student_resp = req_session.post(
url=api_url_add_student,
json=add_student_json)
print(add_student_resp.json())
# return req_session
add_student_json1 = {"classroomId":classroom_id,"studentId":5635578,"userId":5646962,"joinType":0,"ignoreClassHour":1}
add_student_resp1 = req_session.post(
url=api_url_add_student,
json=add_student_json1)
print(add_student_resp1.json())
if __name__ == '__main__':
host_list = [{"ip": "127.0.0.1", "host_name": "ts.sim.huohua.cn"},
{"ip": "127.0.0.1", "host_name": "its.sim.huohua.cn"},
{"ip": "127.0.0.1", "host_name": "student-api.sim.huohua.cn"},
{"ip": "127.0.0.1", "host_name": "sentry.sim.huohua.cn"},
{"ip": "127.0.0.1", "host_name": "gray.sim.huohua.cn"},
{"ip": "127.0.0.1", "host_name": "classroom-api.sim.huohua.cn"},
{"ip": "127.0.0.1", "host_name": "logserver.sim.huohua.cn"},
{"ip": "127.0.0.1", "host_name": "zipkin.sim.huohua.cn"},
{"ip": "127.0.0.1", "host_name": "gs.sim.huohua.cn"}]
A = EduClassroom()
A.kw_ubrd_get_online_test_classroom()

View File

@@ -0,0 +1,142 @@
import os
import re
import threading
class StatisticalFunction:
def __init__(self):
self.java_dir =[]
self.zh_file = []
self.python_dir = []
self.robot_dir = []
def check_lang_every_word(self,file_path):
""" 获取文件中注释外含中文的文档 """
# file_path ='C:\\Users\\HuoH # a=time.time()ua\\Downloads\\peppa-parent-api\\src\\main\\java\\com\\peppa\\parent\\api\\common\\ApiErrorEnum.java'
with open(file_path,'r',encoding='utf-8') as text:
all_content = text.read()
all_content = all_content.encode('utf-8')
all_content = all_content.decode('utf-8','ignore')
yy= re.sub("\/\*\*.*?\*\*\/", "",all_content) # 去除多行注释
yy = re.sub("\/\*[\w\W]*?\*\/|\/\/.*", "", yy) # 去除文本注释
yy = re.sub("(?<!:)\/\/.*", "", yy) # 去除行注释
yy=yy.replace(" ","")
yy=yy.replace('\n','')
# for eachline in text.read():
# line = eachline.strip()
# l2 =line.encode('utf-8')
# l1=l2.decode('utf-8','ignore')
# lineTuple = langid.classify(l1)
# for word in yy:
# lineTuple = langid.classify(word)
# if lineTuple[0] == "zh":
# if file_path not in zh_file:
# zh_file.append(file_path)
zhPattern = re.compile(u'[\u4e00-\u9fa5]+')
match = zhPattern.search(yy)
if match:
print(match,'\n',file_path)
self.zh_file.append(file_path)
# b =time.time()
# print(b-a)
def get_java_file(self,path):
"""获取所有java文件"""
for root, dirs, files in os.walk(path):
for file in files :
if file.endswith('.java'):
# print(os.path.join(root, file))
self.java_dir.append(os.path.join(root, file))
if dirs:
for dir_single in dirs:
if re.match('__',dir_single) :
print(dir_single)
# print(os.path.join(root, dirs[0]))
self.get_java_file(os.path.join(root,dir_single))
return self.java_dir
def muti_threading(self):
# 多线程调用
threads =[]
for file_zh in self.java_dir:
threads.append(
threading.Thread(target=self.check_lang_every_word,args=(file_zh,))
)
for thread in threads:
thread.start()
# print(thread.name)
for thread in threads:
thread.join()
# print(thread.name,"结束")
print("end")
def get_python_file(self,path):
"""获取所有python文件"""
for root, dirs, files in os.walk(path):
for file in files :
if file.endswith('.py'):
# print(os.path.join(root, file))
self.python_dir.append(os.path.join(root, file))
if dirs:
for dir_single in dirs:
if re.match('__',dir_single) :
print(dir_single)
# print(os.path.join(root, dirs[0]))
self.get_python_file(os.path.join(root,dir_single))
return self.python_dir
def get_robot_file(self,path):
"""获取所有robot文件"""
for root, dirs, files in os.walk(path):
for file in files :
if file.endswith('.robot'):
# print(os.path.join(root, file))
self.robot_dir.append(os.path.join(root, file))
if dirs:
for dir_single in dirs:
if re.match('__',dir_single) :
print(dir_single)
# print(os.path.join(root, dirs[0]))
self.get_robot_file(os.path.join(root,dir_single))
return self.robot_dir
@staticmethod
def get_class_function(class_ubrd):
ubrd_function = []
all_function = dir(class_ubrd)
for function in all_function:
if 'kw_in_ubrd' in function :
ubrd_function.append(function)
return ubrd_function
@staticmethod
def statistical(ubrd_function,dir_robot_python):
dict_function = {}
for function in ubrd_function:
count_num_all = 0
for dir_a in dir_robot_python:
with open(dir_a,'r',encoding='utf-8') as text:
all_content = text.read()
count_num_file = all_content.count(function)
count_num_all = count_num_all +count_num_file
count_num_all = count_num_all-1
if count_num_all in dict_function:
dict_function[count_num_all] += 1
else:
dict_function[count_num_all] = 1
return dict_function
if __name__ == '__main__':
pass

View File

@@ -0,0 +1,64 @@
import datetime, time
import random
class UbrdNowTime:
@staticmethod
def kw_ubrd_get_now_time():
now_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
return now_time
def get_few_days_before_or_after_current_time(self, n=0):
"""
| 功能说明: | 获取当前时间的前几天或后几天的日期 |
| 输入参数: | n | 前n天或后n天,通过正负数来区分 |
| 输入参数: | hms | 1表示返回时分秒为当前时间0表示00:00:00 |
| 返回参数: | newtime_str | 当前时间的前几天或后几天的具体日期|
| 作者信息: | 刘睿权 | 修改时间 20211025|
举例说明:
| get_few_days_before_or_after_current_time | -7 |表示当前时间往后推7天的日期|
"""
nowtime = datetime.datetime.now()
Newnowtime = nowtime - datetime.timedelta(days=n)
return datetime.datetime.strftime(Newnowtime, '%Y-%m-%d %H:%M:%S')
@staticmethod
def kw_change_format_time(r_date):
get_time = r_date / 1000
time_array = time.localtime(get_time)
other_style_time = time.strftime("%Y-%m-%d %H:%M:%S", time_array)
return other_style_time
@staticmethod
def kw_ubrd_get_random_time():
'''
获取一个随机的时间
:param r_date:
:return:
'''
get_random_week = random.randint(0, 11)
get_minite_time = get_random_week * 5
if get_minite_time == 0:
get_time = "00"
elif get_minite_time == 5:
get_time = "05"
else:
get_time = str(get_minite_time)
return get_time
@staticmethod
def kw_ubrd_get_week_day(type):
now = datetime.datetime.now()
# 计算当前周的周一和下周一的日期
current_weekday = now.weekday() # 获取当前日期的星期几星期一为0星期日为6
current_monday = now - datetime.timedelta(days=current_weekday) # 当前周的周一
next_monday = current_monday + datetime.timedelta(days=7) # 下周一
# 设置时间为0点
if type == "1":
current_day = current_monday.replace(hour=0, minute=0, second=0, microsecond=0)
else:
current_day = next_monday.replace(hour=0, minute=0, second=0, microsecond=0)
return current_day
if __name__ == '__main__':
print(UbrdNowTime.kw_ubrd_get_week_day(type=2))

View File

@@ -0,0 +1,272 @@
# -*- coding:utf-8 -*-
"""
Author: 罗志鹏
Email: luozhipeng@huohua.cn
Create Date: 2022/03/03 11:25 下午
"""
from base_framework.public_tools import log
from base_framework.public_tools.sqlhelper import MySqLHelper
from library.UBRD_interface import UBRDInterface
from base_framework.public_tools.edu_user_helper import EDUUserHelper
from base_framework.public_tools.redis_api import RedisApi
from base_framework.public_tools.read_config import ReadConfig
from base_framework.base_config.current_pth import env_choose_path
from redis import Redis
from base_framework.public_tools.runner import Runner
from base_framework.public_tools.utils import Tools
from base_framework.public_business.common.UBRD.UBRD_public_business import BaseLogic
from base_framework.public_business.common.UBRD.kw.user_keyword import UserKW
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import base64
obj_runner = Runner()
obj_log = log.get_logger()
obj_my_sql_helper = MySqLHelper()
obj_edu_user_helper = EDUUserHelper()
obj_redis_api = RedisApi()
obj_tools = Tools()
obj_base_logic = BaseLogic()
user_kw_obj = UserKW()
class UserCommon(UBRDInterface):
def __init__(self):
super().__init__()
self.evn_cfg = ReadConfig(env_choose_path)
self.current_business = self.evn_cfg.get_value(sections="run_evn_name", options="current_business")
@staticmethod
def update_user_pwd_by_phone(phone):
"""
功能初始化用户密码为A123456
| 输入参数: | phone string | 手机号 |
| 作者: | lzp | 2022.03.07 |
"""
sql_user_by_phone = "SELECT x.id FROM ucenter.user_profile x WHERE x.phone IN ('{0}')".format(phone)
user_by_phone = obj_my_sql_helper.select_all(sql_user_by_phone)
if isinstance(user_by_phone, tuple):
raise RuntimeError("未找到手机号为{0}的用户".format(phone))
sql_update_pwd = "update ucenter.user_profile set auth_hash = '2ad5381b6a2952287a95b9accb0bb51c'," \
"auth_salt='fFRzeLZGR5' WHERE phone IN ('{0}')".format(phone)
obj_my_sql_helper.update(sql_update_pwd)
obj_log.info('成功更新{0}用户密码为A123456'.format(phone))
def update_teacher_pwd_by_classroom(self, classroom_Code):
"""
功能初始化教师账号密码为Huohua123456
| 输入参数: | classroom_Code string | 课堂code |
| 作者: | lzp | 2022.03.07 |
"""
teacher_info = self.get_teacher_info_by_classroom(classroom_Code)
sql_update_pwd = "update account.account set pwd = 'e1NTSEF9dzE4ZlFhRytVNzZWeFVKMFFIV2VUNy83U2hxOE5oNUhMT0doTnc9PQ==' WHERE id ={0}".format(
teacher_info["account_id"])
obj_my_sql_helper.update(sql_update_pwd)
obj_log.info('成功更新{0}课堂教师{1}密码为Mima@123'.format(classroom_Code, teacher_info["phone"]))
def update_teacher_pwd_by_classroom_2(self,classroom_Code):
"""
功能初始化教师账号密码为MIma@123456
| 输入参数: | classroom_Code string | 课堂code |
| 作者: | lrq | 2024.06.12 |
"""
teacher_info = self.get_teacher_info_by_classroom(classroom_Code)
sql_update_pwd = "update account.account set pwd = 'e1NTSEF9dzE4ZlFhRytVNzZWeFVKMFFIV2VUNy83U2hxOE5oNUhMT0doTnc9PQ==' WHERE id ={0}".format(teacher_info["account_id"])
obj_my_sql_helper.update(sql_update_pwd)
obj_log.info('成功更新{0}课堂教师{1}密码为MIma@123456'.format(classroom_Code,teacher_info["phone"]))
def update_teacher_pwd_by_teacher_id(self, teacher_id):
"""
功能初始化教师账号密码为Huohua123456
| 输入参数: | classroom_Code string | 课堂code |
| 作者: | lzp | 2022.03.07 |
"""
teacher_info = self.get_teacher_info_by_teacher_id(teacher_id)
sql_update_pwd = "update account.account set pwd = 'e1NTSEF9dzE4ZlFhRytVNzZWeFVKMFFIV2VUNy83U2hxOE5oNUhMT0doTnc9PQ==' WHERE id ={0}".format(
teacher_info["account_id"])
obj_my_sql_helper.update(sql_update_pwd)
obj_log.info('成功更新{0}教师{1}密码为Mima@123'.format(teacher_id, teacher_info["phone"]))
def get_chess_classroom_by_mcr_code(self, mcr_code):
"""
功能:获取课堂教师信息 仅支持emp教师
| 输入参数: | teacher_id int | 老师id |
| 作者: | lzp | 2022.03.07 |
"""
sql_chess_classroom = "SELECT x.relation_classroom_code FROM learning_plan.classroom_relation x WHERE x.classroom_code IN ('{0}') and status =1 and relation_type = 1".format(
mcr_code)
chess_classroom = obj_my_sql_helper.select_all(sql_chess_classroom)
return chess_classroom
def get_teacher_info_by_teacher_id(self, teacher_id):
"""
功能:获取课堂教师信息 仅支持emp教师
| 输入参数: | teacher_id int | 老师id |
| 作者: | lzp | 2022.03.07 |
"""
sql_phone_by_classroom_code = "select tp.id,e.phone,tp.account_id from emp.employee e inner join teach_teacher.teacher_profile tp on tp.employee_id = e.id inner join account.account a on a.id= tp.account_id where tp.id = {0}".format(
teacher_id)
teacher_info = obj_my_sql_helper.select_all(sql_phone_by_classroom_code)
return teacher_info[0]
def get_teacher_info_by_classroom(self, classroom_Code):
"""
功能:获取课堂教师信息 仅支持emp教师
| 输入参数: | classroom_Code string | 课堂code |
| 作者: | lzp | 2022.03.07 |
"""
sql_phone_by_classroom_code = "select c.teacher_id,e.phone,tp.account_id from emp.employee e inner join teach_teacher.teacher_profile tp on tp.employee_id = e.id inner join teach_classes.classroom c on c.teacher_id = tp.id inner join account.account a on a.id= tp.account_id where c.code = '{0}'".format(
classroom_Code)
teacher_info = obj_my_sql_helper.select_all(sql_phone_by_classroom_code)
return teacher_info[0]
def get_student_info_by_phone(self, phone):
"""
功能:根据手机号码获取学生基本信息
| 输入参数: | phone string | 手机号 |
"""
user_info = user_kw_obj.kw_ubrd_public_get_user_info_by_phone(phone)
sql = "SELECT id studentId FROM ucenter.student_profile where user_id ='{}'".format(user_info["user_id"])
return obj_my_sql_helper.select_one(sql)
def get_classroom_info_by_code(self, code):
"""
功能:根据课堂号获取课堂信息
| 输入参数: | phone string | 手机号 |
"""
sql = "SELECT id,code,classes_id,classes_code,course_id,course_name,lesson_id,lesson_name,teacher_id,open_time,close_time,is_closed,status FROM teach_classes.classroom where code = '{0}'".format(
code)
return obj_my_sql_helper.select_one(sql)
def register_by_code_phone(self, phone, countryCode=86):
auth_code_send_result = obj_base_logic.logic_public_send_auth_code(
{'h': {'accept-language': 'en'}, 'phone': phone, 'countryCode': '{}'.format(countryCode), 'authType': 2,
'verifyAppId': 2})
obj_log.info(auth_code_send_result)
sms_code = obj_edu_user_helper.get_sms_code_by_phone(phone)
obj_log.info(sms_code)
login_post_result = self.kw_in_ubrd_login_post(
**{'phone': phone, 'countryCode': '86', 'password': 'A123456', 'authCode': sms_code})
obj_log.info(login_post_result)
# 更新密码
obj_my_sql_helper.update(
"update ucenter.user_profile set auth_hash = 'e2a0c8ec7503d1bfea587d5ffa0efc2f',auth_salt='fTvsqofig7' WHERE phone='{}'".format(
phone))
def get_change_pwd_vcode_by_sms_code(self, phone):
""" 获取学生端修改密码验证码 """
sms_code = obj_edu_user_helper.get_sms_code_by_phone(phone)
return sms_code
def get_register_vccode_by_redis(self, phone):
"""
获取学生端注册验证码
"""
key = "api:AUTH_CODE:{0}_SIGN_IN".format(phone)
obj_log.info(key)
return self.get_student_key_value_by_redis(key, 9)
def delete_change_student_pwd_limit_redis(self, phone):
"""
功能:删除学生端修改密码次数限制缓存
| 作者: | lzp | 2021.08. 15 |
"""
key_1 = "GRANULARITY:PHONE_AUTH_RECEIVE_COUNT_CHANNEL_MOBILE_API{0}".format(phone)
key_2 = "GRANULARITY:PHONE_AUTH_RECEIVE_COUNT_CHANNEL_MOBILE_API{0}_1".format(phone)
self.delete_student_key_redis(key_1, 9)
self.delete_student_key_redis(key_2, 9)
def delete_student_key_redis(self, key: object, db: object):
"""
删除指定key
"""
if self.current_business == "hh":
host = 'redis.qa.huohua.cn'
password = 'AcUVeRb8lN'
port = 6379
elif self.current_business == "hhi":
host = 'redis.qa.visparklearning.com'
password = 'hxTjlWBYdK6UpAGF'
port = 6379
else:
raise RuntimeError("读取本地配置环境失败未能正常连接redis")
pool = Redis(host=host, port=port, password=password, db=db)
pool.delete(key)
def get_student_key_value_by_redis(self, key, db):
"""
获取指定key的value
"""
if self.current_business == "hh":
host = 'redis.qa.huohua.cn'
password = 'AcUVeRb8lN'
port = 6379
elif self.current_business == "hhi":
host = 'redis.qa.visparklearning.com'
password = 'hxTjlWBYdK6UpAGF'
port = 6379
else:
raise RuntimeError("读取本地配置环境失败未能正常连接redis")
pool = Redis(host=host, port=port, password=password, db=db, decode_responses=True)
value = pool.get(key)
return value
def check_data_in_list(self, array: list, item):
"""
判断某个数据是否在数组中
"""
if item in array:
return True
else:
return False
def update_classroom_data_to_join(self, classroom_id):
"""
更新学习服务课堂数据
"""
open_time_timestamp = obj_tools.get_format_date(r_type=12)
close_time_timestamp = obj_tools.get_format_date(r_type=12, add_hours=1)
open_time_standard_time = obj_tools.get_format_date(r_type=4)
close_time_standard_time = obj_tools.get_format_date(r_type=4, add_hours=1)
# 更新学员课表数据
sql_learning_schedule = "update learning_plan.learning_schedule set open_time ='{0}',close_time = '{1}' " \
"where classroom_id = {2}".format(open_time_standard_time, close_time_standard_time,
classroom_id)
obj_my_sql_helper.update(sql_learning_schedule)
# 更新教师课表数据
sql_teaching_schedule = "update learning_plan.teaching_schedule set open_time ='{0}',close_time = '{1}', class_status =0 " \
"where classroom_id = {2}".format(open_time_timestamp, close_time_timestamp,
classroom_id)
obj_my_sql_helper.update(sql_teaching_schedule)
def check_data_in_db(self, sql, retry_count):
"""
判断某个数据是否在数据库中
"""
obj_my_sql_helper.check_result_exist(sql, retry_count=retry_count)
def encryption_aes(self,data,key=None):
if not key:
key = b"y3wa93twda35eqer"
cipher = AES.new(key, AES.MODE_ECB)
padded_data = pad(data.encode(), AES.block_size) # PKCS5Padding
encrypted_data = cipher.encrypt(padded_data)
return base64.b64encode(encrypted_data).decode()
if __name__ == '__main__':
user_common_obj = UserCommon()
a = 138475
s=user_common_obj.update_teacher_pwd_by_teacher_id(teacher_id=183152 )
print(s)
# print(user_common_obj.get_user_info_by_phone('13458500234'))
# phone_dict ={'phone': '65-98760021'}
# user_common_obj.get_register_vccode_by_redis('65-98760021')
# user_common_obj.update_classroom_data_to_join(classroom_id=500852944)

View File

@@ -0,0 +1,20 @@
# -*- coding:utf-8 -*-
# @Time : 2022/11/22 18:48
# @Author: luozhipeng
# @File : user_message.py
class ClassesReminder():
def __init__(self):
pass
def before_24h_reminder(self):
pass
def absence_classes_reminder(self):
pass
def before_1h_reminder(self):
pass
def before_2min_reminder(self):
pass

View File

@@ -0,0 +1,9 @@
# 存放小组内的配置信息
[m-userinfo]
login_sso_info = {"user":"liuruiquan","password":"lrq5823LRQ"}
login_parent_info ={"phone":"15328118883","password":"a123456","loginType":2,"countryCode":"null","userType":1,"subjectType":0}
login_student_info ={"phone":"18202810506","countryCode":86,"password":"A123456","authCodeType":2,"loginType":2}
realm_name = {"parent_api":"https://parent-api.huohua.cn","student_api":"https://student-api.huohua.cn","parent_vispark_api":"https://pst-gw.huohua.cn"}
parent_url = {"login_url":"https://m.huohua.cn/passport/login","parentPayEditionPage_url":"/parentPayEditionPage/user_info","recommendedCourse_url":"/personal_center/recommendedCourse"}
student_url = {"login_url":"https://core-api.huohua.cn/token","scheduleCount_url":"/learning/schedule/count/4"}
parent_course_url = {"get_trial_url":"/parent/audition/classes/overseas/course"}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

View File

@@ -0,0 +1,37 @@
# -*- coding:utf-8 -*-
"""
Author: qiaoxinjiu
Email: qiaoxinjiu@qq.com
Create Date: 2026/01/17 5:58 下午
"""
from base_framework.public_tools.mg_keyword import ManageKeyWord
from base_framework.public_tools import utils
from zhyy.library.BusinessKw.SZPurchase.index import PurchaseIndex
obj_get_way = utils.Tools()
obj_purchase_kw = PurchaseIndex()
obj_manage_kw = ManageKeyWord()
class PurchaseLogic():
def __init__(self):
pass
def logic_zhyy_sz_purchase_todo_task(self, post_data_input, phone=None):
"""
| 功能说明: | 深圳采购待办任务处理 |
| 请求参数名 | 说明 | 类型 | 条件 | 是否必填 | |
| userName | 登录名 | string |None | 0 |
作者信息: | 谯新久 | 2026/01/17 |
"""
try:
post_data_input = eval(post_data_input)
except:
post_data_input = post_data_input
return
if __name__ == '__main__':
pass

View File

@@ -0,0 +1,68 @@
# -*-coding:utf-8-*
import os
import sys
# 添加项目根目录到 Python 路径,以便导入 base_framework 模块
current_file_path = os.path.abspath(__file__)
project_root = os.path.abspath(os.path.join(os.path.dirname(current_file_path), '../../'))
if project_root not in sys.path:
sys.path.insert(0, project_root)
from base_framework.public_tools import utils
from base_framework.public_tools.log import get_logger
from base_framework.public_tools.runner import Runner
from base_framework.public_tools.utils import Tools
from base_framework.public_tools import check_resp, convert_json, get_user, custom_check_resp
from base_framework.public_tools.eureka_api import EurekaAPI
import requests
obj_runner = Runner()
obj_log = get_logger()
tools = Tools()
obj_tool = utils.Tools()
s = requests.session()
eureka = EurekaAPI()
class ZhyyInterface:
def __init__(self):
self.domain_url = eureka.get_url_from_config()
def kw_in_zhyy_purchase_todo_get(self, is_check='', **kwargs):
"""
查询采购待办任务 + POST + interface id: 88383
url: http://peppa-personas-server.qa.huohua.cn/crowd_pack_query/list
| 请求参数名 | 说明 | 类型 | 条件 | 是否必填 |
| is_check | is_check默认空不校验返回有值就校验返回 | string | 业务case的时候需要传入值 | False |
"""
user, kwargs = get_user(kwargs)
kwargs = convert_json(kwargs)
url = "%s/erp/purchase-workbench/get-todo" % self.domain_url
obj_log.info("your input:{0}".format(kwargs))
resp = obj_runner.call_rest_api(API_URL=url, req_type="GET", user=user)
check_resp(is_check, resp)
return resp
def kw_in_zhyy_purchase_order_page_post(self, is_check='', **kwargs):
"""
查询采购单列表 + POST + interface id: 88383
url: http://peppa-personas-server.qa.huohua.cn/admin-api/erp/purchase-order/page
| 请求参数名 | 说明 | 类型 | 条件 | 是否必填 |
| is_check | is_check默认空不校验返回有值就校验返回 | string | 业务case的时候需要传入值 | False |
| pageNum | 页码 | integer | None | 0 |
| pageSize | 每页条数 | integer | None | 0 |
"""
user, kwargs = get_user(kwargs)
kwargs = convert_json(kwargs)
url = "%s/erp/purchase-order/page" % self.domain_url
obj_log.info("your input:{0}".format(kwargs))
resp = obj_runner.call_rest_api(API_URL=url, req_type="POST", json=kwargs, user=user)
check_resp(is_check, resp)
return resp
if __name__ == '__main__':
test = ZhyyInterface()
# print(test.domain_url)
a = test.kw_in_zhyy_purchase_todo_get(
user="purchase")
print(a)

134
zhyy/library/__init__.py Normal file
View File

@@ -0,0 +1,134 @@
# coding: utf-8
import importlib
import os
import re
import sys
import traceback
from base_framework.base_config.current_pth import env_choose_path
from base_framework.public_tools.read_config import ReadConfig
from base_framework.public_tools.log import get_logger
evn_cfg = ReadConfig(env_choose_path)
team = evn_cfg.get_value(sections="run_evn_name", options="current_team")
__all__ = ['KwLibrary']
cls_list_all = list()
cls_list_father = list()
cls_list_children = list()
t_c_list = list()
obj_log = get_logger()
def is_import(root):
black_list = [os.sep.join(['platform', 'tools']),'scene_server']
import_flag = True
for black_item in black_list:
if black_item in root:
# print(3, root)
import_flag = False
return import_flag
def class_import(path_list):
root_path = os.path.abspath(os.path.join(os.getcwd(), "../"))
module_path = root_path + os.sep + os.sep.join(path_list)
sys.path.append(module_path)
for root, dirs, files in os.walk(module_path):
# if is_import(root):
for file in files:
if os.path.splitext(file)[1] == '.py' and not file.startswith('__'):
with open(os.sep.join([root, file]), encoding="utf-8") as f:
for line in f.readlines():
cls_match = re.match(r"class\s(.*?)[\(:]", line)
if cls_match:
cls_name = cls_match.group(1)
try:
if cls_name not in ["Runner"]:
# module_list = root.split(os.sep)[len(os.getcwd().split(os.sep)) - 1:]
# module_list.append(os.path.splitext(file)[0])
# module = importlib.import_module('.'.join(module_list))
module_path_list = root.split(os.sep)
module_path_list.append(os.path.splitext(file)[0])
module_path = ''
for index in range(len(module_path_list)):
if module_path_list[index].lower() == 'base_framework' or module_path_list[index].lower() == 'library':
tmp_path = module_path_list[index:]
for path in tmp_path:
module_path = module_path + path + '.'
break
module_path = module_path[0:-1]
module = importlib.import_module(module_path)
class_attr = getattr(module, cls_name)
t_c_list.append(class_attr)
# if class_attr:
# if class_attr.__bases__[0] == object:
# cls_list_children.append(class_attr)
# else:
# cls_list_father.append(class_attr)
except:
traceback.print_exc()
print("ERROR import", file, cls_name)
def sort_class(class_list):
"""
功能将所有的class按继承关系排序子类在前父类在后用于startup启动加载
"""
while len(class_list) > 0:
# cls_list_len = len(cls_list_all)
tmp_class_list = []
del_list = []
for index in range(len(class_list)):
if not cls_list_all:
# 先找出所有的父类是object的基类
if class_list[index].__bases__[0] == object:
# while class_list[index].__bases__[0] == object:
tmp_class_list.append(class_list[index])
del_list.append(class_list[index])
else:
# 寻找前一步晒出基类的子类
if class_list[index].__bases__[0] in cls_list_all[len(cls_list_all) - 1]:
tmp_class_list.append(class_list[index])
del_list.append(class_list[index])
if len(tmp_class_list) > 0:
# 如果父类在已选出的类中,则保存该类
cls_list_all.append(tmp_class_list)
for item in del_list:
class_list.remove(item)
else:
# 如果父类都不在已选出的类中则全部按父类是object处理
cls_list_all.append(class_list)
class_list = []
class_import(["base_framework", "public_tools"])
class_import(["base_framework", "public_business"])
class_import([team, "library"])
# BaseLibrary = type('BaseLibrary', tuple(cls_list_father + cls_list_children), {})
sort_class(t_c_list)
c_len = len(cls_list_all)
obj_cls_tuple = ()
while c_len > 0:
obj_cls_tuple += tuple(cls_list_all[c_len - 1], )
c_len = c_len - 1
print(obj_cls_tuple)
BaseLibrary = type('BaseLibrary', obj_cls_tuple, {})
class KwLibrary(BaseLibrary):
def __init__(self):
class_len = 0
for base in BaseLibrary.__bases__:
try:
if hasattr(base, '__init__'):
if '__init__' in base.__dict__.keys():
if base.__bases__[0] != object or len(base.__init__.__code__.co_varnames) == 1:
base.__init__(self)
print(base)
# obj_log.info("init class:{} success...".format(base))
class_len += 1
except Exception as e:
if base.__bases__[0] == object:
print("INIT", e, base, base.__dict__)
# obj_log.info("import files num: {}".format(class_len))
print("import files num: {}".format(class_len))

Binary file not shown.

Binary file not shown.

View File

Binary file not shown.

View File

@@ -0,0 +1 @@
print("222222222222")

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,337 @@
# -*- coding:utf-8 -*-
"""
Author: 陈江
Email: chenjiang@sparkedu.com
Create Date: 2022/09/23 2:06 下午
"""
from base_framework.public_tools.sqlhelper import MySqLHelper
import json
obj_mysql_helper = MySqLHelper()
class CoursePackage:
def __init__(self):
pass
@staticmethod
def __get_data_name(data_list):
temp_data_list = []
if data_list:
for data in data_list:
temp_data_list.append(
{"course_package_id": data.get('value'), "cc显示_course_package_name": data.get('name'),
"course_package_name": data.get('text')})
return temp_data_list
return None
def get_course_package_by_cc_crm(self, post_data_input):
"""
| 功能说明: | 获取套餐信息根据cc/crm购买可选套餐 |
| 输入参数: |
| 作者信息: | 陈江 | 修改时间 | 2022-9-28 |
"""
sql_crm_vispark_subscribe_chinese = """SELECT
IF (abbreviation="",NAME,abbreviation) AS 'text',name,
id AS 'value',
type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope
FROM
peppa.course_package
WHERE
type = 1
AND business_line = 1
AND business_line_type = 2
AND teaching_method = 0
AND sale_status = 1
AND saleable_status = 1
AND course_subject = ',2,'
AND course_type IN ( ',5,', ',1,' )
AND sale_type = 2
AND course_teaching_method = 1
AND STATUS = 1
AND view_authority_scope LIKE '%,7,%'
ORDER BY
id DESC LIMIT 3;"""
crm_vispark_subscribe_chinese_list = self.__get_data_name(
obj_mysql_helper.select_all(sql_crm_vispark_subscribe_chinese))
sql_crm_vispark_subscribe_sg = """SELECT
IF (abbreviation="",NAME,abbreviation) AS 'text',name,
id AS 'value',
type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope
FROM
peppa.course_package
WHERE
type = 1
AND business_line = 1
AND business_line_type = 1
AND teaching_method = 0
AND sale_status = 1
AND saleable_status = 1
AND course_subject = ',1,'
AND course_type IN ( ',5,' )
AND sale_type = 2
AND course_teaching_method = 1
AND STATUS = 1
AND view_authority_scope LIKE '%,10,%'
ORDER BY
id DESC limit 3;"""
crm_vispark_subscribe_sg_list = self.__get_data_name(
obj_mysql_helper.select_all(sql_crm_vispark_subscribe_sg))
sql_crm_vispark_subscribe_oc = """SELECT
IF (abbreviation="",NAME,abbreviation) AS 'text',name,
id AS 'value',
type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope
FROM
peppa.course_package
WHERE
type = 1
AND business_line = 1
AND business_line_type in( 1,10 )
AND teaching_method = 0
AND sale_status = 1
AND saleable_status = 1
AND course_subject = ',1,'
AND course_type IN ( ',5,' )
AND sale_type = 2
AND course_teaching_method = 1
AND STATUS = 1
AND view_authority_scope LIKE '%,13,%'
ORDER BY
id DESC limit 3;"""
crm_vispark_subscribe_oc_list = self.__get_data_name(
obj_mysql_helper.select_all(sql_crm_vispark_subscribe_oc))
sql_crm_vispark_buy_chinese = """SELECT
IF (abbreviation="",NAME,abbreviation) AS 'text',name,
id AS 'value',
type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope
FROM
peppa.course_package
WHERE
type = 1
AND business_line = 1
AND business_line_type = 2
AND teaching_method = 0
AND sale_status = 1
AND saleable_status = 1
AND course_subject = ',2,'
AND course_type IN ( ',5,', ',1,', ',1,5,', ',5,1,')
AND sale_type = 1
AND course_teaching_method = 1
AND STATUS = 1
AND view_authority_scope LIKE '%,7,%'
ORDER BY
IF (abbreviation="",NAME,abbreviation) asc limit 3;"""
crm_vispark_buy_chinese_list = self.__get_data_name(
obj_mysql_helper.select_all(sql_crm_vispark_buy_chinese))
sql_crm_vispark_buy_us = """SELECT
IF (abbreviation="",NAME,abbreviation) AS 'text',name,
id AS 'value',
type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope
FROM
peppa.course_package
WHERE
type = 1
AND business_line = 1
AND business_line_type = 10
AND teaching_method = 0
AND sale_status = 1
AND saleable_status = 1
AND course_subject = ',1,'
AND course_type IN ( ',5,', ',1,', ',1,5,', ',5,1,')
AND sale_type = 1
AND course_teaching_method = 1
AND STATUS = 1
AND view_authority_scope LIKE '%,11,%'
ORDER BY
id DESC limit 3;"""
crm_vispark_buy_us_list = self.__get_data_name(
obj_mysql_helper.select_all(sql_crm_vispark_buy_us))
sql_crm_vispark_buy_canada = """SELECT
IF (abbreviation="",NAME,abbreviation) AS 'text',name,
id AS 'value',
type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope
FROM
peppa.course_package
WHERE
type = 1
AND business_line = 1
AND business_line_type = 10
AND teaching_method = 0
AND sale_status = 1
AND saleable_status = 1
AND course_subject = ',1,'
AND course_type IN ( ',5,', ',1,', ',1,5,', ',5,1,')
AND sale_type = 1
AND course_teaching_method = 1
AND STATUS = 1
AND view_authority_scope LIKE '%,1012,%'
ORDER BY
id DESC limit 3;"""
crm_vispark_buy_canada_list = self.__get_data_name(
obj_mysql_helper.select_all(sql_crm_vispark_buy_canada))
sql_crm_vispark_buy_sg = """SELECT
IF (abbreviation="",NAME,abbreviation) AS 'text',name,
id AS 'value',
type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope
FROM
peppa.course_package
WHERE
type = 1
AND business_line = 1
AND business_line_type = 1
AND teaching_method = 0
AND sale_status = 1
AND saleable_status = 1
AND course_subject = ',1,'
AND course_type IN ( ',5,', ',1,', ',1,5,', ',5,1,')
AND sale_type = 1
AND course_teaching_method = 1
AND STATUS = 1
AND view_authority_scope LIKE '%,10,%'
ORDER BY
id DESC limit 3;"""
crm_vispark_buy_sg_list = self.__get_data_name(
obj_mysql_helper.select_all(sql_crm_vispark_buy_sg))
sql_crm_vispark_buy_oc = """SELECT
IF (abbreviation="",NAME,abbreviation) AS 'text',name,
id AS 'value',
type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope
FROM
peppa.course_package
WHERE
type = 1
AND business_line = 1
AND business_line_type in( 1,10 )
AND teaching_method = 0
AND sale_status = 1
AND saleable_status = 1
AND course_subject = ',1,'
AND course_type IN ( ',5,', ',1,', ',1,5,', ',5,1,')
AND sale_type = 1
AND course_teaching_method = 1
AND STATUS = 1
AND view_authority_scope LIKE '%,13,%'
ORDER BY
id DESC limit 3;"""
crm_vispark_buy_oc_list = self.__get_data_name(
obj_mysql_helper.select_all(sql_crm_vispark_buy_oc))
sql_crm_vispark_buy_huohua = """SELECT
IF (abbreviation="",NAME,abbreviation) AS 'text',name,
id AS 'value',
type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope
FROM
peppa.course_package
WHERE
teaching_method = 0
AND business_line_type in( 0,4,9 )
-- AND business_line = 0 (以及业务线)
AND sale_status = 1
AND saleable_status = 1
AND course_subject like '%,2,%'
-- AND course_type IN ( ',1,',',5,' ) (课程类型)
AND sale_type = 1
AND course_teaching_method = 1
-- AND STATUS = 1 (状态)
AND view_authority_scope LIKE '%,7,%'
ORDER BY
id DESC limit 3;"""
crm_vispark_buy_huohua_list = self.__get_data_name(
obj_mysql_helper.select_all(sql_crm_vispark_buy_huohua))
sql_crm_vispark_subscribe_huohua = """SELECT
IF (abbreviation="",NAME,abbreviation) AS 'text',name,
id AS 'value',
type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope
FROM
peppa.course_package
WHERE
teaching_method = 0
AND business_line_type in( 0,4,9 )
AND business_line = 1
AND sale_status = 1
AND saleable_status = 1
AND course_subject like '%,2,%'
-- AND course_type IN ( ',1,',',5,' ) (课程类型)
AND sale_type = 2
AND course_teaching_method = 1
-- AND STATUS = 1 (状态)
AND view_authority_scope LIKE '%,7,%'
ORDER BY
id DESC limit 3;"""
crm_vispark_subscribe_huohua_list = self.__get_data_name(
obj_mysql_helper.select_all(sql_crm_vispark_subscribe_huohua))
sql_cc_vispark_subscribe_huohua = """SELECT
IF (abbreviation="",NAME,abbreviation) AS 'text',name,
id AS 'value',
type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope
FROM
peppa.course_package
WHERE
teaching_method = 0
AND business_line_type in( 0,3,9 )
AND business_line = 1
AND sale_status = 1
AND saleable_status = 1
AND course_subject like '%,1,%'
-- AND course_type IN ( ',1,',',5,' ) (课程类型)
AND sale_type = 2
AND course_teaching_method = 1
-- AND STATUS = 1 (状态)
AND view_authority_scope LIKE '%,2,%'
ORDER BY
id DESC limit 3;"""
cc_vispark_subscribe_huohua_list = self.__get_data_name(
obj_mysql_helper.select_all(sql_cc_vispark_subscribe_huohua))
sql_cc_vispark_buy_huohua = """SELECT
IF (abbreviation="",NAME,abbreviation) AS 'text',name,
id AS 'value',
type,business_line,business_line_type,teaching_method,sale_status,saleable_status,course_subject,course_type,sale_type,course_teaching_method,STATUS,view_authority_scope
FROM
peppa.course_package
WHERE
teaching_method = 0
AND business_line_type in( 0,3,9 )
AND business_line = 1
AND sale_status = 1
AND saleable_status = 1
AND course_subject like '%,1,%'
-- AND course_type IN ( ',1,',',5,' ) (课程类型)
AND sale_type = 1
AND course_teaching_method = 1
-- AND STATUS = 1 (状态)
AND view_authority_scope LIKE '%,2,%'
ORDER BY
id DESC limit 3;"""
cc_vispark_buy_huohua_list = self.__get_data_name(
obj_mysql_helper.select_all(sql_cc_vispark_buy_huohua))
data = {"crm中订阅vispark业务的对外汉语-套餐": crm_vispark_subscribe_chinese_list,
"crm中订阅vispark业务的sg-套餐": crm_vispark_subscribe_sg_list,
"crm中订阅vispark业务的海华-套餐": crm_vispark_subscribe_oc_list,
"crm中直购vispark业务的对外汉语-套餐": crm_vispark_buy_chinese_list,
"crm中直购vispark业务的us-套餐": crm_vispark_buy_us_list,
"crm中直购vispark业务的加拿大-套餐": crm_vispark_buy_canada_list,
"crm中直购vispark业务的sg-套餐": crm_vispark_buy_sg_list,
"crm中直购vispark业务的海华-套餐": crm_vispark_buy_oc_list,
"crm中直购vispark业务的火花-套餐": crm_vispark_buy_huohua_list,
"crm中订阅vispark业务的火花-套餐": crm_vispark_subscribe_huohua_list,
"cc中订阅vispark业务的火花-套餐": cc_vispark_subscribe_huohua_list,
"cc中直购vispark业务的火花-套餐": cc_vispark_buy_huohua_list}
return data
if __name__ == '__main__':
cc = CoursePackage()
bb = cc.get_course_package_by_cc_crm('')
print(bb)

View File

@@ -0,0 +1,109 @@
# -*- coding:utf-8 -*-
"""
Author: 陈江
Email: chenjiang@sparkedu.com
Create Date: 2022/09/23 2:06 下午
"""
from base_framework.public_business.common.UBRD.kw.coursePackage_keyword import CoursePackageKW
from base_framework.public_tools import utils
from base_framework.public_tools.sqlhelper import MySqLHelper
from base_framework.public_tools.mg_keyword import ManageKeyWord
from base_framework.public_business import manage_public_business
import json
obj_course_package_kw = CoursePackageKW()
obj_get_way = utils.Tools()
obj_mysql_helper = MySqLHelper()
obj_manage_kw = ManageKeyWord()
obj_manage_business = manage_public_business.ManagePublicBusiness()
class Order:
def __init__(self):
pass
def order_done_pay(self, post_data_input):
"""
| 功能说明: | 订单完成支付 |
| 输入参数: |
|orderId/orderCode/prepayCode| 订单id/code/prepayCode | 必填其一
| podenv | str | 独立环境编号| 非必填
| 作者信息: | 陈江 | 修改时间 | 2022-9-28 |
"""
if not post_data_input.get('orderId') and not post_data_input.get('orderCode') and not post_data_input.get(
'prepayCode'):
raise "orderId/orderCode/prepayCode必传1个"
ordr_info = obj_course_package_kw.get_order_info(post_data_input)
order_id = ordr_info.get('orderId')
mq_body_dict = obj_course_package_kw.kw_ubrd_public_get_service_id_by_order_id({"orderId": order_id})
message_body = {"orderId": order_id, "serviceId": int(mq_body_dict.get('serviceId'))}
if post_data_input.get('podenv'):
obj_get_way.mq_send(mq_body_dict.get('topic'), json.dumps(message_body), pro=post_data_input.get('podenv'))
else:
obj_get_way.mq_send(mq_body_dict.get('topic'), json.dumps(message_body))
order_100_sql = f"SELECT id FROM `order_center`.`order_course_package` WHERE id='{order_id}' AND status=100"
obj_mysql_helper.check_result_exist(order_100_sql, retry_count=20)
subscribe_id_sql = f"SELECT subscribe_id FROM `order_center`.`subscribe_order_log` WHERE order_id={order_id}"
obj_mysql_helper.check_result_exist(subscribe_id_sql, retry_count=20)
subscribe_id = obj_mysql_helper.select_one(subscribe_id_sql).get('subscribe_id')
return {"order_id": order_id, "subscribe_id": subscribe_id}
def add_review_subscribe_order(self, post_data_input):
"""
| 功能说明: | 生成续订阅订单 |
| 输入参数: |
|subscribeIds| 订阅id | 传了用该订阅id生成续订阅订单必传 比如[3355]
| 作者信息: | 张楠 | 修改时间 | 2022-11-10 |
"""
try:
post_data_input = eval(post_data_input)
except:
post_data_input = post_data_input
if not post_data_input.get('subscribeIds'):
raise RuntimeWarning("订阅id必传")
# 构造生成续订阅订单请求参数
response_data = obj_manage_kw.kw_execute_xxl_job(8306, para=json.dumps(post_data_input))
return response_data
def create_order(self, post_data_input):
"""
| 功能说明: | 创建直购订单 |
| 请求参数名 | 说明 | 类型 | 是否必填 | 如无要求时的值 |
| userId | 用户id | string | 0 | |
| phone | 用户手机号 | string | 0 | |
| coursePackageId | 套餐id | integer | 0 | |
| businessLine | 业务线一级类型0:火花中国、1:vispark | integer | 0 | |
| businessLineType | 业务线二级类型1:新加坡数学、2:对外汉语、3:直播逻辑思维、4:直播中文素养、5:直播双语素养、6:AI、7:围棋、8:国际象棋、9:其他、10:北美数学、11:越南数学、12:春风思维、13:口才课 | integer | 0 | |
| category | 业务线三级类型K-SMAP:新加坡AP、P-CM:新加坡CP、A:北美AP、C-北美CP、V-越南AP、N-越南CP、ZHK-对外汉语综合课 | string | 0 | |
| teachingMethod | 授课方式, 0:直播、1:AI | integer | 0 | 0 |
| type | 套餐类型,1:正式课、2:体验课 | integer | 0 | 1 |
| courseSubjectList | 课程学科集合,1:数学、2:语文、3:科学、4:英语、5:编程、7:围棋、8:国际象棋、9:春风思维、10:口才课 | list | 0 | |
| courseId | 课程id | integer | 0 | |
| channel | 渠道1:电商、0:非电商 | integer | 0 | 0 |
| channelValue | 渠道chi | string | 0 | 0 |
| classHourType | 课时类型0:小班课、3:1v1 | integer | 0 | |
| offline | 线下1:线下、0:线上 | integer | 0 | 1 |
| businessType | 业务类型0:常规、1:补差、2:可补差 | integer | 0 | 0 |
| currency | 币种0:人民币、1:美元 | integer | 0 | |
| saleType | 售卖方式1:直购、2:订阅 | integer | 0 | 1 |
| teachingScope | 授课范围1:所有课程、2:自定义课程 | integer | 0 | 1 |
| payPrice | 实付金额 | integer | 0 | |
| createdType | 订单类型,0:默认未选择、 10:团购订单、11:加价购-团购订单、12:加价购-普通订单、20:CC提单、30:CC提单、40:自助下单、50:微店购买| integer | 0| 0 |
Returns:
{orderId, orderCode}
| 作者信息: | lrq | 修改时间 | 2025-03-18 |
"""
try:
post_data_input = eval(post_data_input)
except:
post_data_input = post_data_input
return obj_manage_business.kw_public_manage_create_order(**post_data_input)
if __name__ == '__main__':
a = Order()
a.create_order({"userId":"2557864","channel":"35d75242534611e8b5325cb9018964ec","coursePackageId":"2503934602"})

View File

@@ -0,0 +1,208 @@
# -*- coding:utf-8 -*-
"""
Author: 陈江
Email: chenjiang@sparkedu.com
Create Date: 2022/09/23 2:06 下午
"""
from base_framework.public_business.common.UBRD.UBRD_public_business import BaseLogic
from base_framework.public_business.common.UBRD.kw.coursePackage_keyword import CoursePackageKW
from base_framework.public_tools import utils
from base_framework.public_tools.sqlhelper import MySqLHelper
from base_framework.public_tools.log import get_logger
from base_framework.public_business.common.UBRD.kw.user_keyword import UserKW
from base_framework.public_tools.mg_keyword import ManageKeyWord
from library.CommonFun.handle_tools import Handle_tools
import requests
obj_base_logic = BaseLogic()
obj_course_package_kw = CoursePackageKW()
obj_get_way = utils.Tools()
obj_mysql_helper = MySqLHelper()
log = get_logger()
obj_user_kw = UserKW()
obj_manage_kw = ManageKeyWord()
obj_handle_tools = Handle_tools()
class User:
def __init__(self):
pass
def add_user_recharge(self, post_data_input):
"""
| 功能说明: | 新增用户,充值课时 |
| 输入参数: |
|phone/userId| 用户手机号/用户id | 非必填,不传会默认随机注册一个账号;传的手机号不存在会自动进行注册;
|coursePackageCode/courseId/coursePackageId/coursePackageName| 传一个即可 | 传了就给用充值课时,不传就不充
|classHour | | 充值课时 | 不传需要充值时默认充值100
|new_user_count | | 生成新账号数量,存在这个参数时只会生成相应数量的账号 |
| 作者信息: | 陈江 | 修改时间 | 2022-9-23 |
"""
user_list = []
try:
post_data_input = eval(post_data_input)
except:
post_data_input = post_data_input
if post_data_input.get('new_user_count'):
new_user_count = post_data_input.pop('new_user_count')
for count in range(0, int(new_user_count)):
user_info = obj_base_logic.logic_public_add_user_recharge(post_data_input)
user_list.append(user_info)
return user_list
return obj_base_logic.logic_public_add_user_recharge(post_data_input)
def add_new_user(self, post_data_input):
"""
| 功能说明: | 新增用户 |
| 输入参数: |
|fu_phone/fu_user_Id| 邀请人手机号或者user_id | 非必填,不传新增用户无邀请人;
|new_phone| 新增用户手机号 | 传了用该手机号注册,不传则随机一个手机号
|new_user_count | | 生成新账号数量,存在这个参数时只会生成相应数量的账号 |
| 作者信息: | 陈江 | 修改时间 | 2022-11-01 |
"""
user_list = []
try:
post_data_input = eval(post_data_input)
except:
post_data_input = post_data_input
# 构造增加用户请求参数
add_user_params = {}
# 判断是否需要进行邀请
if post_data_input.get('fu_phone'):
user_info = obj_user_kw.kw_ubrd_public_get_user_info_by_phone(phone=post_data_input.get('fu_phone'))
add_user_params['fromUserId'] = user_info.get('user_id')
elif post_data_input.get('fu_user_Id'):
add_user_params['fromUserId'] = post_data_input.get('fu_user_Id')
else:
pass
if post_data_input.get('new_phone'):
user_info = obj_user_kw.kw_ubrd_public_get_user_info_by_phone(phone=post_data_input.get('new_phone'))
if user_info:
raise RuntimeWarning(f"该手机号{post_data_input.get('new_phone')}已经存在")
add_user_params['phone'] = post_data_input.get('new_phone')
obj_user_kw.kw_ubrd_public_add_user_profile(post_data_input=add_user_params)
user_info = obj_user_kw.kw_ubrd_public_get_user_info_by_phone(phone=post_data_input.get('new_phone'))
return user_info
else:
if post_data_input.get('new_user_count'):
new_user_count = post_data_input.pop('new_user_count')
for count in range(0, int(new_user_count)):
phone = obj_user_kw.kw_ubrd_public_get_unregistered_phone()
add_user_params['phone'] = phone
obj_user_kw.kw_ubrd_public_add_user_profile(post_data_input=add_user_params)
user_info = obj_user_kw.kw_ubrd_public_get_user_info_by_phone(phone=phone)
user_list.append(user_info)
return user_list
else:
phone = obj_user_kw.kw_ubrd_public_get_unregistered_phone()
add_user_params['phone'] = phone
obj_user_kw.kw_ubrd_public_add_user_profile(post_data_input=add_user_params)
user_info = obj_user_kw.kw_ubrd_public_get_user_info_by_phone(phone=phone)
return user_info
def add_new_haihua_user(self, post_data_input):
"""
| 功能说明: | 生成海华标签新用户 |
| 输入参数: |
|fu_phone/fu_user_Id| 邀请人手机号或者user_id | 非必填,不传新增用户无邀请人;
|new_phone| 新增用户手机号 | 传了用该手机号注册,不传则随机一个手机号
|new_user_count | | 生成新账号数量,存在这个参数时只会生成相应数量的账号 |
| 作者信息: | 陈江 | 修改时间 | 2022-11-01 |
"""
user_info_list = self.add_new_user(post_data_input)
if user_info_list:
for user_info in user_info_list:
# 设置权益信息
obj_mysql_helper.insert_one(
f"""INSERT INTO `operation`.`haibao_task_equity`(`user_id`, `order_id`, `order_business_side`, `course_package_id`, `course_package_name`, `equity_num`, `used_num`, `remain_num`, `lock_num`, `start_time`, `end_time`, `effect_period`, `generated_first_task`, `frozen_status`, `refund_status`, `equity_status`, `policy`, `subject_type`, `related_order_id`, `creator_id`, `creator_name`, `created_time`, `modifier_id`, `modifier_name`, `modified_time`) VALUES ({user_info.get('user_id')}, 8881050, 0, 23002, '逻辑思维直播系统课48课包', 20, 0, 20, 0, '2023-02-09 14:44:36', '2023-02-09 14:44:36', 360, 0, 0, 0, 0, 2, 0, 0, 0, '', '2023-02-01 00:00:01', 0, '', '2023-02-09 14:44:36');""")
# 设置海华标签
obj_mysql_helper.insert_one(
f"""INSERT INTO `utag_mass`.`user_tag`(`user_id`, `tag_id`, `tag_name`, `creator_id`, `creator_name`, `created_time`, `status`, `is_del`, `source_type`, `expiration_time`, `modifier_id`, `modifier_name`, `modified_time`) VALUES ({user_info.get('user_id')}, 51, '海外及港澳台用户', 587312, '陈江', '2023-02-09 16:08:24', 1, 0, 0, NULL, 587312, '陈江', '2023-02-09 16:08:24');""")
return user_info_list
def get_unregistered_user(self, post_data_input):
"""
| 功能说明: | 获取未注册手机号 |
| 输入参数: |
|user_count | | 返回未注册手机号数量 |
| 作者信息: | 刘涛婷 | 修改时间 | 2022-11-10 |
"""
user_list = []
try:
post_data_input = eval(post_data_input)
except:
post_data_input = post_data_input
if post_data_input.get('user_count'):
user_count = post_data_input.pop('user_count')
for count in range(0, int(user_count)):
user_phone = obj_user_kw.kw_ubrd_public_get_unregistered_phone()
user_list.append(user_phone)
return user_list
return obj_user_kw.kw_ubrd_public_get_unregistered_phone()
def get_sms_code_by_phone(self, post_data_input):
"""
| 功能说明: | 发送验证码 |
| 输入参数:
| phone | 手机号| 必填
| countryCode | 国家码 | 默认 86
| authType | 验证码类型 | 默认2
| verifyAppId | 验证appId | 默认2
| 作者信息: | 陈江 | 2022/10/21 |
"""
# 发送验证码
obj_base_logic.logic_public_send_auth_code(post_data_input)
# 获取验证码内容
# 根据phone获取phone_code
phone_server_ip = obj_get_way.get_container_ip_from_eureka('PHONE-SERVER', need_jira_id='qa',
eureka_url='http://eureka.qa.huohua.cn')
if post_data_input.get('countryCode'):
phone = f"{post_data_input.get('countryCode')}-{post_data_input.get('phone')}"
else:
phone = post_data_input.get('phone')
if phone_server_ip.get('container_ip'):
url = 'http://{}:8080/encrypt/regdata?biztype=phone&uid=123456&sourceData={}'.format(
phone_server_ip.get('container_ip'), phone)
response = requests.post(url=url) # 三个参数
response_json = response.json()
else:
raise "获取phone server ip失败"
if response_json.get('data'):
msg = obj_mysql_helper.select_all(
'SELECT msg FROM `push_service`.`sms` WHERE `phone_code` = \'{}\' ORDER BY id DESC LIMIT 1 '.format(
response_json.get('data')))
if msg:
web_hook = "https://open.feishu.cn/open-apis/bot/v2/hook/c9baf09d-55f1-46a6-8d67-9d4ff30b1be2"
message_data = {"msg_type": "post", "content": {
"post": {"zh_cn": {"title": "message",
"content": [[{"tag": "text", "text": msg[0].get('msg')}]]}}}}
obj_handle_tools.send_message_by_feishu(web_hook, message_data)
else:
raise f"{phone}获取验证码失败"
return msg[0].get('msg')
else:
return response_json
def register_user(self, post_data_input):
"""
| 功能说明: | 通过注册接口进行注册用户并设置密码A123456(支持邀请码,渠道) |
| 输入参数: |
| | | channelId | string | 渠道id |
| | | invitePhone | string | 手机号 | "12030690001/86-13400114600"
| | | registerPhone | string | 手机号 | "12030690001/86-13400114600"
| 作者信息: | 陈江 | 修改时间 | 2022-11-10 |
"""
return obj_base_logic.logic_public_register_user(post_data_input)
if __name__ == '__main__':
ff = {'phone': '13400234500','coursePackageId':29497}
# User().add_user_recharge(ff)
User().add_new_user(post_data_input={"new_user_count":"2"})

View File

@@ -0,0 +1,190 @@
# Jenkins + Allure 报告集成配置指南
## 前置要求
1. **Jenkins已安装并运行**
2. **安装Allure插件**
- 进入 Jenkins → Manage Jenkins → Manage Plugins
- 搜索并安装 "Allure Plugin"
3. **安装Allure命令行工具**
- 下载https://github.com/allure-framework/allure2/releases
- 解压并添加到系统PATH环境变量
- 或在Jenkins全局工具配置中配置Allure路径
## Jenkins配置步骤
### 方式一使用Jenkinsfile推荐
1. **在Jenkins中创建Pipeline任务**
- 新建任务 → 选择 "Pipeline"
- 任务名称:例如 "ZZYY_Test_Automation"
2. **配置Pipeline**
- Pipeline definition → Pipeline script from SCM
- SCM: Git或其他版本控制
- Script Path: `zhyy/test_case/Jenkinsfile`
- 保存
3. **运行任务**
- 点击 "Build with Parameters"
- 选择运行方式RUN_TYPE
- 填写相应参数
- 点击 "Build"
### 方式二:自由风格项目配置
1. **创建自由风格项目**
- 新建任务 → 选择 "Freestyle project"
- 任务名称:例如 "ZZYY_Test_Automation"
2. **配置源码管理**
- Source Code Management → Git
- Repository URL: 你的Git仓库地址
- Branch: 分支名称
3. **配置构建步骤**
- Build → Add build step → Execute shellLinux/Mac或 Execute Windows batch commandWindows
- 命令示例:
```bash
# Linux/Mac
cd ${WORKSPACE}
python zhyy/test_case/run_tests.py --all --no-report
```
```batch
# Windows
cd %WORKSPACE%
python zhyy\test_case\run_tests.py --all --no-report
```
4. **配置Allure报告**
- Post-build Actions → Add post-build action → Allure Report
- Results path: `zhyy/test_case/reports/allure-results`
- Report path: `zhyy/test_case/reports/allure-report`(可选)
- 保存
5. **配置参数化构建(可选)**
- This project is parameterized → Add Parameter
- 添加Choice Parameter
- Name: `RUN_TYPE`
- Choices: `all`, `feature`, `story`, `dir`, `file`, `keyword`, `marker`
- 添加String Parameter根据需要
- `FEATURE_NAME`, `STORY_NAME`, `DIR_PATH`, `FILE_PATH`, `KEYWORD`, `MARKER`
6. **修改构建命令以使用参数**
```bash
# Linux/Mac
cd ${WORKSPACE}
if [ "${RUN_TYPE}" = "all" ]; then
python zhyy/test_case/run_tests.py --all --no-report
elif [ "${RUN_TYPE}" = "feature" ]; then
python zhyy/test_case/run_tests.py --feature "${FEATURE_NAME}" --no-report
elif [ "${RUN_TYPE}" = "dir" ]; then
python zhyy/test_case/run_tests.py --dir "${DIR_PATH}" --no-report
# ... 其他条件
fi
```
## Allure插件配置
### 全局工具配置
1. **配置Allure命令行工具路径**
- Manage Jenkins → Global Tool Configuration
- Allure Commandline → Add Allure Commandline
- Name: `Allure`(或自定义名称)
- Installation directory: Allure安装路径`C:\allure\bin` 或 `/usr/local/allure/bin`
- 保存
### 项目配置
1. **在项目配置中添加Allure报告**
- Post-build Actions → Allure Report
- Results path: `zhyy/test_case/reports/allure-results`
- 勾选 "Keep allure results history"
## 环境变量配置
### Jenkins全局环境变量
1. **配置Python路径如需要**
- Manage Jenkins → Configure System → Global properties
- Environment variables → Add
- Name: `PYTHONPATH`
- Value: `${WORKSPACE}`
### 项目环境变量
在Pipeline或构建脚本中设置
```groovy
environment {
PYTHONPATH = "${WORKSPACE}"
ALLURE_RESULTS = "${WORKSPACE}/zhyy/test_case/reports/allure-results"
}
```
## 使用示例
### 运行所有测试
```bash
python zhyy/test_case/run_tests.py --all --no-report
```
### 按目录运行
```bash
python zhyy/test_case/run_tests.py --dir "接口/SZPurchase" --no-report
```
### 按Feature标签运行
```bash
python zhyy/test_case/run_tests.py --feature "深圳采购工作台采购订单页面" --no-report
```
## 报告查看
1. **在Jenkins中查看**
- 构建完成后,在项目页面左侧菜单会出现 "Allure Report" 链接
- 点击即可查看详细的测试报告
2. **报告内容**
- 测试用例执行情况
- 通过/失败统计
- 执行时间
- 测试步骤详情
- 截图和日志(如果配置了)
## 常见问题
### 1. Allure命令未找到
- 确保Allure已安装并添加到PATH
- 或在Jenkins全局工具配置中指定Allure路径
### 2. 模块导入错误
- 检查PYTHONPATH环境变量
- 确保项目根目录在Python路径中
### 3. 报告未生成
- 检查allure-results目录是否存在且包含数据
- 检查Jenkins Allure插件配置的路径是否正确
### 4. 权限问题
- 确保Jenkins有权限访问工作空间目录
- 确保有权限执行Python和Allure命令
## 邮件通知配置(可选)
在Post-build Actions中添加
- Email Notification
- 配置收件人、主题等
- 可以附加Allure报告链接
## 定时构建(可选)
在项目配置中:
- Build Triggers → Build periodically
- 例如:`H 2 * * *`每天凌晨2点执行
## 多节点执行(可选)
如果有多台Jenkins节点
- 在Pipeline中配置 `agent { label 'your-node-label' }`
- 或在自由风格项目中配置 "Restrict where this project can be run"

175
zhyy/test_case/Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,175 @@
pipeline {
agent any
options {
// 保留最近10次构建
buildDiscarder(logRotator(numToKeepStr: '10'))
// 超时时间60分钟
timeout(time: 60, unit: 'MINUTES')
}
environment {
// Python路径
PYTHONPATH = "${WORKSPACE}"
// Allure结果目录
ALLURE_RESULTS = "${WORKSPACE}/zhyy/test_case/reports/allure-results"
// Allure报告目录
ALLURE_REPORT = "${WORKSPACE}/zhyy/test_case/reports/allure-report"
}
stages {
stage('Checkout') {
steps {
echo '检出代码...'
checkout scm
}
}
stage('环境准备') {
steps {
echo '准备测试环境...'
script {
// 确保Python环境
sh '''
python --version
pip --version
'''
}
}
}
stage('运行测试') {
steps {
echo '执行测试用例...'
script {
// 根据参数选择运行方式
def runType = params.RUN_TYPE ?: 'all'
def testCommand = ''
switch(runType) {
case 'all':
testCommand = 'python zhyy/test_case/run_tests.py --all --no-report'
break
case 'feature':
def feature = params.FEATURE_NAME ?: ''
testCommand = "python zhyy/test_case/run_tests.py --feature \"${feature}\" --no-report"
break
case 'story':
def story = params.STORY_NAME ?: ''
testCommand = "python zhyy/test_case/run_tests.py --story \"${story}\" --no-report"
break
case 'dir':
def dir = params.DIR_PATH ?: ''
testCommand = "python zhyy/test_case/run_tests.py --dir \"${dir}\" --no-report"
break
case 'file':
def file = params.FILE_PATH ?: ''
testCommand = "python zhyy/test_case/run_tests.py --file \"${file}\" --no-report"
break
case 'keyword':
def keyword = params.KEYWORD ?: ''
testCommand = "python zhyy/test_case/run_tests.py --keyword \"${keyword}\" --no-report"
break
case 'marker':
def marker = params.MARKER ?: ''
testCommand = "python zhyy/test_case/run_tests.py --marker \"${marker}\" --no-report"
break
default:
testCommand = 'python zhyy/test_case/run_tests.py --all --no-report'
}
sh """
cd ${WORKSPACE}
${testCommand}
"""
}
}
post {
always {
// 无论成功失败都收集测试结果
echo '收集测试结果...'
}
}
}
stage('生成Allure报告') {
steps {
echo '生成Allure报告...'
script {
sh """
cd ${WORKSPACE}
allure generate ${ALLURE_RESULTS} -o ${ALLURE_REPORT} --clean || echo "Allure报告生成失败但继续执行"
"""
}
}
}
}
post {
always {
// 发布Allure报告
allure([
includeProperties: false,
jdk: '',
properties: [],
reportBuildPolicy: 'ALWAYS',
results: [[path: 'zhyy/test_case/reports/allure-results']]
])
// 清理工作空间(可选)
// cleanWs()
}
success {
echo '✓ 测试执行成功'
// 可以在这里添加成功通知,如发送邮件、钉钉等
}
failure {
echo '✗ 测试执行失败'
// 可以在这里添加失败通知
}
unstable {
echo '⚠ 测试执行不稳定'
}
}
}
// 参数化构建
properties([
parameters([
choice(
name: 'RUN_TYPE',
choices: ['all', 'feature', 'story', 'dir', 'file', 'keyword', 'marker'],
description: '选择运行方式'
),
string(
name: 'FEATURE_NAME',
defaultValue: '',
description: 'Feature标签名称当RUN_TYPE=feature时使用'
),
string(
name: 'STORY_NAME',
defaultValue: '',
description: 'Story标签名称当RUN_TYPE=story时使用'
),
string(
name: 'DIR_PATH',
defaultValue: '接口/SZPurchase',
description: '测试目录路径当RUN_TYPE=dir时使用相对于TestCase目录'
),
string(
name: 'FILE_PATH',
defaultValue: '接口/SZPurchase/PurchaseOrderManage.py',
description: '测试文件路径当RUN_TYPE=file时使用相对于TestCase目录'
),
string(
name: 'KEYWORD',
defaultValue: 'purchase',
description: '关键字当RUN_TYPE=keyword时使用'
),
string(
name: 'MARKER',
defaultValue: 'smoke',
description: 'Pytest标记当RUN_TYPE=marker时使用'
)
])
])

View File

@@ -0,0 +1,84 @@
# Jenkins + Allure 集成快速参考
## 快速开始
### 1. 安装Allure插件
- Jenkins → Manage Jenkins → Manage Plugins
- 搜索 "Allure Plugin" 并安装
### 2. 配置Allure工具
- Manage Jenkins → Global Tool Configuration
- Allure Commandline → 添加Allure安装路径
### 3. 创建Jenkins任务
#### 方式A使用Jenkinsfile推荐
1. 新建Pipeline任务
2. Pipeline script from SCM
3. Script Path: `zhyy/test_case/Jenkinsfile`
4. 保存并运行
#### 方式B自由风格项目
1. 新建Freestyle project
2. 构建步骤:执行 `jenkins_build.sh``jenkins_build.bat`
3. Post-build Actions → Allure Report
4. Results path: `zhyy/test_case/reports/allure-results`
## 参数化构建
在Jenkins任务中配置以下参数
| 参数名 | 类型 | 说明 | 示例值 |
|--------|------|------|--------|
| RUN_TYPE | Choice | 运行方式 | all, feature, story, dir, file, keyword, marker |
| FEATURE_NAME | String | Feature标签 | 深圳采购工作台采购订单页面 |
| STORY_NAME | String | Story标签 | 验证采购工作台采购订单页面列表查询 |
| DIR_PATH | String | 目录路径 | 接口/SZPurchase |
| FILE_PATH | String | 文件路径 | 接口/SZPurchase/PurchaseOrderManage.py |
| KEYWORD | String | 关键字 | purchase |
| MARKER | String | Pytest标记 | smoke |
## 常用命令
### 本地运行
```bash
# 运行所有测试
python run_tests.py
# 按目录运行
python run_tests.py --dir "接口/SZPurchase"
# 按文件运行
python run_tests.py --file "接口/SZPurchase/PurchaseOrderManage.py"
# 生成并打开报告
python run_tests.py --all --report --open
```
### Jenkins中运行
```bash
# 使用构建脚本(自动检测参数)
bash jenkins_build.sh
# 或直接使用run_tests.py
python run_tests.py --all --no-report
```
## Allure报告路径
- **结果目录**: `zhyy/test_case/reports/allure-results`
- **报告目录**: `zhyy/test_case/reports/allure-report`
- **Jenkins中查看**: 构建完成后点击左侧 "Allure Report" 链接
## 环境变量
Jenkins会自动设置以下环境变量
- `WORKSPACE`: Jenkins工作空间路径
- `BUILD_NUMBER`: 构建编号
- `JENKINS_URL`: Jenkins服务器地址
`run_tests.py` 会自动检测Jenkins环境并调整路径。
## 详细文档
更多配置说明请参考:`JENKINS_SETUP.md`

View File

@@ -0,0 +1,96 @@
# 测试执行说明
## 统一测试执行工具
使用 `run_tests.py` 可以方便地执行各种测试用例。
## 安装依赖
```bash
pip install pytest
pip install pytest-allure-adaptor # 或 allure-pytest
pip install allure-python-commons
```
## 使用方法
### 1. 运行所有测试用例
```bash
python run_tests.py --all
```
### 2. 按 Allure Feature 标签运行
```bash
python run_tests.py --feature "深圳采购工作台采购订单页面"
```
### 3. 按 Allure Story 标签运行
```bash
python run_tests.py --story "验证采购工作台采购订单页面列表查询"
```
### 4. 按目录运行
```bash
python run_tests.py --dir "接口/SZPurchase"
```
### 5. 按文件运行
```bash
python run_tests.py --file "接口/SZPurchase/PurchaseOrderManage.py"
```
### 6. 按关键字运行(匹配文件名或类名)
```bash
python run_tests.py --keyword "purchase"
```
### 7. 按 pytest 标记运行
```bash
python run_tests.py --marker "smoke"
```
### 8. 生成并打开 Allure 报告
```bash
python run_tests.py --all --report --open
```
## 快捷方式Windows
- `run_all.bat` - 运行所有测试用例
- `run_purchase.bat` - 运行采购相关测试用例
## 参数说明
- `--all`: 运行所有测试用例
- `--feature <name>`: 按allure feature标签运行
- `--story <name>`: 按allure story标签运行
- `--dir <path>`: 按目录运行相对于TestCase目录
- `--file <path>`: 按文件运行相对于TestCase目录
- `--keyword <keyword>`: 按关键字运行
- `--marker <marker>`: 按pytest标记运行
- `--report`: 生成Allure报告
- `--open`: 打开Allure报告
- `--no-report`: 不生成Allure报告
## 报告位置
- Allure结果: `reports/allure-results/`
- Allure报告: `reports/allure-report/`
## 查看报告
生成报告后,可以使用以下命令打开:
```bash
allure open reports/allure-report
```
或者直接使用 `--open` 参数自动打开。

View File

@@ -0,0 +1,9 @@
*** Settings ***
*** Variables ***
${parent_phone_1} 13400234900 # 增长业务使用(陈江)(陈洁-login、留资等也在使用),逻辑思维用户
${parentphone_customerId_1} 6022462 #用户 13400234900逻辑思维线索id
${parent_phone_2} 12030990019 #张楠 course_package.robot(陈洁-login、留资等也在使用),中文素养用户
${parent_phone_3} 13400234902 # 海报任务专用

View File

View File

@@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
import allure
import logging
from zhyy.library.BusinessKw.SZPurchase.PurchaseOrderManage import PurchaseOrder
@allure.feature('深圳采购工作台采购订单页面')
class Test_purchase_order(object):
test_case = PurchaseOrder()
def teardown_method(self):
logging.info("-----------------------------End-------------------------------")
@allure.story("验证采购工作台采购订单页面列表查询")
def test_check_purchase_order_page(self):
purchase_order_code = 'PO251209048' # 采购单号 必填
supplier_company_ids = ['334'] # 供应商id 非必填
payment_status = '0' # 付款状态 非必填
status = '0' # 采购单状态 非必填
page_no = 1 # 页码 必填
page_size = 10 # 每页条数 必填
response_data = self.test_case.kw_zhyy_get_purchase_page_post(
note="采购工作台采购订单页面列表查询",
user='purchase',
order_sn=purchase_order_code,
supplier_company_ids=supplier_company_ids,
payment_status=payment_status,
status=status,
page_no=page_no,
page_size=page_size
)
# 断言检查
assert response_data is not None, "响应数据不能为空"
assert 'code' in response_data, "响应数据中缺少code字段"
assert response_data['code'] == 0, "接口调用失败code: {}, msg: {}".format(
response_data.get('code'), response_data.get('msg', '未知错误'))
assert 'data' in response_data, "响应数据中缺少data字段"
assert response_data['data'] is not None, "响应数据中的data字段不能为空"
# 如果传入了采购单号,检查返回的数据中是否包含该采购单号
if purchase_order_code:
data = response_data.get('data', {})
order_found = False
# 检查返回的数据结构,可能包含列表或其他结构
if isinstance(data, dict):
# 如果data是字典可能包含records、list、data等字段
records = data.get('records') or data.get('list') or data.get('data') or []
if isinstance(records, list) and len(records) > 0:
# 检查列表中是否包含指定的采购单号
for item in records:
if isinstance(item, dict):
order_sn = item.get('order_sn') or item.get('orderSn') or item.get('orderSn')
if order_sn == purchase_order_code:
order_found = True
break
elif isinstance(data, list):
# 如果data本身就是列表
for item in data:
if isinstance(item, dict):
order_sn = item.get('order_sn') or item.get('orderSn') or item.get('orderSn')
if order_sn == purchase_order_code:
order_found = True
break
if order_found:
logging.info("✓ 断言通过:返回的数据中包含采购单号 {}".format(purchase_order_code))
else:
logging.warning("⚠ 警告:返回的数据中未找到采购单号: {},但接口调用成功".format(purchase_order_code))
logging.info("✓ 所有断言检查通过")
print("✓ 查询成功,响应数据: {}".format(response_data))

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
import allure
import logging
from zhyy.library.BusinessKw.SZPurchase.index import PurchaseIndex
@allure.feature('深圳采购工作台首页')
class Test_purchase_index(object):
# config = ReadConfig.ReadConfig() # 调用读取配置文件的方法类
test_case = PurchaseIndex()
def teardown_method(self):
logging.info("-----------------------------End-------------------------------")
@allure.story("验证采购工作台待办任务与在办任务功能")
def test_check_todo(self):
get_purchase_data = self.test_case.kw_zhyy_get_todo(note="采购工作台首页待办任务PO与在办任务PO", user='purchase')

View File

@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
"""
测试配置文件
用于设置测试环境的路径和配置
"""
import os
import sys
# 添加项目根目录到 Python 路径,确保能导入 zhyy 模块
current_file_path = os.path.abspath(__file__)
# 从 test_case/TestCase/接口/conftest.py 向上找到项目根目录 d:\zhyy
project_root = os.path.abspath(os.path.join(os.path.dirname(current_file_path), '../../../..'))
if project_root not in sys.path:
sys.path.insert(0, project_root)

View File

Binary file not shown.

View File

@@ -0,0 +1,44 @@
@echo off
REM Jenkins构建脚本Windows
REM 用于在Jenkins中执行测试并生成Allure报告
setlocal enabledelayedexpansion
REM 设置工作目录
if defined WORKSPACE (
cd /d %WORKSPACE%
) else (
cd /d %~dp0..
)
REM 进入测试目录
cd zhyy\test_case
REM 运行测试(根据参数选择运行方式)
if "%RUN_TYPE%"=="" set RUN_TYPE=all
if "%RUN_TYPE%"=="all" (
python run_tests.py --all --no-report
) else if "%RUN_TYPE%"=="feature" (
python run_tests.py --feature "%FEATURE_NAME%" --no-report
) else if "%RUN_TYPE%"=="story" (
python run_tests.py --story "%STORY_NAME%" --no-report
) else if "%RUN_TYPE%"=="dir" (
python run_tests.py --dir "%DIR_PATH%" --no-report
) else if "%RUN_TYPE%"=="file" (
python run_tests.py --file "%FILE_PATH%" --no-report
) else if "%RUN_TYPE%"=="keyword" (
python run_tests.py --keyword "%KEYWORD%" --no-report
) else if "%RUN_TYPE%"=="marker" (
python run_tests.py --marker "%MARKER%" --no-report
) else (
echo 未知的运行类型: %RUN_TYPE%
exit /b 1
)
REM 生成Allure报告Jenkins插件会自动处理这里可选
REM allure generate reports\allure-results -o reports\allure-report --clean
echo 测试执行完成Allure结果已保存到: reports\allure-results
endlocal

View File

@@ -0,0 +1,47 @@
#!/bin/bash
# Jenkins构建脚本Linux/Mac
# 用于在Jenkins中执行测试并生成Allure报告
set -e # 遇到错误立即退出
# 设置工作目录
cd ${WORKSPACE:-$(pwd)}
# 进入测试目录
cd zhyy/test_case
# 运行测试(根据参数选择运行方式)
RUN_TYPE=${RUN_TYPE:-all}
case ${RUN_TYPE} in
all)
python run_tests.py --all --no-report
;;
feature)
python run_tests.py --feature "${FEATURE_NAME}" --no-report
;;
story)
python run_tests.py --story "${STORY_NAME}" --no-report
;;
dir)
python run_tests.py --dir "${DIR_PATH}" --no-report
;;
file)
python run_tests.py --file "${FILE_PATH}" --no-report
;;
keyword)
python run_tests.py --keyword "${KEYWORD}" --no-report
;;
marker)
python run_tests.py --marker "${MARKER}" --no-report
;;
*)
echo "未知的运行类型: ${RUN_TYPE}"
exit 1
;;
esac
# 生成Allure报告Jenkins插件会自动处理这里可选
# allure generate reports/allure-results -o reports/allure-report --clean || true
echo "测试执行完成Allure结果已保存到: reports/allure-results"

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
"Epic","Feature","Story","FAILED","BROKEN","PASSED","SKIPPED","UNKNOWN"
"","深圳采购工作台首页","验证采购工作台待办任务与在办任务功能","0","0","2","0","0"
"","深圳采购工作台采购订单页面","验证采购工作台采购订单页面列表查询","0","0","2","0","0"
"","","","0","0","1","0","0"
1 Epic Feature Story FAILED BROKEN PASSED SKIPPED UNKNOWN
2 深圳采购工作台首页 验证采购工作台待办任务与在办任务功能 0 0 2 0 0
3 深圳采购工作台采购订单页面 验证采购工作台采购订单页面列表查询 0 0 2 0 0
4 0 0 1 0 0

Some files were not shown because too many files have changed in this diff Show More