# ETL详细设计

## 模型部分

### 待沟通

- 目前上游无法提供增量数据，所以每次批处理必须全量，效率低
- 受益人只有三要素证件号码、证件类型、姓名，上游客户号为null
- 客户等级是否有效的判断规则（有效标志IS_DELETE、失效日期END_DATE全为null，生效日期是否有作用），客户ID（CUSTOMER_ID）是否与核心客户号一致
- 数据字典中不同表之间存在大量的重复字段，且从测试环境来看，这些表存在差异，如保单数量:HEALTH_INSURANCE_LISTING << POLICY_INFORMATION < INSURANCEINFO，但没有文档说明，无法保证所取字段的正确性。
- 需提供结案日期
- 转办单主表(CC_SWFFLOWMAIN)中转办项目(complaintProject)为无意义字符串，子项目(sonproject)为空值
- 上游新供的保全表(edorinfo)中保全项(edortype)需要提供枚举值说明
- 投诉信息(CC_COMPLAINSPROMSG)主键(ComplainsProsId)与文档描述不一致，导致无法与投诉信息(cc_action_complaints)关联
- POLICY_INFORMATION和INSURANCEINFO中都有保单状态字段APPFLAG，但枚举内容不一样，以哪个为准？
- INSURANCEINFO.APPNTAGE职业名称都为空，OCCUPATIONNAME职业代码有值但需要codestring，UNDERWRITE_REPORT_LIST表是否可用？
- INSURANCEINFO.APPFLAG,POLICY_INFORMATION.APPFLAG,PERSONAL_INSURANCE.CONTSTATE,数据字典解释均为保单状态，但实际数据各不相同，以哪个为准？

### 一、个人信息（Individual）

1. 从保单主表（POLICY_INFORMATION）中获取投保人的上游客户号和五要素，得到`$投保人列表$`

code

    SELECT CUSTOMERNO,NAME,sex,BIRTHDAY,IDTYPE,IDNO,min(CVALIDATE) FROM POLICY_INFORMATION GROUP BY CUSTOMERNO,NAME,sex,BIRTHDAY,IDTYPE,IDNO;

2. 从个险承保清单自助分析表（POLICY_INFORMATION）中获取被保人的上游客户号和五要素，得到`$被保人列表$`

code

    SELECT INSUREDNO,INSUREDNAME,INSUREDSEX,INSUREDBIRTHDAY,INSUREDIDTYPE,INSUREDIDNO,min(CVALIDATE) FROM POLICY_INFORMATION GROUP BY INSUREDNO,INSUREDNAME,INSUREDSEX,INSUREDBIRTHDAY,INSUREDIDTYPE,INSUREDIDNO;

3. 从个险承保清单自助分析（HEALTH_INSURANCE_LISTING）中获取受益人的上游客户号和三要素，得到`$受益人列表$`，实际数据上游客户全为null，同时五要素不齐，故暂时不考虑

code

    SELECT BENEFICIARY_CUSTOMERNO,BENEFICIARY_NAME,BENEFICIARY_IDTYPE,BENEFICIARY_IDNO FROM HEALTH_INSURANCE_LISTING GROUP BY BENEFICIARY_CUSTOMERNO,BENEFICIARY_NAME,BENEFICIARY_IDTYPE,BENEFICIARY_IDNO;

4. 将`$投保人列表$`，`$被保人列表$` 合并去重，得到`$投被保人列表$`，并按保单生效日期（CVALIDATE）从早到晚排序（客户号生成规则之一）
5. 遍历`$投被保人列表$`
6. 标识及证件信息：根据五要素生成CPID，并生成代理主键IndID同时将CPID以及列表中其他字段分别插入个人信息表（Individual）。注意：要根据证件类型，将证件号码插入不同的字段。（如：证件类型为0，则证件号码插入身份证字段）
7. todo 等级信息：根据上游客户号，从客户等级表（T_CUSTOMER_CLASS）获取客户等级、贡献度分、家庭加分、续期加分、保单加分、总分值。

