diff --git a/code/pep-stats-report/app/db_helper.py b/code/pep-stats-report/app/db_helper.py
index 377c60a..b57bb0a 100644
--- a/code/pep-stats-report/app/db_helper.py
+++ b/code/pep-stats-report/app/db_helper.py
@@ -1,7 +1,13 @@
-def querystring_user_procinst_duration(start, end, except_senior, department):
-    filter_user = "and u.name not in ('刘文峰', '金亮', '姜珍', '余莎莎', '张阳根', '唐国华', '刘国勇', '刘会连', '肖琥', '邱峰')" if except_senior else ""
-    filter_department = "and department_name='{}'".format(department) if department else ""
-    return '''
+SENIORS = ('刘文峰', '金亮', '姜珍', '余莎莎', '张阳根', '唐国华', '刘国勇', '刘会连', '肖琥', '邱峰', '姚文婷')
+IGNORE_PERSONS = ('黄红梅',)
+IGNORE_PERSONS_SUBQUERY = f"and u.name != '{IGNORE_PERSONS[0]}'" if len(IGNORE_PERSONS) == 1 else f"and u.name not in {IGNORE_PERSONS}"
+IGNORE_DEPARTMENTS = ('汇派-质量部', '汇派-生产部', '汇派-计划部', '汇派-人事部', '汇派-采购部',
+                      '党建工会', '工程项目中心', '北京技术中心', '政委', '总经办-培训中心')
+
+
+def querystring_department_user_procinst_duration(start, end, exclude_senior):
+    filter_user = f"and u.name not in {SENIORS + IGNORE_PERSONS}" if exclude_senior else IGNORE_PERSONS_SUBQUERY
+    return f'''
 select department_name,
        user_name,
        procinst_id,
@@ -22,192 +28,84 @@ from (
     inner join workflow_process_history as wph on wpa.procinst_id=wph.procinst_id
     inner join workflow_process_version as wpv on wph.version_id=wpv.id
     inner join workflow_process as wp on wpv.process_id=wp.id
-    inner join user as u on wpa.deal_user_id=u.id
+    inner join "user" as u on wpa.deal_user_id=u.id
     inner join department_user as du on u.id=du."user"
     inner join department as d on du.department=d.id
-    where wpa.end_time >='{}' and wpa.end_time < '{}' and wp.deleted=false and wp.is_enable=true and d.delete=0 and u.delete=0 and u.state=1 and u.active_status=1
-      {} {}
+    where wpa.end_time >='{start}' and wpa.end_time < '{end}' and wp.deleted=false and wp.is_enable=true 
+        and d.delete=0 and u.delete=0 and u.state=1 and u.active_status=1
+        and d.name not in {IGNORE_DEPARTMENTS}
+        {filter_user}
 ) as r
 group by department_name, user_name, procinst_id
-    '''.format(start, end, filter_user, filter_department)
+    '''
 
 
-def querystring_senior_procinst_duration(start, end):
-    return '''
+def querystring_user_procinst_duration(start, end, senior):
+    filter_user = f"and u.name in {SENIORS}" if senior else IGNORE_PERSONS_SUBQUERY
+    return f'''
 select user_name,
        procinst_id,
        sum(duration_in_minutes) as procinst_duration_in_minutes
 from (
-  select distinct u.id as user_id,
-         u.name as user_name,
-         wp.name as process_name,
-         wpa.procinst_id as procinst_id,
-         wpa.task_id as taskinst_id,
-         wpa.task_name as task_name,
-         wpa.start_time as start_time,
-         wpa.end_time as end_time,
-         wpa.total_minutes as duration_in_minutes
-  from workflow_process_achievements as wpa
-  inner join workflow_process_history as wph on wpa.procinst_id=wph.procinst_id
-  inner join workflow_process_version as wpv on wph.version_id=wpv.id
-  inner join workflow_process as wp on wpv.process_id=wp.id
-  inner join user as u on wpa.deal_user_id=u.id
-  where wpa.end_time >='{}' and wpa.end_time < '{}' and wp.deleted=false and wp.is_enable=true and u.delete=0 and u.state=1 and u.active_status=1
-    and u.name in ('刘文峰', '金亮', '姜珍', '余莎莎', '张阳根', '唐国华', '刘国勇', '刘会连', '肖琥', '邱峰')
+    select distinct u.id as user_id,
+           u.name as user_name,
+           wp.name as process_name,
+           wpa.procinst_id as procinst_id,
+           wpa.task_id as taskinst_id,
+           wpa.task_name as task_name,
+           wpa.start_time as start_time,
+           wpa.end_time as end_time,
+           wpa.total_minutes as duration_in_minutes
+    from workflow_process_achievements as wpa
+    inner join workflow_process_history as wph on wpa.procinst_id=wph.procinst_id
+    inner join workflow_process_version as wpv on wph.version_id=wpv.id
+    inner join workflow_process as wp on wpv.process_id=wp.id
+    inner join "user" as u on wpa.deal_user_id=u.id
+    inner join department_user as du on u.id=du."user"
+    inner join department as d on du.department=d.id
+    where wpa.end_time >='{start}' and wpa.end_time < '{end}' and wp.deleted=false and wp.is_enable=true 
+        and d.delete=0 and u.delete=0 and u.state=1 and u.active_status=1
+        and d.name not in {IGNORE_DEPARTMENTS}
+        {filter_user}
 ) as r
 group by user_name, procinst_id
-    '''.format(start, end)
+    '''
 
 
 # 按月统计项企平台使用人数
 def querystring_user_count(start, end):
-    return '''
+    return f'''
 select count(distinct deal_user_id) as pep_using_user_count 
 from workflow_process_achievements as wpa
