/** * 2026-10-12 人员新字段:档案保存、服刑暂停、死亡登记审批回读。 * 暂停测完会恢复发放。死亡登记使用新建的「字段联测勿发放」,不改在册实发人员。 */ const http = require('http') function req(method, path, body) { return new Promise((resolve, reject) => { const data = body ? JSON.stringify(body) : null const headers = { 'Content-Type': 'application/json' } if (data) headers['Content-Length'] = Buffer.byteLength(data) const r = http.request({ hostname: '127.0.0.1', port: 8088, path: '/sjnmtybt' + path, method, headers }, (res) => { let buf = '' res.on('data', (c) => (buf += c)) res.on('end', () => { try { resolve(JSON.parse(buf)) } catch (e) { reject(new Error(buf.slice(0, 400))) } }) }) r.on('error', reject) if (data) r.write(data) r.end() }) } function idCard() { const body = '31011719500101' + String(100 + Math.floor(Math.random() * 800)).padStart(3, '0') const w = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2] const m = '10X98765432' let s = 0 for (let i = 0; i < 17; i++) s += Number(body[i]) * w[i] return body + m[s % 11] } function assert(cond, msg, extra) { if (!cond) { console.error('FAIL', msg, extra == null ? '' : JSON.stringify(extra)) process.exitCode = 1 } else { console.log('OK', msg) } } async function getPerson(id) { const j = await req('GET', '/api/personnel/' + encodeURIComponent(id)) if (j.code !== 200) throw new Error('get ' + id + ' ' + (j.message || j.msg)) return j.data } async function main() { const base = await getPerson('P33040') console.log('pauseTarget', base.id, base.name, base.bizStatus, base.updateTime) const saved = await req('POST', '/api/personnel', { id: base.id, name: base.name, idNumber: base.idNumber, townId: base.townId, villageId: base.villageId, expectedUpdateTime: base.updateTime, personType: '老年农民', householdDistrict: '松江区', householdTown: '叶榭镇', householdVillage: '井凌桥村', contactName: '联测联系人', contactPhone: '13800001111', insuranceJoinDate: '2010-05-01', benefitApplyDate: '2020-01-15', urbanMedicareConfirm: '未确认', personMark: '联测标示', localOrRemote: '同城', operatorRoleKey: 'admin', }) assert(saved.code === 200, '档案字段保存', saved.message) const back = await getPerson('P33040') assert(back.personType === '老年农民', '人员类型回读', back.personType) assert(back.householdDistrict === '松江区', '户籍区县回读', back.householdDistrict) assert(back.householdTown === '叶榭镇', '户籍街镇回读', back.householdTown) assert(back.householdVillage === '井凌桥村', '户籍村居委回读', back.householdVillage) assert(back.contactName === '联测联系人', '联系人回读', back.contactName) assert(back.contactPhone === '13800001111', '联系电话回读', back.contactPhone) assert(String(back.insuranceJoinDate || '').startsWith('2010-05-01'), '参保日期回读', back.insuranceJoinDate) assert(back.personMark === '联测标示', '人员标示回读', back.personMark) assert(back.localOrRemote === '同城', '同城或异地回读', back.localOrRemote) assert(back.bizStatus === 'NORMAL', '档案保存不改业务状态', back.bizStatus) const bad = await req('POST', '/api/special-biz', { personnelId: 'P33040', bizType: 'PAUSE', reason: '服刑收监', amount: 0, operator: '联测', operatorRoleKey: 'social', }) assert(bad.code !== 200, '服刑暂停缺刑期被拒绝', bad.message) let pause = await req('POST', '/api/special-biz', { personnelId: 'P33040', bizType: 'PAUSE', reason: '服刑收监', sentenceStart: '2024-03-01', sentenceEnd: '2026-03-01', amount: 0, operator: '联测', operatorRoleKey: 'social', }) if (pause.code !== 200 && String(pause.message || '').includes('在途')) { const pending = await req('GET', '/api/special-biz/page?bizType=PAUSE&approveStatus=' + encodeURIComponent('待分管领导审批') + '&pageNum=1&pageSize=20') const hit = ((pending.data && pending.data.list) || []).find((p) => p.personnelId === 'P33040') pause = { code: hit ? 200 : 400, data: hit, message: hit ? '复用在途暂停单' : '没有在途暂停单' } } assert(pause.code === 200 && pause.data && pause.data.id, '暂停单创建', pause.message) const pauseId = pause.data && pause.data.id const pauseOk = await req('POST', '/api/special-biz/leader-approve', { bizId: pauseId, action: 'PASS', opinion: '联测通过暂停', operator: '联测领导', }) assert(pauseOk.code === 200, '暂停审批通过', pauseOk.message) const paused = await getPerson('P33040') assert(paused.bizStatus === 'PAUSED', '暂停后业务状态', paused.bizStatus) assert(String(paused.sentenceStart || '').startsWith('2024-03-01'), '刑期开始写入', paused.sentenceStart) assert(String(paused.sentenceEnd || '').startsWith('2026-03-01'), '刑期结束写入', paused.sentenceEnd) assert(String(paused.changeReason || '').includes('服刑'), '变更原因写入', paused.changeReason) const resume = await req('POST', '/api/special-biz', { personnelId: 'P33040', bizType: 'RESUME', reason: '刑期届满恢复', operator: '联测', operatorRoleKey: 'social', }) assert(resume.code === 200 && resume.data && resume.data.id, '恢复单创建', resume.message) const resumeOk = await req('POST', '/api/special-biz/leader-approve', { bizId: resume.data.id, action: 'PASS', opinion: '联测通过恢复', operator: '联测领导', }) assert(resumeOk.code === 200, '恢复审批通过', resumeOk.message) const restored = await getPerson('P33040') assert(restored.bizStatus === 'NORMAL' || restored.bizStatus === 'NEW', '恢复后回到在发', restored.bizStatus) assert(String(restored.sentenceStart || '').startsWith('2024-03-01'), '恢复后刑期仍保留', restored.sentenceStart) assert(String(restored.changeReason || '').includes('恢复发放'), '恢复变更原因', restored.changeReason) const card = idCard() const created = await req('POST', '/api/personnel', { name: '字段联测勿发放', idNumber: card, townId: base.townId, villageId: base.villageId, bizStatus: 'NORMAL', source: '字段联测', retireQualifyType: 'WITHOUT_LAND', operatorRoleKey: 'admin', householdType: '农业', }) assert(created.code === 200 && created.data && created.data.id, '新建死亡联测人员', created.message || card) const deathId = created.data && created.data.id console.log('deathTarget', deathId, card, created.data && created.data.bizStatus) const death = await req('POST', '/api/special-biz', { personnelId: deathId, bizType: 'FUNERAL', deathDate: '2026-10-02', reason: '死亡登记;死亡日期 2026-10-02;与逝者关系:子女', heirName: '联测继承人', heirIdNumber: idCard(), heirBankCard: '6222021234567890123', heirPhone: '13900002222', heirRelation: '子女', heirIdType: '居民身份证', heirBankName: '中国农业银行', heirHouseholdAddress: '上海市松江区叶榭镇联测路1号', heirAddress: '上海市松江区叶榭镇联测路2号', fileUrls: 'local://field-test-death.pdf', amount: 1, operator: '联测', operatorRoleKey: 'social', }) assert(death.code === 200 && death.data && death.data.id, '死亡登记单创建', death.message) const deathOk = await req('POST', '/api/special-biz/leader-approve', { bizId: death.data && death.data.id, action: 'PASS', opinion: '联测通过死亡登记', operator: '联测领导', }) assert(deathOk.code === 200, '死亡登记审批通过', deathOk.message) const dead = await getPerson(deathId) assert(dead.deathStatus === '已死亡', '死亡状态', dead.deathStatus) assert(String(dead.deathDate || '').startsWith('2026-10-02'), '死亡日期', dead.deathDate) assert(dead.cancelReason === '死亡', '注销原因', dead.cancelReason) assert(String(dead.cancelApplyDate || '').length >= 10, '注销申办日期', dead.cancelApplyDate) assert(dead.cancelDeathMonth === '202610', '注销死亡年月', dead.cancelDeathMonth) assert(dead.hasHeir === '是', '是否有继承人', dead.hasHeir) assert(dead.heirName === '联测继承人', '继承人姓名', dead.heirName) assert(dead.heirIdType === '居民身份证', '继承人证件类型', dead.heirIdType) assert(dead.heirBankName === '中国农业银行', '继承人银行名称', dead.heirBankName) assert(String(dead.heirHouseholdAddress || '').includes('联测路1号'), '继承人户籍地址', dead.heirHouseholdAddress) assert(String(dead.heirAddress || '').includes('联测路2号'), '继承人联系地址', dead.heirAddress) assert(dead.heirRelation === '子女', '与参保人关系', dead.heirRelation) console.log('deathStatusBiz', dead.bizStatus, 'cancelApply', dead.cancelApplyDate) } main().catch((e) => { console.error(e) process.exit(1) })