This commit is contained in:
ping 2025-08-19 15:27:26 +08:00
parent b927c6d1ac
commit b632da39e8
8 changed files with 428 additions and 3 deletions

View File

@ -0,0 +1,16 @@
async def homepage_category_tree_delete(ns={}):
if not ns.get('id'):
return {
'status': False,
'msg': '请传递分类ID'
}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
await sor.U('homepage_category_tree', {'id': ns.get('id'), 'del_flg': '1'})
return {
'status': True,
'msg': 'product category deleted successfully'
}
ret = await homepage_category_tree_delete(params_kw)
return ret

View File

@ -1,3 +1,121 @@
async def publish_product_to_excel(ns={}):
if not ns.get('ids') and (not ns.get('result_from_search')):
return {
'status': False,
'msg': '请传递产品ID'
}
result = []
if ns.get('ids'):
db = DBPools()
async with db.sqlorContext('kboss') as sor:
ids = json.loads(ns.get('ids')) if isinstance(ns.get('ids'), str) else ns.get('ids')
# 根据ids查询user_publish_product表
find_sql = """SELECT * FROM user_publish_product WHERE id IN (%s) AND del_flg = '0';""" % ','.join(["'%s'" % id for id in ids])
result = await sor.sqlExe(find_sql, {})
if not result:
return {
'status': False,
'msg': '没有找到匹配的产品'
}
if ns.get('result_from_search'):
result = ns.get('result_from_search')
# 结果转换成 中文名称:值 的字典列表
field_mapping = {
# 'id': 'id',
# 'publish_type': '类型',
'img': '图片链接',
# 'cart_flag': '是否支持显卡',
# 'domain_name': '所属域名',
# 'orgid': '所属机构',
'product_name': '商品名称',
# 'product_category': '所属类别',
'company_name': '企业名称',
# 'company_type': '公司类别',
'contact_person': '联系人',
'job_title': '职务',
'phone_number': '手机号码',
'email': '邮箱',
'cpu': 'cpu',
'memory': '内存',
'gpu': 'gpu',
'sys_disk': '系统盘',
'data_disk': '数据盘',
'net_card': '网卡',
'price': '价格',
'unit': '价格单位',
'discount': '价格折扣',
'discount_price': '折扣后价格',
'service_charge': '服务费',
'short_term': '是否短租',
# 'priority': '排序优先级',
'status': '上架状态',
# 'title': '主题',
# 'label': '标签',
# 'first_page': '是否推送到首页',
# 'admin_push': '是否是运营人员提交',
'requirement_summary': '需求概述',
'related_parameters': '相关参数',
'application_scenario': '应用场景',
'audit_status': '审核状态',
'listing_status': '上架状态',
'reject_reason': '驳回原因',
'update_time': '更新时间',
'create_at': '创建时间',
# 'publish_time': '发布日期',
# 'del_flg': '删除标志'
}
# 转换字典键为中文
for data_dic in result:
# 如果有图片路径 则转换为完整的图片链接
if data_dic.get('img') and data_dic['img'] != 'null':
data_dic['img'] = 'https://' + data_dic['domain_name'] + '/idfile?path=' + data_dic['img']
else:
data_dic['img'] = None
# 转换字典键为中文 对应的值要有映射
# 拆分后:显式循环结构(便于后续处理)
new_data_dic = {}
for key, value in data_dic.items():
if key == 'publish_type':
# 显示类型映射
if value == '1':
value = '产品'
elif value == '2':
value = '需求'
if key == 'audit_status':
# 显示审核状态映射
if value == 'pending':
value = '待审'
elif value == 'approved':
value = '通过'
elif value == 'rejected':
value = '拒绝'
if key == 'listing_status':
# 显示上架状态映射
if value == 'listing':
value = '上架'
elif value == 'delisting':
value = '下架'
if key == 'short_term':
# 显示短租状态映射
if value == '1':
value = '是'
elif value == '0':
value = '否'
# 获取中文映射键
chinese_key = field_mapping.get(key)
# 仅保留有映射关系的字段
if chinese_key is not None:
new_data_dic[chinese_key] = value
data_dic.clear()
data_dic.update(new_data_dic)
return {
'status': True,
'msg': 'export to excel success',
'data': result
}
async def get_user_role(ns={}): async def get_user_role(ns={}):
sor = ns['sor'] sor = ns['sor']
# get role # get role
@ -48,6 +166,12 @@ async def publish_product_search(ns={}):
'status': False, 'status': False,
'msg': 'url_link is required' 'msg': 'url_link is required'
} }
if not ns.get('publish_type'):
return {
'status': False,
'msg': 'publish_type is required'
}
domain_name = ns.get('url_link').split("//")[1].split("/")[0] domain_name = ns.get('url_link').split("//")[1].split("/")[0]
if 'localhost' in domain_name: if 'localhost' in domain_name:

View File

@ -23,6 +23,57 @@ async def get_user_role(ns={}):
return role return role
async def user_browse_history_add(ns={}):
# ns = {
# 'userid': '9KVhsVCJsW_29q3hRhMAr', # 用户ID
# 'product_id': 'p2s2YPPU7uquza3gGw9k2', # 产品ID
# 'ip_address': '192.168.1.1', # IP地址
# 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/98.0.4758.102' # 用户代理
# }
# 必要参数校验
if not ns.get('userid') or not ns.get('product_id'):
return {
'status': False,
'msg': 'userid and product_id are required'
}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
# 根据timestamp时间字段:browse_time去重 查找10个小时内是否存在
browse_time = datetime.datetime.now() - datetime.timedelta(hours=10)
check_sql = """
SELECT * FROM user_browse_history
WHERE userid = '%s' AND product_id = '%s' AND del_flg = '0' AND browse_time >= '%s';
""" % (ns.get('userid'), ns.get('product_id'), browse_time)
check_result = await sor.sqlExe(check_sql, {})
if check_result:
return {
'status': False,
'msg': 'The user has browsed this product within the last 10 hours'
}
# 生成记录ID
record_id = uuid()
# 插入浏览记录
insert_sql = """
INSERT INTO user_browse_history (
id, userid, product_id, ip_address, user_agent, del_flg
) VALUES ('%s', '%s', '%s', '%s', '%s', '0');
""" % (record_id, ns.get('userid'), ns.get('product_id'),
ns.get('ip_address', ''), ns.get('user_agent', ''))
await sor.sqlExe(insert_sql, {})
return {
'status': True,
'msg': 'Browse history recorded successfully',
'data': {'record_id': record_id}
}
except Exception as e:
return {
'status': False,
'msg': 'Failed to record browse history, %s' % str(e)
}
async def publish_product_search_detail(ns={}): async def publish_product_search_detail(ns={}):
""" """
:param ns: :param ns:
@ -36,6 +87,7 @@ async def publish_product_search_detail(ns={}):
db = DBPools() db = DBPools()
async with db.sqlorContext('kboss') as sor: async with db.sqlorContext('kboss') as sor:
user_role = None user_role = None
orgid = None
# 区分运营和普通客户 # 区分运营和普通客户
if userid: if userid:
user_list = await sor.R('users', {'id': userid}) user_list = await sor.R('users', {'id': userid})
@ -61,6 +113,10 @@ async def publish_product_search_detail(ns={}):
else: else:
product['email'] = 'kawa@****.com' product['email'] = 'kawa@****.com'
# 保存浏览记录
if userid:
await user_browse_history_add({'userid': userid, 'product_id': ns.get('id')})
return { return {
'status': True, 'status': True,
'msg': 'Product retrieved successfully', 'msg': 'Product retrieved successfully',

View File

@ -12,6 +12,42 @@ async def search_user_inquiry(ns={}):
async with db.sqlorContext('kboss') as sor: async with db.sqlorContext('kboss') as sor:
search_sql = """select * from product_inquiry where domain_name = '%s' and del_flg = '0' order by update_time desc;""" % domain_name search_sql = """select * from product_inquiry where domain_name = '%s' and del_flg = '0' order by update_time desc;""" % domain_name
result = await sor.sqlExe(search_sql, {}) result = await sor.sqlExe(search_sql, {})
if ns.get('to_excel') == '1':
# 创建映射字段 导出execl
# 结果转换成 中文名称:值 的字典列表
field_mapping = {
'name': '联系人姓名',
'custom_type': '客户类型',
'phone': '联系人电话',
'email': '邮箱',
'company': '公司名称',
'content': '咨询内容',
'feedback': '反馈状态',
}
# 新增值映射字典,集中管理各字段的数值转换规则
value_mapping = {
'custom_type': {'0': '个人', '1': '企业'},
'feedback': {'0': '未反馈', '1': '已反馈'} # 根据表结构补充反馈状态映射
}
# 转换字典键为中文
for data_dic in result:
# 拆分后:显式循环结构(便于后续处理)
new_data_dic = {}
# 按field_mapping定义的顺序处理字段确保输出顺序一致
for key in field_mapping.keys():
# 跳过数据中不存在的字段
if key not in data_dic:
continue
value = data_dic[key]
chinese_key = field_mapping[key]
if key in value_mapping:
mapped_value = value_mapping[key].get(str(value), value) # 若未找到对应映射,保留原始值
new_data_dic[chinese_key] = mapped_value
else:
new_data_dic[chinese_key] = value
data_dic.clear()
data_dic.update(new_data_dic)
return { return {
'status': True, 'status': True,
'msg': 'search success', 'msg': 'search success',

View File

@ -29,6 +29,14 @@ async def enterprise_audit_info_search(ns={}):
'status': False, 'status': False,
'msg': '请传递url_link' 'msg': '请传递url_link'
} }
# 分页
current_page = int(ns['current_page']) if ns.get('current_page') else 1
page_size = int(ns['page_size']) if ns.get('page_size') else 10
offset = (current_page - 1) * page_size
audit_status = ns.get('audit_status')
domain_name = ns.get('url_link').split("//")[1].split("/")[0] domain_name = ns.get('url_link').split("//")[1].split("/")[0]
if 'localhost' in domain_name: if 'localhost' in domain_name:
domain_name = 'dev.opencomputing.cn' domain_name = 'dev.opencomputing.cn'
@ -50,10 +58,36 @@ async def enterprise_audit_info_search(ns={}):
user_role = await get_user_role({'userid': userid, 'sor': sor}) user_role = await get_user_role({'userid': userid, 'sor': sor})
try: try:
if user_role == '客户': # 客户查询 if user_role == '客户': # 客户查询
count_sql = """SELECT COUNT(*) AS total_count FROM enterprise_audit_info WHERE orgid = '%s' AND del_flg = '0';""" % orgid
find_sql = """SELECT * FROM enterprise_audit_info WHERE orgid = '%s' AND del_flg = '0';""" % orgid find_sql = """SELECT * FROM enterprise_audit_info WHERE orgid = '%s' AND del_flg = '0';""" % orgid
else: # 运营查询 enterprise_audit_info和organization表关联查询 enterprise_audit_info中的orgid和organization表中的id关联查询 else: # 运营查询 enterprise_audit_info和organization表关联查询 enterprise_audit_info中的orgid和organization表中的id关联查询
find_sql = """SELECT eai.* FROM enterprise_audit_info AS eai LEFT JOIN organization as org ON eai.orgid = org.id WHERE org.parentid = '%s' AND eai.del_flg = '0' ORDER BY eai.update_time DESC;""" % orgid count_sql = """SELECT COUNT(*) AS total_count FROM enterprise_audit_info AS eai LEFT JOIN organization as org ON eai.orgid = org.id WHERE org.parentid = '%s' AND eai.del_flg = '0';""" % orgid
# 执行查询 find_sql = """SELECT eai.* FROM enterprise_audit_info AS eai LEFT JOIN organization as org ON eai.orgid = org.id WHERE org.parentid = '%s' AND eai.del_flg = '0' ORDER BY eai.update_time DESC LIMIT %s OFFSET %s;""" % (orgid, page_size, offset)
# 拆分find_sql 增加audit_status条件筛选
if audit_status:
# Split the audit_status string into individual statuses
statuses = [status.strip() for status in audit_status.split(',') if status.strip()]
if statuses:
# Create a properly escaped list of statuses for SQL
escaped_statuses = []
for status in statuses:
# Escape single quotes by doubling them (SQL standard)
escaped_status = status.replace("'", "''")
escaped_statuses.append(f"'{escaped_status}'")
# Join the escaped statuses for the IN clause
statuses_str = ','.join(escaped_statuses)
if statuses_str:
count_sql = count_sql.split("WHERE")[0] + f"WHERE eai.audit_status IN ({statuses_str}) AND " + count_sql.split("WHERE")[1]
find_sql = find_sql.split("WHERE")[0] + f"WHERE eai.audit_status IN ({statuses_str}) AND " + find_sql.split("WHERE")[1]
# 非pending
else:
count_sql = count_sql.split("WHERE")[0] + "WHERE eai.audit_status != 'pending' AND " + count_sql.split("WHERE")[1]
find_sql = find_sql.split("WHERE")[0] + "WHERE eai.audit_status != 'pending' AND " + find_sql.split("WHERE")[1]
# 执行查询
total_count = (await sor.sqlExe(count_sql, {}))[0]['total_count']
res = await sor.sqlExe(find_sql, {}) res = await sor.sqlExe(find_sql, {})
# 处理结果中的图片路径 增加前缀 # 处理结果中的图片路径 增加前缀
@ -66,7 +100,12 @@ async def enterprise_audit_info_search(ns={}):
return { return {
'status': True, 'status': True,
'msg': 'enterprise audit info search successfully', 'msg': 'enterprise audit info search successfully',
'data': res 'data': {
'total_count': total_count,
'current_page': current_page,
'page_size': page_size,
'data': res
}
} }
except Exception as e: except Exception as e:
return { return {

View File

@ -0,0 +1,53 @@
async def user_browse_history_add(ns={}):
# ns = {
# 'userid': '9KVhsVCJsW_29q3hRhMAr', # 用户ID
# 'product_id': 'p2s2YPPU7uquza3gGw9k2', # 产品ID
# 'ip_address': '192.168.1.1', # IP地址
# 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/98.0.4758.102' # 用户代理
# }
# 必要参数校验
if not ns.get('userid') or not ns.get('product_id'):
return {
'status': False,
'msg': 'userid and product_id are required'
}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
# 根据timestamp时间字段:browse_time去重 查找10个小时内是否存在
browse_time = datetime.datetime.now() - datetime.timedelta(hours=10)
check_sql = """
SELECT * FROM user_browse_history
WHERE userid = '%s' AND product_id = '%s' AND del_flg = '0' AND browse_time >= '%s';
""" % (ns.get('userid'), ns.get('product_id'), browse_time)
check_result = await sor.sqlExe(check_sql, {})
if check_result:
return {
'status': False,
'msg': 'The user has browsed this product within the last 10 hours'
}
# 生成记录ID
record_id = uuid()
# 插入浏览记录
insert_sql = """
INSERT INTO user_browse_history (
id, userid, product_id, ip_address, user_agent, del_flg
) VALUES ('%s', '%s', '%s', '%s', '%s', '0');
""" % (record_id, ns.get('userid'), ns.get('product_id'),
ns.get('ip_address', ''), ns.get('user_agent', ''))
await sor.sqlExe(insert_sql, {})
return {
'status': True,
'msg': 'Browse history recorded successfully',
'data': {'record_id': record_id}
}
except Exception as e:
return {
'status': False,
'msg': 'Failed to record browse history, %s' % str(e)
}
ret = await user_browse_history_add(params_kw)
return ret

View File

@ -0,0 +1,29 @@
async def user_browse_history_delete(ns={}):
if not ns.get('id'):
return {
'status': False,
'msg': 'record id is required'
}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
# 逻辑删除(更新删除标志)
update_sql = """
UPDATE user_browse_history
SET del_flg = '1'
WHERE id = '%s';
""" % ns.get('id')
await sor.sqlExe(update_sql, {})
return {
'status': True,
'msg': 'Browse history deleted successfully'
}
except Exception as e:
return {
'status': False,
'msg': 'Failed to delete browse history, %s' % str(e)
}
ret = await user_browse_history_delete(params_kw)
return ret

View File

@ -0,0 +1,72 @@
async def user_browse_history_search(ns={}):
# 处理userid
if ns.get('userid'):
userid = ns.get('userid')
else:
userid = await get_user()
if not userid:
return {
'status': False,
'msg': 'no match user'
}
# 参数处理
product_id = ns.get('product_id')
start_time = ns.get('start_time')
end_time = ns.get('end_time')
page_size = int(ns.get('page_size', 10))
current_page = int(ns.get('current_page', 1))
offset = (current_page - 1) * page_size
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
# 基础查询条件
base_conditions = ["del_flg = '0'"]
if userid:
base_conditions.append(f"userid = '{userid}'")
if product_id:
base_conditions.append(f"product_id = '{product_id}'")
if start_time and end_time:
end_time += ' 23:59:59'
base_conditions.append(f"browse_time BETWEEN '{start_time}' AND '{end_time}'")
# 构建查询SQL
where_clause = " AND ".join(base_conditions)
# 根据product_id查询product_info
find_sql = f"""
SELECT * FROM user_browse_history
WHERE {where_clause}
ORDER BY browse_time DESC
LIMIT {page_size} OFFSET {offset};
"""
# 总数查询SQL
count_sql = f"""
SELECT COUNT(*) AS total_count FROM user_browse_history
WHERE {where_clause};
"""
# 执行查询
result = await sor.sqlExe(find_sql, {})
total_count = (await sor.sqlExe(count_sql, {}))[0]['total_count']
return {
'status': True,
'msg': 'Browse history retrieved successfully',
'data': {
'total_count': total_count,
'page_size': page_size,
'current_page': current_page,
'history_list': result
}
}
except Exception as e:
return {
'status': False,
'msg': 'Failed to retrieve browse history, %s' % str(e)
}
ret = await user_browse_history_search(params_kw)
return ret