code

    select CLASS_VALUE,CONTRIBUTION_VALUE,AWARDED3,AWARDED2,AWARDED1,TOTAL_VALUE from T_CUSTOMER_CLASS where CUSTOMER_ID = 上游客户号

8. 籍贯、职业：根据上游客户号，从个险承保清单自助分析（HEALTH_INSURANCE_LISTING）获取投保人籍贯，投保人职业代码，投保人职业名称。注意，此表投保人和被保人都有职业代码名称，取最近的一条记录

code

    select APPNTHOMEADDRESS,APPNTOCCUPATIONCODE,APPNTOCCUPATIONNAME from HEALTH_INSURANCE_LISTING where APPNTNO = 上游客户号 order by POLAPPLYDATE desc

### 二、保单（InsuranceArrangement）

1. 从新契约签单明细清单（INSURANCEINFO）表中获取相关字段插入到InsuranceArrangement表，由于INSURANCEINFO表中是以保单号唯一的，所以直接插入。语法见下，字段映射详见模型文档

code

    INSERT INTO InsuranceArrangement(PolicyNo,PANo)
    SELECT contno,prtno
    FROM  INSURANCEINFO

2. 用保单信息表（POLICY_INFORMATION）去更新InsuranceArrangement，由于POLICY_INFORMATION不是以保单号唯一的，所以先要去重。由于星环inceptor不支持如下在update中使用group by的语法,所以可以对POLICY_INFORMATION按保单号去重后存入临时表`POLICY_INFORMATION_tmp`

code

    insert into POLICY_INFORMATION_tmp(contno,CVALIDATE,ENDDATE)
    SELECT contno,max(CVALIDATE),max(ENDDATE)
    ROM POLICY_INFORMATION
    GROUP BY contno

3. 更新临时表`POLICY_INFORMATION_tmp`中投保人（ApplicantID），主被保险人（InsuredID）为individual表的个体ID(IndID),可根据POLICY_INFORMATION.CUSTOMERNO关联individual上游客户号（SCustID），得到IndID，支持update，临时表设置为ORC事务表

code

    UPDATE POLICY_INFORMATION_tmp
    SET ApplicantID =
    (SELECT ApplicantID
    FROM individual
    WHERE (POLICY_INFORMATION_tmp.CUSTOMERNO = individual.SCustID))
    WHERE 1=1;

4. 最后用临时表更新InsuranceArrangement（code2）,为了支持update，InsuranceArrangement表设置为ORC事务表。语法见下，字段映射详见模型文档

code

    UPDATE InsuranceArrangement
    SET (PINDate,PMDate) =
    (SELECT CVALIDATE,ENDDATE
    FROM POLICY_INFORMATION_tmp
    WHERE (POLICY_INFORMATION_tmp.contno = InsuranceArrangement.PolicyNo))
    WHERE 1=1;

### 三、理赔（InsuranceClaimThread）

1. 首先将个单理赔表（INSURANCE_CLAIM）按保单和赔案号去重并存入临时表`INSURANCE_CLAIM_tmp`

code

    insert into INSURANCE_CLAIM_tmp(ontno,clmno,RISKDATE,INSUREDNO,RPTDATE,LLOCCURREASON,RGTDATE,ACCIDENTDETAIL,LLCLAIMSTATE)
    SELECT contno,clmno,max(RISKDATE),max(INSUREDNO),max(RPTDATE),max(LLOCCURREASON),max(RGTDATE),max(ACCIDENTDETAIL),max(LLCLAIMSTATE)
    FROM INSURANCE_CLAIM GROUP BY contno,clmno

2. 更新`INSURANCE_CLAIM_tmp`中出险人为individual表的个体ID(IndID)
3. 将`INSURANCE_CLAIM_tmp`相应字段直接插入到InsuranceClaimThread表中，字段映射详见模型文档。

### 四、个体关系(IndRelationShip)

1. 从保单信息表（POLICY_INFORMATION）获取投保人和被保人的关系并

