# 语音数据FTP同步服务 > 基于淮南 yydc-tb-server 老代码改造的 Spring Boot 服务 > 用于从FTP服务器读取录音数据,上传至OSS并入库到人大金仓数据库 ## 项目简介 本服务定时从FTP服务器读取通话记录txt文件,下载对应的mp3录音文件,上传到OSS对象存储,并将通话记录保存到人大金仓数据库。**每个地市单独部署一份服务**。 参考老代码(淮南):`src/main/java/淮南/MineService.java`(已废弃,新代码基于其重写) ## 技术栈 | 技术 | 版本 | |-----|------| | Spring Boot | 2.6.13 | | MyBatis | 2.3.1 | | 数据库 | 人大金仓 (KingbaseES) | | 工具库 | Apache Commons Net (FTP) | | JDK | 1.8+ | ## 目录结构 ``` dataservice-yy/ ├── src/main/java/com/threecloud/dataserviceyy/ │ ├── DataserviceYyApplication.java # 启动类 │ ├── config/ │ │ ├── DataSourceConfig.java # 数据源配置 │ │ └── FtpSyncProperties.java # FTP配置 │ ├── entity/ │ │ └── MidVoiceCallRecord.java # 通话记录实体 │ ├── mapper/ │ │ └── MidVoiceCallRecordMapper.java # MyBatis Mapper │ ├── service/ │ │ └── FtpSyncService.java # 核心同步服务 │ └── util/ │ ├── FilePathUtil.java # 路径工具 │ ├── FileUploadUtil.java # OSS上传工具 │ ├── FtpUtil.java # FTP工具 │ └── DateUtil.java # 日期工具 ├── src/main/resources/ │ ├── application.yml # 内部配置 │ ├── logback-spring.xml # 日志配置 │ └── mapper/ │ └── MidVoiceCallRecordMapper.xml # SQL映射 ├── config/ │ └── application-external.yml # 外部配置(部署时修改) ├── sql/ │ └── mid_voice_call_record.sql # 建表SQL ├── pom.xml # Maven配置 └── README.md # 本文档 ``` ## 数据流程 ``` ┌──────────────┐ 1.连接 ┌──────────────┐ │ 本服务 │◀───────────▶│ FTP服务器 │ │ │ 2.下载txt │ (录音盒) │ │ │ 3.下载mp3 │ │ └──────┬───────┘ └──────────────┘ │ │ 4.上传 ▼ ┌──────────────┐ │ OSS存储 │ http://oss地址/voice/合肥/340100/20240101/xxx.mp3 └──────────────┘ │ │ 5.入库 ▼ ┌──────────────┐ │ 人大金仓 │ mid_voice_call_record 表 └──────────────┘ ``` ## 数据库表 ### mid_voice_call_record(通话记录表) ```sql CREATE TABLE "mid_voice"."mid_voice_call_record" ( "id" int8 NOT NULL GENERATED ALWAYS AS IDENTITY, "city_code" varchar(20), "city_name" varchar(100), "call_record_id" varchar(100) NOT NULL, -- 唯一标识,建议加唯一索引 "call_tel" varchar(50) NOT NULL, -- 主叫 "called_tel" varchar(200) NOT NULL, -- 被叫 "call_start_time" timestamp(6) NOT NULL, "call_end_time" timestamp(6) NOT NULL, "call_duration" int4 NOT NULL, -- 秒 "call_direction" varchar(50) NOT NULL, -- 1呼入/2呼出 "device_no" varchar(100) NOT NULL, -- 设备编码(FTP_地市编码) "business_scenario" varchar(100), "recording_file_name" varchar(200) NOT NULL, "recording_file_path" varchar(500) NOT NULL, -- OSS URL "recording_file_size" int4, "call_status" varchar(20), -- 1正常/2未接通 "fail_reason" text, "remarks" text, "sync_time" timestamp(6) DEFAULT CURRENT_TIMESTAMP, "create_time" timestamp(6) DEFAULT CURRENT_TIMESTAMP, "org_code" varchar(64), PRIMARY KEY ("id") ) PARTITION BY LIST ("city_code"); ``` **建议加唯一索引**(防重最后一道防线): ```sql CREATE UNIQUE INDEX idx_call_record_id_unique ON mid_voice.mid_voice_call_record (call_record_id); ``` ## 部署说明 ### 1. 准备JAR包 ```bash cd /Users/wang/sanduoyun/developspace/dataservice-yy mvn clean package -DskipTests ``` 产物:`target/dataservice-yy-0.0.1-SNAPSHOT.jar` ### 2. 部署目录结构 每台服务器(每地市): ``` /opt/dataservice-yy/ ├── dataservice-yy-0.0.1-SNAPSHOT.jar ├── config/ │ └── application-external.yml # 修改此文件配置FTP ├── vaa-ftp-temp/ # 自动创建(临时文件) └── logs/ # 自动创建(运行日志) └── app.log ``` ### 3. 修改外部配置 编辑 `config/application-external.yml`: ```yaml spring: datasource: url: jdbc:kingbase8://你的数据库IP:54321/kingbase?currentSchema=mid_voice username: dcms_dev password: 你的密码 ftp-sync: cities: - city-code: "340100" # 你的地市编码 city-name: "合肥" ftp-host: 10.126.129.7 # FTP服务器 ftp-port: 9979 ftp-username: ftpuser ftp-password: ftppass ftp-source-dir: /340100/record/ # txt目录 ftp-record-dir: /340100/recordfile/ # mp3目录 ftp-archive-dir: /340100/processed/ # 归档目录 vaa-sync: oss: base-url: http://你的OSS地址:9090 upload-url: http://你的OSS地址:9090/apiOss/oss/fileUpload appcode: dataservice-yy appid: 你的appid appsecret: 你的appsecret ``` ### 4. 启动服务 ```bash cd /opt/dataservice-yy nohup java -jar dataservice-yy-0.0.1-SNAPSHOT.jar > /dev/null 2>&1 & ``` ### 5. 验证启动 ```bash tail -f logs/app.log ``` 看到如下日志说明启动成功: ``` 【项目启动】开始执行首次FTP录音同步 【项目启动】首次同步完成,后续将在每2小时自动执行 ``` ## 配置项说明 ### 数据库配置(spring.datasource) | 配置项 | 必填 | 说明 | |-------|-----|------| | `url` | ✓ | JDBC URL(人大金仓:jdbc:kingbase8://IP:port/db) | | `username` | ✓ | 数据库用户 | | `password` | ✓ | 数据库密码 | | `driver-class-name` | ✓ | com.kingbase8.Driver | ### FTP配置(ftp-sync.cities[]) | 配置项 | 必填 | 说明 | |-------|-----|------| | `city-code` | ✓ | 地市编码(如340100),与数据库分区对应 | | `city-name` | ✓ | 地市名称(用于OSS路径) | | `ftp-host` | ✓ | FTP服务器IP | | `ftp-port` | ✓ | FTP端口 | | `ftp-username` | ✓ | FTP用户名 | | `ftp-password` | ✓ | FTP密码 | | `ftp-source-dir` | ✓ | txt源目录(处理后归档) | | `ftp-record-dir` | ✓ | mp3/wav文件目录 | | `ftp-archive-dir` | ✓ | 处理完的txt移动到此目录 | ### FTP全局配置(ftp-sync.*) | 配置项 | 默认值 | 说明 | |-------|-------|------| | `enabled` | true | 是否启用FTP同步 | | `sync-interval-cron` | 0 0 0/2 * * ? | 定时任务(每2小时) | | `temp-path` | ./vaa-ftp-temp | 本地临时文件目录 | | `retain-days` | 10 | 临时文件保留天数 | | `txt-encoding` | GBK | txt文件编码(老系统用GBK) | | `field-separator` | ※ | 字段分隔符(老系统用※) | ### OSS配置(vaa-sync.oss) | 配置项 | 必填 | 说明 | |-------|-----|------| | `base-url` | ✓ | OSS访问基础URL | | `upload-url` | ✓ | OSS上传接口URL | | `appcode` | ✓ | 应用编码 | | `appid` | ✓ | 应用ID | | `appsecret` | ✓ | 应用密钥 | ## txt文件格式 **编码**:GBK **分隔符**:※ **字段顺序**: ``` kssj※jssj※hjls※hjzls※zjhm※bjhm※thsc※...※thfx ``` | 序号 | 字段 | 说明 | 示例 | |-----|------|------|------| | 1 | kssj | 开始时间 | 20240101120000 | | 2 | jssj | 结束时间 | 20240101120030 | | 3 | hjls | 呼叫流水 | 12345 | | 4 | hjzls | 呼叫主/被叫流水 | 67890 | | 5 | zjhm | 主叫号码 | 055312345678 | | 6 | bjhm | 被叫号码 | 12345678 | | 7 | thsc | 通话时长(秒) | 30 | | ... | ... | 中间字段 | ... | | 10 | thfx | 通话方向 | 1=呼入/0=呼出 | **录音文件**:`{ftpRecordDir}/{yyyyMMdd}/{hjls}_{hjzls}.mp3` ## 防重复机制 | 层级 | 方式 | 说明 | |-----|------|------| | 1 | FTP归档 | 处理完的txt移到 `archive/` | | 2 | 数据库前缀查重 | `call_record_id LIKE '340100_FTP_%'` | | 3 | 内存Set去重 | 同批内不重复 | | 4 | 数据库唯一索引 | 建议在 `call_record_id` 上加 | ## 定时任务 默认 `0 0 0/2 * * ?`(每2小时执行一次)。 可在 `application-external.yml` 中修改: ```yaml ftp-sync: sync-interval-cron: "0 0 0/2 * * ?" ``` **常用 Cron 表达式**: - 每分钟:`0 0/1 * * * ?` - 每30分钟:`0 0/30 * * * ?` - 每小时:`0 0 * * * ?` - 每2小时:`0 0 0/2 * * ?` - 每天凌晨2点:`0 0 2 * * ?` ## 日志 ### 日志位置 `logs/app.log`(按天滚动,保留30天) ### 日志前缀 | 前缀 | 含义 | |------|------| | 【FTP定时】 | 定时任务开始/结束 | | 【FTP主流程】 | 同步主流程 | | 【FTP地市】 | 单地市处理 | | 【FTP文件】 | 单txt文件处理 | | 【FTP行】 | 单行记录处理 | | 【FTP防重】 | 数据库防重查询 | | 【异常】 | 错误信息 | ### 常用查看命令 ```bash # 实时日志 tail -f logs/app.log # 只看错误 grep "【异常】" logs/app.log # 只看单地市 grep "【FTP地市】" logs/app.log | grep "合肥" # 同步成功数 grep "保存成功" logs/app.log | wc -l ``` ## 常见问题 ### Q1: 数据库连接失败 检查 `application-external.yml` 的 `spring.datasource.*` 配置,确保: - IP和端口正确 - 用户名密码正确 - 数据库服务已启动 ### Q2: FTP连接失败 检查 `ftp-sync.cities[0].ftp-*` 配置: - FTP服务可达:`telnet 10.126.129.7 9979` - 用户名密码正确 - 目录路径有读写权限 ### Q3: 录音文件下载失败 检查 FTP 上是否存在对应路径的文件: - 路径格式:`{ftp-record-dir}/{yyyyMMdd}/{hjls}_{hjzls}.mp3` - 文件名大小写是否一致 ### Q4: OSS上传失败 检查 `vaa-sync.oss.*` 配置: - `upload-url` 接口是否可达 - `appid` / `appsecret` 是否正确 ### Q5: 数据没有入库 检查: - 数据库 `mid_voice_call_record` 表是否存在 - 日志中是否有 `保存成功` 字样 - 数据库表分区是否覆盖了该地市编码 ## 与老代码(淮南 yydc-tb-server)的差异 | 项 | 老代码 | 新代码 | |---|-------|-------| | 数据源 | Oracle | 人大金仓 | | 配置 | Spring XML | application.yml | | 同步方式 | 手动运行 | 定时任务 | | mp3处理 | 只存URL | 下载+上传OSS | | 防重 | 不防重 | 数据库+FTP双层防重 | | 部署 | 单机单实例 | 多地市独立部署 | | 状态 | 已废弃 | 当前生产 | ## 维护 | 项 | 频率 | |---|------| | 检查日志 | 每天 | | 清理磁盘 | 自动(保留10天) | | 数据库备份 | 由DBA负责 | | 配置文件变更 | 部署时修改外部配置 | ## 联系 - 项目维护:xxx - 内部文档:xxx