工单全渠道统计及字段优化需求文档

多维度、跑批定时任务、多角度分析

页面内容

需求概述

投诉数据对比报表

业务规则

  1. 报表名称:投诉数据对比报表

  2. 菜单路径:工单系统 → 报表系统 → 报表查询 → 投诉数据对比报表

  3. 功能描述

    • 支持按年度、季度、月度对远程银行各渠道的投诉数据进行对比分析。
  4. 报表权限

    • 总行消费者权益保护部工单用户和运营管理部(服务督查室)客户关注岗、值班经理、坐席组长:查询及导出全量数据。
    • 分行工单用户:查询及导出“业务所在城市”为本分行的数据。
    • 支行工单用户:无该报表任何权限,不展示该报表菜单。
  5. 数据范围(投诉渠道)

    • 总行监管转办
    • 信用卡中心监管转办
    • 远程银行中心自收
    • 信用卡中心自收
    • 其他

投诉渠道与工单类型对应关系

投诉渠道工单类型/数据来源解释说明
总行监管转办全渠道投诉报表中“总行监管转办投诉”的数据(总行监管转办工单+分行监管转办工单)总分行非信用卡条线受理的监管机构转办投诉
信用卡中心监管转办全渠道投诉报表中,“投诉来源”为“信用卡中心监管转办投诉”的投诉数据信用卡中心受理的监管机构转办投诉
远程银行中心自收参见下文“自收投诉来源和工单类型对应关系表”信用卡中心自有渠道受理的客户投诉
其他其他投诉工单(暂无)其他渠道受理的客户投诉

自收投诉来源和工单类型对应关系表

自收投诉来源工单类型解释说明
自有渠道投诉工单95577/4006695577 受理的客户投诉
12378呼转投诉工单(12378)12378呼转至95577或4006695577的客户投诉
消保平台转办消保平台转送投诉(总)消保平台转办至95577或4006695577的客户投诉
其他其他(暂无)其他投诉来源的客户投诉
  1. 查询条件

    • 投诉渠道
    • 自收投诉来源
    • 对比时段类型
    • 查询数据时段
    • 对比条件
    • 对比单位
    • 业务条线
    • 被投诉单位
  2. 查询条件说明

    • 查询条件【对比时段类型】选择“年度”时,【查询数据时段】仅支持选择某年份;季、月、自选同理,支持与之对应的具体时间范围。
    • 特别说明:查询条件选择“年”时,没有同比数据,显示“/”。
  3. 对比条件释义

    • “全量”:对全量投诉数据进行对比
    • “剔除重复”:统计对比时段内唯一来电号码的投诉数据,重复号码的多笔投诉仅做一笔
    • “剔除无效”:在“剔除重复”数据基础上,同时剔除各经办单位提出并审核通过的无效投诉
  4. 被投诉单位

    • 按投诉工单中“业务所在城市”字段的数据取值
    • 查询条件选项与投诉工单“业务所在城市”一致,支持单选和全选,默认“全选”
  5. 业务条线

    • 按投诉工单中“涉及条线”字段的数据取值
    • 查询条件选项与投诉工单“涉及条线”一致,支持单选和全选,默认“全选”
  6. 同比、环比数据公式

    • 同比增量 = 本期 - 同期
    • 同比增幅 = (本期 - 同期) / 同期 × 100%
    • 环比增量 = 本期 - 上期(例如:3月-2月)
    • 环比增幅 = (本期 - 上期) / 上期 × 100%

    说明

    • 如果本期为本日、月、季,则同期指上一年同日、月、季;如无同日,则取上年同月最后一日。
  7. 其他说明

    • 反显“投诉人电话号码”,显示该号码近30个自然日内的咨询工单数量。
    • 支持手动调整。

口径确认(已拍板)

1)“剔除重复”的去重键

  • 去重键:仅使用 来电号/投诉人电话号码 去重(不叠加渠道/条线/部门等维度)。
  • 去重范围:在“查询数据时段”内,同一来电号出现的多笔投诉仅计 1 笔。
  • 建议实现:先对明细按 dedup_phone 归一化(去空格、去 +86、去分隔符),再做去重。

2)“剔除无效”的生效时间

  • 生效时间:按“无效剔除申请”的 审批通过时间 生效。
  • 统计规则:在“剔除重复”的集合上,再剔除 审批通过且已生效 的记录(以通过时间为准)。
  • 审计要求:需保留审批人、审批时间、原因、撤销等变更轨迹,以便追溯。