code

    insert into IndRelationShip(RSType,IndID1,Name1,IDCard1,IndID2,Name2,IDCard2,RSSTime,Role2)
    SELECT max(RELATIONTOAPPNT),CUSTOMERNO,max(NAME),max(IDNO),INSUREDNO,max(INSUREDNAME),max(INSUREDIDNO),min(CUSTOMGETPOLDATE),
       CASE max(RELATIONTOAPPNT)
       		WHEN '子女' THEN case max(INSUREDSEX) when 1 THEN '女儿' WHEN 0 THEN '儿子' end
            when '父母' THEN case max(INSUREDSEX) when 1 THEN '母亲' WHEN 0 THEN '父亲' end
            WHEN '配偶' THEN case max(INSUREDSEX) when 1 THEN '妻子' WHEN 0 THEN '丈夫' end
            when '祖父母、外祖父母' THEN case max(INSUREDSEX) when 1 THEN '(外)祖母' WHEN 0 THEN '(外)祖父' end
            when '祖孙、外祖孙' THEN case max(INSUREDSEX) when 1 THEN '(外)孙女' WHEN 0 THEN '(外)祖孙' end
            ELSE max(RELATIONTOAPPNT) END
    FROM POLICY_INFORMATION
    WHERE RELATIONTOAPPNT<>'本人'
    GROUP BY CUSTOMERNO,INSUREDNO

2. 更新IndRelationShip.Role1为IndRelationShip.Role2的相对角色，即role2为母亲则role1为父亲等
3. 此时插入IndRelationShip的IndID1，IndID2为上游系统客户号，根据Individual.SCustID更新为Individual.IndID
4. 根据Individual更新其他信息，如手机号等

### 五、接触(Communication)

### 六、事件(PartyTimeLine)

1. 目前主要有以下事件，一个事件一条记录，包括开始时间和结束时间

- 投保：PRole='投保人'，Scenario='投保',SDate=InsuranceArrangement.PADate,EndDate=InsuranceArrangement.PISDate
- 保全: PRole='投保人'，Scenario='保全',SDate=AUDIT_EDORLIST.CUSAPPDATE,EndDate = AUDIT_EDORLIST.EDORVALIDATE todo 等待上游确认edorinfo.edortype的保全项是否都已包含在AUDIT_EDORLIST.EDORNAME中？
- 报案: PRole='投保人'，Scenario='报案',SDate=InsuranceClaimThread.CNDate,EndDate= TODO 等待上游提供结案日期字段
- 投诉:
- 咨询:

## 标签部分

