This commit is contained in:
lychang
2025-09-16 01:54:46 +08:00
commit 37af6c33ee
48 changed files with 69395 additions and 0 deletions

6
.idea/MarsCodeWorkspaceAppSettings.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="com.codeverse.userSettings.MarscodeWorkspaceAppSettingsState">
<option name="progress" value="1.0" />
</component>
</project>

View File

@@ -0,0 +1,82 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="HtmlUnknownTag" enabled="true" level="WARNING" enabled_by_default="true">
<option name="myValues">
<value>
<list size="8">
<item index="0" class="java.lang.String" itemvalue="nobr" />
<item index="1" class="java.lang.String" itemvalue="noembed" />
<item index="2" class="java.lang.String" itemvalue="comment" />
<item index="3" class="java.lang.String" itemvalue="noscript" />
<item index="4" class="java.lang.String" itemvalue="embed" />
<item index="5" class="java.lang.String" itemvalue="script" />
<item index="6" class="java.lang.String" itemvalue="following" />
<item index="7" class="java.lang.String" itemvalue="schema" />
</list>
</value>
</option>
<option name="myCustomValuesEnabled" value="true" />
</inspection_tool>
<inspection_tool class="HttpUrlsUsage" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="ignoredUrls">
<list>
<option value="http://localhost" />
<option value="http://127.0.0.1" />
<option value="http://0.0.0.0" />
<option value="http://www.w3.org/" />
<option value="http://json-schema.org/draft" />
<option value="http://java.sun.com/" />
<option value="http://xmlns.jcp.org/" />
<option value="http://javafx.com/javafx/" />
<option value="http://javafx.com/fxml" />
<option value="http://maven.apache.org/xsd/" />
<option value="http://maven.apache.org/POM/" />
<option value="http://www.springframework.org/schema/" />
<option value="http://www.springframework.org/tags" />
<option value="http://www.springframework.org/security/tags" />
<option value="http://www.thymeleaf.org" />
<option value="http://www.jboss.org/j2ee/schema/" />
<option value="http://www.jboss.com/xml/ns/" />
<option value="http://www.ibm.com/webservices/xsd" />
<option value="http://activemq.apache.org/schema/" />
<option value="http://schema.cloudfoundry.org/spring/" />
<option value="http://schemas.xmlsoap.org/" />
<option value="http://cxf.apache.org/schemas/" />
<option value="http://primefaces.org/ui" />
<option value="http://tiles.apache.org/" />
<option value="http://one.zhaoxueqing.top" />
</list>
</option>
</inspection_tool>
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredPackages">
<value>
<list size="20">
<item index="0" class="java.lang.String" itemvalue="sentence_transformers" />
<item index="1" class="java.lang.String" itemvalue="tencentcloud-sdk-python-ocr" />
<item index="2" class="java.lang.String" itemvalue="tencentcloud-sdk-python" />
<item index="3" class="java.lang.String" itemvalue="Pillow" />
<item index="4" class="java.lang.String" itemvalue="PyJWT" />
<item index="5" class="java.lang.String" itemvalue="pydantic" />
<item index="6" class="java.lang.String" itemvalue="requests" />
<item index="7" class="java.lang.String" itemvalue="websocket-client" />
<item index="8" class="java.lang.String" itemvalue="pandas" />
<item index="9" class="java.lang.String" itemvalue="openai" />
<item index="10" class="java.lang.String" itemvalue="fastapi" />
<item index="11" class="java.lang.String" itemvalue="docx2txt" />
<item index="12" class="java.lang.String" itemvalue="langchain" />
<item index="13" class="java.lang.String" itemvalue="starlette" />
<item index="14" class="java.lang.String" itemvalue="grpcio" />
<item index="15" class="java.lang.String" itemvalue="uvicorn" />
<item index="16" class="java.lang.String" itemvalue="pymilvus" />
<item index="17" class="java.lang.String" itemvalue="snowland-smx" />
<item index="18" class="java.lang.String" itemvalue="openpyxl" />
<item index="19" class="java.lang.String" itemvalue="eio" />
</list>
</value>
</option>
</inspection_tool>
</profile>
</component>

View File

@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

7
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.11 (image_recognition)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9 (scf)" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/scf-python-dev-demo.iml" filepath="$PROJECT_DIR$/.idea/scf-python-dev-demo.iml" />
</modules>
</component>
</project>

12
.idea/scf-python-dev-demo.iml generated Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Python 3.9 (scf)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
</module>

70
README.md Normal file
View File