3)“对比单位”的定义(你关心的重点)

“对比单位”用于控制报表输出的分组维度(GROUP BY),与“业务条线/被投诉单位”等筛选条件(WHERE)要区分开。

  • 对比单位=业务条线:例如“零售业务部、法律合规部……”,取自工单字段“涉及条线”。
  • 对比单位=投诉部门(分行/机构):例如“北京分行、南京分行……”,取自工单字段“投诉部门/经办单位/机构归属”(需在数据字典中明确唯一来源字段与枚举值)。

建议将 对比单位 设计成枚举:LINE(业务条线) / DEPT(投诉部门/机构)。\n


研发设计分析:需要考虑的问题与解决方案

一、是否需要跑批?结论:建议必须跑批

原因:该报表包含多维筛选 + 去重(仅来电号)+ 剔除无效(审批通过时间生效)+ 同比/环比对照,属于典型 OLAP 统计场景。\n 推荐方式:\n

  • T+1 日批汇总:每天产出“按天分区”的聚合表;查询按年/季/月/自选再二次汇总。
  • 增量回补:当“无效剔除审批通过/撤销”发生时,触发对相关日期分区的回补(建议回补近 90 天窗口,或按审批涉及工单精确回补)。\n

二、维度设计:筛选维度(WHERE)与对比维度(GROUP BY)分离

1)筛选维度(WHERE)

  • 投诉渠道(channel)
  • 自收投诉来源(source)
  • 查询数据时段(period range)
  • 对比条件(全量 / 剔除重复 / 剔除无效)
  • 被投诉单位(业务所在城市,city)【同时承载权限过滤】
  • 业务条线(line)
  • 投诉部门/机构(dept)【可选:当对比单位不是 dept 时也可作为过滤条件】

2)对比维度(GROUP BY)

  • 对比单位=业务条线:按 line 分组输出
  • 对比单位=投诉部门:按 dept 分组输出\n

这样可以同时满足:\n

  • “看条线对比”(零售业务部 vs 法律合规部)\n
  • “看分行/部门对比”(北京分行 vs 南京分行)\n

三、去重(仅来电号)如何落地

关键点:去重键只有来电号,但统计仍需按渠道/条线/部门/城市等维度输出。\n 建议采用“先去重、再归属维度”的策略:\n

  1. 在统计期内按 dedup_phone 分组,选取代表记录(建议:最早创建时间的一笔;若有优先级规则则按优先级)。\n
  2. 代表记录决定该“唯一投诉”的维度归属(channel/source/line/dept/city)。\n

注意:同一来电号跨多个条线/部门出现时,口径会归到“代表记录”的那一个维度,这是“仅来电号去重”的必然结果,需在报表口径说明中写清楚。\n

四、剔除无效(审批通过时间生效)如何落地

建议新增/明确一张“无效剔除”表(或在工单表冗余必要字段):\n

  • order_id\n
  • dedup_phone\n
  • approved_at(审批通过时间)\n
  • status(通过/撤销/驳回)\n
  • approved_byreason\n

计算“剔除无效”时:\n

  • 先得到“剔除重复”的代表记录集合\n
  • 再过滤掉:status=通过approved_at 已生效 的记录\n

五、同比/环比边界处理(必须写进验收标准)

  • 分母为 0:增幅显示 “/”(不可计算),不要显示 0%\n
  • 年维度:按需求无同比,显示“/”\n
  • 自选区间:\n
    • 同比:上年同区间(若无同日→取上年同月最后一日)\n
    • 环比:上一段等长区间\n

六、号码反显与审计

  • 建议默认脱敏展示;仅特定角色可见全号。\n
  • 对“查看全号/导出全号/手动调整咨询量”等操作,记录审计日志(人、时间、IP、变更前后)。\n

全渠道投诉统计 - 简化设计文档

一、设计理念

采用单表聚合设计,通过时间序列编码实现多粒度数据存储,避免复杂的多层表结构。

二、时间序列编码设计

2.1 编码规则

格式:YYYY Q MM DD
位数:4   1  2  2

说明:
- YYYY:4位年份(必填)
- Q:1位季度(1-4,0表示汇总)
- MM:2位月份(01-12,00表示汇总)
- DD:2位日期(01-31,00表示汇总)

2.2 编码示例

时间粒度编码示例含义
日粒度20260103112026年Q1季3月11日
月粒度20260103002026年Q1季3月
季粒度20260100002026年Q1季
年粒度20260000002026年

三、最终统计表设计