- label1: 无来源。Individual.Education
- label2: Individual.Occupation，逻辑见上
- label3: 根据Individual.Birthday年代，更新为70后，80后等
- Label4：根据Individual.Birthday算出年龄，儿童:[0,13),少年:[13,19),青年:[19,41),中年:[41,66),老年:[66,)
- Label5: todo:只有收件地址（POSTAL_INSURANCE.HOMEADDRESS）且无记录
- Label6: todo:只有收件地址（POSTAL_INSURANCE.HOMEADDRESS）且无记录
- Label7：保留暂不实现
- Label8：保留暂不实现
- Label9:todo:上游无直接来源。目前根据Individual.MaritalStat，查询个体关系表(IndRelationShip)，RSType = '配偶'，如果有记录则更新为已婚，否则更新为null
- Label10：查询IndRelationShip，RSType='父母'，首先将返回记录数更新Individual.COChild，若记录数为0，则更新本字段为'无子女'，若记录数为1，则更新本字段为'1孩'，若记录数大于等于2，则更新本字段为'2孩及以上'
- Label11：todo:无来源，逻辑需要说明清楚
- Label12：todo:无来源，逻辑需要说明清楚。Individual.Drinking
- Label13：todo:无来源，逻辑需要说明清楚。Individual.Smoking
- Label14：todo:无来源，逻辑需要说明清楚。Individual.Cdiseases
- Label15：todo:无来源，逻辑需要说明清楚。
- Label16：todo:无来源，逻辑需要说明清楚。Individual.FMHistory
- Label17：todo:无来源，逻辑需要说明清楚。Individual.Pregnancy
- Label18：todo:无来源，逻辑需要说明清楚。
- Label19：保留暂不实现
- Label20：todo:无来源，逻辑需要说明清楚。
- Label21：todo:无来源，Individual.PIncome:[0,120000],'低收入';(120000,1000000],'中产';(1000000,),'富人'
- Label22：todo:无来源，Individual.COVehicle:0,'无车';1,'1辆车';(1,),'2辆车以上'
- Label23：todo:无来源，Individual.CORP:0,'无房';1,'1套房';(1,),'2套房以上'
- Label24：以上游客户号(Individual.SCustID)查询POLICY_INFORMATION表（CUSTOMERNO）且APPFLAG = '有效'，若无记录则更新Individual.NOVPolicy为0，否则更新NOVPolicy为记录数。若NOVPolicy>0，则更新Individual.Label24为'持有寿险有效保单'
- Label25：根据Individual.NOVPolicy+Individual.NOIVPolicy的值更新此标签为：无保单、1件保单、2件保单、3-5件保单、6-10件保单、11-20件保单、20-50件保单、50件以上保单
- Label26：InsuranceArrangement.PolicyNo = AUDIT_LN_LIST.CONTNO，以AUDIT_LN_LIST.LNMONEY更新InsuranceArrangement.LoanMoney，以个体ID(Individual.IndID)查询InsuranceArrangement表(Individual.IndID=InsuranceArrangement.ApplicantID)，若无记录更新Individual.Label26为Null,若LoanMoney>0则更新Individual.Label26为'有贷款'
- Label27：todo:无来源，逻辑需要说明清楚。
- Label28：保留暂不实现
- Label29：以上游客户号(Individual.SCustID)查询POLICY_INFORMATION表（CUSTOMERNO），POLICY_INFORMATION.APPFLAG='有效' and POLICY_INFORMATION.PAYINTV='趸缴'，若有记录则更新Individual.NOSPInsurance和本标签为'有趸交保单'
- Label30：以上游客户号(Individual.SCustID)查询POLICY_INFORMATION表，若无记录则更新Individual.ISelf为NULL，若有记录且RELATIONTOAPPNT仅为'本人'则若无记录则更新Individual.ISelf为'仅本人投保'，若有记录且RELATIONTOAPPNT不为'本人'则若无记录则更新Individual.ISelf为'本人未投保'，否则更新Individual.ISelf为'本人已投保'
- Label31：以上游客户号(Individual.SCustID)查询POLICY_INFORMATION表（CUSTOMERNO），若无记录则更新Individual.Iparent为NULL，若有记录且RELATIONTOAPPNT仅为'父母'则若无记录则更新Individual.Iparent为'仅父母投保'，若有记录且RELATIONTOAPPNT不为'父母'则若无记录则更新Individual.Iparent为'父母未投保'，否则更新Individual.Iparent为'父母已投保'
- Label32：以上游客户号(Individual.SCustID)查询POLICY_INFORMATION表（CUSTOMERNO），若无记录则更新Individual.IChildren为NULL，若有记录且RELATIONTOAPPNT仅为'子女'则若无记录则更新Individual.IChildren为'仅子女投保'，若有记录且RELATIONTOAPPNT不为'子女'则若无记录则更新Individual.IChildren为'子女未投保'，否则更新Individual.IChildren为'子女已投保'
- Label33：以上游客户号(Individual.SCustID)查询POLICY_INFORMATION表（CUSTOMERNO），若无记录则更新Individual.Imate为NULL，若有记录且RELATIONTOAPPNT仅为'配偶'则若无记录则更新Individual.Imate为'仅配偶投保'，若有记录且RELATIONTOAPPNT不为'配偶'则若无记录则更新Individual.Imate为'配偶未投保'，否则更新Individual.Imate为'配偶已投保'
- Label34：保留暂不实现
- Label35：保留暂不实现
- Label36：保留暂不实现
- Label37：保留暂不实现
- Label38：保留暂不实现
- Label39：保留暂不实现
- Label40：保留暂不实现
- Label41：保留暂不实现
- Label42：保留暂不实现
- Label43：todo:逻辑需要说明清楚（到上游源表和字段）。
- Label44：以上游客户号(Individual.SCustID)查询POLICY_INFORMATION表且APPFLAG<>'有效'，若无记录则更新Individual.NOIVPolicy为0，否则更新NOIVPolicy为记录数。若NOIVPolicy>0，则更新Individual.Label44为'有失效保单'
- Label45：以上游客户号(Individual.SCustID)查询POLICY_INFORMATION表且riskperiod='长期险'，若无记录则更新Individual.Label45为NULL，若有记录且paycount= payendyear则更新Individual.Label45为'有缴费期满长险保单'
- Label46：以上游客户号(Individual.SCustID)查询POLICY_INFORMATION表，若无记录则更新Individual.Label46为NULL，若有记录且paycount= payendyear则更新Individual.Label46为'有续期缴费的保单'，否则更新Individual.Label46为'无有续期缴费的保单'
- Label47：todo:逻辑需要说明清楚（到上游源表和字段）。
- Label48：以个体ID(Individual.IndID)查询InsuranceArrangement表(Individual.IndID=InsuranceArrangement.ApplicantID)，取最早日期(PADate)并更新此标签为：首次投保日期XXXX-XX-XX
- Label49：以个体ID(Individual.IndID)查询InsuranceArrangement表(Individual.IndID=InsuranceArrangement.ApplicantID)，取最近日期(PADate)更新Individual.LAPPDate，最后用当前日期减去Individual.LAPPDate更新此标签为：最后一次投保距今XXX天
- Label50：以个体ID(Individual.IndID)查询InsuranceArrangement表(Individual.IndID=InsuranceArrangement.ApplicantID)，以最近日期(PADate)的AgentChannel更新Individual.LPChannel，并更新此标签为：'最近保单'+Individual.LPChannel
- Label51：以个体ID(Individual.IndID)查询InsuranceArrangement表(Individual.IndID=InsuranceArrangement.ApplicantID)，以最近日期(PADate)的AgentOrg更新Individual.LPOrg，并更新此标签为：'最近'+Individual.LPOrg+'机构'
- Label52：以个体ID(Individual.IndID)查询InsuranceArrangement表(Individual.IndID=InsuranceArrangement.ApplicantID)，以最早日期(PADate)的AgentChannel更新Individual.FAChannel，并更新此标签为：'首单'+Individual.FAChannel
- Label53：以个体ID(Individual.IndID)查询InsuranceArrangement表(Individual.IndID=InsuranceArrangement.ApplicantID)，以最早日期(PADate)的AgentOrg更新Individual.FAOrg，并更新此标签为：'首单'+Individual.FAOrg+'机构'
- Label54：以个体ID(Individual.IndID)查询InsuranceArrangement表(Individual.IndID=InsuranceArrangement.ApplicantID)，以最近日期(PADate)的PolicyState更新Individual.LPState，todo:逻辑需要说明清楚（到上游源表和字段）。
- Label55：由于上游保全类型信息分散在AUDIT_EDORLIST.EDORNAME和edorinfo.edortype 中，同时edorinfo.edortype为code需要转为string（具体映射见下）。以上游客户号(Individual.SCustID)分别查询AUDIT_EDORLIST表和edorinfo表，取保全生效日期最近的记录的更新的保全类型更新Individual.LPOSType。todo:edorinfo.edortype为code，需要转为string，edorinfo中需要增加上游客户号，否则需要多一次关联，建议大数据平台处理完成。
- Label56：以上游客户号(Individual.SCustID)查询POLICY_INFORMATION.CONTNO,再根据CONTNO查询INSURANCEINFO表 and INSURANCEINFO.APPFLAG = '已退保',若有记录则更新此标签为'有退保保单'
- Label57：以上游客户号(Individual.SCustID)查询POLICY_INFORMATION.CONTNO,再根据CONTNO查询AUDIT_CLAIM_INSURANCE表，取AUDIT_CLAIM_INSURANCE.RGTDATE最近对应的AUDIT_CLAIM_INSURANCE.RISKTYPE更新此标签。todo:实际数据仅有寿险、意外险、健康险，取值是否正确？
- Label58：todo:逻辑需要说明清楚（到上游源表和字段）。
- Label59：以个体ID(Individual.IndID)查询InsuranceClaimThread表(Individual.IndID=InsuranceClaimThread.ApplicantID)， 以最近日期(CNDate)的ClaimCaseStatus更新Individual.CNStatus，并更新此标签
- Label60：保留暂不实现
- Label61：todo:逻辑需要说明清楚（到上游源表和字段）。
- Label62：保留暂不实现
- Label63：todo:逻辑需要说明清楚（到上游源表和字段）。
- Label64：todo:逻辑需要说明清楚（到上游源表和字段）。
- Label65：以上游客户号(Individual.SCustID)查询POLICY_INFORMATION表，若无记录则更新Individual.Label65为NULL，若仅一条记录则更新Individual.Label65为'一单寿险客户'
- Label66：以上游客户号(Individual.SCustID)查询POLICY_INFORMATION表，若无记录则更新Individual.Label66为NULL，若 paycount= payendyear更新Individual.Label66为'缴费期满客户'
- Label67：若Individual.Birthday的月日与上海人寿司庆日月日相同，则设置此标签为：'生日与司庆日同天'。todo:需要确定司庆日是几月几号
- Label68:以上游客户号(Individual.SCustID)查询POLICY_INFORMATION表, and POLICY_INFORMATION.APPFLAG='有效'，若有记录则设置此标签为'持有有效保单'，否则则设置为'未持有有效保单'
- Label69：Individual.CustClass，逻辑见上(一.7)
- Label70:Individual.IndID = PartyTimeLine.PartyID,取PartyTimeLine.SDate最近的记录对应的Scenario更新此标签
- Label71：Individual.IndID = PartyTimeLine.PartyID,取PartyTimeLine.SDate最近的记录对应的Channel更新此标签
- Label72：保留暂不实现
- Label73
- Label74
- Label75
- Label76
- Label77：Individual.Birthday 距离当前日期5天内，更新Individual.Label77为'本人生日临近'，否则为NULL
- Label78：以个体ID(Individual.IndID)查询IndRelationShip表，若Role2为'父亲'或'母亲'的IndID2的生日(通过IndID2再反向关联Individual)，则更新Individual.Label78为'父母生日临近'，否则为NULL
- Label79：以个体ID(Individual.IndID)查询IndRelationShip表，若Role2为'儿子'或'女儿'的IndID2的生日(通过IndID2再反向关联Individual)，则更新Individual.Label79为'子女生日临近'，否则为NULL
- Label80
- Label81
- Label82
- Label83
- Label84
- Label85
- Label86
- Label87
- Label88
- Label89
- Label90
- Label91
- Label92
- Label93
- Label94
- Label95
- Label96：根据Individual.Birthday算出当前年龄，如果50<=年龄<60，则更新Individual.Label96为'大龄'，否则为NULL
- Label97：根据Individual.Birthday算出当前年龄，如果60<=年龄，则更新Individual.Label97为'高龄'，否则为NULL
- Label98：根据Individual.Occupation，若为记者、律师、公务员、媒体、金融行业，则更新Individual.Label98为'敏感职业'，否则为NULL
- Label99
- Label100
- Label101
- Label102
- Label103
- Label104
- Label105
- Label106
- Label107
- Label108
- Label109：以个体ID(Individual.IndID)查询InsuranceClaimThread，若ClaimCaseStatus='报案'且 InsuranceClaimThread.CNDate距当前日期超过7天，则更新Individual.Label109为'理赔报案追踪'
- Label110：以个体ID(Individual.IndID)查询InsuranceClaimThread，若ClaimCaseStatus='受理' or '立案'，则更新Individual.Label110为'理赔处理中'
- Label111
- Label112：先以个体ID(Individual.IndID)得到InsuranceArrangement.PolicyNo，再以此关联AUDIT_EDORLIST，若有记录且保全状态EDORSTATE<>'确认生效' and '复核终止' and '强制终止'，则更新Individual.Label111为'保全处理中'
- Label113
- Label114
- Label115
- Label116
- Label117
- Label118