-inner join user as u on wpa.deal_user_id=u.id
-where wpa.end_time >='{}' and wpa.end_time < '{}' and u.delete=0 and u.state=1 and u.active_status=1
-    '''.format(start, end)
-
-
-# 个人处理流程平均耗时
-def querystring_procinst_duration_by_company(start, end):
-    return '''
-SELECT ROUND(AVG(procinst_duration_in_minutes_by_user) /60, 2) as procinst_duration_in_hours from (
-  SELECT user_name,
-         ROUND(AVG(procinst_duration_in_minutes), 2) as procinst_duration_in_minutes_by_user
-  from ({}) as r2
-  GROUP by r2.user_name
-) as r3
-    '''.format(querystring_user_procinst_duration(start, end, False, ''))
+inner join "user" as u on wpa.deal_user_id=u.id
+inner join department_user as du on u.id=du."user"
+inner join department as d on du.department=d.id
+where wpa.end_time >='{start}' and wpa.end_time < '{end}' and u.delete=0 and u.state=1 and u.active_status=1
+    and d.name not in {IGNORE_DEPARTMENTS}
+    {IGNORE_PERSONS_SUBQUERY}
+    '''
 
 
-# 部门数(高于公司平均数)
-def querystring_department_count_gt_avg(start, end, avg):
-    return '''
-SELECT count(*) from (
-  SELECT department_name,
-         ROUND(AVG(procinst_duration_in_hours_by_user), 2) as procinst_duration_in_hours_by_department
-  from (
-    SELECT department_name,
-           user_name,
-           ROUND(AVG(procinst_duration_in_minutes) /60, 2) as procinst_duration_in_hours_by_user
-    from ({}) as r2
-    GROUP by r2.department_name, r2.user_name
-  ) as r3
-  GROUP by r3.department_name
-) as r4
-WHERE procinst_duration_in_hours_by_department > {}
-    '''.format(querystring_user_procinst_duration(start, end, True, ''), avg)
-
-
-# 用户数(高于公司平均数)
-def querystring_user_count_gt_avg(start, end, avg):
-    return '''
-SELECT count(*) from (
-  SELECT user_name,
-         ROUND(AVG(procinst_duration_in_minutes) /60, 2) as procinst_duration_in_hours_by_user
-  from ({}) as r2
-  group by r2.user_name
-) as r3
-WHERE r3.procinst_duration_in_hours_by_user > {}
-    '''.format(querystring_user_procinst_duration(start, end, False, ''), avg)
-
-
-# 部门处理流程平均耗时
-def querystring_procinst_duration_by_department(start, end):
-    return """
-SELECT department_name,
-       ROUND(AVG(procinst_duration_in_hours_by_user), 2) as procinst_duration_in_hours_by_department
-from (
-  SELECT department_name,
-         user_name,
-         ROUND(AVG(procinst_duration_in_minutes) /60, 2) as procinst_duration_in_hours_by_user
-  from ({}) as r2
-  GROUP by r2.department_name, r2.user_name
-) as r3
-GROUP by r3.department_name
-order by procinst_duration_in_hours_by_department DESC
-    """.format(querystring_user_procinst_duration(start, end, True, ''))
-
-
-# 部门处理流程平均耗时(高管)
-def querystring_procinst_duration_by_senior(start, end):
-    return '''
-SELECT ROUND(AVG(procinst_duration_in_hours_by_user), 2) as procinst_duration_in_hours_by_department
-from (
-    SELECT user_name,
-           ROUND(AVG(procinst_duration_in_minutes) /60, 2) as procinst_duration_in_hours_by_user
-    from ({}) as r2
-    GROUP by r2.user_name
-) as r3
-    '''.format(querystring_senior_procinst_duration(start, end))
-
-
-# 用户单流程处理耗时(高管)
-def querystring_procinst_duration_by_user_senior(start, end):
+# 用户处理流程耗时+用户处理流程条数
+def querystring_procinst_by_user(start, end, senior=False):
     return '''
 SELECT user_name,
+       count(*) as procinst_count_by_user,
        ROUND(AVG(procinst_duration_in_minutes) /60, 2) as procinst_duration_in_hours_by_user
 from ({}) as r2
-GROUP by r2.user_name
-order by procinst_duration_in_hours_by_user DESC
-    '''.format(querystring_senior_procinst_duration(start, end))
-
-
-# 各部门耗时较长用户(高管)
-def querystring_user_gt_senior_avg(start, end, avg):
-    return '''
-SELECT * from (
-    SELECT user_name,
-           ROUND(AVG(procinst_duration_in_minutes) /60, 2) as procinst_duration_in_hours_by_user
-    from ({}) as r2
-    GROUP by r2.user_name
-) as r3
-WHERE r3.procinst_duration_in_hours_by_user > {}
+group by r2.user_name
 order by procinst_duration_in_hours_by_user DESC
-    '''.format(querystring_senior_procinst_duration(start, end), avg)
+    '''.format(querystring_user_procinst_duration(start, end, senior))
 
 
-# 各部门耗时较长用户
-def querystring_user_gt_department_avg(start, end, department, avg):
+# 各部门用户处理流程耗时+各部门用户处理流程条数
+def querystring_procinst_by_department_user(start, end):
     return """
-SELECT * from (
-  SELECT user_name,
-         ROUND(AVG(procinst_duration_in_minutes) /60, 2) as procinst_duration_in_hours_by_user
-  from ({}) as r2
-  group by r2.user_name
-) as r3
-WHERE r3.procinst_duration_in_hours_by_user > {}
-order by procinst_duration_in_hours_by_user desc
-    """.format(querystring_user_procinst_duration(start, end, True, department), avg)
-
-
-# 按部门统计用户单流程处理耗时
-def querystring_procinst_duration_by_user(start, end, department):
-    return """
-SELECT user_name,
+SELECT department_name,
+       user_name,
+       count(*) as procinst_count_by_user,
        ROUND(AVG(procinst_duration_in_minutes) /60, 2) as procinst_duration_in_hours_by_user
 from ({}) as r2
-group by r2.user_name
+group by r2.department_name, r2.user_name
 order by procinst_duration_in_hours_by_user DESC
-    """.format(querystring_user_procinst_duration(start, end, True, department))
-
-
-# 按部门统计用户处理流程数量
-def querystring_procinst_count_by_user(start, end, department):
-    return '''
-SELECT user_name,
-       COUNT(*) as procinst_count_by_user
-from ({}) as r2
-group by r2.user_name
-    '''.format(querystring_user_procinst_duration(start, end, True, department))
-
-
-# 用户处理流程数量(高管)
-def querystring_procinst_count_by_senior(start, end):
-    return '''
-SELECT user_name,
-       COUNT(*) as procinst_count_by_user
-from ({}) as r2
-group by r2.user_name
-    '''.format(querystring_senior_procinst_duration(start, end))
+    """.format(querystring_department_user_procinst_duration(start, end, True))
diff --git a/code/pep-stats-report/config.ini b/code/pep-stats-report/config.ini
index 9cda0fd..d5611ca 100644
--- a/code/pep-stats-report/config.ini
+++ b/code/pep-stats-report/config.ini
@@ -1,9 +1,9 @@
 [clickhouse]
-host = 10.8.30.161
+host = 10.8.30.95
 port = 30900
 username = default
 password =
-database = pepca9
+database = pepca
 
 [qiniu]
 access-key=5XrM4wEB9YU6RQwT64sPzzE6cYFKZgssdP5Kj3uu
diff --git a/code/pep-stats-report/main.py b/code/pep-stats-report/main.py
index efacb36..4e23b8d 100644
--- a/code/pep-stats-report/main.py
+++ b/code/pep-stats-report/main.py
@@ -1,12 +1,17 @@
 import os
 import datetime
 import configparser
+import math
+from decimal import Decimal, ROUND_HALF_UP
 from clickhouse_driver import Client
 from docx import Document
-from docx.shared import Inches, Pt, RGBColor
+from docx.shared import Inches, Cm, Pt, RGBColor
 from docx.enum.text import WD_PARAGRAPH_ALIGNMENT, WD_LINE_SPACING
-from docx.oxml.ns import qn
+from docx.enum.table import WD_TABLE_ALIGNMENT, WD_CELL_VERTICAL_ALIGNMENT
+from docx.oxml.ns import qn, nsdecls
+from docx.oxml import parse_xml
 import matplotlib.pyplot as plt
+import matplotlib.ticker as ticker
 import numpy as np
 from app import db_helper, image_helper
 import qiniu
@@ -16,6 +21,46 @@ REPORT_DIR = 'output'
 LOG_DIR = 'log'
 
 
+# 小于十万的整数转换
+def number_to_chinese(number):
+    chinese_digits = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']
+    chinese_units = ['', '十', '百', '千', '万']
+
+    if number == 0:
+        return chinese_digits[0]
+
+    result = ''
+    unit_index = 0
+    last_digit_is_zero = False  # 记录上一位数字是否为零
+    while number > 0:
+        digit = number % 10
+        if digit != 0:
+            if last_digit_is_zero:
+                result = chinese_digits[0] + result  # 添加零
+            result = chinese_digits[digit] + chinese_units[unit_index] + result
+            last_digit_is_zero = False
+        else:
+            # 如果当前位是零,并且结果不为空,则记录上一位为零
+            if result != '':
+                last_digit_is_zero = True
+
+        unit_index += 1
+        number //= 10
+
+    # 如果结果以'一十'开头,则去掉'一',只保留'十'
+    if result[:2] == chinese_digits[1] + chinese_units[1]:
+        result = result[1:]
+
+    return result
+
+
+def round_to_integer(v):
+    if math.isnan(v):
+        return v
+    integer_in_decimal = Decimal(v).quantize(Decimal('0'), rounding=ROUND_HALF_UP)  # 取整,如:2.5 -> 3 (not 2), 3.5 -> 4
+    return int(integer_in_decimal)
+
+
 def get_report_year_month():
     # 获取上月的最后一天
     last_day = datetime.datetime.now().replace(day=1) - datetime.timedelta(days=1)
@@ -36,9 +81,9 @@ def get_report_start_end():
 
 def set_paragraph_space(p):
     # 设置间距
-    p.paragraph_format.space_before = 0  # 段前
-    p.paragraph_format.space_after = 0  # 段后
-    p.paragraph_format.line_spacing = Pt(20)  # 设置段落行距为20磅
+    p.paragraph_format.space_before = Pt(6)  # 段前
+    p.paragraph_format.space_after = Pt(6)  # 段后
+    p.paragraph_format.line_spacing = 1.5  # 设置段落行距:1.5倍行距
 
 
 def set_paragraph_format(p):
@@ -47,23 +92,23 @@ def set_paragraph_format(p):
     p.paragraph_format.first_line_indent = Inches(0.3)
 
 
-def set_heading_format(h, r):
+def set_heading_format(h, r, head_level=1):
     set_paragraph_space(h)
     r.font.name = 'Times New Roman'
     r._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体')
-    r.font.size = Pt(10.5)  # 五号
+    r.font.size = Pt(16) if head_level == 1 else Pt(15)  # 三号 -> Pt(16), 小三 -> Pt(15)
     r.font.color.rgb = RGBColor(0, 0, 0)
     r.bold = True
 
 
 def set_title_format(h, r):
-    h.paragraph_format.space_before = Pt(12)  # 段前1行
-    h.paragraph_format.space_after = Pt(12)  # 段后1行
-    h.paragraph_format.line_spacing = Pt(20)  # 设置段落行距为20磅
+    h.paragraph_format.space_before = Pt(6)  # 段前0.5行
+    h.paragraph_format.space_after = Pt(6)  # 段后0.5行
+    h.paragraph_format.line_spacing = 1.5  # 1.5倍行距
     h.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
     r.font.name = u'Times New Roman'
     r._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体')
-    r.font.size = Pt(10.5)  # 五号
+    r.font.size = Pt(18)  # 小二
     r.font.color.rgb = RGBColor(0, 0, 0)
     r.bold = True
 
@@ -72,206 +117,287 @@ def set_global_normal_style(doc):
     style = doc.styles['Normal']
     style.font.name = 'Times New Roman'  # 英文、数字字体
     style._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体')  # 中文字体
-    style.font.size = Pt(10.5)  # 五号
-    style.paragraph_format.space_before = 0  # 段前
-    style.paragraph_format.space_after = 0  # 段后
-    style.paragraph_format.line_spacing_rule = WD_LINE_SPACING.SINGLE  # 单倍行距
+    style.font.size = Pt(14)  # 四号
+    style.paragraph_format.space_before = Pt(6)  # 段前0.5行
+    style.paragraph_format.space_after = Pt(6)  # 段后0.5行
+    style.paragraph_format.line_spacing_rule = WD_LINE_SPACING.ONE_POINT_FIVE  # 1.5倍行距
 
 
-def create_bar_chart(doc, x, y, mean, title, filename):
-    plt.rcParams["font.sans-serif"] = ["SimHei"]  # 设置字体
-    plt.rcParams["axes.unicode_minus"] = False  # 正常显示负号
+# 设置单元格底纹
+def set_table_cell_shading_color(table_cell, color):
+    shading_elm = parse_xml(r''.format(nsdecls('w'), bgColor=color))
+    table_cell._tc.get_or_add_tcPr().append(shading_elm)
 
-    if title == '部门流程处理平均耗时':
-        plt.figure(figsize=(12, 6), layout='tight')
-        plt.margins(x=0.01)
-    else:
-        plt.figure(figsize=(6.4, 3.4), layout='tight')
 
-    # 绘制柱状图
-    plt.bar(x, y, color=(31 / 255, 168 / 255, 201 / 255))
-    # 绘制均值线
-    plt.axhline(mean, linestyle='dashed', color='#FF8C00')
-    # 添加标题
-    plt.title(title)
-    # 设置x轴标签旋转角度
-    plt.xticks(rotation=-30, ha='left')
-    # 添加图例
-    plt.legend(['均值:{}'.format(mean)], loc='upper right')
-    # 保存图形
-    plt.savefig('{}/{}'.format(REPORT_DIR, filename))
-    plt.close()
-    # 插入图形到 Word 文档中
-    p = doc.add_paragraph()
-    p.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
-    r = p.add_run()
-    r.add_picture('{}/{}'.format(REPORT_DIR, filename), width=Inches(6))
+# 设置表格行底纹
+def set_table_row_shading_color(table_row, color):
+    shading_dict = {}
+    for i, cell in enumerate(table_row.cells):
+        shading_dict['shading_elm_' + str(i)] = parse_xml(r''.format(nsdecls('w'), bgColor=color))
+        cell._tc.get_or_add_tcPr().append(shading_dict['shading_elm_' + str(i)])
+
+
+def add_heading(doc, head_level, head_content):
+    h = doc.add_heading(level=head_level)
+    r = h.add_run(head_content)
+    set_heading_format(h, r, head_level)
 
 
-def create_bar_twinx_chart(doc, x, y1, y2, mean, title, filename):
+def create_twinx_chart(department, title, filename):
     plt.rcParams["font.sans-serif"] = ["SimHei"]  # 设置字体
     plt.rcParams["axes.unicode_minus"] = False  # 正常显示负号
 
+    x = []
+    y1 = []
+    y2 = []
+    for user in department['d_users']:
+        x.append(user[0])  # 用户名
+        y1.append(user[2])  # 用户处理流程耗时
+        y2.append(user[1])  # 用户处理流程条数
+
     x_range = np.arange(len(x))
 
-    width = 0.25  # the width of the bars
+    bgcolor = '#d9d9d9'
 
-    fig, ax1 = plt.subplots()
-    ax1.set_ylabel('耗时')
-    rects = ax1.bar(x_range, y1, width, color=(31 / 255, 168 / 255, 201 / 255), label='耗时')
-    ax1.bar_label(rects, padding=3)
-    ax1.set_xticks(x_range + width / 2, x)
+    fig, ax1 = plt.subplots(figsize=(8, 5), layout='constrained', facecolor=bgcolor)
+    ax1.set_ylabel('平均处理流程时间(小时)')
+    y1_bar = ax1.bar(x_range, y1, width=0.25, label='流程处理时间', color=(91 / 255, 155 / 255, 213 / 255))
+
+    ax1.set_xticks(x_range, x)
     # 设置x轴标签旋转角度
     plt.xticks(rotation=-30, ha='left')
-    # 绘制均值线
-    plt.axhline(mean, linestyle='dashed', color='#FF8C00', label=f'均值:{mean}')
 
     ax2 = ax1.twinx()  # instantiate a second axes that shares the same x-axis
 
-    ax2.set_ylabel('流程数')
-    rects = ax2.bar(x_range + width, y2, width, color='#5AC189', label='流程数')
-    ax2.bar_label(rects, padding=3)
+    y2_line, = ax2.plot(x_range, y2, label='处理流程条数', color='#ff0000', marker='o', linestyle='dashed', linewidth=2)
+    ax2.set_ylabel('处理流程条数(条)')
+    ax2.set_ylim(0, max(y2) + 1)
+    ax2.yaxis.set_major_locator(ticker.MaxNLocator(integer=True))  # 设置整数刻度
+
+    annotate_kwargs = {'color': '#ffffff', 'arrowprops': dict(color='#a5a5a5', arrowstyle='-')}
+
+    # y2_line:添加数据标签
+    # UserWarning: constrained_layout not applied because axes sizes collapsed to zero.
+    for x, y in zip(x_range, y2):
+        xtext_offset = 0.01 if len(x_range) == 1 else 0.1
+        ax2.annotate(f"{y}", xy=(x, y), xytext=(x + xtext_offset, y + 0.1), backgroundcolor='#000000', **annotate_kwargs)
+
+    # 绘制均值线:相对于左侧坐标轴"平均处理流程时间(小时)"绘制
+    hline_kwargs = {'linestyle': 'solid', 'linewidth': 2}
+    axhline1 = ax1.axhline(department['d_elapse'], label='部门平均时间', color='#ed7d31', **hline_kwargs)
+    axhline2 = ax1.axhline(procinst_duration_by_company, label='公司平均时间', color='#a5a5a5', **hline_kwargs)
+
+    # 获取 y1 轴两个刻度之间的距离
+    y1_ticks = ax1.get_yticks()
+    y1_ticks_distance = y1_ticks[1] - y1_ticks[0] if len(y1_ticks) > 1 else y1_ticks[0]
+
+    # y1_bar:添加数据标签
+    bar_label_kwargs = {'color': '#ffffff', 'backgroundcolor': '#8ba7de'}
+    y1_threshold = y1_ticks_distance / 2
+    y1_bar_large = [v if v > y1_threshold else '' for v in y1]
+    y1_bar_small = [v if v <= y1_threshold else '' for v in y1]
+    ax1.bar_label(y1_bar, y1_bar_large, padding=-20, **bar_label_kwargs)
+    ax1.bar_label(y1_bar, y1_bar_small, padding=3, **bar_label_kwargs)
+
+    # 均值线:添加数据标签
+    x_pos = 0 if len(x_range) == 1 else 1
+    xtext_benchmark = 0 if len(x_range) == 1 else 1
+    xtext_offset = 0.03 if len(x_range) == 1 else 0.3
+    ytext_offset = y1_ticks_distance / 3
+    ax1.annotate(f"{department['d_elapse']}", xy=(x_pos, department['d_elapse']),
+                 xytext=(xtext_benchmark - xtext_offset, department['d_elapse'] + ytext_offset),
+                 backgroundcolor='#f5b482', **annotate_kwargs)
+    ax1.annotate(f"{procinst_duration_by_company}", xy=(x_pos, procinst_duration_by_company),
+                 xytext=(xtext_benchmark + xtext_offset, procinst_duration_by_company + ytext_offset),
+                 backgroundcolor='#ef95a0', **annotate_kwargs)
+
+    plt.title(title, fontsize=14, pad=30)  # pad: 标题与坐标轴顶部的偏移量
+    fig.legend(handles=[y1_bar, axhline1, axhline2, y2_line], loc='outside lower center', ncols=4, facecolor=bgcolor, edgecolor=bgcolor)
+
+    ax1.set_xlabel(department['d_name'], fontsize=12, labelpad=8)  # 在图形下方显示部门名称
+    ax1.set_facecolor(bgcolor)  # 设置绘图区域的背景色
+    # 去掉绘图区域的边框线和坐标轴刻度线
+    axes = [ax1, ax2]
+    for ax in axes:
+        ax.spines['top'].set_visible(False)
+        ax.spines['bottom'].set_visible(False)
+        ax.spines['left'].set_visible(False)
+        ax.spines['right'].set_visible(False)
+        ax.tick_params(axis='both', which='both', length=0)
+
+    # 设置绘图区与图片边缘的距离
+    # plt.subplots_adjust(left=0.08, right=0.94, top=0.9, bottom=0.24)
 
-    plt.title(title)
-    fig.legend(loc='upper right', ncols=1, bbox_to_anchor=(1, 1), bbox_transform=ax1.transAxes)
-    fig.tight_layout()  # otherwise the right y-label is slightly clipped
     # 保存图形
     plt.savefig('{}/{}'.format(REPORT_DIR, filename))
     plt.close()
-    # 插入图形到 Word 文档中
-    p = doc.add_paragraph()
-    p.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
-    r = p.add_run()
-    r.add_picture('{}/{}'.format(REPORT_DIR, filename), width=Inches(6))
 
 
-def add_section(doc, rank, department, d_elapse):
-    h = doc.add_heading(level=2)
-    r = h.add_run('{}. {}'.format(rank, department))
-    set_heading_format(h, r)
-
-    try:
-        qs = db_helper.querystring_user_gt_senior_avg(
-            start_time, end_time, d_elapse
-        ) if department == '高管' else db_helper.querystring_user_gt_department_avg(
-            start_time, end_time, department, d_elapse)
-        user_gt_department_avg = client.execute(qs)
-
-        qs = db_helper.querystring_procinst_duration_by_user_senior(
-            start_time, end_time
-        ) if department == '高管' else db_helper.querystring_procinst_duration_by_user(
-            start_time, end_time, department)
-        procinst_duration_by_user = client.execute(qs)
-
-        qs = db_helper.querystring_procinst_count_by_senior(
-            start_time, end_time
-        ) if department == '高管' else db_helper.querystring_procinst_count_by_user(
-            start_time, end_time, department)
-        procinst_count_by_user = client.execute(qs)
-    except Exception as error:
-        print('数据库查询错误:', error)
-        logging.error('数据库查询错误:{}'.format(error))
-        raise RuntimeError('数据库查询错误:', error)
+def add_section(doc, department_data):
+    d_name = department_data['d_name']
+    d_elapse = department_data['d_elapse']
+    d_elapse_rank = department_data['d_elapse_rank']
+    d_count = department_data['d_count']
+    d_count_rank = department_data['d_count_rank']
+    d_users = department_data['d_users']
 
-    users = []
-    for user, _ in user_gt_department_avg:
-        users.append(user)
+    d_users_gt_company_avg_duration = [u[0] for u in d_users if u[2] > procinst_duration_by_company]
 
-    p = doc.add_paragraph()
-    p.add_run('{}{}月份个人处理流程平均耗时时长在公司排名为'.format(department, last_month))
-    p.add_run('{}'.format(rank)).underline = True
-    if len(procinst_duration_by_user) == 1:
-        p.add_run(',{}公司平均水平。'.format(
-            '高于' if d_elapse > procinst_duration_by_company else
-            '低于' if d_elapse < procinst_duration_by_company else '等于'))
-    else:
-        p.add_run(',其中耗时较长的有')
-        p.add_run('{}'.format('、'.join(users))).underline = True
-        p.add_run(',{}({}小时/流程)。'
-                  .format('高于公司平均水平和部门平均水平' if d_elapse >= procinst_duration_by_company else '低于公司平均水平但高于部门平均水平',
-                          d_elapse))
-    set_paragraph_format(p)
+    add_heading(doc, 2, '({}){}'.format(number_to_chinese(d_elapse_rank), d_name))
 
-    x = []
-    y1 = []
-    y2 = []
+    p = doc.add_paragraph(f'部门人均处理流程平均耗时时长排名:{d_elapse_rank};', style='List Bullet')
+    set_paragraph_space(p)
 
-    for user, elapse in procinst_duration_by_user:
-        x.append(user)
-        y1.append(elapse)
+    operator = '>' if d_elapse > procinst_duration_by_company else '<' if d_elapse < procinst_duration_by_company else '=='
+    p = doc.add_paragraph(f'部门处理流程平均耗时时长:{d_elapse}小时{operator}公司平均{procinst_duration_by_company}小时;', style='List Bullet')
+    set_paragraph_space(p)
 
-    for name in x:
-        for user, count in procinst_count_by_user:
-            if user == name:
-                y2.append(count)
-                break
+    users_count = len(d_users_gt_company_avg_duration)
+    p = doc.add_paragraph(style='List Bullet')
+    p.add_run(f'部门处理流程平均耗时大于公司平均水平的有{users_count}人')
+    p.add_run(f":{'、'.join(d_users_gt_company_avg_duration)};" if users_count > 0 else ";")
+    set_paragraph_space(p)
 
-    create_bar_twinx_chart(doc, x, y1, y2, d_elapse, "个人流程处理平均耗时", "{}-流程处理平均耗时.png".format(department))
-    caption = doc.add_paragraph('图 2-{} {}个人处理流程平均耗时'.format(rank, department))
-    caption.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
+    p = doc.add_paragraph(f'人均处理流程条数排名:{d_count_rank};', style='List Bullet')
+    set_paragraph_space(p)
 
+    operator = '>' if d_count > procinst_count_by_company else '<' if d_count < procinst_count_by_company else '=='
+    p = doc.add_paragraph(f'人均处理流程条数:{d_count}条{operator}公司平均{procinst_count_by_company}条;', style='List Bullet')
+    set_paragraph_space(p)
 
-def add_chapter_2(doc):
-    h = doc.add_heading(level=1)
-    r = h.add_run('二、各部门成员详情')
-    set_heading_format(h, r)
+    pic_name = f"{d_name}-平均处理流程时间及流程处理条数.png"
+    create_twinx_chart(department_data, "部门成员平均处理流程时间及流程处理条数", pic_name)
 
-    departments_excluded = ['汇派-质量部', '汇派-生产部', '汇派-计划部', '汇派-人事部', '汇派-采购部', '党建工会', '工程项目中心', '北京技术中心']
+    # 插入图形到 Word 文档中
+    p = doc.add_paragraph()
+    p.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
+    r = p.add_run()
+    r.add_picture('{}/{}'.format(REPORT_DIR, pic_name), width=Inches(6))
 
-    d = []
-    for department, elapse in procinst_duration_by_department:
-        if department not in departments_excluded:
-            d.append((department, elapse))
 
-    for index, (department, elapse) in enumerate(d):
-        add_section(doc, index + 1, department, elapse)
+def add_chapter_2(doc):
+    add_heading(doc, 1, '二、各部门成员详情')
+
+    # 部门流程处理条数排行
+    department_rank_by_count = [item[0] for item in procinst_by_department]  # procinst_by_department 默认按照流程条数倒序排列
+
+    # 部门流程处理耗时排行
+    department_sorted_by_elapse = sorted(procinst_by_department, key=lambda d: d[2], reverse=True)
+    department_rank_by_elapse = [item[0] for item in department_sorted_by_elapse]
+
+    for item in department_sorted_by_elapse:
+        department = item[0]
+        d_count = item[1]
+        d_elapse = item[2]
+        d_count_rank = department_rank_by_count.index(department) + 1
+        d_elapse_rank = department_rank_by_elapse.index(department) + 1
+
+        add_section(doc, {'d_name': department,
+                          'd_elapse': d_elapse,
+                          'd_elapse_rank': d_elapse_rank,
+                          'd_count': d_count,
+                          'd_count_rank': d_count_rank,
+                          'd_users': item[3]})
+
+
+def insert_table(doc, table_headers, col_width_dict, table_data, table_style):
+    # 在文档中添加一个空表格
+    table = doc.add_table(rows=0, cols=len(table_headers), style='Table Grid')
+
+    # 添加表头
+    heading_cells = table.add_row().cells
+    for i, header in enumerate(table_headers):
+        heading_cells[i].text = header
+        heading_cells[i].paragraphs[0].runs[0].bold = True  # 字体加粗
+
+    # 循环添加数据
+    avg_row_added = False
+    for i, row_data in enumerate(table_data):
+        if table_style in ['style2', 'style3']:
+            avg_value = {'style2': procinst_duration_by_company, 'style3': procinst_count_by_company}
+            if not avg_row_added and row_data[1] < avg_value[table_style]:
+                # 添加“平均”值行
+                row = table.add_row()
+                row_cells = row.cells
+                row_cells[0].merge(row_cells[1]).text = '平均'  # 合并单元格
+                row_cells[2].text = str(avg_value[table_style])
+                set_table_row_shading_color(row, 'ff0000')
+                row_cells[0].paragraphs[0].runs[0].bold = True  # 字体加粗
+                row_cells[2].paragraphs[0].runs[0].bold = True
+                avg_row_added = True
+
+        row = table.add_row()
+        row_cells = row.cells
+
+        row_cells[0].text = str(i + 1)
+        for j, cell_value in enumerate(row_data):
+            col_index = j + 1
+            row_cells[col_index].text = str(cell_value)
+
+        if table_style == 'style1':
+            if row_data[1] >= procinst_count_by_company:
+                set_table_cell_shading_color(row_cells[2], '#fedb61')
+            if row_data[2] >= procinst_duration_by_company:
+                set_table_cell_shading_color(row_cells[3], '#ff0000')
+        elif table_style == 'style2' and row_data[1] >= procinst_duration_by_company:
+            set_table_row_shading_color(row, 'fedb61')
+
+    if table_style == 'style1':
+        # 统计值:平均值
+        row_cells = table.add_row().cells
+        avg_data = [len(table_data) + 1, '公司平均', procinst_count_by_company, procinst_duration_by_company]
+        for i, cell_value in enumerate(avg_data):
+            row_cells[i].text = str(cell_value)
+            row_cells[i].paragraphs[0].runs[0].bold = True  # 字体加粗
+
+    # 设置表格垂直对齐方式
+    table.alignment = WD_TABLE_ALIGNMENT.CENTER
+
+    # 设置单元格样式
+    for row in table.rows:
+        for cell in row.cells:
+            cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER  # 垂直居中
+            cell.paragraphs[0].alignment = WD_PARAGRAPH_ALIGNMENT.CENTER  # 水平居中
+        for col_index, col_width in col_width_dict.items():  # 设置列宽
+            row.cells[col_index].width = Cm(col_width)
+        row.height = Cm(0.7)  # 设置行高
 
 
 def add_chapter_1(doc):
-    h = doc.add_heading(level=1)
-    r = h.add_run('一、公司各部门横向比较情况')
-    set_heading_format(h, r)
-
     try:
         qs = db_helper.querystring_user_count(start_time, end_time)
         user_count = client.execute(qs)[0][0]
-
-        qs = db_helper.querystring_department_count_gt_avg(start_time, end_time, procinst_duration_by_company)
-        department_count_gt_avg = client.execute(qs)[0][0]
-
-        qs = db_helper.querystring_user_count_gt_avg(start_time, end_time, procinst_duration_by_company)
-        user_count_gt_avg = client.execute(qs)[0][0]
     except Exception as error:
         print('数据库查询错误:', error)
         logging.error('数据库查询错误:{}'.format(error))
         raise RuntimeError('数据库查询错误:', error)
 
-    if procinst_duration_by_senior is not None and procinst_duration_by_senior > procinst_duration_by_company:
-        department_count_gt_avg += 1
+    department_count_gt_avg_count = len(list(filter(lambda d: d[1] > procinst_count_by_company, procinst_by_department)))
+    department_count_gt_avg_duration = len(list(filter(lambda d: d[2] > procinst_duration_by_company, procinst_by_department)))
+    user_count_gt_avg_count = len(list(filter(lambda d: d[1] > procinst_count_by_company, procinst_by_user)))
+    user_count_gt_avg_duration = len(list(filter(lambda d: d[2] > procinst_duration_by_company, procinst_by_user)))
 
+    add_heading(doc, 1, '一、公司各部门横向比较情况')
+    add_heading(doc, 2, '(一)一览表')
     p = doc.add_paragraph()
-    p.add_run('{}年{}月,公司共计'.format(last_year, last_month))
-    p.add_run('{}'.format(user_count)).underline = True
-    p.add_run('人使用项企平台,个人处理流程平均耗时')
-    p.add_run('{}'.format(procinst_duration_by_company)).underline = True
-    p.add_run('小时/流程,其中,平均单一流程处理耗时高于公司平均数的有')
-    p.add_run('{}'.format(department_count_gt_avg)).underline = True
-    p.add_run('个部门,')
-    p.add_run('{}'.format(user_count_gt_avg)).underline = True
-    p.add_run('人。')
+    p.add_run('{}年{}月,公司共计{}人使用项企平台,'.format(last_year, last_month, user_count))
+    p.add_run('个人处理流程平均耗时{}小时/流程,平均单一流程处理耗时高于公司平均数的有{}个部门,{}人。'.format(
+        procinst_duration_by_company, department_count_gt_avg_duration, user_count_gt_avg_duration))
+    p.add_run('个人处理流程平均条数为{}条,个人处理流程平均条数高于公司平均数的有{}个部门,{}人。'.format(
+        procinst_count_by_company, department_count_gt_avg_count, user_count_gt_avg_count))
     set_paragraph_format(p)
 
-    # 准备数据
-    x = []
-    y = []
-    for department, elapse in procinst_duration_by_department:
-        x.append(department)
-        y.append(elapse)
+    table_data = list(map(lambda d: (d[0], d[1], d[2]), procinst_by_department))
+    insert_table(doc, ['序号', '部门', '人均处理流程条数', '平均单条流程处理时间'], {0: 1.5, 1: 5.8, 2: 3.6, 3: 4.3}, table_data, 'style1')
 
-    # 创建柱状图并保存为文件
-    create_bar_chart(doc, x, y, procinst_duration_by_company, "部门流程处理平均耗时", "部门流程处理平均耗时.png")
-    caption = doc.add_paragraph('图 1 部门流程处理平均耗时')
-    caption.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
+    add_heading(doc, 2, '(二)部门平均单一流程处理耗时排行')
+    table_data = list(map(lambda d: (d[0], d[2]), procinst_by_department))
+    table_data.sort(key=lambda d: d[1], reverse=True)
+    insert_table(doc, ['排名', '部门', '平均单条流程处理时间'], {0: 1.5, 1: 5.8, 2: 4.3}, table_data, 'style2')
+
+    add_heading(doc, 2, '(三)部门人均处理流程平均条数排行')
+    table_data = list(map(lambda d: (d[0], d[1]), procinst_by_department))
+    insert_table(doc, ['排名', '部门', '人均处理流程条数'], {0: 1.5, 1: 5.8, 2: 4.3}, table_data, 'style3')
 
 
 def upload_to_qiniu(filename):
@@ -321,14 +447,15 @@ def generate_word_report():
 
     # 添加标题
     h = doc.add_heading(level=0)
-    r = h.add_run('项企流程效能分析结果公示')
+    r = h.add_run('{}月项企流程效能分析结果公示'.format(last_month))
     set_title_format(h, r)
 
     # 添加段落
     p_first = doc.add_paragraph(
-        '为了提升公司整体工作效率,确保跨部门工作高效推进,充分发挥我司自研平台的优势,现基于项企系统数据,利用Superset工具打造了项企流程效能分析平台,能够对我司项企平台的数据进行分析,进而通过量化数据发现我司办公流程流转中的问题,' \
-        '现将{}年{}月公司员工处理流程平均响应时长(响应时长为从流程流转到当前节点到当前节点处理完成的耗时,单位:小时)情况结果进行公示如下:'
-            .format(last_year, last_month))
+        '为了提升公司整体工作效率,确保跨部门工作高效推进,充分发挥我司自研平台的优势,现基于项企系统数据对我司项企平台的使用数据进行分析,'
+        '进而通过量化数据发现我司办公流程流转中的问题,'
+        '现将{}年{}月公司员工处理流程平均响应时长(响应时长为从流程流转到当前节点到当前节点处理完成的耗时,单位:小时)'
+        '以及处理流程条数情况结果进行公示如下:'.format(last_year, last_month))
     set_paragraph_format(p_first)
 
     # 一、公司各部门横向比较情况
@@ -338,11 +465,13 @@ def generate_word_report():
     add_chapter_2(doc)
 
     p_last = doc.add_paragraph(
-        '现项企业流程效能分析是公司管理工具逐步完善尝试和摸索的阶段,只是单一评估维度,大家若对评估维度有相应意见,欢迎积极提出,共同促进项企流程效能分析不断完善,进而提升公司整体效率!')
+        '针对以上数据,请各部门负责人认真对待并针对部门情况进行分析。'
+        '现项企业流程效能分析是公司管理工具逐步完善尝试和摸索的阶段,评估维度、呈现形式均在调整优化中,'
+        '欢迎大家对效能评估维提出建设意见,共同促进项企流程效能分析不断完善,进而提升公司整体效率!')
     set_paragraph_format(p_last)
 
     # 保存文档
-    report_file_name = '项企流程效能分析结果公示(全员)-{}年{}月.docx'.format(last_year, last_month)
+    report_file_name = '项企流程效能分析结果公示-{}年{}月.docx'.format(last_year, last_month)
     report_file = f'{REPORT_DIR}/{report_file_name}'
     doc.save(report_file)
     logging.info('本地报表创建成功:{}'.format(report_file))
@@ -375,6 +504,41 @@ def try_create_dir(dir_name):
         print(f"目录'{dir_name}'创建成功!")
 
 
+def generate_procinst_by_department(procinst_by_senior, procinst_by_department_user):
+    departments = {}  # 存储每个部门的信息
+
+    procinst_by_senior_user = [('高管', item[0], item[1], item[2]) for item in procinst_by_senior]
+    all_departments = procinst_by_department_user + procinst_by_senior_user
+
+    for item in all_departments:
+        department = item[0]
+        user_name = item[1]
+        procinst_count_by_user = item[2]
+        procinst_duration_by_user = item[3]
+
+        if department not in departments:
+            departments[department] = {
+                'd_users': [],
+                'd_count': [],
+                'd_duration': []
+            }
+
+        departments[department]['d_users'].append((user_name, procinst_count_by_user, procinst_duration_by_user))
+        departments[department]['d_count'].append(procinst_count_by_user)
+        departments[department]['d_duration'].append(procinst_duration_by_user)
+
+    rslt = []
+
+    for department, info in departments.items():
+        d_count_average = round_to_integer(np.mean(info['d_count']))
+        d_duration_average = round(np.mean(info['d_duration']), 2)
+        rslt.append((department, d_count_average, d_duration_average, info['d_users']))
+
+    rslt.sort(key=lambda d: d[1], reverse=True)  # 按照部门处理流程条数倒序排列
+
+    return rslt
+
+
 if __name__ == '__main__':
     try:
         try_create_dir(REPORT_DIR)
@@ -391,19 +555,14 @@ if __name__ == '__main__':
         last_year, last_month = get_report_year_month()
         start_time, end_time = get_report_start_end()
 
-        qs1 = db_helper.querystring_procinst_duration_by_company(start_time, end_time)
-        procinst_duration_by_company = client.execute(qs1)[0][0]
-
-        qs2 = db_helper.querystring_procinst_duration_by_department(start_time, end_time)
-        procinst_duration_by_department = client.execute(qs2)
-
-        qs3 = db_helper.querystring_procinst_duration_by_senior(start_time, end_time)
-        procinst_duration_by_senior = client.execute(qs3)[0][0]
+        qs1 = db_helper.querystring_procinst_by_user(start_time, end_time)
+        procinst_by_user = client.execute(qs1)
+        procinst_count_by_company = round_to_integer(np.mean([item[1] for item in procinst_by_user]))
+        procinst_duration_by_company = round(np.mean([item[2] for item in procinst_by_user]), 2)
 
-        for index, (department, elapse) in enumerate(procinst_duration_by_department):
-            if procinst_duration_by_senior is not None and procinst_duration_by_senior >= elapse:
-                procinst_duration_by_department.insert(index, ('高管', procinst_duration_by_senior))
-                break
+        qs2 = db_helper.querystring_procinst_by_user(start_time, end_time, senior=True)
+        qs3 = db_helper.querystring_procinst_by_department_user(start_time, end_time)
+        procinst_by_department = generate_procinst_by_department(client.execute(qs2), client.execute(qs3))
 
         generate_word_report()  # 生成报表文件
     except Exception as error:
diff --git a/code/pep-stats-report/requirements.txt b/code/pep-stats-report/requirements.txt
index e91d0da..6e2eda5 100644
--- a/code/pep-stats-report/requirements.txt
+++ b/code/pep-stats-report/requirements.txt
@@ -11,7 +11,7 @@ fonttools==4.39.3
 idna==3.4
 importlib-resources==5.12.0
 kiwisolver==1.4.4
-lxml==4.9.2
+lxml==4.9.3
 matplotlib==3.7.1
 numpy==1.24.2
 packaging==23.0
@@ -22,7 +22,7 @@ pyinstaller-hooks-contrib==2023.2
 PyMuPDF==1.22.3
 pyparsing==3.0.9
 python-dateutil==2.8.2
-python-docx==0.8.11
+python-docx==1.1.0
 pytz==2023.3
 pytz-deprecation-shim==0.1.0.post0
 pywin32==306
@@ -31,6 +31,7 @@ qiniu==7.10.0
 requests==2.28.2
 self==2020.12.3
 six==1.16.0
+typing_extensions==4.8.0
 tzdata==2023.3
 tzlocal==4.3
 urllib3==1.26.15
diff --git a/code/pep-stats-report/requirements_linux.txt b/code/pep-stats-report/requirements_linux.txt
index 05d878e..582cf08 100644
--- a/code/pep-stats-report/requirements_linux.txt
+++ b/code/pep-stats-report/requirements_linux.txt
@@ -11,7 +11,7 @@ fonttools==4.39.3
 idna==3.4
 importlib-resources==5.12.0
 kiwisolver==1.4.4
-lxml==4.9.2
+lxml==4.9.3
 matplotlib==3.7.1
 numpy==1.24.2
 packaging==23.0
@@ -22,7 +22,7 @@ pyinstaller-hooks-contrib==2023.2
 PyMuPDF==1.22.3
 pyparsing==3.0.9
 python-dateutil==2.8.2
-python-docx==0.8.11
+python-docx==1.1.0
 pytz==2023.3
 pytz-deprecation-shim==0.1.0.post0
 pywin32-ctypes==0.2.0
@@ -30,6 +30,7 @@ qiniu==7.10.0
 requests==2.28.2
 self==2020.12.3
 six==1.16.0
+typing_extensions==4.8.0
 tzdata==2023.3
 tzlocal==4.3
 urllib3==1.26.15