本文作者:袁晟
一、背景介绍
在音乐版权领域中,签约是时常发生的一件事情,云音乐需要通过合同签约来拿到对应艺人的歌曲授权,才可以对歌曲进行后续的上架等操作。
在没有电子签约能力的时候,商务正常的签约流程如下:
一个合同,经过上面一套过程下来,整体需要15-30天的签约周期。目前,云音乐内容技术部基于e签宝,搭建了一套文件签约流程,然而目前的流程在后续业务接入时存在较多问题需要改善。
- 签约模板发布不灵活:目前的签约模板基于代码生成,每次需求改动需要经历整个需求上线周期。如果出现线上文件内容需要紧急替换的,需要额外增加时间做发布操作,大大增加了线上问题修复的时间。
- 签约流程不灵活:在目前的版权域和音乐人域中,存在大大小小的签约场景几十种(如下图)。对于现有的逻辑体系中,无法做到在特定节点前后增减具体处理节点。目前对于这种场景只能新增实现类,通过拷贝或者继承等方式去处理额外逻辑。开发者需要评估逻辑是否兼容,而且对于后续修改代码的人会造成一些理解成本。
- 签约文件准确性无法保障:对于生成的签约文件,历史上发生过多次文件内容异常导致需要重签的情况。比如,签约文件中缺少了盖章内容或者文件中缺少歌曲信息等。
二、项目方案
2.1 模板配置能力
对于现有的合同内容来说,主要分为两块内容,一块是法务侧提供的静态内容数据。这块内容对于相同场景的每个合同来说都是一样的。但是其中还存在如图中的一些空缺位置,这块内容主要是此次签约的一些动态数据。比如,此次的签约时间、歌曲信息、艺人信息等。这块内容需要在每次签约发起的时候动态填写。并且在业务的发展过程中,时常存在模板内容变更的场景,我们需要能对模板内容进行动态的调整。
签约模板整体创建流程如下:
具体的配置页面如下,使用html + 动态参数填充生成具体的pdf内容:
Html转pdf的能力目前使用的是itextpdf
来进行实现,由于部分协议中需要导入图片内容,存在图片过大导致超出pdf范围的可能性,所以需要对图片大小进行缩放
scss
ITextRenderer renderer = new ITextRenderer();
// 如果携带图片则加上以下两行代码,将图片标签转换为Itext自己的图片对象,Base64ImgReplacedElementFactory为图片处理类
renderer.getSharedContext().setReplacedElementFactory(new Base64ImgReplacedElementFactory());
在Base64ImgReplacedElementFactory中,我们对传入的图片进行整体缩放逻辑处理
java
public class Base64ImgReplacedElementFactory implements ReplacedElementFactory {
/**
* 实现createReplacedElement 替换html中的Img标签
*
* @param c 上下文
* @param box 盒子
* @param uac 回调
* @param cssWidth css宽
* @param cssHeight css高
* @return ReplacedElement
*/
public ReplacedElement createReplacedElement(LayoutContext c, BlockBox box, UserAgentCallback uac,
int cssWidth, int cssHeight) {
Element e = box.getElement();
if (e == null) {
return null;
}
String nodeName = e.getNodeName();
// 找到img标签
if (nodeName.equals("img")) {
String attribute = e.getAttribute("src");
FSImage fsImage;
try {
// 生成itext图像
fsImage = buildImage(attribute, uac);
} catch (BadElementException | IOException e1) {
log.warn("buildImage failed",e);
fsImage = null;
}
if (fsImage != null) {
double scale = 1.0;
// 10000大小 scale=1 就差不多了
if (fsImage.getWidth() > 8000) {
scale = fsImage.getWidth() / 8000.0;
}
fsImage.scale((int) (fsImage.getWidth() / scale), (int) (fsImage.getHeight() / scale));
return new ITextImageElement(fsImage);
}
}
return null;
}
}
对于后期发布的版本,如果存在线上问题等情况需要临时回滚等操作,目前提供了多版本控制的能力。
目前模板配置已经应用于大部分的签约场景中,对于后续的开发和迭代上减少了大量人力成本。并且对于现有的部分前端内容,如一些用户手册等也已经接入,用于减少动态内容的开发效率。
2.2 流程配置能力
对于各种签约场景流程或者数据处理逻辑上各不相同,由于考虑到需要适配多种签约场景,为了更好的兼容现有的业务场景,以及后续能方便新场景的接入。所以在功能设计的时候不能和历史代码设计思路一样,将签约中的状态固定死,需要有对于签约链路的动态调整能力,将这块能力交由到业务方使其方便其灵活配置。这就需要在设计的时候考虑能对场景灵活的进行调整。这边设计时候的思路是基于签约状态+签约事件的概念,基于事件驱动的逻辑来触发签约流程的整体流转。
如上图为一个完整的签约流程,其中每个方框节点代表了签约流程中的一个状态(一个签约流程在某个时间点只会处于某一个状态),对于每个状态中间的连线,则代表了状态流转所需要触发的具体事件。在每个状态变更成功后,签约平台都会发送对应的MQ消息到对应的Topic中,业务方可以监听其进行后续的业务流转。
目前的流程配置很好解决了需求变更导致流程中需要增减节点的情况,而且可以避免前后发布时候的数据处理问题。
2.3 文件巡检能力
基于已生成的Pdf文件,需要确保文件内容的准确,所以定期需要对生成的文件内容进行解析和正确性检测。
对于历史数据,由于数据不可追溯,使用PaddleOCR库(github.com/PaddlePaddl...)对pdf进行图像识别,返回每行的文本数据已经文本匹配度,整体效果如下
目前线上文件巡检已经涵盖18种主要签约场景,目前匹配准确率约为97.9%。通过线上文件巡检,定位和发现3万+历史歌曲文件存在异常情况,及时排查和修复了业务逻辑,并对问题数据进行上报防止资损。
2.4 流程数据监控
对于不同的签约场景,会有不同的流程节点,我们需要去关注特定节点的block情况。比如音乐人签约中存在人审,人审的周期一般是1-2天,那我们就需要对于人审环节增加两天的预期阻塞,如果此节点阻塞超过两天就会被标记为一条签约异常数据。
整体处理流程如下:
数据报表页面部分内容如下:
基于签约流程数据监控,发现了较多阻塞的异常数据,并针对各个场景进行了数据分析,经过分析需要是产品设计上存在部分问题,提交了修改建议到产品侧进行优化。
三、成果和总结
以上就是签约平台的整体的设计思路。在项目的整体推进过程中,我们对于工具的兼容性等设计上的思路更加清晰了。在平台上线后,通过签约平台的开发和后续的业务接入,也帮助我们熟悉和梳理了音乐人/版权相关签约流程的业务逻辑,方便了后续业务问题的定位和排查。同时,我们也需要接入更多的签约场景,优化我们平台侧的对外能力,做到更好的服务业务方。