新增员工党员字段
This commit is contained in:
@@ -43,6 +43,10 @@ public class CcdiBaseStaff implements Serializable {
|
|||||||
/** 入职时间 */
|
/** 入职时间 */
|
||||||
private Date hireDate;
|
private Date hireDate;
|
||||||
|
|
||||||
|
/** 是否党员:0-否 1-是 */
|
||||||
|
@TableField("is_party_member")
|
||||||
|
private Integer partyMember;
|
||||||
|
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,10 @@ public class CcdiBaseStaffAddDTO implements Serializable {
|
|||||||
/** 入职时间 */
|
/** 入职时间 */
|
||||||
private Date hireDate;
|
private Date hireDate;
|
||||||
|
|
||||||
|
/** 是否党员:0-否 1-是 */
|
||||||
|
@NotNull(message = "是否党员不能为空")
|
||||||
|
private Integer partyMember;
|
||||||
|
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
@NotBlank(message = "状态不能为空")
|
@NotBlank(message = "状态不能为空")
|
||||||
private String status;
|
private String status;
|
||||||
|
|||||||
@@ -52,6 +52,10 @@ public class CcdiBaseStaffEditDTO implements Serializable {
|
|||||||
/** 入职时间 */
|
/** 入职时间 */
|
||||||
private Date hireDate;
|
private Date hireDate;
|
||||||
|
|
||||||
|
/** 是否党员:0-否 1-是 */
|
||||||
|
@NotNull(message = "是否党员不能为空")
|
||||||
|
private Integer partyMember;
|
||||||
|
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
|
|||||||
@@ -63,8 +63,15 @@ public class CcdiBaseStaffExcel implements Serializable {
|
|||||||
@ColumnWidth(15)
|
@ColumnWidth(15)
|
||||||
private Date hireDate;
|
private Date hireDate;
|
||||||
|
|
||||||
|
/** 是否党员 */
|
||||||
|
@ExcelProperty(value = "是否党员", index = 7)
|
||||||
|
@ColumnWidth(12)
|
||||||
|
@DictDropdown(dictType = "ccdi_yes_no_flag")
|
||||||
|
@Required
|
||||||
|
private Integer partyMember;
|
||||||
|
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
@ExcelProperty(value = "状态", index = 7)
|
@ExcelProperty(value = "状态", index = 8)
|
||||||
@ColumnWidth(10)
|
@ColumnWidth(10)
|
||||||
@DictDropdown(dictType = "ccdi_employee_status")
|
@DictDropdown(dictType = "ccdi_employee_status")
|
||||||
@Required
|
@Required
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ public class CcdiBaseStaffVO implements Serializable {
|
|||||||
/** 入职时间 */
|
/** 入职时间 */
|
||||||
private Date hireDate;
|
private Date hireDate;
|
||||||
|
|
||||||
|
/** 是否党员:0-否 1-是 */
|
||||||
|
private Integer partyMember;
|
||||||
|
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ public class ImportFailureVO {
|
|||||||
@Schema(description = "年收入")
|
@Schema(description = "年收入")
|
||||||
private BigDecimal annualIncome;
|
private BigDecimal annualIncome;
|
||||||
|
|
||||||
|
@Schema(description = "是否党员:0-否 1-是")
|
||||||
|
private Integer partyMember;
|
||||||
|
|
||||||
@Schema(description = "状态")
|
@Schema(description = "状态")
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
|
|||||||
@@ -320,6 +320,9 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi
|
|||||||
if (StringUtils.isEmpty(addDTO.getPhone())) {
|
if (StringUtils.isEmpty(addDTO.getPhone())) {
|
||||||
throw new RuntimeException("电话不能为空");
|
throw new RuntimeException("电话不能为空");
|
||||||
}
|
}
|
||||||
|
if (addDTO.getPartyMember() == null) {
|
||||||
|
throw new RuntimeException("是否党员不能为空");
|
||||||
|
}
|
||||||
if (StringUtils.isEmpty(addDTO.getStatus())) {
|
if (StringUtils.isEmpty(addDTO.getStatus())) {
|
||||||
throw new RuntimeException("状态不能为空");
|
throw new RuntimeException("状态不能为空");
|
||||||
}
|
}
|
||||||
@@ -357,6 +360,9 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi
|
|||||||
if (!"0".equals(addDTO.getStatus()) && !"1".equals(addDTO.getStatus())) {
|
if (!"0".equals(addDTO.getStatus()) && !"1".equals(addDTO.getStatus())) {
|
||||||
throw new RuntimeException("状态只能填写'在职'或'离职'");
|
throw new RuntimeException("状态只能填写'在职'或'离职'");
|
||||||
}
|
}
|
||||||
|
if (addDTO.getPartyMember() != 0 && addDTO.getPartyMember() != 1) {
|
||||||
|
throw new RuntimeException("是否党员只能填写'0'或'1'");
|
||||||
|
}
|
||||||
|
|
||||||
validateAnnualIncome(addDTO.getAnnualIncome(), "年收入");
|
validateAnnualIncome(addDTO.getAnnualIncome(), "年收入");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ public class CcdiBaseStaffServiceImpl implements ICcdiBaseStaffService {
|
|||||||
CcdiBaseStaff staff = baseStaffMapper.selectById(staffId);
|
CcdiBaseStaff staff = baseStaffMapper.selectById(staffId);
|
||||||
CcdiBaseStaffVO vo = convertToVO(staff);
|
CcdiBaseStaffVO vo = convertToVO(staff);
|
||||||
if (staff != null) {
|
if (staff != null) {
|
||||||
vo.setAssetInfoList(assetInfoService.selectByFamilyId(staff.getIdCard()).stream().map(asset -> {
|
vo.setAssetInfoList(assetInfoService.selectByFamilyIdAndPersonId(staff.getIdCard(), staff.getIdCard()).stream().map(asset -> {
|
||||||
CcdiAssetInfoVO assetInfoVO = new CcdiAssetInfoVO();
|
CcdiAssetInfoVO assetInfoVO = new CcdiAssetInfoVO();
|
||||||
BeanUtils.copyProperties(asset, assetInfoVO);
|
BeanUtils.copyProperties(asset, assetInfoVO);
|
||||||
return assetInfoVO;
|
return assetInfoVO;
|
||||||
@@ -131,6 +131,7 @@ public class CcdiBaseStaffServiceImpl implements ICcdiBaseStaffService {
|
|||||||
@Transactional
|
@Transactional
|
||||||
public int insertBaseStaff(CcdiBaseStaffAddDTO addDTO) {
|
public int insertBaseStaff(CcdiBaseStaffAddDTO addDTO) {
|
||||||
validateAnnualIncome(addDTO.getAnnualIncome(), "年收入");
|
validateAnnualIncome(addDTO.getAnnualIncome(), "年收入");
|
||||||
|
validatePartyMember(addDTO.getPartyMember(), "是否党员");
|
||||||
// 检查员工ID唯一性
|
// 检查员工ID唯一性
|
||||||
if (baseStaffMapper.selectById(addDTO.getStaffId()) != null) {
|
if (baseStaffMapper.selectById(addDTO.getStaffId()) != null) {
|
||||||
throw new RuntimeException("该员工ID已存在");
|
throw new RuntimeException("该员工ID已存在");
|
||||||
@@ -161,6 +162,7 @@ public class CcdiBaseStaffServiceImpl implements ICcdiBaseStaffService {
|
|||||||
@Transactional
|
@Transactional
|
||||||
public int updateBaseStaff(CcdiBaseStaffEditDTO editDTO) {
|
public int updateBaseStaff(CcdiBaseStaffEditDTO editDTO) {
|
||||||
validateAnnualIncome(editDTO.getAnnualIncome(), "年收入");
|
validateAnnualIncome(editDTO.getAnnualIncome(), "年收入");
|
||||||
|
validatePartyMember(editDTO.getPartyMember(), "是否党员");
|
||||||
CcdiBaseStaff existing = baseStaffMapper.selectById(editDTO.getStaffId());
|
CcdiBaseStaff existing = baseStaffMapper.selectById(editDTO.getStaffId());
|
||||||
if (existing == null) {
|
if (existing == null) {
|
||||||
throw new RuntimeException("员工不存在");
|
throw new RuntimeException("员工不存在");
|
||||||
@@ -291,4 +293,13 @@ public class CcdiBaseStaffServiceImpl implements ICcdiBaseStaffService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validatePartyMember(Integer partyMember, String fieldLabel) {
|
||||||
|
if (partyMember == null) {
|
||||||
|
throw new RuntimeException(fieldLabel + "不能为空");
|
||||||
|
}
|
||||||
|
if (partyMember != 0 && partyMember != 1) {
|
||||||
|
throw new RuntimeException(fieldLabel + "只能填写'0'或'1'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,13 +14,14 @@
|
|||||||
<result property="phone" column="phone"/>
|
<result property="phone" column="phone"/>
|
||||||
<result property="annualIncome" column="annual_income"/>
|
<result property="annualIncome" column="annual_income"/>
|
||||||
<result property="hireDate" column="hire_date"/>
|
<result property="hireDate" column="hire_date"/>
|
||||||
|
<result property="partyMember" column="is_party_member"/>
|
||||||
<result property="status" column="status"/>
|
<result property="status" column="status"/>
|
||||||
<result property="createTime" column="create_time"/>
|
<result property="createTime" column="create_time"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<select id="selectBaseStaffPageWithDept" resultMap="CcdiBaseStaffVOResult">
|
<select id="selectBaseStaffPageWithDept" resultMap="CcdiBaseStaffVOResult">
|
||||||
SELECT
|
SELECT
|
||||||
e.staff_id, e.name, e.dept_id, e.id_card, e.phone, e.annual_income, e.hire_date, e.status, e.create_time,
|
e.staff_id, e.name, e.dept_id, e.id_card, e.phone, e.annual_income, e.hire_date, e.is_party_member, e.status, e.create_time,
|
||||||
d.dept_name
|
d.dept_name
|
||||||
FROM ccdi_base_staff e
|
FROM ccdi_base_staff e
|
||||||
LEFT JOIN sys_dept d ON e.dept_id = d.dept_id
|
LEFT JOIN sys_dept d ON e.dept_id = d.dept_id
|
||||||
@@ -47,12 +48,12 @@
|
|||||||
<!-- 批量插入或更新员工信息(只更新非null字段) -->
|
<!-- 批量插入或更新员工信息(只更新非null字段) -->
|
||||||
<insert id="insertOrUpdateBatch" parameterType="java.util.List">
|
<insert id="insertOrUpdateBatch" parameterType="java.util.List">
|
||||||
INSERT INTO ccdi_base_staff
|
INSERT INTO ccdi_base_staff
|
||||||
(staff_id, name, dept_id, id_card, phone, annual_income, hire_date, status,
|
(staff_id, name, dept_id, id_card, phone, annual_income, hire_date, is_party_member, status,
|
||||||
create_time, create_by, update_by, update_time)
|
create_time, create_by, update_by, update_time)
|
||||||
VALUES
|
VALUES
|
||||||
<foreach collection="list" item="item" separator=",">
|
<foreach collection="list" item="item" separator=",">
|
||||||
(#{item.staffId}, #{item.name}, #{item.deptId}, #{item.idCard},
|
(#{item.staffId}, #{item.name}, #{item.deptId}, #{item.idCard},
|
||||||
#{item.phone}, #{item.annualIncome}, #{item.hireDate}, #{item.status}, NOW(),
|
#{item.phone}, #{item.annualIncome}, #{item.hireDate}, #{item.partyMember}, #{item.status}, NOW(),
|
||||||
#{item.createBy}, #{item.updateBy}, NOW())
|
#{item.createBy}, #{item.updateBy}, NOW())
|
||||||
</foreach>
|
</foreach>
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
@@ -61,6 +62,7 @@
|
|||||||
phone = COALESCE(VALUES(phone), phone),
|
phone = COALESCE(VALUES(phone), phone),
|
||||||
annual_income = COALESCE(VALUES(annual_income), annual_income),
|
annual_income = COALESCE(VALUES(annual_income), annual_income),
|
||||||
hire_date = COALESCE(VALUES(hire_date), hire_date),
|
hire_date = COALESCE(VALUES(hire_date), hire_date),
|
||||||
|
is_party_member = COALESCE(VALUES(is_party_member), is_party_member),
|
||||||
status = COALESCE(VALUES(status), status),
|
status = COALESCE(VALUES(status), status),
|
||||||
update_by = COALESCE(VALUES(update_by), update_by),
|
update_by = COALESCE(VALUES(update_by), update_by),
|
||||||
update_time = NOW()
|
update_time = NOW()
|
||||||
@@ -69,12 +71,12 @@
|
|||||||
<!-- 批量插入员工信息 -->
|
<!-- 批量插入员工信息 -->
|
||||||
<insert id="insertBatch" parameterType="java.util.List">
|
<insert id="insertBatch" parameterType="java.util.List">
|
||||||
INSERT INTO ccdi_base_staff
|
INSERT INTO ccdi_base_staff
|
||||||
(staff_id, name, dept_id, id_card, phone, annual_income, hire_date, status,
|
(staff_id, name, dept_id, id_card, phone, annual_income, hire_date, is_party_member, status,
|
||||||
create_time, create_by, update_by, update_time)
|
create_time, create_by, update_by, update_time)
|
||||||
VALUES
|
VALUES
|
||||||
<foreach collection="list" item="item" separator=",">
|
<foreach collection="list" item="item" separator=",">
|
||||||
(#{item.staffId}, #{item.name}, #{item.deptId}, #{item.idCard},
|
(#{item.staffId}, #{item.name}, #{item.deptId}, #{item.idCard},
|
||||||
#{item.phone}, #{item.annualIncome}, #{item.hireDate}, #{item.status}, NOW(),
|
#{item.phone}, #{item.annualIncome}, #{item.hireDate}, #{item.partyMember}, #{item.status}, NOW(),
|
||||||
#{item.createBy}, #{item.updateBy}, NOW())
|
#{item.createBy}, #{item.updateBy}, NOW())
|
||||||
</foreach>
|
</foreach>
|
||||||
</insert>
|
</insert>
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ class CcdiBaseStaffMapperTest {
|
|||||||
|
|
||||||
assertTrue(xml.contains("annual_income"), xml);
|
assertTrue(xml.contains("annual_income"), xml);
|
||||||
assertTrue(xml.contains("#{item.annualIncome}"), xml);
|
assertTrue(xml.contains("#{item.annualIncome}"), xml);
|
||||||
|
assertTrue(xml.contains("is_party_member"), xml);
|
||||||
|
assertTrue(xml.contains("#{item.partyMember}"), xml);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,28 @@ class CcdiBaseStaffImportServiceImplTest {
|
|||||||
assertDoesNotThrow(() -> service.validateStaffData(buildDto(new BigDecimal("12345.67")), false, Collections.emptySet(), Collections.emptySet()));
|
assertDoesNotThrow(() -> service.validateStaffData(buildDto(new BigDecimal("12345.67")), false, Collections.emptySet(), Collections.emptySet()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validateStaffData_shouldAllowPartyMemberValuesZeroAndOne() {
|
||||||
|
CcdiBaseStaffAddDTO nonPartyMember = buildDto(null);
|
||||||
|
nonPartyMember.setPartyMember(0);
|
||||||
|
CcdiBaseStaffAddDTO partyMember = buildDto(null);
|
||||||
|
partyMember.setPartyMember(1);
|
||||||
|
|
||||||
|
assertDoesNotThrow(() -> service.validateStaffData(nonPartyMember, false, Collections.emptySet(), Collections.emptySet()));
|
||||||
|
assertDoesNotThrow(() -> service.validateStaffData(partyMember, false, Collections.emptySet(), Collections.emptySet()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validateStaffData_shouldRejectInvalidPartyMemberValue() {
|
||||||
|
CcdiBaseStaffAddDTO dto = buildDto(null);
|
||||||
|
dto.setPartyMember(2);
|
||||||
|
|
||||||
|
RuntimeException exception = assertThrows(RuntimeException.class,
|
||||||
|
() -> service.validateStaffData(dto, false, Set.of(), Set.of()));
|
||||||
|
|
||||||
|
assertEquals("是否党员只能填写'0'或'1'", exception.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void validateStaffData_shouldRejectNegativeAnnualIncome() {
|
void validateStaffData_shouldRejectNegativeAnnualIncome() {
|
||||||
RuntimeException exception = assertThrows(RuntimeException.class,
|
RuntimeException exception = assertThrows(RuntimeException.class,
|
||||||
@@ -51,6 +73,7 @@ class CcdiBaseStaffImportServiceImplTest {
|
|||||||
dto.setIdCard("320101199001010014");
|
dto.setIdCard("320101199001010014");
|
||||||
dto.setPhone("13812345678");
|
dto.setPhone("13812345678");
|
||||||
dto.setStatus("0");
|
dto.setStatus("0");
|
||||||
|
dto.setPartyMember(1);
|
||||||
dto.setAnnualIncome(annualIncome);
|
dto.setAnnualIncome(annualIncome);
|
||||||
return dto;
|
return dto;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ class CcdiBaseStaffServiceImplTest {
|
|||||||
addDTO.setIdCard("320101199001010011");
|
addDTO.setIdCard("320101199001010011");
|
||||||
addDTO.setPhone("13812345678");
|
addDTO.setPhone("13812345678");
|
||||||
addDTO.setStatus("0");
|
addDTO.setStatus("0");
|
||||||
|
addDTO.setPartyMember(1);
|
||||||
addDTO.setAnnualIncome(new BigDecimal("12345.67"));
|
addDTO.setAnnualIncome(new BigDecimal("12345.67"));
|
||||||
addDTO.setAssetInfoList(List.of(
|
addDTO.setAssetInfoList(List.of(
|
||||||
buildAssetDto("房产"),
|
buildAssetDto("房产"),
|
||||||
@@ -70,6 +71,7 @@ class CcdiBaseStaffServiceImplTest {
|
|||||||
assertEquals(1, result);
|
assertEquals(1, result);
|
||||||
ArgumentCaptor<CcdiBaseStaff> staffCaptor = ArgumentCaptor.forClass(CcdiBaseStaff.class);
|
ArgumentCaptor<CcdiBaseStaff> staffCaptor = ArgumentCaptor.forClass(CcdiBaseStaff.class);
|
||||||
verify(baseStaffMapper).insert(staffCaptor.capture());
|
verify(baseStaffMapper).insert(staffCaptor.capture());
|
||||||
|
assertEquals(1, staffCaptor.getValue().getPartyMember());
|
||||||
assertEquals(new BigDecimal("12345.67"), staffCaptor.getValue().getAnnualIncome());
|
assertEquals(new BigDecimal("12345.67"), staffCaptor.getValue().getAnnualIncome());
|
||||||
ArgumentCaptor<List<CcdiAssetInfoDTO>> captor = ArgumentCaptor.forClass(List.class);
|
ArgumentCaptor<List<CcdiAssetInfoDTO>> captor = ArgumentCaptor.forClass(List.class);
|
||||||
verify(assetInfoService).replaceByFamilyId(eq("320101199001010011"), captor.capture());
|
verify(assetInfoService).replaceByFamilyId(eq("320101199001010011"), captor.capture());
|
||||||
@@ -92,6 +94,7 @@ class CcdiBaseStaffServiceImplTest {
|
|||||||
editDTO.setIdCard("320101199001010011");
|
editDTO.setIdCard("320101199001010011");
|
||||||
editDTO.setPhone("13812345678");
|
editDTO.setPhone("13812345678");
|
||||||
editDTO.setStatus("0");
|
editDTO.setStatus("0");
|
||||||
|
editDTO.setPartyMember(0);
|
||||||
editDTO.setAnnualIncome(new BigDecimal("45678.90"));
|
editDTO.setAnnualIncome(new BigDecimal("45678.90"));
|
||||||
editDTO.setAssetInfoList(List.of(buildAssetDto("车辆")));
|
editDTO.setAssetInfoList(List.of(buildAssetDto("车辆")));
|
||||||
|
|
||||||
@@ -104,6 +107,7 @@ class CcdiBaseStaffServiceImplTest {
|
|||||||
assertEquals(1, result);
|
assertEquals(1, result);
|
||||||
ArgumentCaptor<CcdiBaseStaff> staffCaptor = ArgumentCaptor.forClass(CcdiBaseStaff.class);
|
ArgumentCaptor<CcdiBaseStaff> staffCaptor = ArgumentCaptor.forClass(CcdiBaseStaff.class);
|
||||||
verify(baseStaffMapper).updateById(staffCaptor.capture());
|
verify(baseStaffMapper).updateById(staffCaptor.capture());
|
||||||
|
assertEquals(0, staffCaptor.getValue().getPartyMember());
|
||||||
assertEquals(new BigDecimal("45678.90"), staffCaptor.getValue().getAnnualIncome());
|
assertEquals(new BigDecimal("45678.90"), staffCaptor.getValue().getAnnualIncome());
|
||||||
verify(assetInfoService, never()).deleteByFamilyId("320101199001010011");
|
verify(assetInfoService, never()).deleteByFamilyId("320101199001010011");
|
||||||
verify(assetInfoService).replaceByFamilyId("320101199001010011", editDTO.getAssetInfoList());
|
verify(assetInfoService).replaceByFamilyId("320101199001010011", editDTO.getAssetInfoList());
|
||||||
@@ -122,6 +126,7 @@ class CcdiBaseStaffServiceImplTest {
|
|||||||
editDTO.setIdCard("320101199001010011");
|
editDTO.setIdCard("320101199001010011");
|
||||||
editDTO.setPhone("13812345678");
|
editDTO.setPhone("13812345678");
|
||||||
editDTO.setStatus("0");
|
editDTO.setStatus("0");
|
||||||
|
editDTO.setPartyMember(1);
|
||||||
editDTO.setAssetInfoList(List.of(buildAssetDto("车辆")));
|
editDTO.setAssetInfoList(List.of(buildAssetDto("车辆")));
|
||||||
|
|
||||||
when(baseStaffMapper.selectById(1001L)).thenReturn(existing);
|
when(baseStaffMapper.selectById(1001L)).thenReturn(existing);
|
||||||
@@ -135,17 +140,18 @@ class CcdiBaseStaffServiceImplTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void selectBaseStaffById_shouldReturnAssetInfoList() {
|
void selectBaseStaffById_shouldReturnSelfOwnedAssetInfoList() {
|
||||||
CcdiBaseStaff staff = new CcdiBaseStaff();
|
CcdiBaseStaff staff = new CcdiBaseStaff();
|
||||||
staff.setStaffId(1001L);
|
staff.setStaffId(1001L);
|
||||||
staff.setName("张三");
|
staff.setName("张三");
|
||||||
staff.setIdCard("320101199001010011");
|
staff.setIdCard("320101199001010011");
|
||||||
staff.setStatus("0");
|
staff.setStatus("0");
|
||||||
|
staff.setPartyMember(1);
|
||||||
staff.setAnnualIncome(new BigDecimal("88888.88"));
|
staff.setAnnualIncome(new BigDecimal("88888.88"));
|
||||||
|
|
||||||
CcdiAssetInfo assetInfo = new CcdiAssetInfo();
|
CcdiAssetInfo assetInfo = new CcdiAssetInfo();
|
||||||
assetInfo.setFamilyId("320101199001010011");
|
assetInfo.setFamilyId("320101199001010011");
|
||||||
assetInfo.setPersonId("320101199201010022");
|
assetInfo.setPersonId("320101199001010011");
|
||||||
assetInfo.setAssetMainType("车辆");
|
assetInfo.setAssetMainType("车辆");
|
||||||
assetInfo.setAssetSubType("小汽车");
|
assetInfo.setAssetSubType("小汽车");
|
||||||
assetInfo.setAssetName("家庭车辆");
|
assetInfo.setAssetName("家庭车辆");
|
||||||
@@ -153,14 +159,16 @@ class CcdiBaseStaffServiceImplTest {
|
|||||||
assetInfo.setAssetStatus("正常");
|
assetInfo.setAssetStatus("正常");
|
||||||
|
|
||||||
when(baseStaffMapper.selectById(1001L)).thenReturn(staff);
|
when(baseStaffMapper.selectById(1001L)).thenReturn(staff);
|
||||||
when(assetInfoService.selectByFamilyId("320101199001010011")).thenReturn(List.of(assetInfo));
|
when(assetInfoService.selectByFamilyIdAndPersonId("320101199001010011", "320101199001010011"))
|
||||||
|
.thenReturn(List.of(assetInfo));
|
||||||
|
|
||||||
CcdiBaseStaffVO result = service.selectBaseStaffById(1001L);
|
CcdiBaseStaffVO result = service.selectBaseStaffById(1001L);
|
||||||
|
|
||||||
assertNotNull(result.getAssetInfoList());
|
assertNotNull(result.getAssetInfoList());
|
||||||
|
assertEquals(1, result.getPartyMember());
|
||||||
assertEquals(new BigDecimal("88888.88"), result.getAnnualIncome());
|
assertEquals(new BigDecimal("88888.88"), result.getAnnualIncome());
|
||||||
assertEquals(1, result.getAssetInfoList().size());
|
assertEquals(1, result.getAssetInfoList().size());
|
||||||
assertEquals("320101199201010022", result.getAssetInfoList().get(0).getPersonId());
|
assertEquals("320101199001010011", result.getAssetInfoList().get(0).getPersonId());
|
||||||
assertEquals("车辆", result.getAssetInfoList().get(0).getAssetMainType());
|
assertEquals("车辆", result.getAssetInfoList().get(0).getAssetMainType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.ruoyi.info.collection.utils;
|
|||||||
|
|
||||||
import com.ruoyi.common.core.domain.entity.SysDictData;
|
import com.ruoyi.common.core.domain.entity.SysDictData;
|
||||||
import com.ruoyi.common.utils.DictUtils;
|
import com.ruoyi.common.utils.DictUtils;
|
||||||
|
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffExcel;
|
||||||
import com.ruoyi.info.collection.domain.excel.CcdiAssetInfoExcel;
|
import com.ruoyi.info.collection.domain.excel.CcdiAssetInfoExcel;
|
||||||
import com.ruoyi.info.collection.domain.excel.CcdiStaffFmyRelationExcel;
|
import com.ruoyi.info.collection.domain.excel.CcdiStaffFmyRelationExcel;
|
||||||
import org.apache.poi.ss.usermodel.CellStyle;
|
import org.apache.poi.ss.usermodel.CellStyle;
|
||||||
@@ -72,6 +73,31 @@ class EasyExcelUtilTemplateTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void importTemplateWithDictDropdown_shouldAddPartyMemberDropdownToBaseStaffTemplate() throws Exception {
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
|
||||||
|
try (MockedStatic<DictUtils> mocked = mockStatic(DictUtils.class)) {
|
||||||
|
mocked.when(() -> DictUtils.getDictCache("ccdi_employee_status"))
|
||||||
|
.thenReturn(List.of(
|
||||||
|
buildDictData("在职", "0"),
|
||||||
|
buildDictData("离职", "1")
|
||||||
|
));
|
||||||
|
mocked.when(() -> DictUtils.getDictCache("ccdi_yes_no_flag"))
|
||||||
|
.thenReturn(List.of(
|
||||||
|
buildDictData("是", "1"),
|
||||||
|
buildDictData("否", "0")
|
||||||
|
));
|
||||||
|
|
||||||
|
EasyExcelUtil.importTemplateWithDictDropdown(response, CcdiBaseStaffExcel.class, "员工信息");
|
||||||
|
}
|
||||||
|
|
||||||
|
try (Workbook workbook = WorkbookFactory.create(new ByteArrayInputStream(response.getContentAsByteArray()))) {
|
||||||
|
Sheet sheet = workbook.getSheetAt(0);
|
||||||
|
assertTrue(hasValidationOnColumn(sheet, 7), "是否党员列应包含下拉校验");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void assertTextColumn(Sheet sheet, int columnIndex) {
|
private void assertTextColumn(Sheet sheet, int columnIndex) {
|
||||||
CellStyle style = sheet.getColumnStyle(columnIndex);
|
CellStyle style = sheet.getColumnStyle(columnIndex);
|
||||||
assertNotNull(style, "文本列应设置默认样式");
|
assertNotNull(style, "文本列应设置默认样式");
|
||||||
@@ -90,9 +116,13 @@ class EasyExcelUtilTemplateTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private SysDictData buildDictData(String label) {
|
private SysDictData buildDictData(String label) {
|
||||||
|
return buildDictData(label, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SysDictData buildDictData(String label, String value) {
|
||||||
SysDictData dictData = new SysDictData();
|
SysDictData dictData = new SysDictData();
|
||||||
dictData.setDictLabel(label);
|
dictData.setDictLabel(label);
|
||||||
dictData.setDictValue(label);
|
dictData.setDictValue(value);
|
||||||
return dictData;
|
return dictData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# 员工基础信息新增是否党员字段后端实施计划
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** 在员工基础信息后端链路中新增“是否党员”字段,保证数据库、实体、接口、导入导出与最小测试契约保持一致。
|
||||||
|
|
||||||
|
**Architecture:** 继续沿用 `ccdi_base_staff` 现有维护链路,在表上增加 `is_party_member` 字段,并在 `CcdiBaseStaff` 的实体、DTO、VO、Excel、Mapper XML 与服务校验中同步补齐。实现保持最短路径,不新增新的接口层或旁路转换逻辑,只在现有员工维护链路上扩字段。
|
||||||
|
|
||||||
|
**Tech Stack:** MySQL, Java 21, Spring Boot 3, MyBatis Plus, EasyExcel, JUnit 5, Markdown
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 文件结构与职责
|
||||||
|
|
||||||
|
**后端源码**
|
||||||
|
|
||||||
|
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiBaseStaff.java`
|
||||||
|
新增 `partyMember` 字段并映射 `is_party_member`。
|
||||||
|
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiBaseStaffAddDTO.java`
|
||||||
|
新增新增接口入参字段与非空校验。
|
||||||
|
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiBaseStaffEditDTO.java`
|
||||||
|
新增编辑接口入参字段与非空校验。
|
||||||
|
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CcdiBaseStaffVO.java`
|
||||||
|
新增详情/列表返回字段。
|
||||||
|
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/excel/CcdiBaseStaffExcel.java`
|
||||||
|
新增 Excel 导入导出列,并挂接“是/否”字典下拉。
|
||||||
|
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/ImportFailureVO.java`
|
||||||
|
新增导入失败记录字段回显。
|
||||||
|
- `ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiBaseStaffMapper.xml`
|
||||||
|
在列表查询、批量新增、批量更新 SQL 中补 `is_party_member`。
|
||||||
|
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiBaseStaffServiceImpl.java`
|
||||||
|
补新增/编辑链路校验,约束 `partyMember` 只能为 `0/1`。
|
||||||
|
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiBaseStaffImportServiceImpl.java`
|
||||||
|
补导入链路的必填和枚举值校验。
|
||||||
|
|
||||||
|
**SQL**
|
||||||
|
|
||||||
|
- `sql/migration/2026-04-17-add-base-staff-party-member.sql`
|
||||||
|
以幂等方式为 `ccdi_base_staff` 增加字段,并补充 `ccdi_yes_no_flag` 字典数据。
|
||||||
|
- `sql/ccdi_yes_no_flag_dict.sql`
|
||||||
|
提供“是/否标记”字典初始化脚本,供新环境或独立初始化使用。
|
||||||
|
|
||||||
|
**测试**
|
||||||
|
|
||||||
|
- `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiBaseStaffServiceImplTest.java`
|
||||||
|
验证服务层新增/修改/详情查询会透传 `partyMember`。
|
||||||
|
- `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiBaseStaffImportServiceImplTest.java`
|
||||||
|
验证导入场景仅允许 `0/1`。
|
||||||
|
- `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/mapper/CcdiBaseStaffMapperTest.java`
|
||||||
|
验证 Mapper XML 已包含 `is_party_member` 与 `#{item.partyMember}`。
|
||||||
|
- `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/utils/EasyExcelUtilTemplateTest.java`
|
||||||
|
验证员工模板已补“是否党员”下拉列。
|
||||||
|
|
||||||
|
## 实施步骤
|
||||||
|
|
||||||
|
- [ ] 在 `ccdi_base_staff` 表增加 `is_party_member`,默认值为 `0`,避免历史数据为空。
|
||||||
|
- [ ] 在员工基础信息实体、DTO、VO、Excel 对象中补齐 `partyMember`。
|
||||||
|
- [ ] 在 `CcdiBaseStaffMapper.xml` 的列表、批量新增、批量更新 SQL 中补 `is_party_member`。
|
||||||
|
- [ ] 在 `CcdiBaseStaffServiceImpl` 与 `CcdiBaseStaffImportServiceImpl` 中增加 `0/1` 值域校验。
|
||||||
|
- [ ] 新增 `ccdi_yes_no_flag` 字典脚本,保证导入模板下拉可用。
|
||||||
|
- [ ] 补充并执行后端定向测试;若执行受现有依赖问题阻塞,需要在记录中明确注明阻塞原因。
|
||||||
|
|
||||||
|
## 验证记录
|
||||||
|
|
||||||
|
- 已尝试执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn -pl ccdi-info-collection -Dtest=CcdiBaseStaffServiceImplTest,CcdiBaseStaffImportServiceImplTest,CcdiBaseStaffMapperTest,EasyExcelUtilTemplateTest test
|
||||||
|
mvn -pl ccdi-info-collection -DskipTests compile
|
||||||
|
```
|
||||||
|
|
||||||
|
- 当前结果:
|
||||||
|
- `test` 在进入本次新增断言前,被模块内既有测试编译问题拦截,表现为缺少 `org.springframework.data.redis.core.*` 类型。
|
||||||
|
- `compile` 被模块当前既有依赖缺失拦截,表现为缺少 `com.ruoyi.common.annotation.*`、`org.springframework.data.redis.core.*`、`IdCardUtil` 等类型。
|
||||||
|
- 上述阻塞不是本次“是否党员”字段新增引入的新问题,但会影响自动化验证结论,需要后续先修复模块依赖基线。
|
||||||
|
|
||||||
|
## 完成标准
|
||||||
|
|
||||||
|
- 员工基础信息接口可读写 `partyMember`
|
||||||
|
- 员工列表、详情、导入导出链路都包含 `partyMember`
|
||||||
|
- 数据库字段与字典 SQL 已补齐
|
||||||
|
- 后端测试契约已同步更新
|
||||||
|
- 已明确记录当前自动化验证阻塞点
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# 员工信息仅展示本人资产后端实施计划
|
||||||
|
|
||||||
|
## 变更目标
|
||||||
|
|
||||||
|
- 员工详情接口返回的 `assetInfoList` 仅包含员工本人资产
|
||||||
|
- 员工信息页不再通过详情接口混入亲属资产数据
|
||||||
|
|
||||||
|
## 变更范围
|
||||||
|
|
||||||
|
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiBaseStaffServiceImpl.java`
|
||||||
|
- `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiBaseStaffServiceImplTest.java`
|
||||||
|
|
||||||
|
## 实施步骤
|
||||||
|
|
||||||
|
1. 将员工详情聚合资产的查询口径从 `family_id = 员工身份证号` 调整为 `family_id = 员工身份证号 and person_id = 员工身份证号`
|
||||||
|
2. 保持员工新增、编辑时的 `replaceByFamilyId` 逻辑不变,继续由后端写入本人资产
|
||||||
|
3. 调整单元测试,验证员工详情仅返回本人资产
|
||||||
|
|
||||||
|
## 验证要点
|
||||||
|
|
||||||
|
- 查询员工详情时,`assetInfoList` 不再返回亲属资产
|
||||||
|
- 现有员工新增、编辑、删除链路不受影响
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# 修复员工资产信息表注释乱码后端实施计划
|
||||||
|
|
||||||
|
## 变更目标
|
||||||
|
|
||||||
|
- 修复 `ccdi_asset_info` 表中 `family_id`、`person_id` 列注释乱码问题
|
||||||
|
- 保持表结构、字段类型和业务数据不变,仅修正元数据注释
|
||||||
|
|
||||||
|
## 变更范围
|
||||||
|
|
||||||
|
- `sql/migration/2026-04-17-fix-ccdi-asset-info-comment-encoding.sql`
|
||||||
|
|
||||||
|
## 实施步骤
|
||||||
|
|
||||||
|
1. 查询 `information_schema.COLUMNS`,确认 `ccdi_asset_info` 列注释实际存在乱码
|
||||||
|
2. 新增增量 SQL,使用 `ALTER TABLE ... MODIFY COLUMN ... COMMENT` 修复 `family_id`、`person_id` 注释
|
||||||
|
3. 通过 `bin/mysql_utf8_exec.sh` 以 `utf8mb4` 会话执行脚本
|
||||||
|
4. 再次查询 `information_schema.COLUMNS` 验证注释已恢复为中文
|
||||||
|
|
||||||
|
## 验证要点
|
||||||
|
|
||||||
|
- `family_id` 注释显示为“归属员工证件号”
|
||||||
|
- `person_id` 注释显示为“资产实际持有人证件号”
|
||||||
|
- 字段类型仍为 `VARCHAR(100) NOT NULL`
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# 员工信息页资产提示文案移除前端实施计划
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** 移除员工信息页资产信息区域中“员工信息页仅维护员工本人资产”“资产持有人默认为当前员工本人,无需额外填写”的备注展示。
|
||||||
|
|
||||||
|
**Architecture:** 本次仅调整员工信息维护页模板展示,不改动资产表单字段、默认值、提交参数或后端接口。实现上直接删除备注 DOM 和对应样式,保持页面其余交互不变。
|
||||||
|
|
||||||
|
**Tech Stack:** Vue 2, Element UI, JavaScript, Markdown
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 文件结构与职责
|
||||||
|
|
||||||
|
- `ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
|
||||||
|
删除资产信息区域的备注文案和无用样式。
|
||||||
|
|
||||||
|
## 实施步骤
|
||||||
|
|
||||||
|
- [ ] 删除资产信息区备注展示块
|
||||||
|
- [ ] 删除 `assets-helper` 对应样式,避免残留无用 CSS
|
||||||
|
- [ ] 运行前端构建确认页面模板调整未引入语法错误
|
||||||
|
|
||||||
|
## 验证
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && npm run build:prod
|
||||||
|
```
|
||||||
|
|
||||||
|
## 完成标准
|
||||||
|
|
||||||
|
- 员工信息页不再展示上述两条资产备注
|
||||||
|
- 资产新增、编辑、提交逻辑保持不变
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# 员工基础信息新增是否党员字段前端实施计划
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** 在员工信息维护页面新增“是否党员”基础字段,并打通列表展示、详情回显、编辑录入与导入失败记录展示。
|
||||||
|
|
||||||
|
**Architecture:** 保持 `ruoyi-ui/src/views/ccdiBaseStaff/index.vue` 的现有页面结构不变,仅在已有“基本信息”区域与员工列表中插入一个新字段。字段值与后端保持一致,前端统一使用 `0/1` 数值口径,并通过页面内格式化方法展示为“是/否”。
|
||||||
|
|
||||||
|
**Tech Stack:** Vue 2, Element UI, JavaScript, npm, nvm, Markdown
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 文件结构与职责
|
||||||
|
|
||||||
|
**前端源码**
|
||||||
|
|
||||||
|
- `ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
|
||||||
|
新增列表列、编辑表单、详情弹窗、失败记录弹窗和格式化方法。
|
||||||
|
|
||||||
|
**依赖接口**
|
||||||
|
|
||||||
|
- `ruoyi-ui/src/api/ccdiBaseStaff.js`
|
||||||
|
本次接口路径不变,继续复用现有新增/编辑/详情 API,只承接新增字段。
|
||||||
|
|
||||||
|
## 实施步骤
|
||||||
|
|
||||||
|
- [ ] 在员工列表中增加“是否党员”列,统一显示“是/否”。
|
||||||
|
- [ ] 在新增/编辑弹窗的基本信息区域增加“是否党员”单选项,默认值设为“否”。
|
||||||
|
- [ ] 在详情弹窗中增加“是否党员”展示,保证历史员工查看时能回显。
|
||||||
|
- [ ] 在导入失败记录弹窗中增加“是否党员”列,便于排查模板数据问题。
|
||||||
|
- [ ] 在页面 methods 中新增 `formatPartyMember`,统一处理 `0/1/null` 的展示。
|
||||||
|
- [ ] 使用 `nvm` 选择当前机器可用的 Node 版本后执行前端构建验证。
|
||||||
|
|
||||||
|
## 验证记录
|
||||||
|
|
||||||
|
- 已尝试执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source ~/.nvm/nvm.sh && nvm use
|
||||||
|
source ~/.nvm/nvm.sh && nvm use 14.21.3 && npm run build:prod
|
||||||
|
```
|
||||||
|
|
||||||
|
- 当前结果:
|
||||||
|
- 仓库内未提供 `.nvmrc`,直接执行 `nvm use` 无法自动切到项目版本。
|
||||||
|
- 当前机器存在 `v14.21.3`、`v25.9.0` 与 `system(v24.14.0)`,后续前端验证建议优先使用 `v14.21.3`。
|
||||||
|
- 已使用 `v14.21.3` 成功执行 `npm run build:prod`,构建通过,仅保留项目原有的包体积告警。
|
||||||
|
|
||||||
|
## 完成标准
|
||||||
|
|
||||||
|
- 员工列表、详情、编辑弹窗可见“是否党员”
|
||||||
|
- 提交新增/编辑时会带上 `partyMember`
|
||||||
|
- 导入失败记录能展示该字段
|
||||||
|
- 已明确前端构建使用 `nvm` 的版本前提与当前环境限制
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# 员工信息仅展示本人资产前端实施计划
|
||||||
|
|
||||||
|
## 变更目标
|
||||||
|
|
||||||
|
- 员工信息页资产区域统一按“本人资产”口径展示
|
||||||
|
- 详情弹窗移除“资产实际持有人身份证号”和“归属类型”
|
||||||
|
- 员工资产编辑表单不再保留亲属资产相关提示与校验
|
||||||
|
|
||||||
|
## 变更范围
|
||||||
|
|
||||||
|
- `ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
|
||||||
|
|
||||||
|
## 实施步骤
|
||||||
|
|
||||||
|
1. 更新资产信息区提示文案,明确员工信息页仅维护员工本人资产
|
||||||
|
2. 删除详情弹窗中的“资产实际持有人身份证号”“归属类型”列
|
||||||
|
3. 清理表单中 `personId` 的必填与格式校验,新增资产时默认带入当前员工身份证号
|
||||||
|
4. 更新员工资产导入弹窗提示文案,明确仅允许导入员工本人资产
|
||||||
|
|
||||||
|
## 验证要点
|
||||||
|
|
||||||
|
- 员工详情弹窗仅显示本人资产字段
|
||||||
|
- 新增、编辑员工资产时不再出现亲属资产口径提示
|
||||||
|
- 员工身份证号变更后,表单内资产仍跟随当前员工身份证号
|
||||||
@@ -116,6 +116,11 @@
|
|||||||
<el-table-column label="所属部门" align="center" prop="deptName" :show-overflow-tooltip="true"/>
|
<el-table-column label="所属部门" align="center" prop="deptName" :show-overflow-tooltip="true"/>
|
||||||
<el-table-column label="电话" align="center" prop="phone" width="120"/>
|
<el-table-column label="电话" align="center" prop="phone" width="120"/>
|
||||||
<el-table-column label="年收入" align="center" prop="annualIncome" width="140"/>
|
<el-table-column label="年收入" align="center" prop="annualIncome" width="140"/>
|
||||||
|
<el-table-column label="是否党员" align="center" prop="partyMember" width="100">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ formatPartyMember(scope.row.partyMember) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="状态" align="center" prop="status" width="100">
|
<el-table-column label="状态" align="center" prop="status" width="100">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-tag v-if="scope.row.status === '0'" type="success">在职</el-tag>
|
<el-tag v-if="scope.row.status === '0'" type="success">在职</el-tag>
|
||||||
@@ -217,7 +222,14 @@
|
|||||||
<el-date-picker v-model="form.hireDate" type="date" placeholder="选择入职时间" value-format="yyyy-MM-dd" style="width: 100%" />
|
<el-date-picker v-model="form.hireDate" type="date" placeholder="选择入职时间" value-format="yyyy-MM-dd" style="width: 100%" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12" />
|
<el-col :span="12">
|
||||||
|
<el-form-item label="是否党员" prop="partyMember">
|
||||||
|
<el-radio-group v-model="form.partyMember">
|
||||||
|
<el-radio :label="1">是</el-radio>
|
||||||
|
<el-radio :label="0">否</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-form-item label="状态" prop="status">
|
<el-form-item label="状态" prop="status">
|
||||||
<el-radio-group v-model="form.status">
|
<el-radio-group v-model="form.status">
|
||||||
@@ -229,10 +241,6 @@
|
|||||||
<span>资产信息</span>
|
<span>资产信息</span>
|
||||||
<el-button type="primary" plain size="mini" icon="el-icon-plus" @click="handleAddAsset">新增资产</el-button>
|
<el-button type="primary" plain size="mini" icon="el-icon-plus" @click="handleAddAsset">新增资产</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="assets-helper">
|
|
||||||
<div>新增、编辑时无需填写实际持有人身份证号</div>
|
|
||||||
<div>系统会默认带入并保留已有归属信息</div>
|
|
||||||
</div>
|
|
||||||
<el-form-item label-width="0" prop="assetInfoList">
|
<el-form-item label-width="0" prop="assetInfoList">
|
||||||
<div v-if="!form.assetInfoList || !form.assetInfoList.length" class="empty-assets">
|
<div v-if="!form.assetInfoList || !form.assetInfoList.length" class="empty-assets">
|
||||||
<i class="el-icon-office-building"></i>
|
<i class="el-icon-office-building"></i>
|
||||||
@@ -333,6 +341,9 @@
|
|||||||
<el-descriptions-item label="入职时间">
|
<el-descriptions-item label="入职时间">
|
||||||
{{ employeeDetail.hireDate ? parseTime(employeeDetail.hireDate, '{y}-{m}-{d}') : '-' }}
|
{{ employeeDetail.hireDate ? parseTime(employeeDetail.hireDate, '{y}-{m}-{d}') : '-' }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="是否党员">
|
||||||
|
{{ formatPartyMember(employeeDetail.partyMember) }}
|
||||||
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="状态">
|
<el-descriptions-item label="状态">
|
||||||
<el-tag v-if="employeeDetail.status === '0'" type="success" size="small">在职</el-tag>
|
<el-tag v-if="employeeDetail.status === '0'" type="success" size="small">在职</el-tag>
|
||||||
<el-tag v-else type="danger" size="small">离职</el-tag>
|
<el-tag v-else type="danger" size="small">离职</el-tag>
|
||||||
@@ -351,12 +362,6 @@
|
|||||||
暂无资产信息
|
暂无资产信息
|
||||||
</div>
|
</div>
|
||||||
<el-table v-else :data="employeeDetail.assetInfoList" border class="detail-assets-table">
|
<el-table v-else :data="employeeDetail.assetInfoList" border class="detail-assets-table">
|
||||||
<el-table-column label="资产实际持有人身份证号" prop="personId" min-width="220" />
|
|
||||||
<el-table-column label="归属类型" prop="ownerType" min-width="100">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<span>{{ formatAssetOwnerType(scope.row) }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="资产大类" prop="assetMainType" min-width="120" />
|
<el-table-column label="资产大类" prop="assetMainType" min-width="120" />
|
||||||
<el-table-column label="资产小类" prop="assetSubType" min-width="120" />
|
<el-table-column label="资产小类" prop="assetSubType" min-width="120" />
|
||||||
<el-table-column label="资产名称" prop="assetName" min-width="140" />
|
<el-table-column label="资产名称" prop="assetName" min-width="140" />
|
||||||
@@ -429,7 +434,7 @@
|
|||||||
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline;" @click="importAssetTemplate">下载员工资产模板</el-link>
|
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline;" @click="importAssetTemplate">下载员工资产模板</el-link>
|
||||||
</div>
|
</div>
|
||||||
<div class="el-upload__tip" slot="tip">
|
<div class="el-upload__tip" slot="tip">
|
||||||
<span>仅支持导入员工本人资产数据,文件需为"xls"或"xlsx"格式,系统将根据 personId/person_id 自动识别归属员工。</span>
|
<span>仅支持导入员工本人资产数据,文件需为"xls"或"xlsx"格式,导入身份证号需与员工本人一致。</span>
|
||||||
</div>
|
</div>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
<div slot="footer" class="dialog-footer">
|
<div slot="footer" class="dialog-footer">
|
||||||
@@ -460,12 +465,17 @@
|
|||||||
style="margin-bottom: 15px"
|
style="margin-bottom: 15px"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<el-table :data="failureList" v-loading="failureLoading">
|
<el-table :data="failureList" v-loading="failureLoading">
|
||||||
<el-table-column label="姓名" prop="name" align="center" />
|
<el-table-column label="姓名" prop="name" align="center" />
|
||||||
<el-table-column label="柜员号" prop="staffId" align="center" />
|
<el-table-column label="柜员号" prop="staffId" align="center" />
|
||||||
<el-table-column label="身份证号" prop="idCard" align="center" />
|
<el-table-column label="身份证号" prop="idCard" align="center" />
|
||||||
<el-table-column label="电话" prop="phone" align="center" />
|
<el-table-column label="电话" prop="phone" align="center" />
|
||||||
<el-table-column label="年收入" prop="annualIncome" align="center" width="140" />
|
<el-table-column label="年收入" prop="annualIncome" align="center" width="140" />
|
||||||
|
<el-table-column label="是否党员" prop="partyMember" align="center" width="100">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ formatPartyMember(scope.row.partyMember) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="失败原因" prop="errorMessage" align="center" min-width="200" :show-overflow-tooltip="true" />
|
<el-table-column label="失败原因" prop="errorMessage" align="center" min-width="200" :show-overflow-tooltip="true" />
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
@@ -622,6 +632,9 @@ export default {
|
|||||||
annualIncome: [
|
annualIncome: [
|
||||||
{ validator: (rule, value, callback) => this.validateAnnualIncomeRule(value, callback, "年收入"), trigger: "blur" }
|
{ validator: (rule, value, callback) => this.validateAnnualIncomeRule(value, callback, "年收入"), trigger: "blur" }
|
||||||
],
|
],
|
||||||
|
partyMember: [
|
||||||
|
{ required: true, message: "请选择是否党员", trigger: "change" }
|
||||||
|
],
|
||||||
status: [
|
status: [
|
||||||
{ required: true, message: "请选择状态", trigger: "change" }
|
{ required: true, message: "请选择状态", trigger: "change" }
|
||||||
]
|
]
|
||||||
@@ -701,8 +714,14 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
'form.idCard'(newIdCard, oldIdCard) {
|
'form.idCard'(newIdCard) {
|
||||||
this.syncAssetPersonIds(newIdCard, oldIdCard);
|
if (!Array.isArray(this.form.assetInfoList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.form.assetInfoList = this.form.assetInfoList.map(asset => ({
|
||||||
|
...asset,
|
||||||
|
personId: newIdCard || ""
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -956,6 +975,7 @@ export default {
|
|||||||
phone: null,
|
phone: null,
|
||||||
annualIncome: null,
|
annualIncome: null,
|
||||||
hireDate: null,
|
hireDate: null,
|
||||||
|
partyMember: 0,
|
||||||
status: "0",
|
status: "0",
|
||||||
relatives: [],
|
relatives: [],
|
||||||
assetInfoList: []
|
assetInfoList: []
|
||||||
@@ -1008,7 +1028,6 @@ export default {
|
|||||||
},
|
},
|
||||||
validateAssetInfoList(assetInfoList) {
|
validateAssetInfoList(assetInfoList) {
|
||||||
const requiredFields = [
|
const requiredFields = [
|
||||||
{ key: "personId", label: "资产实际持有人身份证号" },
|
|
||||||
{ key: "assetMainType", label: "资产大类" },
|
{ key: "assetMainType", label: "资产大类" },
|
||||||
{ key: "assetSubType", label: "资产小类" },
|
{ key: "assetSubType", label: "资产小类" },
|
||||||
{ key: "assetName", label: "资产名称" },
|
{ key: "assetName", label: "资产名称" },
|
||||||
@@ -1033,11 +1052,6 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!idCardPattern.test(asset.personId)) {
|
|
||||||
this.$modal.msgError(`第${rowNo}条资产的资产实际持有人身份证号格式不正确`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const field of numericFields) {
|
for (const field of numericFields) {
|
||||||
const value = asset[field.key];
|
const value = asset[field.key];
|
||||||
if (value !== null && value !== undefined && String(value).trim() !== "") {
|
if (value !== null && value !== undefined && String(value).trim() !== "") {
|
||||||
@@ -1056,9 +1070,9 @@ export default {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
createEmptyAssetRow(defaultPersonId = "") {
|
createEmptyAssetRow() {
|
||||||
return {
|
return {
|
||||||
personId: defaultPersonId || "",
|
personId: this.form.idCard || "",
|
||||||
assetMainType: "",
|
assetMainType: "",
|
||||||
assetSubType: "",
|
assetSubType: "",
|
||||||
assetName: "",
|
assetName: "",
|
||||||
@@ -1080,29 +1094,11 @@ export default {
|
|||||||
return value !== null && value !== undefined && String(value).trim() !== "";
|
return value !== null && value !== undefined && String(value).trim() !== "";
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
syncAssetPersonIds(newIdCard, oldIdCard) {
|
|
||||||
if (!Array.isArray(this.form.assetInfoList)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.form.assetInfoList = this.form.assetInfoList.map(asset => {
|
|
||||||
if (!asset || typeof asset !== "object") {
|
|
||||||
return asset;
|
|
||||||
}
|
|
||||||
const shouldSync = !asset.personId || asset.personId === oldIdCard;
|
|
||||||
if (!shouldSync) {
|
|
||||||
return asset;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...asset,
|
|
||||||
personId: newIdCard || ""
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
handleAddAsset() {
|
handleAddAsset() {
|
||||||
if (!Array.isArray(this.form.assetInfoList)) {
|
if (!Array.isArray(this.form.assetInfoList)) {
|
||||||
this.form.assetInfoList = [];
|
this.form.assetInfoList = [];
|
||||||
}
|
}
|
||||||
this.form.assetInfoList.push(this.createEmptyAssetRow(this.form.idCard));
|
this.form.assetInfoList.push(this.createEmptyAssetRow());
|
||||||
},
|
},
|
||||||
handleRemoveAsset(index) {
|
handleRemoveAsset(index) {
|
||||||
if (!Array.isArray(this.form.assetInfoList)) {
|
if (!Array.isArray(this.form.assetInfoList)) {
|
||||||
@@ -1110,11 +1106,14 @@ export default {
|
|||||||
}
|
}
|
||||||
this.form.assetInfoList.splice(index, 1);
|
this.form.assetInfoList.splice(index, 1);
|
||||||
},
|
},
|
||||||
formatAssetOwnerType(asset) {
|
formatPartyMember(value) {
|
||||||
if (!asset) {
|
if (value === 1 || value === "1") {
|
||||||
return "-";
|
return "是";
|
||||||
}
|
}
|
||||||
return asset.personId && asset.personId === this.employeeDetail.idCard ? "本人" : "亲属";
|
if (value === 0 || value === "0") {
|
||||||
|
return "否";
|
||||||
|
}
|
||||||
|
return "-";
|
||||||
},
|
},
|
||||||
/** 搜索按钮操作 */
|
/** 搜索按钮操作 */
|
||||||
handleQuery() {
|
handleQuery() {
|
||||||
@@ -1159,6 +1158,7 @@ export default {
|
|||||||
getBaseStaff(staffId).then(response => {
|
getBaseStaff(staffId).then(response => {
|
||||||
this.form = {
|
this.form = {
|
||||||
...response.data,
|
...response.data,
|
||||||
|
partyMember: response.data.partyMember !== null && response.data.partyMember !== undefined ? response.data.partyMember : 0,
|
||||||
assetInfoList: response.data.assetInfoList || []
|
assetInfoList: response.data.assetInfoList || []
|
||||||
};
|
};
|
||||||
this.form.annualIncome = response.data.annualIncome;
|
this.form.annualIncome = response.data.annualIncome;
|
||||||
@@ -1673,16 +1673,6 @@ export default {
|
|||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.employee-edit-dialog .assets-helper {
|
|
||||||
margin: -4px 0 12px;
|
|
||||||
padding: 10px 12px;
|
|
||||||
background: #f4f8ff;
|
|
||||||
border: 1px solid #d9ecff;
|
|
||||||
border-radius: 6px;
|
|
||||||
color: #606266;
|
|
||||||
line-height: 1.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.employee-edit-dialog .assets-table-wrapper {
|
.employee-edit-dialog .assets-table-wrapper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
|
|||||||
14
sql/ccdi_yes_no_flag_dict.sql
Normal file
14
sql/ccdi_yes_no_flag_dict.sql
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
-- 是否标记字典初始化脚本
|
||||||
|
|
||||||
|
DELETE FROM sys_dict_data WHERE dict_type = 'ccdi_yes_no_flag';
|
||||||
|
DELETE FROM sys_dict_type WHERE dict_type = 'ccdi_yes_no_flag';
|
||||||
|
|
||||||
|
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
|
||||||
|
VALUES ('是否标记', 'ccdi_yes_no_flag', '0', 'admin', NOW(), '是否标记列表');
|
||||||
|
|
||||||
|
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark)
|
||||||
|
VALUES
|
||||||
|
(1, '是', '1', 'ccdi_yes_no_flag', '', 'primary', 'N', '0', 'admin', NOW(), '是'),
|
||||||
|
(2, '否', '0', 'ccdi_yes_no_flag', '', 'danger', 'Y', '0', 'admin', NOW(), '否');
|
||||||
|
|
||||||
|
-- 执行完成后,请在字典管理中刷新缓存,确保模板下拉立即生效。
|
||||||
33
sql/migration/2026-04-17-add-base-staff-party-member.sql
Normal file
33
sql/migration/2026-04-17-add-base-staff-party-member.sql
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
SET @base_staff_party_member_sql = IF(
|
||||||
|
EXISTS(
|
||||||
|
SELECT 1
|
||||||
|
FROM information_schema.columns
|
||||||
|
WHERE table_schema = DATABASE()
|
||||||
|
AND table_name = 'ccdi_base_staff'
|
||||||
|
AND column_name = 'is_party_member'
|
||||||
|
),
|
||||||
|
'SELECT 1',
|
||||||
|
'ALTER TABLE `ccdi_base_staff` ADD COLUMN `is_party_member` TINYINT(1) NOT NULL DEFAULT 0 COMMENT ''是否党员:0-否 1-是'' AFTER `hire_date`'
|
||||||
|
);
|
||||||
|
|
||||||
|
PREPARE base_staff_party_member_stmt FROM @base_staff_party_member_sql;
|
||||||
|
EXECUTE base_staff_party_member_stmt;
|
||||||
|
DEALLOCATE PREPARE base_staff_party_member_stmt;
|
||||||
|
|
||||||
|
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
|
||||||
|
SELECT '是否标记', 'ccdi_yes_no_flag', '0', 'admin', NOW(), '是否标记列表'
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1 FROM sys_dict_type WHERE dict_type = 'ccdi_yes_no_flag'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark)
|
||||||
|
SELECT 1, '是', '1', 'ccdi_yes_no_flag', '', 'primary', 'N', '0', 'admin', NOW(), '是'
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1 FROM sys_dict_data WHERE dict_type = 'ccdi_yes_no_flag' AND dict_value = '1'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark)
|
||||||
|
SELECT 2, '否', '0', 'ccdi_yes_no_flag', '', 'danger', 'Y', '0', 'admin', NOW(), '否'
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1 FROM sys_dict_data WHERE dict_type = 'ccdi_yes_no_flag' AND dict_value = '0'
|
||||||
|
);
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE `ccdi_asset_info`
|
||||||
|
MODIFY COLUMN `family_id` VARCHAR(100) NOT NULL COMMENT '归属员工证件号',
|
||||||
|
MODIFY COLUMN `person_id` VARCHAR(100) NOT NULL COMMENT '资产实际持有人证件号';
|
||||||
Reference in New Issue
Block a user