chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
# Celery 的broker配置。存储ft指令执行结果。
|
||||
# 密码加密规则和backend一致, 暂不支持集群redis
|
||||
redis_url: "redis://bisheng-redis:6379/5"
|
||||
@@ -0,0 +1,57 @@
|
||||
# 可根据loguru的文档配置不同 handlers
|
||||
logger_conf:
|
||||
# 默认输出到控制台的日志级别, 大于等于此级别都会输出
|
||||
level: DEBUG
|
||||
# 默认输出格式
|
||||
format: '[{time:YYYY-MM-DD HH:mm:ss.SSSSSS}] [{level.name} process-{process.id}-{thread.id} {name}:{line}] - trace={extra[trace_id]} {message}'
|
||||
# 参考loguru.add()中的参数可以配置多个handler
|
||||
handlers:
|
||||
# 文件路径,支持插入一些系统环境变量,若环境变量不存在则置空。例如 HOSTNAME: 主机名。后端会处理环境变量的替换
|
||||
- sink: "/app/logs/bisheng_uns.log"
|
||||
# 日志级别
|
||||
level: INFO
|
||||
# 和原生不一样,后端会将配置使用eval()执行转为函数用来过滤特定日志级别。推荐lambda
|
||||
# filter: "lambda record: record['level'].name == 'INFO'"
|
||||
# 日志格式化函数,extra内支持trace_id
|
||||
format: "[{time:YYYY-MM-DD HH:mm:ss.SSSSSS}]|{level}|BISHENG|{extra[trace_id]}|{process.id}|{thread.id}|{message}"
|
||||
# 每天的几点进行切割
|
||||
rotation: "00:00"
|
||||
retention: "3 Days"
|
||||
- sink: "/app/logs/err-v0-BISHENG-UNS-{HOSTNAME}.log"
|
||||
level: ERROR
|
||||
filter: "lambda record: record['level'].name == 'ERROR'"
|
||||
format: "[{time:YYYY-MM-DD HH:mm:ss.SSSSSS}]|{level}|BISHENG|{extra[trace_id]}||{process.id}|{thread.id}|||#EX_ERR:POS={name},line {line},ERR=500,EMSG={message}"
|
||||
rotation: "00:00"
|
||||
retention: "3 Days"
|
||||
|
||||
# pdf解析需要用到的模型配置, 配置了rt_server环境变量的话会替换为对应的地址
|
||||
pdf_model_params:
|
||||
layout_ep: "http://192.168.106.12:9001/v2.1/models/elem_layout_v1/infer"
|
||||
cell_model_ep: "http://192.168.106.12:9001/v2.1/models/elem_table_cell_detect_v1/infer"
|
||||
rowcol_model_ep: "http://192.168.106.12:9001/v2.1/models/elem_table_rowcol_detect_v1/infer"
|
||||
table_model_ep: "http://192.168.106.12:9001/v2.1/models/elem_table_detect_v1/infer"
|
||||
ocr_model_ep: "http://192.168.106.12:9001/v2.1/models/elem_ocr_collection_v3/infer"
|
||||
|
||||
# 是否全部走ocr识别, false的话则由代码逻辑判断是否需要走ocr识别
|
||||
is_all_ocr: false
|
||||
# ocr识别需要的配置项
|
||||
ocr_conf:
|
||||
params:
|
||||
sort_filter_boxes: true,
|
||||
enable_huarong_box_adjust: true,
|
||||
rotateupright: false,
|
||||
support_long_image_segment: true,
|
||||
split_long_sentence_blank: true
|
||||
scene_mapping:
|
||||
print:
|
||||
det: general_text_det_mrcnn_v2.0
|
||||
recog: transformer-blank-v0.2-faster
|
||||
hand:
|
||||
det: general_text_det_mrcnn_v2.0
|
||||
recog: transformer-hand-v1.16-faster
|
||||
print_recog:
|
||||
recog: transformer-blank-v0.2-faster
|
||||
hand_recog:
|
||||
recog: transformer-hand-v1.16-faster
|
||||
det:
|
||||
det: general_text_det_mrcnn_v2.0
|
||||
@@ -0,0 +1,87 @@
|
||||
# 数据库配置, 当前加密串的密码是1234,
|
||||
# 密码加密参考 https://dataelem.feishu.cn/wiki/BSCcwKd4Yiot3IkOEC8cxGW7nPc#Gxitd1xEeof1TzxdhINcGS6JnXd
|
||||
database_url:
|
||||
"mysql+pymysql://root:gAAAAABlp4b4c59FeVGF_OQRVf6NOUIGdxq8246EBD-b0hdK_jVKRs1x4PoAn0A6C5S6IiFKmWn0Nm5eBUWu-7jxcqw6TiVjQA==@mysql:3306/bisheng?charset=utf8mb4"
|
||||
|
||||
# 缓存配置 redis://[[username]:[password]]@localhost:6379/0
|
||||
# 如果设置了密码,需要参考MySQL密码的加密逻辑对密码进行加密。eg: redis://root:gAAAAABlp4b4c59FeVGF_OQRVf6NOUIGdxq8246EBD-b0hdK_jVKRs1x4PoAn0A6C5S6IiFKmWn0Nm5eBUWu-7jxcqw6TiVjQA==@redis:6379/0
|
||||
# 普通模式:
|
||||
redis_url: "redis://redis:6379/1"
|
||||
|
||||
# 集群模式或者哨兵模式(只能选其一):
|
||||
# redis_url:
|
||||
# mode: "cluster"
|
||||
# startup_nodes:
|
||||
# - {"host": "192.168.106.115", "port": 6002}
|
||||
# password: encrypt(gAAAAABlp4b4c59FeVGF_OQRVf6NOUIGdxq8246EBD-b0hdK_jVKRs1x4PoAn0A6C5S6IiFKmWn0Nm5eBUWu-7jxcqw6TiVjQA==)
|
||||
# #sentinel
|
||||
# mode: "sentinel"
|
||||
# sentinel_hosts: [("redis", 6379)]
|
||||
# sentinel_master: "mymaster"
|
||||
# sentinel_password: encrypt(gAAAAABlp4b4c59FeVGF_OQRVf6NOUIGdxq8246EBD-b0hdK_jVKRs1x4PoAn0A6C5S6IiFKmWn0Nm5eBUWu-7jxcqw6TiVjQA==)
|
||||
# db: 1
|
||||
|
||||
# celery的broken地址
|
||||
celery_redis_url: "redis://redis:6379/2"
|
||||
celery_task:
|
||||
# 对celery熟悉的用户可以自定义配置任务的路由,启动不同类型的worker处理不同类型的异步任务。注意工作流的执行只能在一个进程内!!!
|
||||
task_routers:
|
||||
bisheng.worker.knowledge.*: # 知识库文件处理相关任务
|
||||
queue: knowledge_celery
|
||||
bisheng.worker.workflow.*: # 工作流相关任务
|
||||
queue: workflow_celery
|
||||
|
||||
# 知识库的milvus和es配置 支持使用 !env ${PATH} 填写环境变量的值, 若环境变量不存在则会报错
|
||||
vector_stores:
|
||||
milvus:
|
||||
connection_args: !env ${BS_MILVUS_CONNECTION_ARGS}
|
||||
is_partition: !env ${BS_MILVUS_IS_PARTITION}
|
||||
partition_suffix: !env ${BS_MILVUS_PARTITION_SUFFIX}
|
||||
elasticsearch:
|
||||
url: !env ${BS_ELASTICSEARCH_URL}
|
||||
ssl_verify: !env ${BS_ELASTICSEARCH_SSL_VERIFY}
|
||||
|
||||
|
||||
# 对象存储, 目前只支持minio
|
||||
object_storage:
|
||||
type: minio
|
||||
minio:
|
||||
schema: !env ${BS_MINIO_SCHEMA}
|
||||
cert_check: !env ${BS_MINIO_CERT_CHECK}
|
||||
endpoint: !env ${BS_MINIO_ENDPOINT}
|
||||
sharepoint: !env ${BS_MINIO_SHAREPOINT}
|
||||
access_key: !env ${BS_MINIO_ACCESS_KEY}
|
||||
secret_key: !env ${BS_MINIO_SECRET_KEY}
|
||||
public_bucket: 'bisheng' # 公共bucket,存储平台上一些需要持久化的文件。会设置为可公开访问
|
||||
tmp_bucket: 'tmp-dir' # 临时bucket,会对传到此bucket内的文件设置有效期
|
||||
|
||||
environment:
|
||||
env: dev
|
||||
uns_support: ['png','jpg','jpeg','bmp','doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx', 'txt', 'md', 'html', 'pdf', 'csv', 'tiff']
|
||||
|
||||
# 可根据loguru的文档配置不同 handlers
|
||||
logger_conf:
|
||||
# 默认输出到sys.stdout的日志级别, 大于等于此级别都会输出
|
||||
level: DEBUG
|
||||
# 默认输出格式
|
||||
format: '<level>[{time:YYYY-MM-DD HH:mm:ss.SSSSSS}] [{level.name} process-{process.id}-{thread.id} {name}:{line}]</level> - <level>trace={extra[trace_id]} {message}</level>'
|
||||
# 参考loguru.add()中的参数可以配置多个handler
|
||||
handlers:
|
||||
# 文件路径,支持插入一些系统环境变量,若环境变量不存在则置空。例如 HOSTNAME: 主机名。后端会处理环境变量的替换
|
||||
- sink: "/app/data/bisheng.log"
|
||||
# 日志级别
|
||||
level: INFO
|
||||
# 日志格式化函数,extra内支持trace_id
|
||||
format: '<level>[{time:YYYY-MM-DD HH:mm:ss.SSSSSS}] [{level.name} process-{process.id}-{thread.id} {name}:{line}]</level> - <level>trace={extra[trace_id]} {message}</level>'
|
||||
# 每天的几点进行切割
|
||||
rotation: "00:00"
|
||||
retention: "3 Days"
|
||||
enqueue: ture
|
||||
- sink: "/app/data/statistic.log"
|
||||
level: INFO
|
||||
# 和原生不一样,后端会将配置使用eval()执行转为函数用来过滤特定日志级别。推荐lambda
|
||||
filter: "lambda record: record['level'].name == 'INFO' and record['message'].startswith('k=s')"
|
||||
format: "[{time:YYYY-MM-DD HH:mm:ss.SSSSSS}]|{level}|BISHENG|{extra[trace_id]}||{process.id}|{thread.id}|||#EX_ERR:POS={name},line {line},ERR=500,EMSG={message}"
|
||||
rotation: "00:00"
|
||||
retention: "3 Days"
|
||||
enqueue: ture
|
||||
@@ -0,0 +1,66 @@
|
||||
#!/bin/bash
|
||||
set -xe
|
||||
|
||||
export PYTHONPATH="./"
|
||||
|
||||
start_mode=${1:-api}
|
||||
|
||||
start_knowledge(){
|
||||
# 知识库解析的celery worker
|
||||
celery -A bisheng.worker.main worker -l info -c 20 -P threads -Q knowledge_celery -n knowledge@%h
|
||||
}
|
||||
|
||||
start_workflow(){
|
||||
# 工作流相关的celery worker
|
||||
celery -A bisheng.worker.main worker -l info -c 100 -P threads -Q workflow_celery -n workflow@%h
|
||||
}
|
||||
|
||||
start_beat(){
|
||||
# 定时任务调度
|
||||
celery -A bisheng.worker.main beat -l info
|
||||
}
|
||||
|
||||
start_linsight(){
|
||||
# 灵思后台任务worker
|
||||
python bisheng/linsight/worker.py --worker_num 4 --max_concurrency 5
|
||||
}
|
||||
start_default(){
|
||||
# 默认其他任务的执行worker,目前是定时统计埋点数据
|
||||
celery -A bisheng.worker.main worker -l info -c 100 -P threads -Q celery -n celery@%h
|
||||
}
|
||||
|
||||
if [ "$start_mode" = "api" ]; then
|
||||
echo "Starting API server..."
|
||||
uvicorn bisheng.main:app --host 0.0.0.0 --port 7860 --no-access-log --workers 8
|
||||
elif [ "$start_mode" = "knowledge" ]; then
|
||||
echo "Starting Knowledge Celery worker..."
|
||||
start_knowledge
|
||||
elif [ "$start_mode" = "workflow" ]; then
|
||||
echo "Starting Workflow Celery worker..."
|
||||
start_workflow
|
||||
elif [ "$start_mode" = "beat" ]; then
|
||||
echo "Starting Celery beat..."
|
||||
start_beat
|
||||
elif [ "$start_mode" = "default" ]; then
|
||||
echo "Starting default celery worker..."
|
||||
start_default
|
||||
elif [ "$start_mode" = "linsight" ]; then
|
||||
echo "Starting LinSight worker..."
|
||||
start_linsight
|
||||
elif [ "$start_mode" = "worker" ]; then
|
||||
echo "Starting All worker..."
|
||||
# 处理知识库相关任务的worker
|
||||
start_knowledge &
|
||||
# 处理工作流相关任务的worker
|
||||
start_workflow &
|
||||
# 处理linsight相关任务的worker
|
||||
start_linsight &
|
||||
# 默认其他任务的执行worker,目前是定时统计埋点数据
|
||||
start_default &
|
||||
start_beat
|
||||
|
||||
echo "All workers started successfully."
|
||||
else
|
||||
echo "Invalid start mode. Use api、worker、knowledge、workflow、beat、default、linsight."
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,232 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================
|
||||
# BiSheng 运维管理脚本
|
||||
# 用法: ./bisheng.sh <命令> [参数]
|
||||
# =============================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
COMPOSE_FILE="${SCRIPT_DIR}/docker-compose.yml"
|
||||
COMPOSE_CMD="docker compose -f ${COMPOSE_FILE}"
|
||||
|
||||
# 容器名常量(与 docker-compose.yml 对应)
|
||||
BACKEND_CONTAINER="bisheng-backend"
|
||||
WORKER_CONTAINER="bisheng-backend-worker"
|
||||
|
||||
# 所有可管理的 compose service 名称
|
||||
ALL_SERVICES=(backend backend_worker frontend mysql redis elasticsearch minio milvus etcd)
|
||||
|
||||
# ─── 颜色输出 ────────────────────────────────────────────────
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m'
|
||||
|
||||
info() { echo -e "${GREEN}[INFO]${RESET} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${RESET} $*"; }
|
||||
error() { echo -e "${RED}[ERROR]${RESET} $*" >&2; }
|
||||
header() { echo -e "${CYAN}${BOLD}$*${RESET}"; }
|
||||
|
||||
# ─── 帮助 ────────────────────────────────────────────────────
|
||||
usage() {
|
||||
header "═══════════════════════════════════════════════"
|
||||
header " BiSheng 运维管理脚本"
|
||||
header "═══════════════════════════════════════════════"
|
||||
echo ""
|
||||
echo -e "${BOLD}查看日志:${RESET}"
|
||||
echo " $0 logs backend 实时跟踪 backend 日志(默认最近 200 行)"
|
||||
echo " $0 logs worker 实时跟踪 backend_worker 日志(默认最近 200 行)"
|
||||
echo " $0 logs backend -n 0 实时跟踪 backend 所有历史日志"
|
||||
echo " $0 logs worker -n 500 查看 worker 最近 500 行日志"
|
||||
echo ""
|
||||
echo -e "${BOLD}镜像版本管理:${RESET}"
|
||||
echo " $0 version 查看当前配置的镜像版本"
|
||||
echo " $0 version v3.0.0 修改 backend、worker、frontend 的版本为 v3.0.0"
|
||||
echo ""
|
||||
echo -e "${BOLD}进入容器 Shell:${RESET}"
|
||||
echo " $0 exec backend 进入 backend 容器"
|
||||
echo " $0 exec worker 进入 backend_worker 容器"
|
||||
echo ""
|
||||
echo -e "${BOLD}更新镜像并重启:${RESET}"
|
||||
echo " $0 update 拉取最新镜像并重启 backend + worker"
|
||||
echo " $0 update backend 只更新并重启 backend"
|
||||
echo " $0 update worker 只更新并重启 worker"
|
||||
echo ""
|
||||
echo -e "${BOLD}重启容器:${RESET}"
|
||||
echo " $0 restart 重启 backend + worker"
|
||||
echo " $0 restart backend 重启 backend"
|
||||
echo " $0 restart worker 重启 worker"
|
||||
echo " $0 restart frontend 重启 frontend"
|
||||
echo " $0 restart <service...> 重启任意多个 service"
|
||||
echo ""
|
||||
echo -e "${BOLD}可用 service 名称:${RESET}"
|
||||
echo " ${ALL_SERVICES[*]}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ─── service 别名解析 ─────────────────────────────────────────
|
||||
resolve_service() {
|
||||
case "$1" in
|
||||
backend) echo "backend" ;;
|
||||
worker|backend_worker) echo "backend_worker" ;;
|
||||
frontend) echo "frontend" ;;
|
||||
mysql) echo "mysql" ;;
|
||||
redis) echo "redis" ;;
|
||||
es|elasticsearch) echo "elasticsearch" ;;
|
||||
minio) echo "minio" ;;
|
||||
milvus) echo "milvus" ;;
|
||||
etcd) echo "etcd" ;;
|
||||
*) echo "$1" ;; # 原样传入,让 docker compose 自行报错
|
||||
esac
|
||||
}
|
||||
|
||||
# ─── 查看日志 ─────────────────────────────────────────────────
|
||||
cmd_logs() {
|
||||
local target="${1:-}"
|
||||
shift || true
|
||||
|
||||
local lines=200
|
||||
|
||||
# 解析可选 -n <行数>
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-n)
|
||||
lines="${2:-200}"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
local service
|
||||
case "$target" in
|
||||
backend) service="backend" ;;
|
||||
worker|backend_worker) service="backend_worker" ;;
|
||||
*)
|
||||
error "未知目标 '${target}',请使用 backend 或 worker"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$lines" -eq 0 ]; then
|
||||
info "实时跟踪 ${service} 所有历史日志(Ctrl+C 退出)..."
|
||||
${COMPOSE_CMD} logs -f "${service}"
|
||||
else
|
||||
info "实时跟踪 ${service} 最近 ${lines} 行日志(Ctrl+C 退出)..."
|
||||
${COMPOSE_CMD} logs -f --tail="${lines}" "${service}"
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── 修改版本号 ───────────────────────────────────────────────
|
||||
cmd_version() {
|
||||
local new_version="${1:-}"
|
||||
|
||||
if [[ -z "$new_version" ]]; then
|
||||
info "当前 docker-compose.yml 配置的版本:"
|
||||
grep -E "image:.*dataelement/bisheng-(backend|frontend):" "$COMPOSE_FILE" | awk '{$1=$1};1'
|
||||
return 0
|
||||
fi
|
||||
|
||||
info "正在将 backend, backend_worker, frontend 版本修改为: ${new_version}"
|
||||
|
||||
# 兼容 macOS 和 Linux 的 sed -i 用法
|
||||
if sed --version 2>/dev/null | grep -q GNU; then
|
||||
sed -i -E "s|(image: dataelement/bisheng-backend):.*|\1:${new_version}|g" "$COMPOSE_FILE"
|
||||
sed -i -E "s|(image: dataelement/bisheng-frontend):.*|\1:${new_version}|g" "$COMPOSE_FILE"
|
||||
else
|
||||
# macOS/BSD sed
|
||||
sed -i '' -E "s|(image: dataelement/bisheng-backend):.*|\1:${new_version}|g" "$COMPOSE_FILE"
|
||||
sed -i '' -E "s|(image: dataelement/bisheng-frontend):.*|\1:${new_version}|g" "$COMPOSE_FILE"
|
||||
fi
|
||||
|
||||
info "✅ 版本修改完成:"
|
||||
grep -E "image:.*dataelement/bisheng-(backend|frontend):" "$COMPOSE_FILE" | awk '{$1=$1};1'
|
||||
warn "注意:只是修改了配置文件,若要生效请执行 '$0 update'"
|
||||
}
|
||||
|
||||
# ─── 进入容器 ─────────────────────────────────────────────────
|
||||
cmd_exec() {
|
||||
local target="${1:-}"
|
||||
local container
|
||||
|
||||
case "$target" in
|
||||
backend) container="${BACKEND_CONTAINER}" ;;
|
||||
worker|backend_worker) container="${WORKER_CONTAINER}" ;;
|
||||
*)
|
||||
error "未知目标 '${target}',请使用 backend 或 worker"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
info "进入容器 ${container} ..."
|
||||
docker exec -it "${container}" /bin/bash 2>/dev/null \
|
||||
|| docker exec -it "${container}" /bin/sh
|
||||
}
|
||||
|
||||
# ─── 更新镜像并重启 ───────────────────────────────────────────
|
||||
cmd_update() {
|
||||
local targets=()
|
||||
|
||||
if [[ $# -eq 0 ]]; then
|
||||
targets=("backend" "backend_worker" "frontend")
|
||||
else
|
||||
for t in "$@"; do
|
||||
targets+=("$(resolve_service "$t")")
|
||||
done
|
||||
fi
|
||||
|
||||
info "拉取最新镜像:${targets[*]}"
|
||||
${COMPOSE_CMD} pull "${targets[@]}"
|
||||
|
||||
info "重启服务(不重建依赖):${targets[*]}"
|
||||
${COMPOSE_CMD} up -d --no-deps "${targets[@]}"
|
||||
|
||||
info "✅ 更新完成"
|
||||
${COMPOSE_CMD} ps "${targets[@]}"
|
||||
}
|
||||
|
||||
# ─── 重启容器 ─────────────────────────────────────────────────
|
||||
cmd_restart() {
|
||||
local targets=()
|
||||
|
||||
if [[ $# -eq 0 ]]; then
|
||||
targets=("backend" "backend_worker" "frontend")
|
||||
else
|
||||
for t in "$@"; do
|
||||
targets+=("$(resolve_service "$t")")
|
||||
done
|
||||
fi
|
||||
|
||||
info "重启服务:${targets[*]}"
|
||||
${COMPOSE_CMD} restart "${targets[@]}"
|
||||
|
||||
info "✅ 重启完成"
|
||||
${COMPOSE_CMD} ps "${targets[@]}"
|
||||
}
|
||||
|
||||
# ─── 入口 ────────────────────────────────────────────────────
|
||||
main() {
|
||||
if [[ $# -eq 0 ]]; then
|
||||
usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
local cmd="$1"; shift
|
||||
|
||||
case "$cmd" in
|
||||
logs) cmd_logs "$@" ;;
|
||||
version) cmd_version "$@" ;;
|
||||
exec) cmd_exec "$@" ;;
|
||||
update) cmd_update "$@" ;;
|
||||
restart) cmd_restart "$@" ;;
|
||||
help|-h|--help) usage ;;
|
||||
*)
|
||||
error "未知命令: ${cmd}"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -0,0 +1,30 @@
|
||||
services:
|
||||
ft_server:
|
||||
container_name: bisheng-ft-server
|
||||
image: dataelement/bisheng-ft:v0.5.0
|
||||
shm_size: "4g"
|
||||
ports:
|
||||
- "8000:8000"
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
volumes:
|
||||
- ${DOCKER_VOLUME_DIRECTORY:-.}/bisheng-ft/config.yaml:/opt/bisheng-ft/sft_server/config.yaml # 服务启动所需的配置文件地址,默认不用改
|
||||
- ${DOCKER_VOLUME_DIRECTORY:-.}/data/llm:/opt/bisheng-ft/models/model_repository # 这个是存放基座模型的目录,挂载到本机目录
|
||||
- ${DOCKER_VOLUME_DIRECTORY:-.}/data/finetune_output:/opt/bisheng-ft/finetune_output # 这个是存放微调过程的中间日志和微调训练后模型的目录,挂载到本机目录,不能与存放基座模型的目录相同
|
||||
security_opt:
|
||||
- seccomp:unconfined
|
||||
command: bash start-sft-server.sh # 启动服务
|
||||
restart: on-failure
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||
start_period: 30s
|
||||
interval: 90s
|
||||
timeout: 30s
|
||||
retries: 3
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- driver: nvidia
|
||||
count: all
|
||||
capabilities: [gpu]
|
||||
@@ -0,0 +1,14 @@
|
||||
services:
|
||||
office:
|
||||
container_name: bisheng-office
|
||||
image: onlyoffice/documentserver:7.1.1
|
||||
ports:
|
||||
- "8701:80"
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
JWT_ENABLED: "false"
|
||||
volumes:
|
||||
- ${DOCKER_VOLUME_DIRECTORY:-.}/office/bisheng:/var/www/onlyoffice/documentserver/sdkjs-plugins/bisheng
|
||||
command: bash -c "supervisorctl restart all"
|
||||
restart: on-failure
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
services:
|
||||
bisheng-unstructured:
|
||||
container_name: bisheng-unstructured
|
||||
image: dataelement/bisheng-unstructured:v0.0.3.14
|
||||
ports:
|
||||
- "10001:10001"
|
||||
environment:
|
||||
# 填写ocr_sdk或rt服务的根地址
|
||||
# server_address: bisheng-rt:9001
|
||||
# 这里填 ocr_sdk 或 rt
|
||||
# server_type: ocr_sdk
|
||||
TZ: Asia/Shanghai
|
||||
volumes:
|
||||
- ${DOCKER_VOLUME_DIRECTORY:-.}/bisheng-uns/config.yaml:/opt/bisheng-unstructured/bisheng_unstructured/config/config.yaml
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:10001/health"]
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
restart: on-failure
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
services:
|
||||
mysql:
|
||||
container_name: bisheng-mysql
|
||||
image: mysql:8.0
|
||||
|
||||
ports:
|
||||
- "3306:3306"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: "1234" # 数据库密码,如果修改需要同步修改bisheng/congfig/config.yaml配置database_url的mysql连接密码
|
||||
MYSQL_DATABASE: bisheng
|
||||
TZ: Asia/Shanghai
|
||||
volumes:
|
||||
- ${DOCKER_VOLUME_DIRECTORY:-.}/mysql/conf/my.cnf:/etc/mysql/my.cnf
|
||||
- ${DOCKER_VOLUME_DIRECTORY:-.}/mysql/data:/var/lib/mysql
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "exit | mysql -u root -p$$MYSQL_ROOT_PASSWORD"]
|
||||
start_period: 30s
|
||||
interval: 20s
|
||||
timeout: 10s
|
||||
retries: 4
|
||||
restart: on-failure
|
||||
|
||||
redis:
|
||||
container_name: bisheng-redis
|
||||
image: redis:7.0.4
|
||||
ports:
|
||||
- "6379:6379"
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
volumes:
|
||||
- ${DOCKER_VOLUME_DIRECTORY:-.}/data/redis:/data
|
||||
- ${DOCKER_VOLUME_DIRECTORY:-.}/redis/redis.conf:/etc/redis.conf
|
||||
command: redis-server /etc/redis.conf
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", 'redis-cli ping|grep -e "PONG\|NOAUTH"']
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
restart: on-failure
|
||||
|
||||
backend:
|
||||
container_name: bisheng-backend
|
||||
image: dataelement/bisheng-backend:v2.4.0
|
||||
ports:
|
||||
- "7860:7860"
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
BS_MILVUS_CONNECTION_ARGS: '{"host":"milvus","port":"19530","user":"","password":"","secure":false}'
|
||||
BS_MILVUS_IS_PARTITION: 'true'
|
||||
BS_MILVUS_PARTITION_SUFFIX: '1'
|
||||
BS_ELASTICSEARCH_URL: 'http://elasticsearch:9200'
|
||||
BS_ELASTICSEARCH_SSL_VERIFY: '{}' # 可根据自己部署的密码进行配置 '{"basic_auth": ("elastic", "elastic")}'
|
||||
BS_MINIO_SCHEMA: 'false'
|
||||
BS_MINIO_CERT_CHECK: 'false'
|
||||
BS_MINIO_ENDPOINT: 'minio:9000'
|
||||
BS_MINIO_SHAREPOINT: 'minio:9000'
|
||||
BS_MINIO_ACCESS_KEY: 'minioadmin'
|
||||
BS_MINIO_SECRET_KEY: 'minioadmin'
|
||||
volumes:
|
||||
- ${DOCKER_VOLUME_DIRECTORY:-.}/bisheng/config/config.yaml:/app/bisheng/config.yaml
|
||||
- ${DOCKER_VOLUME_DIRECTORY:-.}/bisheng/entrypoint.sh:/app/entrypoint.sh
|
||||
- ${DOCKER_VOLUME_DIRECTORY:-.}/data/bisheng:/app/data
|
||||
security_opt:
|
||||
- seccomp:unconfined
|
||||
command: sh entrypoint.sh api # 启动api服务
|
||||
restart: on-failure
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:7860/health"]
|
||||
start_period: 30s
|
||||
interval: 90s
|
||||
timeout: 30s
|
||||
retries: 3
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
|
||||
backend_worker:
|
||||
container_name: bisheng-backend-worker
|
||||
image: dataelement/bisheng-backend:v2.4.0
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
BS_MILVUS_CONNECTION_ARGS: '{"host":"milvus","port":"19530","user":"","password":"","secure":false}'
|
||||
BS_MILVUS_IS_PARTITION: 'true'
|
||||
BS_MILVUS_PARTITION_SUFFIX: '1'
|
||||
BS_ELASTICSEARCH_URL: 'http://elasticsearch:9200'
|
||||
BS_ELASTICSEARCH_SSL_VERIFY: '{}' # 可根据自己部署的密码进行配置 '{"basic_auth": ("elastic", "elastic")}'
|
||||
BS_MINIO_SCHEMA: 'false'
|
||||
BS_MINIO_CERT_CHECK: 'false'
|
||||
BS_MINIO_ENDPOINT: 'minio:9000'
|
||||
BS_MINIO_SHAREPOINT: 'minio:9000'
|
||||
BS_MINIO_ACCESS_KEY: 'minioadmin'
|
||||
BS_MINIO_SECRET_KEY: 'minioadmin'
|
||||
volumes:
|
||||
- ${DOCKER_VOLUME_DIRECTORY:-.}/bisheng/config/config.yaml:/app/bisheng/config.yaml
|
||||
- ${DOCKER_VOLUME_DIRECTORY:-.}/bisheng/entrypoint.sh:/app/entrypoint.sh
|
||||
- ${DOCKER_VOLUME_DIRECTORY:-.}/data/bisheng:/app/data
|
||||
security_opt:
|
||||
- seccomp:unconfined
|
||||
command: sh entrypoint.sh worker # 启动celery的异步worker服务,用来处理一些耗时的任务
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
|
||||
|
||||
frontend:
|
||||
container_name: bisheng-frontend
|
||||
image: dataelement/bisheng-frontend:v2.4.0
|
||||
ports:
|
||||
- "3001:3001"
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
volumes:
|
||||
- ${DOCKER_VOLUME_DIRECTORY:-.}/nginx/nginx.conf:/etc/nginx/nginx.conf
|
||||
- ${DOCKER_VOLUME_DIRECTORY:-.}/nginx/conf.d:/etc/nginx/conf.d
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
- backend
|
||||
|
||||
elasticsearch:
|
||||
container_name: bisheng-es
|
||||
image: docker.io/bitnamilegacy/elasticsearch:8.12.0
|
||||
user: root
|
||||
ports:
|
||||
- "9200:9200"
|
||||
- "9300:9300"
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
volumes:
|
||||
- ${DOCKER_VOLUME_DIRECTORY:-.}/data/es:/bitnami/elasticsearch/data
|
||||
restart: on-failure
|
||||
|
||||
etcd:
|
||||
container_name: bisheng-milvus-etcd
|
||||
image: quay.io/coreos/etcd:v3.5.5
|
||||
environment:
|
||||
ETCD_AUTO_COMPACTION_MODE: revision
|
||||
ETCD_AUTO_COMPACTION_RETENTION: "1000"
|
||||
ETCD_QUOTA_BACKEND_BYTES: "4294967296"
|
||||
ETCD_SNAPSHOT_COUNT: "50000"
|
||||
TZ: Asia/Shanghai
|
||||
volumes:
|
||||
- ${DOCKER_VOLUME_DIRECTORY:-.}/data/milvus-etcd:/etcd
|
||||
command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
|
||||
restart: on-failure
|
||||
healthcheck:
|
||||
test: ["CMD", "etcdctl", "endpoint", "health"]
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
|
||||
minio:
|
||||
container_name: bisheng-milvus-minio
|
||||
image: minio/minio:RELEASE.2023-03-20T20-16-18Z
|
||||
environment:
|
||||
MINIO_ACCESS_KEY: minioadmin
|
||||
MINIO_SECRET_KEY: minioadmin
|
||||
ports:
|
||||
- "9100:9000"
|
||||
- "9101:9001"
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ${DOCKER_VOLUME_DIRECTORY:-.}/data/milvus-minio:/minio_data
|
||||
command: minio server /minio_data --console-address ":9001"
|
||||
restart: on-failure
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
|
||||
milvus:
|
||||
container_name: bisheng-milvus-standalone
|
||||
image: milvusdb/milvus:v2.5.10
|
||||
command: ["milvus", "run", "standalone"]
|
||||
security_opt:
|
||||
- seccomp:unconfined
|
||||
environment:
|
||||
ETCD_ENDPOINTS: etcd:2379
|
||||
MINIO_ADDRESS: minio:9000
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ${DOCKER_VOLUME_DIRECTORY:-.}/data/milvus:/var/lib/milvus
|
||||
restart: on-failure
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"]
|
||||
start_period: 90s
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
ports:
|
||||
- "19530:19530"
|
||||
- "9091:9091"
|
||||
depends_on:
|
||||
- etcd
|
||||
- minio
|
||||
@@ -0,0 +1,12 @@
|
||||
[client]
|
||||
default-character-set=utf8mb4
|
||||
|
||||
[mysql]
|
||||
default-character-set=utf8mb4
|
||||
|
||||
[mysqld]
|
||||
init_connect='SET collation_connection = utf8mb4_unicode_ci, NAMES utf8mb4'
|
||||
character-set-server=utf8mb4
|
||||
collation-server=utf8mb4_unicode_ci
|
||||
# skip-character-set-client-handshake
|
||||
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
|
||||
@@ -0,0 +1,72 @@
|
||||
|
||||
# 在http区域内一定要添加下面配置, 支持websocket
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
'' close;
|
||||
}
|
||||
|
||||
|
||||
upstream backend_server {
|
||||
server backend:7860; # backend api
|
||||
}
|
||||
|
||||
upstream minio_server {
|
||||
server minio:9000;
|
||||
}
|
||||
|
||||
server {
|
||||
gzip on;
|
||||
gzip_comp_level 2;
|
||||
gzip_min_length 1000;
|
||||
gzip_types text/xml text/css;
|
||||
gzip_http_version 1.1;
|
||||
gzip_vary on;
|
||||
gzip_disable "MSIE [4-6] \.";
|
||||
|
||||
listen 3001;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html/platform;
|
||||
index index.html index.htm;
|
||||
# 禁止浏览器缓存 index.html
|
||||
location = /index.html {
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate" always;
|
||||
add_header Pragma "no-cache" always;
|
||||
add_header Expires 0 always;
|
||||
}
|
||||
try_files $uri $uri/ /index.html;
|
||||
add_header X-Frame-Options SAMEORIGIN;
|
||||
}
|
||||
|
||||
location /workspace/ {
|
||||
alias /usr/share/nginx/html/client/;
|
||||
index index.html index.htm;
|
||||
# 禁止浏览器缓存 /workspace/index.html
|
||||
location = /workspace/index.html {
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate" always;
|
||||
add_header Pragma "no-cache" always;
|
||||
add_header Expires 0 always;
|
||||
}
|
||||
try_files $uri $uri/ /workspace/index.html;
|
||||
}
|
||||
|
||||
location ~ ^(/workspace)?/api(/|$) {
|
||||
rewrite ^/workspace(/.*)$ $1 break;
|
||||
proxy_pass http://backend_server;
|
||||
proxy_read_timeout 300s;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
client_max_body_size 200m;
|
||||
add_header Access-Control-Allow-Origin $host;
|
||||
add_header X-Frame-Options SAMEORIGIN;
|
||||
}
|
||||
|
||||
location ~ ^(/workspace)?/bisheng|/tmp-dir {
|
||||
rewrite ^/workspace(/.*)$ $1 break;
|
||||
proxy_pass http://minio_server;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
|
||||
# 在http区域内一定要添加下面配置, 支持websocket
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
'' close;
|
||||
}
|
||||
|
||||
server {
|
||||
gzip on;
|
||||
gzip_comp_level 2;
|
||||
gzip_min_length 1000;
|
||||
gzip_types text/xml text/css;
|
||||
gzip_http_version 1.1;
|
||||
gzip_vary on;
|
||||
gzip_disable "MSIE [4-6] \.";
|
||||
|
||||
listen 8443;
|
||||
location /api {
|
||||
proxy_pass http://backend:7860;
|
||||
proxy_read_timeout 300s;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
client_max_body_size 50m;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
error_log /var/log/nginx/error.log notice;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
sendfile on;
|
||||
#tcp_nopush on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
#gzip on;
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
}
|
||||
@@ -0,0 +1,487 @@
|
||||
(function (window, undefined) {
|
||||
let selectText = ''
|
||||
|
||||
window.Asc.plugin.init = function (e) {
|
||||
selectText = e
|
||||
}
|
||||
window.Asc.plugin.event_onClick = function () {
|
||||
selectText = ''
|
||||
}
|
||||
|
||||
window.Asc.plugin.button = function (id) {
|
||||
}
|
||||
|
||||
const EventMap = {
|
||||
sendToParent (method, data) {
|
||||
let params = {
|
||||
type: 'onExternalFrameMessage',
|
||||
method,
|
||||
data
|
||||
}
|
||||
window.top.postMessage(JSON.stringify(params), location.origin)
|
||||
},
|
||||
focusInDocument (data) {
|
||||
window.Asc.scope.field = {
|
||||
id: data.id,
|
||||
fieldFlag: data.fieldFlag,
|
||||
$index: data.$index || 1
|
||||
}
|
||||
window.Asc.plugin.callCommand(function () {
|
||||
let field = Asc.scope.field || {}
|
||||
let index = field.$index ? field.$index : 1
|
||||
let oDoc = Api.GetDocument()
|
||||
let flag = `{{${field.fieldFlag}}}`
|
||||
let oRange = oDoc.Search(flag)
|
||||
let cur = 1
|
||||
for (let i = 0; i < oRange.length; i++) {
|
||||
if (oRange[i].GetText() === flag) {
|
||||
if (cur === index) {
|
||||
oRange[i].Select()
|
||||
break
|
||||
}
|
||||
cur = cur + 1
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
focusTableInDoc (data) {
|
||||
window.Asc.scope.marker = data.marker
|
||||
window.Asc.plugin.callCommand(function () {
|
||||
let flag = Asc.scope.marker || ''
|
||||
let oDoc = Api.GetDocument()
|
||||
let oRange = oDoc.GetBookmarkRange(flag)
|
||||
oRange.Select()
|
||||
})
|
||||
},
|
||||
addMarker (data) {
|
||||
let flag = '{{' + data.fieldFlag + '}}'
|
||||
window.Asc.plugin.executeMethod('PasteText', [flag])
|
||||
},
|
||||
addBookMarker (data) {
|
||||
window.Asc.scope.value = data
|
||||
window.Asc.plugin.callCommand(function () {
|
||||
let oDoc = Api.GetDocument()
|
||||
let range = oDoc.GetRangeBySelect()
|
||||
let params = {
|
||||
type: 'onExternalFrameMessage',
|
||||
method: 'addBookMarker'
|
||||
}
|
||||
let marker = Asc.scope.value
|
||||
let markers = []
|
||||
if (range) {
|
||||
let texts = range.GetText()
|
||||
let pars = range.GetAllParagraphs() || []
|
||||
let txtList = []
|
||||
for (let i = 0; i < pars.length; i++) {
|
||||
let text = pars[i].GetText()
|
||||
txtList.push(text)
|
||||
}
|
||||
let table = pars[0] ? pars[0].GetParentTable() : null
|
||||
let count = table ? table.GetRowsCount() : 0
|
||||
for (let i = 0; i < count; i++) {
|
||||
let row = table.GetRow(i)
|
||||
let firstCell = row.GetCell(0)
|
||||
let cellText = firstCell ? firstCell.GetContent().GetElement(0).GetText() : ''
|
||||
// 序号
|
||||
let isNumbering = false
|
||||
if (firstCell.GetContent().GetElement(0).GetNumbering()) {
|
||||
isNumbering = true
|
||||
let cellCount = row.GetCellsCount()
|
||||
for (let j = 1; j < cellCount; j++) {
|
||||
let cellItem = row.GetCell(j)
|
||||
if (!cellItem.GetContent().GetElement(0).GetNumbering()) {
|
||||
cellText = cellItem.GetContent().GetElement(0).GetText()
|
||||
firstCell = cellItem
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cellText && txtList.includes(cellText)) {
|
||||
let cRange = firstCell.Search(cellText)[0]
|
||||
cRange.AddBookmark(marker.key + i)
|
||||
markers.push(marker.key + i)
|
||||
}
|
||||
}
|
||||
// range.AddBookmark(Asc.scope.value.key)
|
||||
params.data = Object.assign(marker, {
|
||||
key: markers.join(','),
|
||||
texts
|
||||
})
|
||||
} else {
|
||||
params.data = false
|
||||
}
|
||||
window.top.postMessage(JSON.stringify(params), location.origin)
|
||||
})
|
||||
},
|
||||
deleteBookMarker (data) {
|
||||
window.Asc.scope.value = data
|
||||
window.Asc.plugin.callCommand(function () {
|
||||
let oDoc = Api.GetDocument()
|
||||
let markers = Asc.scope.value || []
|
||||
for (let i = 0; i < markers.length; i++) {
|
||||
oDoc.DeleteBookmark(markers[i])
|
||||
}
|
||||
})
|
||||
},
|
||||
// 批量删除循环应用内标签
|
||||
deleteLoopApp (list) {
|
||||
window.Asc.scope.value = list
|
||||
window.Asc.plugin.callCommand(function () {
|
||||
let list = window.Asc.scope.value || []
|
||||
let oDoc = Api.GetDocument()
|
||||
list.forEach(row => {
|
||||
if (row.loopType === 0) {
|
||||
oDoc.SearchAndReplace({ searchString: `{{${row.startTag}}}`, replaceString: '' }, `{{${row.startTag}}}`, '')
|
||||
oDoc.SearchAndReplace({ searchString: `{{${row.endTag}}}`, replaceString: '' }, `{{${row.endTag}}}`, '')
|
||||
} else if (row.loopType === 1) {
|
||||
oDoc.DeleteBookmark(row.bookmark)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
// 更新占位符
|
||||
replaceMarker (data) {
|
||||
window.Asc.scope.st = '{{' + data.newValue + '}}'
|
||||
// 原来的值
|
||||
if (data.oldValue) {
|
||||
window.Asc.scope.old = '{{' + data.oldValue + '}}'
|
||||
} else {
|
||||
this.addMarker(data)
|
||||
return
|
||||
}
|
||||
window.Asc.plugin.callCommand(function () {
|
||||
let oDocument = Api.GetDocument()
|
||||
oDocument.SearchAndReplace({ searchString: Asc.scope.old, replaceString: Asc.scope.st }, Asc.scope.old, Asc.scope.st)
|
||||
}, false)
|
||||
},
|
||||
// 查找并插入占位符
|
||||
findAndInsertMarker (data) {
|
||||
window.Asc.scope.st = '{{' + data.fieldName + '}}'
|
||||
window.Asc.scope.searchStr = data.fieldValue
|
||||
window.Asc.plugin.callCommand(function () {
|
||||
let oDocument = Api.GetDocument()
|
||||
oDocument.SearchAndReplace({ searchString: Asc.scope.searchStr, replaceString: Asc.scope.st }, Asc.scope.searchStr, Asc.scope.st)
|
||||
}, false)
|
||||
},
|
||||
insertPosition (data) {
|
||||
if (!selectText) {
|
||||
let postData = {
|
||||
text: selectText,
|
||||
...data,
|
||||
selected: false
|
||||
}
|
||||
this.sendToParent('addRange', postData)
|
||||
return false
|
||||
}
|
||||
window.Asc.scope.postData = data
|
||||
window.Asc.plugin.callCommand(function() {
|
||||
let postData = Asc.scope.postData || {}
|
||||
let oDoc = Api.GetDocument()
|
||||
let oRange = oDoc.GetRangeBySelect()
|
||||
let selectText = oRange.GetText()
|
||||
let oAllPar = oRange.GetAllParagraphs()
|
||||
let oPar = oAllPar[oAllPar.length - 1]
|
||||
let parText = oPar.GetText()
|
||||
if (oAllPar.length > 1) {
|
||||
oRange.AddText(`{{${postData.start}}}`, 'before')
|
||||
if (selectText.includes(parText)) {
|
||||
let newRange = oPar.GetRange(0, parText.length - 1)
|
||||
newRange.AddText(`{{${postData.end}}}`, 'after')
|
||||
} else {
|
||||
oRange.AddText(`{{${postData.end}}}`, 'after')
|
||||
}
|
||||
} else {
|
||||
let isEnd = parText.substr(0 - selectText.length) === selectText
|
||||
isEnd = isEnd || selectText.includes(parText)
|
||||
console.log('end = ', isEnd)
|
||||
let start = Math.max(parText.indexOf(selectText), 0)
|
||||
let end = start + Math.min(parText.length, selectText.length) - 1
|
||||
let newRange = oPar.GetRange(start, end)
|
||||
oRange.AddText(`{{${postData.start}}}`, 'before')
|
||||
newRange.AddText(`{{${postData.end}}}`, 'after')
|
||||
}
|
||||
|
||||
postData.selected = true
|
||||
postData.text = selectText
|
||||
let params = {
|
||||
type: 'onExternalFrameMessage',
|
||||
method: 'addRange',
|
||||
data: postData
|
||||
}
|
||||
window.top.postMessage(JSON.stringify(params), location.origin)
|
||||
})
|
||||
},
|
||||
deletePosition (data) {
|
||||
window.Asc.scope.range = data
|
||||
window.Asc.plugin.callCommand(function () {
|
||||
let oDocument = Api.GetDocument()
|
||||
let { start, end } = Asc.scope.range
|
||||
let markers = [`{{${start}}}`, `{{${end}}}`]
|
||||
for (let j = 0; j < markers.length; j++) {
|
||||
oDocument.SearchAndReplace({ searchString: markers[j], replaceString: '' }, markers[j], '')
|
||||
}
|
||||
})
|
||||
},
|
||||
deletePositionMarker (data) {
|
||||
window.Asc.scope.data = data
|
||||
window.Asc.plugin.callCommand(function () {
|
||||
let oDocument = Api.GetDocument()
|
||||
let markers = Asc.scope.data || []
|
||||
for (let j = 0; j < markers.length; j++) {
|
||||
oDocument.SearchAndReplace({ searchString: markers[j], replaceString: '' }, markers[j], '')
|
||||
}
|
||||
})
|
||||
},
|
||||
deletePositionArray (data) {
|
||||
window.Asc.scope.data = data
|
||||
window.Asc.plugin.callCommand(function () {
|
||||
let oDocument = Api.GetDocument()
|
||||
let markers = Asc.scope.data || []
|
||||
for (let j = 0; j < markers.length; j++) {
|
||||
oDocument.SearchAndReplace({ searchString: markers[j], replaceString: '' }, markers[j], '')
|
||||
}
|
||||
})
|
||||
},
|
||||
replaceRangePosition (data) {
|
||||
window.Asc.scope.list = data
|
||||
window.Asc.plugin.callCommand(function () {
|
||||
let list = Asc.scope.list || []
|
||||
let oDocument = Api.GetDocument()
|
||||
list.forEach(row => {
|
||||
oDocument.SearchAndReplace({ searchString: row.str, replaceString: row.newStr }, row.str, row.newStr)
|
||||
})
|
||||
})
|
||||
},
|
||||
delMarker (data) {
|
||||
let fields = []
|
||||
data.forEach(item => {
|
||||
fields.push({
|
||||
text: '{{' + item.fieldFlag + '}}',
|
||||
type: 'field'
|
||||
})
|
||||
})
|
||||
window.Asc.scope.st = fields
|
||||
window.Asc.plugin.callCommand(function () {
|
||||
let oDocument = Api.GetDocument()
|
||||
let markers = Asc.scope.st.slice(0)
|
||||
for (let j = 0; j < markers.length; j++) {
|
||||
let marker = markers[j]
|
||||
if (marker.type === 'field') {
|
||||
oDocument.SearchAndReplace({ searchString: marker.text, replaceString: '' })
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
delMarkerGroup (data) {
|
||||
this.delMarker(data.fields)
|
||||
},
|
||||
// excel
|
||||
insertCellName (field) {
|
||||
window.Asc.scope.field = field
|
||||
window.Asc.plugin.callCommand(function () {
|
||||
let fieldItem = Asc.scope.field
|
||||
let sheetObj = Api.GetActiveSheet()
|
||||
let sheetName = sheetObj.GetName()
|
||||
let oRange = Api.GetSelection()
|
||||
let oCount = oRange.GetCount()
|
||||
let params = {
|
||||
type: 'onExternalFrameMessage',
|
||||
method: 'addCellName'
|
||||
}
|
||||
if (oCount !== 1) {
|
||||
params.data = false
|
||||
} else {
|
||||
let oAddr = oRange.GetAddress(true, true, '', false)
|
||||
let sheetFlag = `${sheetName}!${oAddr}`
|
||||
// let name = [fieldItem.fieldName, 'DEF', fieldItem.id].join('')
|
||||
// let nameObj = sheetObj.GetDefName(name)
|
||||
// console.log('inser cell before = ', name, sheetFlag, nameObj)
|
||||
// let result = sheetObj.AddDefName(name, sheetFlag)
|
||||
// console.log('insert cell ', name, sheetFlag, result)
|
||||
fieldItem.fieldFlag = sheetFlag
|
||||
fieldItem.$success = oAddr !== ''
|
||||
params.data = fieldItem
|
||||
}
|
||||
window.top.postMessage(JSON.stringify(params), location.origin)
|
||||
})
|
||||
},
|
||||
getFocusedCell () {
|
||||
window.Asc.plugin.callCommand(function () {
|
||||
let sheetObj = Api.GetActiveSheet()
|
||||
let sheetName = sheetObj.GetName()
|
||||
let oRange = Api.GetSelection()
|
||||
let params = {
|
||||
type: 'onExternalFrameMessage',
|
||||
method: 'getFocusedCell'
|
||||
}
|
||||
let oAddr = oRange.GetAddress(true, true, '', false)
|
||||
let sheetFlag = `${sheetName}!${oAddr}`
|
||||
params.data = sheetFlag
|
||||
window.top.postMessage(JSON.stringify(params), location.origin)
|
||||
})
|
||||
},
|
||||
loadFileFlags (data) {
|
||||
window.Asc.scope.list = data
|
||||
window.Asc.plugin.callCommand(function () {
|
||||
let list = Asc.scope.list || []
|
||||
let oDocument = Api.GetDocument()
|
||||
let oParCount = oDocument.GetElementsCount()
|
||||
let dataMap = {}
|
||||
for (let i = 0; i < oParCount; i++) {
|
||||
let oPar = oDocument.GetElement(i)
|
||||
let ctype = oPar.GetClassType()
|
||||
if (ctype === 'table') {
|
||||
list.forEach(row => {
|
||||
let flag = `{{${row.fieldFlag}}}`
|
||||
let rs = oPar.Search(flag)
|
||||
for (let i = 0; i < rs.length; i++) {
|
||||
let oRange = rs[i]
|
||||
if (oRange && oRange.GetText() === flag) {
|
||||
if (dataMap[row.id]) {
|
||||
dataMap[row.id] = dataMap[row.id] + 1
|
||||
} else {
|
||||
dataMap[row.id] = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if (ctype === 'paragraph') {
|
||||
let oParText = oPar.GetText()
|
||||
list.forEach(row => {
|
||||
let count = oParText.split(`{{${row.fieldFlag}}}`).length - 1
|
||||
if (dataMap[row.id]) {
|
||||
dataMap[row.id] = dataMap[row.id] + count
|
||||
} else {
|
||||
dataMap[row.id] = count
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
let params = {
|
||||
type: 'onExternalFrameMessage',
|
||||
method: 'loadFieldFlagCount',
|
||||
data: dataMap
|
||||
}
|
||||
window.top.postMessage(JSON.stringify(params), location.origin)
|
||||
})
|
||||
},
|
||||
/**
|
||||
* data.sheetName: 要聚焦的sheet名称
|
||||
* data.cellName: 要聚焦的cell名称,如C1, D3等
|
||||
*/
|
||||
focusCell (data) {
|
||||
window.Asc.scope.data = data
|
||||
window.Asc.plugin.callCommand(function () {
|
||||
const theData = Asc.scope.data
|
||||
const theSheet = Api.GetSheet(theData.sheetName || '')
|
||||
if (theSheet) {
|
||||
theSheet.SetActive()
|
||||
|
||||
const theCell = theSheet.GetRange(theData.cellName || '')
|
||||
if (theCell) {
|
||||
theCell.Select()
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getSelectedText (data) {
|
||||
window.Asc.scope.data = data
|
||||
window.Asc.plugin.callCommand(function () {
|
||||
const theData = Asc.scope.data
|
||||
const oDoc = Api.GetDocument()
|
||||
const oRange = oDoc.GetRangeBySelect()
|
||||
if (oRange) {
|
||||
const oParas = oRange.GetAllParagraphs()
|
||||
// 只能选择一个段落,否则认为不成功
|
||||
if (oParas.length === 1) {
|
||||
|
||||
const params = {
|
||||
type: 'onExternalFrameMessage',
|
||||
method: 'getSelectedText',
|
||||
data: {
|
||||
id: theData.id,
|
||||
text: oRange.GetText()
|
||||
}
|
||||
}
|
||||
window.top.postMessage(JSON.stringify(params), location.origin)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function receiveMessage (e) {
|
||||
let data = e.data ? JSON.parse(e.data) : {}
|
||||
if (data.type === 'onExternalPluginMessage') {
|
||||
switch (data.method) {
|
||||
case 'focus':
|
||||
EventMap.focusInDocument(data.data)
|
||||
break
|
||||
case 'focusTable':
|
||||
EventMap.focusTableInDoc(data.data)
|
||||
break
|
||||
case 'insert':
|
||||
EventMap.addMarker(data.data)
|
||||
break
|
||||
case 'addBookMarker':
|
||||
EventMap.addBookMarker(data.data)
|
||||
break
|
||||
case 'delBookMarker':
|
||||
EventMap.deleteBookMarker(data.data)
|
||||
break
|
||||
case 'delLoopApp':
|
||||
EventMap.deleteLoopApp(data.data)
|
||||
break
|
||||
case 'update':
|
||||
EventMap.replaceMarker(data.data)
|
||||
break
|
||||
case 'findAndInsertMarker':
|
||||
EventMap.findAndInsertMarker(data.data)
|
||||
break
|
||||
case 'addRange':
|
||||
EventMap.insertPosition(data.data)
|
||||
break
|
||||
case 'updateRange':
|
||||
EventMap.replaceRangePosition(data.data)
|
||||
break
|
||||
case 'delRange':
|
||||
EventMap.deletePosition(data.data)
|
||||
break
|
||||
case 'delRangeArray':
|
||||
EventMap.deletePositionArray(data.data)
|
||||
break
|
||||
case 'delQuoteGroup':
|
||||
EventMap.deletePositionMarker(data.data)
|
||||
break
|
||||
case 'remove':
|
||||
EventMap.delMarker([ data.data ])
|
||||
break
|
||||
case 'removeQuestion':
|
||||
EventMap.delMarkerGroup(data.data)
|
||||
break
|
||||
// excel
|
||||
case 'addCellName':
|
||||
EventMap.insertCellName(data.data)
|
||||
break
|
||||
case 'loadFieldFlagCount':
|
||||
EventMap.loadFileFlags(data.data)
|
||||
break
|
||||
// 聚焦到某个单元格
|
||||
case 'focusCell':
|
||||
EventMap.focusCell(data.data)
|
||||
break
|
||||
// 获取当前选中的单元格
|
||||
case 'getFocusedCell':
|
||||
EventMap.getFocusedCell()
|
||||
break
|
||||
// 获取当前选中的文字
|
||||
case 'getSelectedText':
|
||||
EventMap.getSelectedText(data.data)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('message', receiveMessage, false)
|
||||
})(window, undefined)
|
||||
@@ -0,0 +1,15 @@
|
||||
(function () {
|
||||
window.Asc.plugin.init = function (e) {}
|
||||
window.Asc.plugin.event_onClick = function () {}
|
||||
window.Asc.plugin.button = function (id) {}
|
||||
|
||||
function onMessage(e) {
|
||||
var data = e.data ? JSON.parse(e.data) : {}
|
||||
if (data.action === 'insetMarker') {
|
||||
const flag = '{{' + data.data + '}}'
|
||||
window.Asc.plugin.executeMethod('PasteText', [flag])
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('message', onMessage, false)
|
||||
})()
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "文档自动化",
|
||||
"guid": "asc.{D2A0F3BE-CC8D-4956-BCD9-6CBEA6E8960E}",
|
||||
"variations": [
|
||||
{
|
||||
"description": "插入label-配置",
|
||||
"url": "index.html",
|
||||
"icons": [
|
||||
"icon.png",
|
||||
"icon.png",
|
||||
"icon.png",
|
||||
"icon.png"
|
||||
],
|
||||
"EditorsSupport": [
|
||||
"word",
|
||||
"cell",
|
||||
"slide"
|
||||
],
|
||||
"isViewer": false,
|
||||
"isVisual": false,
|
||||
"isModal": true,
|
||||
"isInsideMode": false,
|
||||
"isSystem": false,
|
||||
"initOnSelectionChanged": true,
|
||||
"hideClose": true,
|
||||
"initDataType": "text",
|
||||
"isDisplayedInViewer": true,
|
||||
"isUpdateOleOnResize": true,
|
||||
"events": [
|
||||
"onClick",
|
||||
"onTargetPositionChanged"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<script src="../pluginBase.js"></script>
|
||||
<script src="./bisheng.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user