@@ -0,0 +1,70 @@
# 快速构建 scf-python
**中文** | [English](./README_EN.md)
## 简介
scf-python 模板使用 Tencent SCF 组件及其触发器能力,方便的在腾讯云创建,配置和管理一个 scf-python 应用。
## 快速开始
### 1. 安装
```bash
# 安装 Serverless Cloud Framework
npm install -g serverless-cloud-framework
```
### 2. 创建
通过如下命令直接下载该例子:
```bash
scf init scf-python --name example
cd example
```
### 3. 部署
`serverless.yml` 文件所在的项目根目录,运行以下指令,将会弹出二维码,直接扫码授权进行部署:
```bash
scf deploy
```
> **说明**:如果鉴权失败,请参考 [权限配置](https://cloud.tencent.com/document/product/1154/43006) 进行授权。
### 4. 查看状态
执行以下命令,查看您部署的项目信息:
```bash
scf info
```
### 5. 移除
可以通过以下命令移除 scf-python 应用
```bash
scf remove
```
### 账号配置(可选)
serverless 默认支持扫描二维码登录,用户扫描二维码后会自动生成一个 `.env` 文件并将密钥存入其中.
如您希望配置持久的环境变量/秘钥信息,也可以本地创建 `.env` 文件,
把从[API 密钥管理](https://console.cloud.tencent.com/cam/capi)中获取的 `SecretId``SecretKey` 填入其中.
> 如果没有腾讯云账号,可以在此[注册新账号](https://cloud.tencent.com/register)。
```bash
# 腾讯云的配置信息
touch .env
```
```
# .env file
TENCENT_SECRET_ID=123
TENCENT_SECRET_KEY=123
```

80
README_EN.md Normal file
View File

@@ -0,0 +1,80 @@
# Quickly create and deploy scf-python application
[中文](./README.md) | **English**
## Introduction
Easily deploy scf-python applications to Tencent Cloud's serverless infrastructure using this Serverless Cloud Framework Component.
Your application will auto-scale, never charge you for idle time, and require little-to-zero administration.
## Quick Start
### 1. Install
```bash
# Install Serverless Cloud Framework
npm install -g serverless-cloud-framework
```
### 2. Initialize
Initializing the scf-python template by running this following command:
```bash
scf init scf-python --name example
cd example
```
### 3. Deploy
You can use following command to deploy the APP.
```bash
cd scf-python
scf deploy
```
This command will walk you through signing up a Tencent Cloud Account to deploy the APP.
### 4. Monitor
Anytime you need to know more about your running express instance, you can run `scf info` to view the most critical info.
This is especially helpful when you want to know the outputs of your instances so that you can reference them in another instance.
You will also see a url where you'll be able to view more info about your instance on the Serverless Dashboard.
It also shows you the status of your instance, when it was last deployed, and how many times it was deployed.
To dig even deeper, you can pass the --debug flag to view the state of your component instance in case the deployment failed for any reason.
```bash
scf info
```
### 5. Remove
If you wanna tear down your entire infrastructure that was created during deployment,
just run `scf remove` and serverless will remove all the data it needs from the built-in state storage system to delete only the relevant cloud resources that it created.
```bash
scf remove
```
### Setting up credentials (Optional)
By default, you are able to login your Tencent Cloud account by scanning QR code and an `.env` file with credentials is auto generated.
The credentials will be expired after 2 hours.
If you would like to use persistent credentials,
you can [create an API Key here](https://console.cloud.tencent.com/cam/capi) and add the `SecretId` and `SecretKey` into the `.env` file
> If you don's have a Tencent Cloud Account, you can register [here](https://cloud.tencent.com/register)
```bash
# Add your Tencent credentials here
touch .env
```
```
# .env file
TENCENT_SECRET_ID=123
TENCENT_SECRET_KEY=123
```

220
access_token.py Normal file
View File

@@ -0,0 +1,220 @@
import requests
import time
def transform_data(data, mapping):
temp = {}
for k, v in mapping.items():
if k in data:
if k == "date":
timestamp = str(int(time.mktime(time.strptime(data[k], "%Y-%m-%d %H:%M:%S"))))
temp[v] = timestamp + "000"
else:
temp[v] = data[k]
return temp
class SmartSheet:
def __init__(self, doc_id, sheet_id):
self.doc_id = doc_id
self.sheet_id = sheet_id
self._columns = None
self._data = None
@staticmethod
def _post(url, body):
resp = requests.post(url, json=body)
if resp.status_code == 200:
return resp.json()
return {}
def _get_columns(self):
url = "https://a.cmdp.cn/umss/v1/wxw/common/submit"
body = {
"url": "https://qyapi.weixin.qq.com/cgi-bin/wedoc/smartsheet/get_fields",
"method": "POST",
"body": {
"docid": self.doc_id,
"sheet_id": self.sheet_id
}
}
response = self._post(url, body)
if response:
fields = response["fields"]
return fields
return []
def _get_data(self, filters: dict, cursor: int = 0, limit: int = 1000):
url = "https://a.cmdp.cn/umss/v1/wxw/smartSheet/getRecords"
body = {
"docid": self.doc_id,
"sheet_id": self.sheet_id,
"filter_spec": filters,
"offset": cursor,
"limit": limit
}
response = self._post(url, body)
if response:
cursor = response["next"]
records = response["records"]
return cursor, records
return 0, []
def _add(self, records: list):
url = "https://a.cmdp.cn/umss/v1/wxw/smartSheet/addRecords"
body = {
"docid": self.doc_id,
"sheet_id": self.sheet_id,
"key_type": "CELL_VALUE_KEY_TYPE_FIELD_TITLE",
"records": records
}
response = self._post(url, body)
if response:
return True
return False
def _transform_data(self, data: dict):
result = {}
columns = self.columns
for i in data:
type_info = columns[i]
i_type = type_info.get("field_type")
if i_type == "FIELD_TYPE_TEXT":
result[i] = [
{
"text": data[i],
"type": "text"
}
]
elif i_type == "FIELD_TYPE_SINGLE_SELECT":
options = {i["text"]: i for i in type_info['property_single_select']["options"]}
result[i] = [
options[data[i]]
]
else:
result[i] = data[i]
return result
def _convert_data(self, data: dict):
va = {}
for v in data:
type_info = self.columns[v]
i_type = type_info.get("field_type")
if i_type == "FIELD_TYPE_TEXT":
va[v] = "".join([m["text"] for m in data[v]])
elif i_type == "FIELD_TYPE_DATE_TIME":
va[v] = 0 if data[v] is None else data[v]
elif i_type == "FIELD_TYPE_NUMBER":
va[v] = 0 if data[v] is None else data[v]
elif i_type == "FIELD_TYPE_SELECT":
va[v] = [m["text"] for m in data[v]]
elif i_type == "FIELD_TYPE_SINGLE_SELECT":
va[v] = data[v][0]["text"] if data[v] else ""
elif i_type == "FIELD_TYPE_TWOWAYLINKRECORDS":
va[v] = data[v]
else:
print(i_type, data[v],type_info)
return va
@property
def columns(self):
if self._columns is None:
self._columns = self._get_columns()
return {i["field_title"]: i for i in self._columns}
@property
def data(self):
if self._data is None:
self._data = self.search({})
return self._data
def search(self, filters: dict):
cursor = 0
result = []
while True:
cursor, records = self._get_data(filters, cursor=cursor)
result += records
if cursor == 0:
break
return result
def add(self, data: dict):
return self._add([{"values": self._transform_data(data)}])
def batch_add(self, data_list: list):
return self._add([{"values": self._transform_data(i)} for i in data_list])
def to_json(self):
data = []
for i in self.data:
values = i["values"]
va = self._convert_data(values)
va['_rid'] = i["record_id"]
data.append(va)
return {"columns": self.columns, "data": data}
class SmartTableApi:
def __init__(self, doc_id):
self.doc_id = doc_id
self.metadata_id = None
self._metadata = None
self.metadata_table = None
self._get_sheet_list()
@staticmethod
def _post(url, body):
resp = requests.post(url, json=body)
if resp.status_code == 200:
return resp.json()
return {}
def _get_sheet_list(self):
url = "https://a.cmdp.cn/umss/v1/wxw/common/submit"
body = {
"url": "https://qyapi.weixin.qq.com/cgi-bin/wedoc/smartsheet/get_sheet",
"method": "POST",
"body": {
"docid": self.doc_id
}
}
resp = self._post(url, body)
sheet_list = resp.get("sheet_list", [])
result = {i["title"]: i["sheet_id"] for i in sheet_list}
self.metadata_id = result.get("Metadata")
if "Metadata" in result:
del result["Metadata"]
self._metadata = result
@property
def metadata(self):
return self._metadata
def _update_metadata(self, sheet_id, sheet_name):
if not self.metadata_table:
self.metadata_table = SmartSheet(self.doc_id, self.metadata_id)
self.metadata_table.add({"名称": sheet_name, "类型": "sheet", "": sheet_id})
pass
def add_sheet(self, sheet_name):
url = "https://a.cmdp.cn/umss/v1/wxw/addSheet"
body = {
"docid": self.doc_id,
"properties": {
"title": sheet_name
}
}
resp = self._post(url, body)
if resp.get("properties"):
self._update_metadata(sheet_id=resp["properties"]["sheet_id"], sheet_name=sheet_name)
return resp
def get_sheet(self, sheet_id):
return SmartSheet(self.doc_id, sheet_id)
def get_sheet_by_name(self, sheet_name):
if sheet_name in self.metadata:
return self.get_sheet(self.metadata[sheet_name])
else:
return None

116
esmart.py Normal file
View File

@@ -0,0 +1,116 @@
import json
import time
from access_token import SmartSheet
from lib import WXworkBaseAPI
class ESmart:
def __init__(self, time_out: int = 10 * 60):
self.time_out = time_out
self.timestamp = int(time.time())
self.report_sheet = SmartSheet(
"dc48YTUb9gNYCpIWg5v90-U5HIWT72bm3Fkw9Jvi33_sP3I0H32QEDFNOf69TS1bbUjdsYLmvArqqUootD34nm8Q", "L1wFol")
self.user_sheet = SmartSheet(
"dc1zao6J8YnS9p86FKKvKQadukmFXlXv3dU1qVQrgcjB81zdSVI7qoi9a7K_OpN4BTyXc8EbYVXxpnQNUUTMWwIw",
"8vVxEx")
self.notice_sheet = SmartSheet(
"dc1zao6J8YnS9p86FKKvKQadukmFXlXv3dU1qVQrgcjB81zdSVI7qoi9a7K_OpN4BTyXc8EbYVXxpnQNUUTMWwIw", "2943X5")
self.user_log_sheet = SmartSheet(
"dcqzSDHPl2ZWoLkEB3Id1wMMaW0meMSwGLjfnuHD0VMh0DVidKdal-3wYfsh7Zb1pGn9moDuzjvhtlRZAXoO-Hjg", "g6dTI2")
self.base_sheet = SmartSheet(
"dcqzSDHPl2ZWoLkEB3Id1wMMaW0meMSwGLjfnuHD0VMh0DVidKdal-3wYfsh7Zb1pGn9moDuzjvhtlRZAXoO-Hjg", "1gTulR")
self._base_data = None
self._user_info = None
@staticmethod
def _convert_user_info(data):
_data = {}
for i in data["data"]:
if "来源对象ID#eid" in i:
v = {
"eid": i["来源对象ID#eid"],
"name": i["名称#name"],
"value": i["数值#value"],
"type": i["类型"],
"_rid": i["_rid"],
}
k = v["name"].split("_")
k = k[0] if len(k) > 0 else ""
_data[k] = v
return _data
@property
def base_data(self):
if self._base_data is None:
self._base_data = self.base_sheet.to_json()
if self._check_timeout():
self._base_data = self.base_sheet.to_json()
return self._base_data
@property
def user_info(self):
if self._user_info is None:
self._user_info = self._convert_user_info(self.user_sheet.to_json())
if self._check_timeout():
self._user_info = self._convert_user_info(self.user_sheet.to_json())
return self._user_info
def get_user_info(self, user_id):
now = int(time.time())
if now - self.timestamp > self.time_out:
print("refresh")
self.timestamp = now
def _check_timeout(self):
now = int(time.time())
if now - self.timestamp > self.time_out:
print("refresh")
self.timestamp = now
return True
else:
return False
def refresh(self):
self._base_data = self.base_sheet.to_json()
if __name__ == '__main__':
esmart = ESmart()
# with open("package.json", "w",encoding="utf-8") as f:
# f.write(json.dumps(esmart.user_info,ensure_ascii=False))
with open("package.json", "r",encoding="utf-8") as f:
u_mapping = json.loads(f.read())
cache_mapping = {v["深交所上市公司代码"]: [u_mapping.get(j,{}).get('_rid') for j in v.get("跟进负责人",[]) if u_mapping.get(j)] for v in esmart.base_data["data"]}
print(cache_mapping)
# user_sheet.search({
# "conjunction": "CONJUNCTION_AND",
# "conditions": [{
# "field_id": 'fmTMGx',
# "field_type": "FIELD_TYPE_TEXT",
# "operator": "OPERATOR_CONTAINS",
# "string_value": {
# "value": [
# ""
# ]
# }
# }]
# })
# for i in m["data"]:
# print(i)
#
# data = {
# "*发布日期": time.strftime("%Y-%m-%d", time.localtime()),
# "*栏目#gName": "",
# "状态": "待发布",
# "*摘要#subject": "",
# "置顶#isTop": False,
# "*详情#content": "",
# "*标题#title": "",
# "消息类型#msgType": "中性",
# "*消息接收对象#sendList": [
# ""
# ]
# }
# print(data)
#

47
groupchat/main.py Normal file
View File

@@ -0,0 +1,47 @@
# 企业微信客户群聊分析
from os import stat_result
import pandas as pd
import re
def parser_chat_text(content: str):
result = []
temp = ["", "", "", ""]
message = ""
pattern = "(.*?@{0,1}.*?@{0,1}.*?) (.{1,2}/.{1,2}) (.{2}:.{2}:.{2})"
for i in content.split("\n"):
a = re.findall(pattern, i)
if a:
if message:
temp[3] = message
message = ""
result.append(temp.copy())
user, date, time = a[0]
temp[0] = user
temp[1] = date
temp[2] = time
else:
message += i
stat_result = {}
for user, date, time, content in result:
if user in stat_result:
times, word_count = stat_result[user]
stat_result[user] = (times + 1, word_count + len(content))
else:
stat_result[user] = (1, len(content))
return result, [[t, stat_result[t][0], stat_result[t][1]] for t in stat_result]
if __name__ == '__main__':
df = pd.read_excel("聊天记录.xlsx")
for group_name,group_messages in df.values:
writer = pd.ExcelWriter(f"{group_name}-聊天记录分析.xlsx", engine="xlsxwriter")
chat_messages ,chat_analyzer = parser_chat_text(group_messages)
df1 = pd.DataFrame(chat_messages,columns=["user","date","time","content"])
df1 = df1.sort_values("date",ascending=False)
df2 = pd.DataFrame(chat_analyzer,columns=["user","frequence","word_count"])
df2 = df2.sort_values("frequence",ascending=False)
df2.to_excel(writer, sheet_name="记录分析",index=False)
df1.to_excel(writer, sheet_name="记录详情",index=False)
writer.close()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
groupchat/聊天记录.xlsx Normal file

Binary file not shown.

0
identifier.sqlite Normal file
View File

751
index.py Normal file
View File

@@ -0,0 +1,751 @@
# !/usr/bin/env python
# -*- coding:utf-8 -*-
import base64
import json
import time
from abc import abstractmethod
import requests
from access_token import SmartTableApi, transform_data
from lib import WXworkAuth, CloudFunctionAPI, WXworkCalendarAPI, parse_data, WXworkApprovalAPI, WXworkUserInfoAPI,WXworkDocAPI
crop_id = "wx98dfa35ad7e4b271"
space_id = "s.wx98dfa35ad7e4b271.744621152iSk"
doc_id = 'dc1zao6J8YnS9p86FKKvKQadukmFXlXv3dU1qVQrgcjB81zdSVI7qoi9a7K_OpN4BTyXc8EbYVXxpnQNUUTMWwIw'
wxc_app_secret = "gyN5moBa6Ev1Vd0ZLUVrsEtAO_goppWKUBdsnSng-Ks"
wapi_app_secret = "oM0hwIi8GRPw6HWk9o5__g3v5ziz5CGyBUo2FASSrVw"
wxc_auth = WXworkAuth(crop_id, wxc_app_secret)
wapi_auth = WXworkAuth(crop_id, wapi_app_secret)
cf_api = CloudFunctionAPI("http://10.0.0.39/cloud")
wxc_api = WXworkCalendarAPI(wxc_auth)
wapi = WXworkApprovalAPI(wapi_auth)
wup = WXworkUserInfoAPI(wapi_auth)
ssp = WXworkDocAPI(wxc_auth,space_id)
def file_exists(file_path):
_url = f"https://a.cmdp.cn/basiceg/v1/csp/exist/{file_path}"
response = requests.get(_url.format(file_path=file_path))
return response.json().get("exist", False)
class CalendarDB:
def __init__(self):
self.name = None
self.user_id = None
self.user_info = None
self.calendar_id = None
self.calendar_connection = None
self.cloud_connection = None
def login(self, login_user: str):
self.name = login_user
self.calendar_connection = wxc_api
self.cloud_connection = cf_api
user_info = self.cloud_connection.get_user_info_by_name(user_name=self.name)
self.user_id = user_info.get('id')
user_tags = self.cloud_connection.parse_tags(user_info.get('tags', []))
calendar_ids = [i for i in user_tags.get('calendar', {}).get('id', {})]
self.calendar_id = calendar_ids[0] if calendar_ids else None
if not self.calendar_id:
self.calendar_id = self.create_calendar("访问日历")
if self.calendar_id:
self.cloud_connection.update_user_tag(user_id=self.user_id, key="calendar_id", value=self.calendar_id)
return {"calendar": self.calendar_id}
def create_calendar(self, calendar_name: str):
return self.calendar_connection.create_calendar(calendar_name, self.name)
@abstractmethod
def _transfer_calendar_data(self, data: dict) -> dict:
pass
def get_all(self):
return self.calendar_connection.get_calendar_list(self.calendar_id)
def add(self, data: dict):
_data = data.copy()
data.pop("_uid")
schedule_name = f"{data.get('company_name')}[{data.get('company_code')}] 拜访计划"
start_time = data.get("visit_time")
end_time = data.get("end_time")
content = json.dumps(data, ensure_ascii=False)
address = data.get("company_address")
self.calendar_connection.create_schedule(self.calendar_id, schedule_name, start_time, end_time, self.name,
content,
address)
return self.trigger(_data )
@abstractmethod
def trigger(self, data: dict):
pass
def delete(self, schedule_id: str):
self.trigger({})
pass
def get_report_ticket(file_path: str, download_name: str):
"""
四.生成文件下载地址生成【通用】
说明
调用接口获取ticket然后拼接地址后发给用户
接口地址
https://a.cmdp.cn/v1/cloudfile/file/createTicket
输入参数[post]
{
"appId": "",
"uri": "s3://usercentre03-dev/user/documents/my document/00002rfdata11792529c9f6e136e282b6416ff20d3f.json", //文件的s3地址
"startTime": "2024-03-21 12:12:12",
"endTime": "",
"maxTimes": 0,
"uid": "",
"name": "1.json", //下载时的文件名
"isPublic":false
}
返回示例:
{
"code": 200,
"msg": null,
"data": "LCXNDEwla",
"validations": null,
"token": null,
"success": true
}
拼接文件下载地址
https://ea.cmdp.cn/v1/cloudfile/file/?ticket=LCXNDEwla
"""
url = "https://a.cmdp.cn/v1/cloudfile/file/createTicket"
body = {
"appId": "",
"uri": file_path,
# 文件的s3地址
"startTime": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())),
"endTime": "",
"maxTimes": 0,
"uid": "",
"name": download_name, # 下载时的文件名
"isPublic": False
}
headers = {
"Content-Type": "application/json"
}
response = requests.post(url, json=body, headers=headers)
ticket = response.json().get("data")
download_url = f"https://a.cmdp.cn/v1/cloudfile/file/?ticket={ticket}"
return download_url
class SalesTools:
@staticmethod
def get_user_info(security_code: str):
"""
二.用户列表查询
说明
【同步调用】
接口地址
http://10.0.0.39/cloud/in/api/userQuery
输入参数[post]
{
"secCode": "000002",
"status":10,
"PageSize": 10,
"PageNo": 1
}"""
return cf_api.get_user_info_by_sec(security_code)
@staticmethod
def update_user_tag(user_id: str):
"""
三.用户打标签
说明
【同步调用】
接口地址
http://10.0.0.39/cloud/in/api/user
输入参数[post]
{
"id": "1722805021193383937",
"tags": [
"客户类型_上市公司",
"客户权限_动态令牌"
]
}
"""
cf_api.update_user_tags(user_id, {"客户类型": "上市公司", "客户权限": "动态令牌"})
@staticmethod
def get_org_info(security_code: str):
"""
四.通用机构信息查询
说明
接口地址
http://ea.cmdp.cn/apm/v1/ability/api000353
输入参数[post]
{
"_data": {
"searchType": "stock",
"secCode": "000002" //证券代码
}
}
"""
url = "http://a.cmdp.cn/apm/v1/ability/api000353"
body = {
"_data": {
"searchType": "stock",
"secCode": security_code
}
}
headers = {
"Content-Type": "application/json"
}
response = requests.post(url, json=body, headers=headers)
return response.json().get("data", {})
@staticmethod
def get_report_rule(tags: [str]):
"""
四.尽调报告的规则文件地址查询
说明
接口地址
https://a.cmdp.cn/cmnd/ndisksvr/ms/v1/dir_list/usys/pbcs/release
输入参数[post]
{
"opUserId": "genesys_app",
"sourceUserId": "genesys_app",
"filter":"'fileInfo.extends.resource.delStatus' eq 1 and 'fileInfo.extends.pbcMeta.name' eq '尽调报告云盘项目名称,需要问@冯昕宇'",【企业售前画像报告】
"tags":["报告年度_2023","biz_项目主体类别_单体","biz_业务场所_深交所"]
}
"""
url = "https://a.cmdp.cn/cmnd/ndisksvr/ms/v1/dir_list/usys/pbcs/release"
body = {
"opUserId": "genesys_app",
"sourceUserId": "genesys_app",
"filter": f"'fileInfo.extends.resource.delStatus' eq 1 and 'fileInfo.extends.pbcMeta.name' eq '企业售前画像报告'",
"tags": tags
}
headers = {
"Content-Type": "application/json"
}
response = requests.post(url, json=body, headers=headers)
return response.json()
@staticmethod
def get_report_info(user_id: str, security_code: str, rule_path: str):
"""
四.尽调报告执行
说明
执行后报告需要生成ticket的下载地址
接口地址
https://a.cmdp.cn/v1/cmp/cal/fn/PerformRuleFile
输入参数[post]
{
"issync": 2,
"ruleobj": "temp-003/rule.json",
"dataobj": {
"uid": "用户ID", //填入真实用户ID执行后会在微信公众收到结果消息可以直接打开报告
"_data": {
"secCode": "000002"
},
"_var": {
"config": {
"prodName": "企业售前画像报告"
}
}
}
}
"""
url = "https://a.cmdp.cn/v1/cmp/cal/fn/PerformRuleFile"
body = {
"issync": 2, # 同步执行
"ruleobj": rule_path,
"dataobj": {
"uid": user_id,
"_data": {
"secCode": security_code
},
"_var": {
"config": {
"prodName": "企业售前画像报告"
}
}
}}
headers = {
"Content-Type": "application/json"
}
response = requests.post(url, json=body, headers=headers)
return response.json().get("dumpmsg", {}).get("_out", {})
@staticmethod
def get_regression_testing(eid: str):
"""
执行后报告需要生成ticket的下载地址2024年半年报
@卢天宝说明一下文件存储路径,@常连钰直接根据文件存储路径生成下载ticket但是需要判断是否存在https://a.cmdp.cn/basiceg/v1/csp/exist/{path}
存储路径announce02/{textId}._val_01.json , announce02/{textId}._val_01.docx
textId获取
请求地址posthttp://a.cmdp.cn/cnd/v1/entity/search/cmdb_bfmapping
参数
{
"tags": [
"报告期_2024QR1", //替换为真实报告期
"GE8002005E_100000000002334460" //GE8002005E_{eid}
]
}
返回示例:
{
"total": 1,
"records": [
{
"eid": "100000000037315576",
"tags": {
"公司名称_上海凌云实业发展股份有限公司": true,
"GE8002005E_100000000002334460": true,
"证券代码_900957": true,
"报告期_2024QR1": true,
"TEXTID_1219815286": true, //其中1219815286就是text的值
"公告标题_凌云凌云B股2024年第一季度报告": true
}
}
]
}
"""
def get_text_id(report_date, entity_id):
url = "https://a.cmdp.cn/cnd/v1/entity/search/cmdb_bfmapping"
body = {
"tags": [
f"报告期_{report_date}",
f"GE8002005E_{entity_id}"
]
}
headers = {
"Content-Type": "application/json"
}
response = requests.post(url, json=body, headers=headers)
result = response.json()
tags_ = result.get("records", [{}])
if tags_:
tags = tags_[0].get("tags", [{}])
else:
tags = []
text_id = None
for tag in tags:
l = tag.split("_")
if l[0] == "TEXTID":
text_id = l[1]
return text_id
def get_report_datetime():
report_datetime = time.localtime(time.time())
year = report_datetime.tm_year
month = report_datetime.tm_mon
if month >= 9:
return [f"{year}SAR",f"{year-1}AR"]
elif month >= 4:
return [f"{year-1}SAR",f"{year-1}AR"]
else:
return [f"{year-1}SAR",f"{year-2}AR"]
def get_report(tid):
if not tid:
return None
docx_path = f"announce02/{tid}_val_01.docx"
json_path = f"announce02/{tid}_val_01.json"
if file_exists(json_path) and file_exists(docx_path):
return flag , get_report_ticket("s3://" + docx_path, "回归测试报告.docx")
else:
print("file not exist")
return None, None
flags = get_report_datetime()
result = ""
for flag in flags:
text_id = get_text_id(flag,eid)
f, r = get_report(text_id)
if r:
result += f'<br><p color="black">{f}:</p><a href="{r}" target="_blank" style="color: rgb(38, 126, 240); text-decoration-line: underline; outline-style: none; cursor: pointer; font-size: 16px; font-weight: 400;">{r}</a>'
if result != "":
return result
else:
return "暂无"
@staticmethod
def get_current_product_id():
now = time.localtime(time.time())
year = now.tm_year
month = now.tm_mon
if month >= 5:
year = year - 1
else:
year = year - 2
url = "https://a.cmdp.cn/cmnd/ndisksvr/ms/v1/dir_listall/usys/products/release"
body = {
"opUserId": "genesys_app",
"sourceUserId": "genesys_app",
"filter": "'fileInfo.extends.resource.delStatus' eq 1 and 'fileInfo.extends.resource.label' eq '小智智填-定期报告专版'",
"tags": ["tech_sbom", f"prod_year_{year}", "type_入口", "prod_小智转写"]
}
print(url,json.dumps(body,ensure_ascii=False))
response = requests.post(url, json=body)
if response.status_code == 200:
response_json = response.json()
code = response_json.get("code", -1)
if code == 200:
data = response_json.get("data", {})
records = data.get("records", [])
if records:
widget_info = records[0].get("fileInfo", {}).get("extends", {}).get("widget", {})
product_id = widget_info.get("id")
return product_id
return None
@staticmethod
def get_smart_genie_product(entity_id: str, product_id: str):
"""
说明
选样本后的订购【打印或复制】小智类产品
内部接口eid和个人uid是输入的。
产品选择是输入产品id
产品 产品id 说明
小智复核-定期报告专版 已有产品,示例
小智智填-定期报告专版 1854436156570607618 产品id待补充
接口地址
https://cloud.cmdp.cn/api/product/deliver/print
输入参数[post]
{
"eid": "eid不填就是查询用户所属公司的",
"pid": "必填入口产品id",
"priceType":"产品版本,枚举:体验、标准版、旗舰版、特别优惠版";售前产品固定:标准版
}
输入示例
{
"eid": "100000000002334059",
"pid": "1858326969193848833",
"priceType":"标准版"
}
返回参数
{"Code":"状态码""data":"入口产品id"}
返回示例
{"Code":200"data":"1853245209740582914"}
打印成功后会有公众号通知
"""
url = "https://cloud.cmdp.cn/api/product/deliver/print"
body = {
"eid": entity_id,
"pid": product_id if product_id else "",
"priceType": "体验版"
}
response = requests.post(url, json=body)
if response.status_code == 200:
response_json = response.json()
code = response_json.get("code", -1)
if code == 200:
return response_json.get("data", "")
return None
@staticmethod
def get_product_qrcode(entity_id: str, product_id: str):
"""
接口示例:
标签查fid
curl --location --request POST "http://10.0.0.39/octopus/usr/genesys_app/file/query" ^
--header "User-Agent: Apifox/1.0.0 (https://apifox.com)" ^
--header "Content-Type: application/json" ^
--header "Accept: */*" ^
--header "Host: 10.0.0.39" ^
--header "Connection: keep-alive" ^
--data-raw "{ \"Category\": \"default\", \"ownerID\":\"genesys_app\", \"query\": \"\\\"uid_1652264045856165888\\\"\", \"path\":\"/system/users\", \"includeHistory\":false}"
fid查文件
curl --location --request GET "http://10.0.0.39/octopus/usr/genesys_app/file/1-23823/meta" ^
--header "User-Agent: Apifox/1.0.0 (https://apifox.com)" ^
--header "Accept: */*" ^
--header "Host: 10.0.0.39" ^
--header "Connection: keep-alive"
query查询标签
查询标签 【"eid_{eid}","pid_{pid}","priceType_标准版"
"""
def get_file_by_fid(file_id):
url = f"http://10.0.0.39/octopus/usr/genesys_app/file/{file_id}/meta"
try:
response = requests.get(url)
if response.status_code == 200:
return response.json()
else:
return {}
except Exception:
return {}
def get_file_id(entity_id: str, product_id: str):
url = "http://10.0.0.39/octopus/usr/genesys_app/file/query"
body = {
"Category": "default",
"ownerID": "genesys_app",
"query": f"\"eid_{entity_id}\" & \"pid_{product_id}\" & \"priceType_标准版\"",
"path": "/system/users",
"includeHistory": False
}
response = requests.post(url, json=body)
if response.status_code == 200:
return response.json()
else:
return []
if not product_id:
return None
file_ids = get_file_id(entity_id, product_id)
if not file_ids:
return None
file_info = get_file_by_fid(file_ids[0])
return file_info.get("uri")
@staticmethod
def send_company_mail(to_address: str, subject: str, content: str):
url = "https://a.cmdp.cn/apm/v1/ability/api000357"
body = {"_data": {
"type": "html",
"tos": [to_address],
"subject": subject,
"text": content
}
}
response = requests.post(url, json=body)
return response.json()
@staticmethod
def get_qrcode_base64(product_qrcode_path):
if not product_qrcode_path:
return ""
response = requests.get(product_qrcode_path)
if response.status_code == 200:
return base64.b64encode(response.content).decode('utf-8')
else:
return ""
@staticmethod
def get_information_disclosure_picture(year:str,security_code:str):
basiceg_url = "http://a.cmdp.cn/basiceg/v1/bytes"
file_path = f"/usercentre03-dev/user/cache/{year}/信披成绩单/{security_code}_idsr.jpg"
static_path = f"/product-data/static/xpcjd/{security_code}_idsr.jpg"
if file_exists(file_path):
resp = requests.get(basiceg_url+file_path)
if resp.status_code == 200:
data = resp.content
resp = requests.put(basiceg_url+static_path,data=data)
return f'<img src="http://www.cmdp.cn/a/s/xpcjd/{security_code}_idsr.jpg" width="100%">'
else:
return ""
st = SalesTools()
class CalendarEventDB(CalendarDB):
def _transfer_calendar_data(self, data: dict) -> dict:
pass
def trigger(self, data: dict):
security_code = data.get("company_code")
user_list = st.get_user_info(security_code)
# 获取公司信息
company_info = st.get_org_info(security_code)
eid = company_info.get("eid")
full_name = company_info.get("fullName")
# 打标签
if user_list:
for user in user_list:
user_id = user.get("id")
st.update_user_tag(user_id)
# 获取bp id
bp_info = self.cloud_connection.get_user_info_by_name(self.name)
bp_id = bp_info.get("id")
bp_mail = bp_info.get("email")
# 获取二维码
# product_id = st.get_current_product_id()
# product_id ="1909049700811497474"
# st.get_smart_genie_product(eid, product_id)
# product_qrcode_path = st.get_product_qrcode(eid, product_id)
# qrcode = st.get_qrcode_base64(product_qrcode_path)
# 获取报告文件
now = time.localtime(time.time())
year = now.tm_year
month = now.tm_mon
if month >= 5:
year = year - 1
else:
year = year - 2
report_tags = [f"报告年度_{year}", "biz_项目主体类别_单体",
"biz_业务场所_深交所"]
report_rule_info = st.get_report_rule(report_tags)
report_rule_path = report_rule_info.get("data", {}).get("records", "")
if report_rule_path:
report_rule_path = report_rule_path[0].get("filePath", "")
report_info = st.get_report_info(bp_id, security_code, report_rule_path)
report_ticket = report_info.get("data").get("path")
regression_testing = st.get_regression_testing(eid)
with open("template.html", "r", encoding="utf-8") as f:
content = f.read()
content = content.replace("{{report_ticket}}", report_ticket)
content = content.replace("{{regression_testing}}", regression_testing)
# content = content.replace("{{qrcode}}", qrcode)
content = content.replace("{{picture}}",st.get_information_disclosure_picture("2024",security_code))
st.send_company_mail(bp_mail, f"客户拜访准备文件-{full_name}[{security_code}]", content)
ssp.add_row(doc_id=doc_id,sheet_id="tTMuVB",records=[
{
"values": {
"证券代码": [
{
"type": "text",
"text": data["company_code"]
}
],
"证券简称": [{
"type": "text",
"text": data["company_name"]
}]
,
"地点": [{
"type": "text",
"text": data["company_address"]
}]
,
"拜访时间": data["visit_time"]+"000"
,
"结束时间": data["end_time"]+"000",
"拜访人": [{
"type": "text",
"text": data["_uid"]
}]
}
}])
ceb = CalendarEventDB()
def get_approval(template_id):
approval_list = wapi.get_approval_list_weekly(template_id)
info = {}
for approval_id in approval_list:
approval_info = wapi.get_approval_info(approval_id)
approval_info = parse_data(approval_info["info"],
{"Number-1697103952657": "company_code", "Text-1697103919621": "company_name",
"Text-1728374569134": "company_address", "Date-1728370374837": "visit_time",
"Date-1728370485478": "end_time"})
_uid = approval_info["_uid"]
if _uid not in info:
_u = wup.get_user_info(_uid)
info[_uid] = {"name": _u["name"]}
else:
_p = info[_uid]
company_code = approval_info["company_code"]
if company_code not in _p:
_p[company_code] = 1
else:
_p[company_code] += 1
return info
def generate_approval_report_html(times_report):
htms = "<!DOCTYPE html><html lang='en'><head><meta charset='UTF-8'><title></title><style>table{width:100%;border-collapse:collapse;font-family:Arial,sans-serif;box-shadow:0 2px 10px rgba(0,0,0,0.1)}caption{font-size:2em;font-weight:bold;margin:1em 0}th,td{border:1px solid#ccc;text-align:center;padding:15px;transition:background-color 0.3s}thead tr{background-color:#007BFF;color:#fff}tbody tr:nth-child(odd){background-color:#f9f9f9}tbody tr:hover{background-color:#e2e6ea}tfoot tr td{text-align:right;padding-right:20px}th{border-bottom:2px solid#007BFF}</style> </head><body><table border='1'><tr><th>姓名</th><th>公司代码</th><th>次数</th></tr>"
htme = "</table></body></html>"
content = ""
for key in times_report:
key_report = times_report[key]
name = key_report["name"]
key_report_list = [(i, key_report[i]) for i in key_report if i != "name"]
size = len(key_report_list)
for idx, key_info in enumerate(key_report_list):
if idx == 0:
content += f"<tr><td rowspan='{size}'>{name}</td><td>{key_info[0]}</td><td>{key_info[1]}</td></tr>"
else:
content += f"<tr><td>{key_info[0]}</td><td>{key_info[1]}</td></tr>"
return htms + content + htme
def new_product_print(info):
url = "https://cloud.cmdp.cn/api/product/print/customize"
body = {"userId": info["user_id"], "secCode": info["company_code"], "prodName": info["service_name"],
"opUserId": info["_uid"]}
r = requests.post(url, json=body)
return r.json()
def main_handler(event, context):
data = json.loads(event.get("body"))
template_id = data.get("template_id", "C4ZT8ZuPRbKC76KmWCrmRAKyVdneoq9wh1ESaUrrJ")
if data.get("action") == "get_approval":
result = get_approval(template_id)
result = generate_approval_report_html(result)
address = data.get("address", "lychang@genesysinfo.net")
st.send_company_mail(address, "客户拜访审批次数报表", result)
return {"code": 200, "msg": "success"}
elif data.get("action") == "otp":
data_list = data.get("data",[{}])
sta = SmartTableApi("dcqzSDHPl2ZWoLkEB3Id1wMMaW0meMSwGLjfnuHD0VMh0DVidKdal-3wYfsh7Zb1pGn9moDuzjvhtlRZAXoO-Hjg")
mapping = {
"userName": "用户名称",
"secCode": "公司代码",
"companyName": "公司名称",
"count": "使用动态口令次数",
"date": "时间",
"goodName": "产品"
}
data_list = [transform_data(i, mapping) for i in data_list]
cursor = sta.get_sheet("g6dTI2")
if cursor.batch_add(data_list):
return {"code": 200, "msg": "success"}
else:
return {"code": 500, "msg": "error"}
elif data.get("action") == "ip":
resp = requests.get("http://cip.cc")
return resp.text
elif template_id == "C4ZT8ZuPRbKC76KmWCrmRAKyVdneoq9wh1ESaUrrJ":
info = parse_data(data, {"Number-1697103952657": "company_code", "Text-1697103919621": "company_name",
"Text-1728374569134": "company_address", "Date-1728370374837": "visit_time",
"Date-1728370485478": "end_time"})
ceb.login(info["_uid"])
result = ceb.add(info)
return {"code": 200, "msg": "success"}
elif template_id == "3WMWPcxrt28TRT72wqH74KSjMDBhmZPJguv1VEZZ":
info = parse_data(data, {
"Selector-1688785038239": "service_name",
"Number-1661195930200": "company_code",
"Number-1727161672205": "user_id"
})
new_product_print(info)
return {"code": 200, "msg": "success"}
else:
return {"code": 400, "msg": "not found"}

516
lib.py Normal file
View File

@@ -0,0 +1,516 @@
# !/usr/bin/env python
# -*- coding:utf-8 -*-
import time
import requests
class WXworkAuth:
def __init__(self, app_id, app_key):
self.crop_id = app_id
self.crop_secret = app_key
self.access_token = None
self.timestamp = None
def _get_access_token(self):
self.timestamp = int(time.time())
url = f"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={self.crop_id}&corpsecret={self.crop_secret}"
response = requests.get(url)
access_info = response.json()
if 'access_token' in access_info:
self.access_token = access_info['access_token']
else:
raise Exception('获取access_token失败')
def get_access_token(self):
_now = int(time.time())
if not self.access_token or (_now - self.timestamp) > 7200:
self.refresh_token()
return self.access_token
def refresh_token(self):
try:
self._get_access_token()
except Exception as e:
print(e, '重新获取')
self._get_access_token()
class CloudFunctionAPI:
def __init__(self, base_url):
self.base_url = base_url
def get_user_info_by_id(self, user_id: str) -> dict:
url = f"{self.base_url}/in/api/user"
body = {
"uid": user_id
}
response = requests.get(url, params=body)
if response.status_code != 200:
return {}
user_info = response.json().get('data', {})
return user_info
def get_user_info_by_sec(self, security_code: str) -> list:
url = f"{self.base_url}/in/api/userQuery"
body = {
"secCode": security_code,
"status": 10,
"PageSize": 10,
"PageNo": 1
}
response = requests.post(url, json=body)
if response.status_code != 200:
return []
user_info = response.json().get("list")
if user_info is None:
user_info = [{}]
return user_info
def get_user_info_by_name(self, user_name: str) -> dict:
url = f"{self.base_url}/in/api/userQuery"
body = {
"loginName": user_name,
"status": 10,
"PageSize": 10,
"PageNo": 1
}
response = requests.post(url, json=body)
if response.status_code != 200:
return {}
user_info = response.json().get("list")
if user_info is None:
user_info = {}
else:
user_info = user_info[0]
return user_info
@staticmethod
def parse_tags(tags: list):
def _parse_tag(_tag: str):
return _tag.split("_")
temp_dict = {}
for tag in tags:
tag_split = _parse_tag(tag)
size = len(tag_split)
cursor = None
for idx, i in enumerate(tag_split):
if idx == 0:
cursor = temp_dict
val = {} if idx < size - 1 else True
cursor[i] = val
if isinstance(val,bool):
cursor = None
else:
cursor = cursor[i]
print(temp_dict)
return temp_dict
def get_user_tags(self, user_id: str = "", user_name: str = "") -> dict:
if user_id:
user_info = self.get_user_info_by_id(user_id)
elif user_name:
user_info = self.get_user_info_by_name(user_name)
else:
return {}
tags = user_info.get('tags', [])
return self.parse_tags(tags)
def update_user_tag(self, user_id: str, key: str, value: str):
url = f"{self.base_url}/in/api/userTags"
body = {
"userId": user_id,
"tags": [f"{key}_{value}"]
}
response = requests.post(url, json=body)
if response.status_code != 200:
return {}
user_info = response.json()
return user_info
def update_user_tag_by_name(self, user_name: str, key: str, value: str):
user_info = self.get_user_info_by_name(user_name)
user_id = user_info.get('id')
if not user_id:
return {}
return self.update_user_tag(user_id, key, value)
def update_user_tags(self, user_id: str, tags: dict):
url = f"{self.base_url}/in/api/userTags"
body = {
"userId": user_id,
"tags": [f"{key}_{value}" for key, value in tags.items()]
}
response = requests.post(url, json=body)
if response.status_code != 200:
return {}
user_info = response.json()
return user_info
class WXworkBaseAPI:
def __init__(self, auth: WXworkAuth):
self.auth = auth
@property
def access_token(self):
return self.auth.get_access_token()
class WXworkApprovalAPI(WXworkBaseAPI):
def get_approval_list(self, template_id: str, start_time: int, end_time: int) -> list:
url = f"https://qyapi.weixin.qq.com/cgi-bin/oa/getapprovalinfo?access_token={self.access_token}"
body = {
"starttime": str(start_time),
"endtime": str(end_time),
"new_cursor": "",
"size": 100,
"filters": [
{
"key": "template_id",
"value": template_id
},
{
"key": "sp_status",
"value": "2"
}
]
}
response = requests.post(url, json=body)
if response.status_code != 200:
return []
else:
approval_info = response.json()
return approval_info["sp_no_list"]
def get_approval_info(self, approval_id: str) -> dict:
url = f"https://qyapi.weixin.qq.com/cgi-bin/oa/getapprovaldetail?access_token={self.access_token}"
body = {
"sp_no": approval_id
}
response = requests.post(url, json=body)
approval_info = response.json()
return approval_info
def get_approval_list_weekly(self, template_id) -> list:
current_time = time.time()
# 获取当前时间的结构体
current_local_time = time.localtime(current_time)
# 星期几0=星期一6=星期天)
weekday = current_local_time.tm_wday
# 计算下一个星期天的天数
days_until_sunday = (6 - weekday) % 7
this_weekday = int(current_time + (days_until_sunday * 86400))
last_weekday = int(this_weekday - 7 * 24 * 3600)
return self.get_approval_list(template_id, last_weekday, this_weekday)
class WXworkCalendarAPI(WXworkBaseAPI):
def create_calendar(self, calendar_name, userid):
url = f"https://qyapi.weixin.qq.com/cgi-bin/oa/calendar/add?access_token={self.access_token}"
body = {
"calendar": {
"admins": [
userid
],
"set_as_default": 1,
"summary": calendar_name,
"color": "#FF3030",
"description": "访问日历",
"is_public": 0,
"is_corp_calendar": 0,
"shares": [
{
"userid": userid,
"permission": 1
},
],
}
}
response = requests.post(url, json=body)
cinfo = response.json()
if "cal_id" in cinfo:
return cinfo["cal_id"]
else:
return cinfo
def delete_calendar(self, calendar_id):
url = f"https://qyapi.weixin.qq.com/cgi-bin/oa/calendar/del?access_token={self.access_token}"
body = {
"cal_id": calendar_id
}
response = requests.post(url, json=body)
calendar_info = response.json()
return calendar_info
def get_calendar_list(self, calendar_id):
url = f"https://qyapi.weixin.qq.com/cgi-bin/oa/schedule/get_by_calendar?access_token={self.access_token}"
body = {
"cal_id": calendar_id,
"offset": 0,
"limit": 1000
}
response = requests.post(url, json=body)
calendar_list = response.json()
return calendar_list
def create_schedule(self, calendar_id, schedule_name, start_time, end_time, user_id, content, address=""):
url = f"https://qyapi.weixin.qq.com/cgi-bin/oa/schedule/add?access_token={self.access_token}"
if start_time == end_time:
end_time = str(int(start_time) + 1)
body = {
"schedule": {
"admins": [
user_id
],
"start_time": start_time,
"end_time": end_time,
"is_whole_day": 0,
"attendees": [{
"userid": user_id
}],
"summary": schedule_name,
"description": content,
"reminders": {
"is_remind": 1,
"remind_before_event_secs": 3600,
"timezone": 8
},
"location": address,
"cal_id": calendar_id
}
}
response = requests.post(url, json=body)
schedule_info = response.json()
return schedule_info
def get_schedule(self, schedule_id):
url = f"https://qyapi.weixin.qq.com/cgi-bin/oa/schedule/get?access_token={self.access_token}"
body = {
"schedule_id_list": [
schedule_id
]
}
response = requests.post(url, json=body)
schedule_list = response.json()
return schedule_list
def delete_schedule(self, schedule_id):
url = f"https://qyapi.weixin.qq.com/cgi-bin/oa/schedule/del?access_token={self.access_token}"
body = {
"schedule_id": schedule_id
}
response = requests.post(url, json=body)
schedule_info = response.json()
return schedule_info
class WXworkUserInfoAPI(WXworkBaseAPI):
def get_user_info(self, user_id):
url = f"https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token={self.access_token}&userid={user_id}"
response = requests.get(url)
user_info = response.json()
return user_info
class WXworkDocAPI:
def __init__(self, auth:WXworkAuth,space_id):
self.space_id = space_id
self.auth = auth
self.access_token = self.auth.get_access_token()
def create_space(self, space_name):
url = f"https://qyapi.weixin.qq.com/cgi-bin/wedrive/space_create?access_token={self.access_token}"
body = {
"space_name": space_name,
"auth_info": [{
"type": 1,
"userid": "lychang",
"auth": 7
}],
"space_sub_type": 0
}
response = requests.post(url, json=body)
if response.status_code != 200:
return {}
else:
approval_info = response.json()
return approval_info
def get_space_info(self):
url = f"https://qyapi.weixin.qq.com/cgi-bin/wedrive/space_info?access_token={self.access_token}"
body = {
"spaceid": self.space_id
}
response = requests.post(url, json=body)
if response.status_code != 200:
return {}
else:
approval_info = response.json()
return approval_info
def get_space_file_list(self):
url = f"https://qyapi.weixin.qq.com/cgi-bin/wedrive/file_list?access_token={self.access_token}"
body = {
"spaceid": self.space_id,
"fatherid": self.space_id,
"sort_type": 1,
"start": 0,
"limit": 100
}
response = requests.post(url, json=body)
if response.status_code != 200:
return {}
else:
approval_info = response.json()
return approval_info
def get_document_info(self,doc_id):
self.doc_id = doc_id
url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/document/get?access_token={self.access_token}"
body = {
"docid": self.doc_id
}
response = requests.post(url, json=body)
if response.status_code != 200:
return {}
else:
approval_info = response.json()
return approval_info
def get_table_info(self,doc_id, sheet_id, view_ids: list):
url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/smartsheet/get_views?access_token={self.access_token}"
body = {
"docid": doc_id,
"sheet_id": sheet_id,
"view_ids": view_ids,
"offset": 0,
"limit": 1
}
response = requests.post(url, json=body)
if response.status_code != 200:
return {}
else:
approval_info = response.json()
return approval_info
def get_table_data(self, doc_id, sheet_id, view_id):
url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/smartsheet/get_records?access_token={self.access_token}"
body = {
"docid": doc_id,
"sheet_id": sheet_id,
"view_id": view_id,
"record_ids": [],
"key_type": "CELL_VALUE_KEY_TYPE_FIELD_TITLE",
"field_titles": [],
"field_ids": [],
"sort": [],
"offset": 0,
"limit": 100
}
response = requests.post(url, json=body)
if response.status_code != 200:
return {}
else:
approval_info = response.json()
return approval_info
def get_sheet_data(self, doc_id):
url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/smartsheet/get_sheet?access_token={self.access_token}"
body = {
"docid": doc_id
}
response = requests.post(url, json=body)
if response.status_code != 200:
return {}
else:
approval_info = response.json()
return approval_info
def create_table(self, doc_name, user_ids: list, father_id=None):
"""
doc_type 文档类型, 3:文档 4:表格 10:智能表格
"""
url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/create_doc?access_token={self.access_token}"
body = {
"doc_type": 10,
"doc_name": doc_name,
"admin_users": user_ids
}
if self.space_id:
body["spaceid"] = self.space_id
if father_id:
body["fatherid"] = father_id
else:
body["fatherid"] = self.space_id
response = requests.post(url, json=body)
if response.status_code != 200:
return {}
else:
approval_info = response.json()
return approval_info
def add_row(self,doc_id,sheet_id,records:list):
url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/smartsheet/add_records?access_token={self.access_token}"
body = {
"docid": doc_id,
"sheet_id": sheet_id,
"key_type": "CELL_VALUE_KEY_TYPE_FIELD_TITLE",
"records": records
}
response = requests.post(url, json=body)
if response.status_code != 200:
return {}
else:
approval_info = response.json()
return approval_info
def parse_data(data: dict, mapping: dict) -> dict:
user_id = data.get("applyer").get("userid")
app_data = data.get("apply_data").get("contents")
_info = {}
for i in app_data:
key = i.get("title")[0]["text"]
cp_id = i.get("id")
control = i.get("control")
value = i.get("value")
if control == "Text":
result = value.get("text")
elif control == "Number":
result = value.get("new_number")
elif control == "Date":
result = value.get("date").get("s_timestamp")
elif control == "Selector":
_t = value.get("selector").get("type")
if _t == "multi":
opts = value.get("selector").get("options",[])
result = [j["key"] for j in opts]
else:
opts = i.get("selector").get("options", [])
result = opts[0]["key"]
else:
result = ""
_info[cp_id] = {"key": key, "value": result}
_r = {v: _info.get(k, {}).get("value") for k, v in mapping.items()}
_r["_uid"] = user_id
return _r

62743
package.json Normal file

File diff suppressed because it is too large Load Diff

33
serverless.yml Normal file
View File

@@ -0,0 +1,33 @@
app: demo
stage: dev
name: scf-python
component: scf
inputs:
src: ./
name: ${name}-${stage}-${app} # 云函数名称,默认为 ${name}-${stage}-${app}
description: Calendar API Serverless Function
handler: index.main_handler
runtime: Python3.6
namespace: default
region: ap-guangzhou
type: event # 函数类型,默认为 event(事件类型)web(web类型)
memorySize: 128 # 内存大小单位MB
timeout: 900 # 函数执行超时时间,单位秒
initTimeout: 300 # 初始化超时时间,单位秒
vpcConfig: # 私有网络配置
vpcId: vpc-q1m7ajxh # 私有网络的Id
subnetId: subnet-9ehoo5vi # 子网ID
events: # 触发器
- http:
parameters:
qualifier: $LATEST # 别名配置
enable: true # 是否启用此触发器
timeout: 300 # 触发器超时时间,单位秒
authType: NONE # 身份认证
netConfig:
enableExtranet: true # 开启外网访问
enableIntranet: true # 开启内网访问

20
st/a.py Normal file
View File

@@ -0,0 +1,20 @@
import pandas as pd
if __name__ == '__main__':
df = pd.read_excel("result1.xlsx",dtype="object")
df.fillna("", inplace=True)
data = []
for i in df.values:
cnt = ""
for j in i[4].split("\n"):
content = j.replace("nan"," ")
content = content.replace(f"母公司代码为{i[0]}的公司",f"{i[1]}")
content= content.replace("用户类型: 付费, 三级",f"- 母公司名称: {i[1]}")
cnt += content + "\n"
if "用户类型" in cnt:
print(cnt)
i[4] = cnt
data.append(i)
df2 = pd.DataFrame(data,columns=df.columns)
df2.to_excel("result1.xlsx",index=False)

BIN
st/amd.xlsx Normal file

Binary file not shown.

BIN
st/c.xlsx Normal file

Binary file not shown.

23
st/hunyuan.py Normal file
View File

@@ -0,0 +1,23 @@
from openai import OpenAI
if __name__ == '__main__':
def chat(message):
# 构造 client
client = OpenAI(
api_key="sk-2NpKKyU5savpy6xQ4pEV0Bv63xCnSdsfnrDcFg9FXlwTjovD", # 混元 APIKey
base_url="https://api.hunyuan.cloud.tencent.com/v1", # 混元 endpoint
)
completion = client.chat.completions.create(
model="hunyuan-turbos-latest",
messages=[
{
"role": "user",
"content": message
}
],
extra_body={
"enable_enhancement": True, # <- 自定义参数
},
)
return completion.choices[0].message.content

BIN
st/m.xlsx Normal file

Binary file not shown.

BIN
st/result.xlsx Normal file

Binary file not shown.

BIN
st/result【re】.xlsx Normal file

Binary file not shown.

70
st/st.py Normal file
View File

@@ -0,0 +1,70 @@
import pandas as pd
from openai import OpenAI
def chat(message):
# 构造 client
client = OpenAI(
api_key="3N7zycBwsadKjvXBZeAZFVc1VnUJ7yLbbFie5zan5kleBrkZ9BzZkhkFjk2KxZUxs", # 混元 APIKey
base_url="https://api.stepfun.com/v1"
)
completion = client.chat.completions.create(
model="step-2-mini",
messages=[
{
"role": "user",
"content": message
}
],
extra_body={
"enable_enhancement": True, # <- 自定义参数
},
)
return completion.choices[0].message.content
if __name__ == '__main__':
df = pd.read_excel("公告类别基本资料2025-1-3【深主板】.xls",dtype="object")
df.fillna("", inplace=True)
resource = ""
notice = ""
result = []
output = ""
temp = ["", "", "", "", "", "", "", ""]
try:
for i in df.values:
a,b,c,d,e,f,g,h =i
if f != "":
if temp[5] == "":
temp[4] = e
temp[5] = f
if f != temp[5]:
temp[6] = resource
temp[7] = notice
prompt = "以下内容是上市公司做信息披露时的要点信息在尽量小幅修改内容和保留引用文件完整名称的情况下请将其内容转换成markdown格式综合使用标题设置、多彩字体颜色、字体加粗和生动icon等方式来进行格式化设置突出重点提示内容方便用户阅读。只返回markdown内容。\n" + notice
# temp[7] = chat(prompt).replace("```markdown\n", "").replace("```", "")
result.append(temp.copy())
temp[4] = e
temp[5] = f
resource = ""
notice = ""
if b !="":
temp[0] = a
temp[1] = b
if d !="":
temp[2] = c
temp[3] = d
resource += g+"\n" if g else ""
notice += h+"\n" if h else ""
except Exception:
pass
df = pd.DataFrame(result,columns=["公告大类编码","公告大类名称","公告中类编码","公告中类名称","公告子类编码","公告子类名称","报批材料","披露要点"])
df.to_excel("c.xlsx")

0
st/tst.py Normal file
View File

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

175
template.html Normal file
View File

@@ -0,0 +1,175 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset=UTF-8>
<style>
h4 {
text-align: left;
}
@media print {
.headerLineTitle {
width: 1.5in;
display: inline-block;
margin: 0 0 0.0001pt;
font-size: 11pt;
font-family: "Calibri", "sans-serif";
font-weight: bold;
}
.headerLineText {
display: inline;
margin: 0 0 0.0001pt;
font-size: 11pt;
font-family: "Calibri", "sans-serif";
font-weight: normal;
}
.pageHeader {
font-size: 14pt;
font-family: "Calibri", "sans-serif";
font-weight: bold;
visibility: visible;
display: block;
}
}
</style>
<title>email</title>
</head>
<body>
<div style="font-size: 15px; font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Microsoft YaHei',serif;"
data-mail-from="wemail-pc">
<div><span style="background-color: rgb(255, 255, 255);"><spanstyle="font-size:
11pt;">&nbsp;尊敬的xxx</span></span></div>
<div><span style="background-color: rgb(255, 255, 255);"><span style="font-size: 11pt;"><br></span></span></div>
<div style="margin: 0;padding: 0;position: relative;">
<div style="margin-bottom:0; padding-bottom:0;">
<div style="margin: 0;padding-bottom:0;">
<div style="margin-bottom:0; padding-bottom:0;">
<div style="margin: 0 10px;padding-bottom:0;">
<div style="position: relative; margin-bottom:0; line-height: 1.859;"><span
style="background-color: rgb(255, 255, 255);">&nbsp; &nbsp; &nbsp;<span
style="font-size: 11pt; background-color: rgb(255, 255, 255);">您好!我是致远速联的【具体人员姓名】。很高兴即将与您交流见面。如今交易所的监管愈发的严格,信息披露合规风险的管控愈发关键。</span></span><span
style="font-size: 11pt;">我们深知您对于这方面的重视,因此此次交流旨在与您</span><span
style="font-size: 11pt; color: rgb(38, 126, 240);">探讨如何巧妙运用技术手段达成有效的风险规避</span><span
style="font-size: 11pt;">。期望能为您带来有价值的信息和解决方案!</span></div>
</div>
</div>
</div>
</div>
</div>
<div style="margin: 0;padding: 0;position: relative;">
<div style="margin-bottom:0; padding-bottom:0; margin-top:0; padding-top:0;">
<div style="margin: 0;padding-bottom:0; padding-top:0;">
<div style="margin-bottom:0; padding-bottom:0; margin-top:0; padding-top:0;">
<div style="margin: 0 10px;padding-bottom:0; padding-top:0;">
<div
style="position: relative; margin-bottom:0; font-family: 'Microsoft YaHei',serif; font-size: 11pt; line-height: 1.859; background-color: transparent; color: rgb(0, 0, 0);">
<span
style="background-color: transparent; font-family: 'Microsoft YaHei',serif; font-size: 11pt; color: rgb(0, 0, 0);"><span
style="font-size: 11pt; background-color: transparent; font-family: 'Microsoft YaHei',serif; color: rgb(0, 0, 0);"><span
style="font-size: 11pt; background-color: rgb(255, 255, 255); font-family: 'Microsoft YaHei',serif; font-weight: normal; font-style: normal; text-decoration-line: none; color: rgb(0, 0, 0);"><span
style="background-color: rgb(255, 255, 255);">&nbsp; &nbsp;
&nbsp;</span>在正式拜访您之前,我们已经提前准备了相关资料供您查看,以方便您更好的了解我们的产品。如果您在下载的过程中遇到任何问题,请随时与我们联系。我们的客服团队将竭诚为您服务。感谢您对我们公司的信任与支持!</span></span></span>
</div>
<div
style="position: relative; margin-bottom:0; font-family: 'Microsoft YaHei',serif; font-size: 11pt; line-height: 1.859; background-color: transparent; color: rgb(0, 0, 0);">
<span
style="background-color: transparent; font-family: 'Microsoft YaHei',serif; font-size: 11pt; color: rgb(0, 0, 0);"><span
style="font-size: 11pt; background-color: transparent; font-family: 'Microsoft YaHei',serif; color: rgb(0, 0, 0);"><span
style="font-size: 11pt; background-color: rgb(255, 255, 255); font-family: 'Microsoft YaHei',serif; font-weight: normal; font-style: normal; text-decoration-line: none; color: rgb(0, 0, 0);"><br></span></span></span>
</div>
</div>
</div>
</div>
</div>
</div>
<div style="margin: 0;padding: 0;position: relative;">
<div style="margin-bottom:0; padding-bottom:0; margin-top:0; padding-top:0;">
<div style="margin: 0;padding-bottom:0; padding-top:0;">
<div style="margin-bottom:0; padding-bottom:0; margin-top:0; padding-top:0;">
<div style="margin: 0 10px;padding-bottom:0; padding-top:0;">
<div
style="position: relative; margin-bottom:0; font-family: 'Microsoft YaHei',serif; font-size: 11pt; line-height: 1.859; background-color: transparent; color: rgb(0, 0, 0);">
<b
style="color: rgb(0, 0, 0); font-size: 11pt; font-family: 'Microsoft YaHei',serif; background-color: transparent;"><span
style="font-size: 16.001pt; color: rgb(38, 126, 240); font-family: 'Microsoft YaHei',serif; background-color: transparent;"><span
style="color: rgb(38, 126, 240); font-size: 16.001pt; background-color: rgb(255, 255, 255); font-family: 'Microsoft YaHei',serif; font-style: normal; text-decoration-line: none;">画像报告</span></span></b>
</div>
<div
style="position: relative; margin-bottom:0; font-family: 'Microsoft YaHei',serif; font-size: 11pt; line-height: 1.859; background-color: transparent; color: rgb(0, 0, 0);">
<b
style="background-color: transparent; color: rgb(38, 126, 240); font-size: 16.001pt;"><span
style="font-size: 16.001pt;"><a href="{{report_ticket}}" target="_blank"
style="color: rgb(38, 126, 240); text-decoration-line: underline; outline-style: none; cursor: pointer; font-size: 16px; font-weight: 400;">{{report_ticket}}</a></span></b>
</div>
</div>
</div>
</div>
</div>
</div>
<div style="margin: 0;padding: 0;position: relative;">
<div style="margin-bottom:0; padding-bottom:0; margin-top:0; padding-top:0;">
<div style="margin: 0;padding-bottom:0; padding-top:0;">
<div style="margin-bottom:0; padding-bottom:0; margin-top:0; padding-top:0;">
<div style="margin: 0 10px;padding-bottom:0; padding-top:0;">
<div
style="position: relative; margin-bottom:0; font-family: 'Microsoft YaHei',serif; font-size: 16.001pt; line-height: 1.859; background-color: transparent; color: rgb(38, 126, 240);">
<b
style="background-color: transparent; font-size: 16.001pt; margin-bottom:0; padding-bottom:0;"><span
style="font-size: 16pt;">往期校验报告</span></b></div>
<div
style="position: relative; margin-bottom:0; font-family: 'Microsoft YaHei',serif; font-size: 16.001pt; line-height: 1.859; background-color: transparent; color: rgb(38, 126, 240);">
<b
style="background-color: transparent; font-size: 16.001pt; margin-top:0; padding-top:0; margin-bottom:0; padding-bottom:0;">{{regression_testing}}</b>
</div>
</div>
</div>
</div>
</div>
</div>
<div style="margin: 0;padding: 0;position: relative;">
<div style="margin-bottom:0; padding-bottom:0; margin-top:0; padding-top:0;">
<div style="margin: 0;padding-bottom:0; padding-top:0;">
<div style="margin-bottom:0; padding-bottom:0; margin-top:0; padding-top:0;">
<div style="margin: 0 10px;padding-bottom:0; padding-top:0;">
<div
style="position: relative; margin-bottom:0; font-family: 'Microsoft YaHei',serif; font-size: 16.001pt; line-height: 1.859; background-color: transparent; color: rgb(38, 126, 240); margin-top:0; padding-top:0;">
<b style="color: rgb(38, 126, 240); font-size: 16.001pt; margin-top:0; padding-top:0;"><span
style="color: rgb(38, 126, 240); font-size: 16pt; font-weight: bold; background-color: rgb(255, 255, 255); font-family: 'Microsoft YaHei',serif; font-style: normal; text-decoration-line: none;">CMDP智能董办手册</span><br
style="color: rgb(0, 0, 0); font-size: 16px; font-weight: 400; background-color: rgb(255, 255, 255);"></b>
</div>
<div
style="position: relative; margin-bottom:0; font-family: 'Microsoft YaHei',serif; font-size: 16.001pt; line-height: 1.859; background-color: transparent; color: rgb(38, 126, 240); margin-top:0; padding-top:0;">
<b style="color: rgb(38, 126, 240); font-size: 16.001pt; margin-top:0; padding-top: 0;"><a
href="https://a.cmdp.cn/v1/cloudfile/file/?ticket=LCXjyksIA" target="_blank"
style="color: rgb(38, 126, 240); text-decoration-line: underline; outline-style: none; cursor: pointer; font-size: 16px; font-weight: 400; background-color: rgb(255, 255, 255);">https://a.cmdp.cn/v1/cloudfile/file/?ticket=LCXjyksIA</a><br
style="color: rgb(0, 0, 0); font-size: 16px; font-weight: 400; background-color: rgb(255, 255, 255);"></b>
</div>
<!-- <div
style="position: relative; margin-bottom:0; font-family: 'Microsoft YaHei',serif; font-size: 16.001pt; line-height: 1.859; background-color: transparent; color: rgb(38, 126, 240); margin-top:0; padding-top:0;">
<br></div>
<div
style="position: relative; margin-bottom:0; font-family: 'Microsoft YaHei',serif; font-size: 16pt; line-height: 1.859; background-color: transparent; color: rgb(38, 126, 240); margin-top:0; padding-top:0;">
<span
style="font-family: 'Microsoft YaHei',serif; font-size: 16pt; font-weight: bold; font-style: normal; text-decoration-line: none; color: rgb(38, 126, 240); background-color: rgb(255, 255, 255);">2023年年报小智智填-定期报告专版产品试用</span>
</div>
<div
style="position: relative; margin-bottom:0; font-family: 'Microsoft YaHei',serif; font-size: 16pt; line-height: 1.859; background-color: transparent; color: rgb(38, 126, 240); margin-top:0; padding-top:0;">
<span
style="font-family: 'Microsoft YaHei',serif; font-size: 16pt; font-weight: bold; font-style: normal; text-decoration-line: none; color: rgb(38, 126, 240); background-color: rgb(255, 255, 255);"><img
id="7100638799184082" src="data:application/octet-stream;base64,{{qrcode}}"
style="vertical-align: bottom; width: 399px; height: 354px; max-width: initial;"
width="399" height="354" alt="产品试用"></span></div> -->
<div>{{picture}}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

31
test.py Normal file
View File

@@ -0,0 +1,31 @@
import datetime
import pytz
# 时间字符串转UTC时间的函数
def convert_to_utc(time_str, time_format='%Y-%m-%d %H:%M:%S', source_timezone=None):
# 解析时间字符串为datetime对象
local_dt = datetime.datetime.strptime(time_str, time_format)
# 如果指定了源时区则将本地时间转换为UTC时间
if source_timezone:
tz = pytz.timezone(source_timezone)
local_dt = tz.localize(local_dt)
utc_dt = local_dt.astimezone(pytz.UTC)
else:
# 如果没有指定源时区则假设时间字符串已经是UTC时间
utc_dt = pytz.UTC.localize(local_dt)
return utc_dt
# 示例用法
time_str = '2024-05-20 14:30:00'
# 假设时间字符串是北京时间(UTC+8)
utc_time = convert_to_utc(time_str, source_timezone='Asia/Shanghai')
print(convert_to_utc(time_str).timestamp())
print(f'UTC时间: {utc_time.strftime("%Y-%m-%d %H:%M:%S")}')
# 如果需要获取UTC时间戳
timestamp = utc_time.timestamp()
print(f'UTC时间戳: {timestamp}')

284
xp/test.py Normal file

File diff suppressed because one or more lines are too long