CREATE TABLE ads_complaint_final_stats (
    -- 时间维度
    time_code STRING COMMENT '时间编码(YYYYQMMDD)',
    year_code STRING COMMENT '年份(YYYY)',
    quarter_code STRING COMMENT '季度编码(含汇总标识)',
    month_code STRING COMMENT '月份编码(含汇总标识)',
    day_code STRING COMMENT '日编码(含汇总标识)',

    -- 维度类型
    dimension_type STRING COMMENT '维度类型(LINE-条线/DEPT-部门)',
    dimension_code STRING COMMENT '维度代码',
    dimension_name STRING COMMENT '维度名称',

    -- 渠道维度
    channel_root STRING COMMENT '渠道根分类(电话/APP/网点等)',
    complaint_source STRING COMMENT '投诉来源(二级分类)',

    -- 状态维度
    is_deleted_flag INT COMMENT '是否删除(0-正常/1-已删除)',
    is_effective_flag INT COMMENT '是否生效(0-无效/1-有效)',

    -- 统计指标
    total_complaint_count BIGINT COMMENT '投诉总量',
    distinct_phone_count BIGINT COMMENT '去重后总量(按电话去重)',

    -- 元数据
    data_granularity STRING COMMENT '数据粒度(Y/Q/M/D)',
    etl_time TIMESTAMP COMMENT 'ETL时间',
    dt STRING COMMENT '分区日期'
)
COMMENT '全渠道投诉最终统计表'
PARTITIONED BY (dt STRING)
STORED AS ORC
TBLPROPERTIES ('orc.compress'='SNAPPY');

四、数据写入示例

4.1 日粒度数据

-- 日粒度:2026年3月11日
INSERT INTO ads_complaint_final_stats
SELECT
    '2026010311' as time_code,
    '2026' as year_code,
    '202601' as quarter_code,  -- 2026年Q1
    '202603' as month_code,     -- 2026年3月
    '11' as day_code,
    dimension_type,
    dimension_code,
    dimension_name,
    channel_root,
    complaint_source,
    is_deleted_flag,
    is_effective_flag,
    COUNT(*) as total_complaint_count,
    COUNT(DISTINCT phone_no) as distinct_phone_count,
    'D' as data_granularity,
    CURRENT_TIMESTAMP as etl_time,
    '20260311' as dt
FROM dwd_complaint_detail
WHERE dt = '20260311'
GROUP BY
    dimension_type, dimension_code, dimension_name,
    channel_root, complaint_source,
    is_deleted_flag, is_effective_flag;

4.2 月粒度数据

-- 月粒度:2026年3月
INSERT INTO ads_complaint_final_stats
SELECT
    '2026010300' as time_code,  -- 日部分为00
    '2026',
    '202601',
    '202603',
    '00',
    -- 其他维度同上
    'M' as data_granularity,
    CURRENT_TIMESTAMP,
    '20260331'  -- 用月末日期做分区
FROM ...
GROUP BY ...;

4.3 季粒度数据

-- 季粒度:2026年Q1
INSERT INTO ads_complaint_final_stats
SELECT
    '2026010000' as time_code,  -- 月、日均为00
    '2026',
    '202601',
    '0000',      -- 月为0000表示汇总
    '00',
    'Q' as data_granularity,
    ...
FROM ...
WHERE quarter_code = '202601';

4.4 年粒度数据

-- 年粒度:2026年
INSERT INTO ads_complaint_final_stats
SELECT
    '2026000000' as time_code,  -- 季、月、日均为0
    '2026',
    '0000',      -- 季为0000表示汇总
    '0000',
    '00',
    'Y' as data_granularity,
    ...
FROM ...
WHERE year_code = '2026';

五、查询示例

5.1 查询某日数据

-- 查询2026年3月11日,按条线统计
SELECT
    dimension_name,
    channel_root,
    SUM(CASE WHEN is_effective_flag=1 THEN distinct_phone_count ELSE 0 END) as effective_count,
    SUM(distinct_phone_count) as total_count
FROM ads_complaint_final_stats
WHERE time_code = '2026010311'  -- 精确日粒度
    AND dimension_type = 'LINE'
GROUP BY dimension_name, channel_root;

5.2 查询月度趋势

-- 查询2026年Q1各月趋势
SELECT
    time_code,
    month_code,
    SUM(distinct_phone_count) as monthly_total
FROM ads_complaint_final_stats
WHERE time_code LIKE '20260103%'  -- 匹配Q1所有日粒度
    AND data_granularity = 'D'
    AND dimension_type = 'DEPT'
    AND dimension_code = 'BJ001'  -- 北京分行
GROUP BY time_code, month_code
ORDER BY time_code;

5.3 同比环比查询

-- 2026年3月 vs 2025年3月(同比)
WITH current_month AS (
    SELECT SUM(distinct_phone_count) as cnt
    FROM ads_complaint_final_stats
    WHERE time_code = '2026010300'  -- 2026年3月
        AND dimension_type = 'LINE'
        AND dimension_code = 'RETAIL'
),
last_year_month AS (
    SELECT SUM(distinct_phone_count) as cnt
    FROM ads_complaint_final_stats
    WHERE time_code = '2025010300'  -- 2025年3月
        AND dimension_type = 'LINE'
        AND dimension_code = 'RETAIL'
)
SELECT
    current_month.cnt as current_value,
    last_year_month.cnt as last_year_value,
    CASE
        WHEN last_year_month.cnt = 0 OR last_year_month.cnt IS NULL THEN '/'
        ELSE ROUND((current_month.cnt - last_year_month.cnt) * 100.0 / last_year_month.cnt, 2)
    END as yoy_ratio
FROM current_month, last_year_month;

六、跑批简化方案

6.1 单表覆盖策略

-- 每日跑批:同时写入所有粒度
INSERT OVERWRITE TABLE ads_complaint_final_stats
PARTITION(dt='${bizdate}')
-- 日粒度
SELECT
    CONCAT(year_code, quarter_code, month_code, day_code) as time_code,
    -- ... 其他字段
    'D' as data_granularity
FROM daily_agg

UNION ALL

-- 月粒度(月末执行)
SELECT
    CONCAT(year_code, quarter_code, month_code, '00') as time_code,
    'M' as data_granularity
FROM month_agg
WHERE day_code = 'LAST_DAY'

UNION ALL

-- 季粒度(季末执行)
SELECT
    CONCAT(year_code, quarter_code, '0000') as time_code,
    'Q' as data_granularity
FROM quarter_agg
WHERE month_code = 'LAST_MONTH_OF_QUARTER'

UNION ALL

-- 年粒度(年末执行)
SELECT
    CONCAT(year_code, '000000') as time_code,
    'Y' as data_granularity
FROM year_agg
WHERE quarter_code = 'Q4';

6.2 调度配置

调度任务:
  - 名称: 每日增量统计
    频率: 每天
    执行: 写入当日粒度和汇总月/季/年(若月末/季末/年末)

  - 名称: 月末全量重刷
    频率: 每月1号
    执行: 重刷上月所有日粒度和月粒度

  - 名称: 季末全量重刷
    频率: 每季度初
    执行: 重刷上季度所有粒度

七、数据查询API

@RestController
@RequestMapping("/api/v1/complaint/stats")
public class ComplaintStatsApi {

    @GetMapping("/query")
    public Result queryStats(
        @RequestParam String startTime,  // 2026010100
        @RequestParam String endTime,    // 2026033100
        @RequestParam String granularity, // D/M/Q/Y
        @RequestParam String dimensionType,
        @RequestParam(required=false) String dimensionCode,
        @RequestParam(required=false) Boolean includeYoY,
        @RequestParam(required=false) Boolean includeMoM
    ) {
        // 1. 解析时间范围
        String timePattern = buildTimePattern(startTime, endTime, granularity);

        // 2. 查询数据
        List<StatsVO> data = sqlSession.selectList(
            "queryTimeSeries",
            Map.of(
                "timePattern", timePattern,
                "dimensionType", dimensionType,
                "dimensionCode", dimensionCode,
                "dataGranularity", granularity
            )
        );

        // 3. 计算同比环比(如需)
        if (includeYoY || includeMoM) {
            enrichWithYoYMoM(data, granularity);
        }

        return Result.success(data);
    }
}

八、简化后的优势

  1. 表结构简单:只需维护1张最终统计表
  2. 查询高效:直接通过time_code过滤,无需多层JOIN
  3. 维护成本低:无复杂的层级表关系
  4. 扩展性好:新增维度只需添加字段
  5. 时序语义清晰:编码自带时间粒度信息

九、注意事项

  1. 编码唯一性:确保time_code+维度组合唯一
  2. 数据冗余:不同粒度数据共存是设计使然
  3. 存储空间:相比分表会多存储,但简化了查询
  4. 索引设计:建议对time_codedimension_type建立索引

文档版本:v3.0(简化版)
更新日期:2024-01-xx
责任人:数据架构组