fix: persist battery group configuration
| | |
| | | private ITBatteryGroupFieldService batteryGroupFieldService; |
| | | |
| | | /** 分页查询电池组字段列表。 */ |
| | | @PreAuthorize("@ss.hasPermi('system:batteryGroupField:list')") |
| | | @PreAuthorize("@ss.hasPermi('system:batteryGroup:list')") |
| | | @GetMapping("/list") |
| | | public TableDataInfo list(TBatteryGroupField field) |
| | | { |
| | |
| | | } |
| | | |
| | | /** 查询电池组字段详情。 */ |
| | | @PreAuthorize("@ss.hasPermi('system:batteryGroupField:query')") |
| | | @PreAuthorize("@ss.hasPermi('system:batteryGroup:query')") |
| | | @GetMapping(value = "/{fieldId}") |
| | | public AjaxResult getInfo(@PathVariable Long fieldId) |
| | | { |
| | |
| | | } |
| | | |
| | | /** 新增电池组字段。 */ |
| | | @PreAuthorize("@ss.hasPermi('system:batteryGroupField:add')") |
| | | @PreAuthorize("@ss.hasPermi('system:batteryGroup:add')") |
| | | @Log(title = "电池组字段管理", businessType = BusinessType.INSERT) |
| | | @PostMapping |
| | | public AjaxResult add(@Validated @RequestBody TBatteryGroupField field) |
| | | { |
| | | field.setCreateBy(getUsername()); |
| | | return toAjax(batteryGroupFieldService.insertBatteryGroupField(field)); |
| | | } |
| | | |
| | | /** 编辑电池组字段基础资料。 */ |
| | | @PreAuthorize("@ss.hasPermi('system:batteryGroupField:edit')") |
| | | @PreAuthorize("@ss.hasPermi('system:batteryGroup:edit')") |
| | | @Log(title = "电池组字段管理", businessType = BusinessType.UPDATE) |
| | | @PutMapping |
| | | public AjaxResult edit(@Validated @RequestBody TBatteryGroupField field) |
| | |
| | | } |
| | | |
| | | /** 启用或禁用电池组字段。 */ |
| | | @PreAuthorize("@ss.hasPermi('system:batteryGroupField:edit')") |
| | | @PreAuthorize("@ss.hasPermi('system:batteryGroup:edit')") |
| | | @Log(title = "电池组字段管理", businessType = BusinessType.UPDATE) |
| | | @PutMapping("/changeStatus") |
| | | public AjaxResult changeStatus(@RequestBody TBatteryGroupField field) |
| | |
| | | } |
| | | |
| | | /** 批量逻辑删除电池组字段。 */ |
| | | @PreAuthorize("@ss.hasPermi('system:batteryGroupField:remove')") |
| | | @PreAuthorize("@ss.hasPermi('system:batteryGroup:remove')") |
| | | @Log(title = "电池组字段管理", businessType = BusinessType.DELETE) |
| | | @DeleteMapping("/{fieldIds}") |
| | | public AjaxResult remove(@PathVariable Long[] fieldIds) |
| | |
| | | private ITBatteryGroupTemplateService batteryGroupTemplateService; |
| | | |
| | | /** 查询模板下拉选项。 */ |
| | | @PreAuthorize("@ss.hasPermi('system:batteryGroupTemplate:list')") |
| | | @PreAuthorize("@ss.hasPermi('system:batteryGroup:list')") |
| | | @GetMapping("/options") |
| | | public AjaxResult options() |
| | | { |
| | |
| | | } |
| | | |
| | | /** 查询模板详情及字段关系。 */ |
| | | @PreAuthorize("@ss.hasPermi('system:batteryGroupTemplate:query')") |
| | | @PreAuthorize("@ss.hasPermi('system:batteryGroup:list')") |
| | | @GetMapping("/{templateId}") |
| | | public AjaxResult getInfo(@PathVariable Long templateId) |
| | | { |
| | |
| | | } |
| | | |
| | | /** 新增电池组模板。 */ |
| | | @PreAuthorize("@ss.hasPermi('system:batteryGroupTemplate:add')") |
| | | @PreAuthorize("@ss.hasPermi('system:batteryGroup:add')") |
| | | @Log(title = "电池组模板管理", businessType = BusinessType.INSERT) |
| | | @PostMapping |
| | | public AjaxResult add(@Validated @RequestBody TBatteryGroupTemplate template) |
| | |
| | | } |
| | | |
| | | /** 编辑电池组模板并替换字段关系。 */ |
| | | @PreAuthorize("@ss.hasPermi('system:batteryGroupTemplate:edit')") |
| | | @PreAuthorize("@ss.hasPermi('system:batteryGroup:edit')") |
| | | @Log(title = "电池组模板管理", businessType = BusinessType.UPDATE) |
| | | @PutMapping |
| | | public AjaxResult edit(@Validated @RequestBody TBatteryGroupTemplate template) |
| New file |
| | |
| | | ALTER TABLE t_battery_group_relation_field |
| | | ADD COLUMN field_value VARCHAR(500) NULL COMMENT '电池组模板字段值' AFTER battery_group_field_id; |
| New file |
| | |
| | | package com.ruoyi.config; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertTrue; |
| | | |
| | | import java.io.InputStream; |
| | | import java.nio.charset.StandardCharsets; |
| | | import org.junit.jupiter.api.Test; |
| | | |
| | | /** 电池组写入与测试选项查询的 Mapper 契约。 */ |
| | | class BatterySelectionMapperContractTest |
| | | { |
| | | @Test |
| | | void batteryGroupBatteriesFeedEnabledTestOptions() |
| | | { |
| | | String groupMapper = resource("mapper/system/TBatteryGroupMapper.xml"); |
| | | String testMapper = resource("mapper/system/TTestMapper.xml"); |
| | | |
| | | assertTrue(groupMapper.contains("insert into t_battery_group_battery")); |
| | | assertTrue(testMapper.contains("id=\"selectEnabledBatteryOptions\"")); |
| | | assertTrue(testMapper.contains("from t_battery_group_battery battery")); |
| | | assertTrue(testMapper.contains("battery_group.status = '0'")); |
| | | assertTrue(testMapper.contains("battery.battery_no batteryNo")); |
| | | assertTrue(groupMapper.contains("t.test_status not in ('COMPLETED', 'TERMINATED')")); |
| | | } |
| | | |
| | | private static String resource(String path) |
| | | { |
| | | try (InputStream stream = BatterySelectionMapperContractTest.class.getClassLoader().getResourceAsStream(path)) |
| | | { |
| | | if (stream == null) throw new AssertionError("缺少资源 " + path); |
| | | return new String(stream.readAllBytes(), StandardCharsets.UTF_8); |
| | | } |
| | | catch (Exception exception) |
| | | { |
| | | throw new AssertionError("无法读取资源 " + path, exception); |
| | | } |
| | | } |
| | | } |
| | |
| | | assertTrue(v10.contains("collector_id")); |
| | | assertTrue(v10.contains("fk_test_battery_collector")); |
| | | assertFalse(v10.matches("(?s).*\\bINSERT\\s+INTO\\b.*")); |
| | | |
| | | String v11 = resource("db/migration/V11__persist_battery_group_field_values.sql"); |
| | | assertTrue(v11.contains("ALTER TABLE t_battery_group_relation_field")); |
| | | assertTrue(v11.contains("field_value VARCHAR(500)")); |
| | | assertFalse(v11.matches("(?s).*\\bINSERT\\s+INTO\\b.*")); |
| | | } |
| | | |
| | | private static String sha256(String path) |
| | |
| | | import static org.mockito.Mockito.when; |
| | | |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import org.junit.jupiter.api.Test; |
| | | import org.junit.jupiter.api.extension.ExtendWith; |
| | |
| | | import org.mockito.junit.jupiter.MockitoExtension; |
| | | import com.ruoyi.system.domain.TBatteryGroup; |
| | | import com.ruoyi.system.domain.TBatteryGroupBattery; |
| | | import com.ruoyi.system.domain.TBatteryGroupRelationField; |
| | | import com.ruoyi.system.mapper.TBatteryGroupMapper; |
| | | |
| | | /** 电池组 Service 单元测试。 */ |
| | |
| | | .map(TBatteryGroupBattery::getBatteryNo).toList()); |
| | | verify(batteryGroupMapper, never()).batchInsertRelationFields(anyList()); |
| | | } |
| | | |
| | | @Test |
| | | void updateShouldPersistStatusAndTemplateFieldValue() |
| | | { |
| | | TBatteryGroup current = new TBatteryGroup(); |
| | | current.setGroupId(12L); |
| | | current.setStatus("0"); |
| | | TBatteryGroup group = new TBatteryGroup(); |
| | | group.setGroupId(12L); |
| | | group.setGroupName("样品组"); |
| | | group.setModel("M-01"); |
| | | group.setStatus("1"); |
| | | group.setBatteryNos(Collections.singletonList("BAT-01")); |
| | | group.setTemplateId(5L); |
| | | TBatteryGroupRelationField relation = new TBatteryGroupRelationField(); |
| | | relation.setBatteryGroupFieldId(7L); |
| | | relation.setFieldValue("72Ah"); |
| | | group.setTemplateFieldRelations(Collections.singletonList(relation)); |
| | | |
| | | when(batteryGroupMapper.selectBatteryGroupById(12L)).thenReturn(current); |
| | | when(batteryGroupMapper.countTestingByGroupId(12L)).thenReturn(0); |
| | | when(batteryGroupMapper.selectBatteryByNo("BAT-01", 12L)).thenReturn(null); |
| | | when(batteryGroupMapper.selectTemplate(5L)).thenReturn(new TBatteryGroup()); |
| | | when(batteryGroupMapper.selectTemplateFieldIds(5L)).thenReturn(Collections.singletonList(7L)); |
| | | when(batteryGroupMapper.selectValidTemplateFields(5L, Collections.singletonList(7L))) |
| | | .thenReturn(Collections.singletonList(relation)); |
| | | when(batteryGroupMapper.updateBatteryGroup(group)).thenReturn(1); |
| | | |
| | | assertEquals(1, batteryGroupService.updateBatteryGroup(group)); |
| | | |
| | | verify(batteryGroupMapper).updateBatteryGroup(group); |
| | | ArgumentCaptor<List<TBatteryGroupRelationField>> captor = ArgumentCaptor.forClass(List.class); |
| | | verify(batteryGroupMapper).batchInsertRelationFields(captor.capture()); |
| | | assertEquals("72Ah", captor.getValue().get(0).getFieldValue()); |
| | | assertEquals("1", group.getStatus()); |
| | | } |
| | | } |
| | |
| | | private Long batteryGroupFieldId; |
| | | private String fieldName; |
| | | private String fieldType; |
| | | private String fieldValue; |
| | | private String delFlag; |
| | | public Long getRelationId() { return relationId; } public void setRelationId(Long v) { relationId = v; } |
| | | public Long getGroupId() { return groupId; } public void setGroupId(Long v) { groupId = v; } |
| | |
| | | public Long getBatteryGroupFieldId() { return batteryGroupFieldId; } public void setBatteryGroupFieldId(Long v) { batteryGroupFieldId = v; } |
| | | public String getFieldName() { return fieldName; } public void setFieldName(String v) { fieldName = v; } |
| | | public String getFieldType() { return fieldType; } public void setFieldType(String v) { fieldType = v; } |
| | | public String getFieldValue() { return fieldValue; } public void setFieldValue(String v) { fieldValue = v; } |
| | | public String getDelFlag() { return delFlag; } public void setDelFlag(String v) { delFlag = v; } |
| | | } |
| | |
| | | public int insertBatteryGroupField(TBatteryGroupField field) |
| | | { |
| | | validateField(field, false); |
| | | if (StringUtils.isEmpty(field.getStatus())) |
| | | { |
| | | field.setStatus(NORMAL); |
| | | } |
| | | validateStatus(field.getStatus()); |
| | | field.setDelFlag(0L); |
| | | field.setCreateTime(new Date()); |
| | | return batteryGroupFieldMapper.insertBatteryGroupField(field); |
| | |
| | | public int updateBatteryGroupField(TBatteryGroupField field) |
| | | { |
| | | validateField(field, true); |
| | | validateStatus(field.getStatus()); |
| | | field.setUpdateTime(new Date()); |
| | | return batteryGroupFieldMapper.updateBatteryGroupField(field); |
| | | } |
| | |
| | | public class TBatteryGroupServiceImpl implements ITBatteryGroupService |
| | | { |
| | | private static final String ENABLED = "0"; |
| | | private static final String TESTING = "1"; |
| | | |
| | | @Autowired |
| | | private TBatteryGroupMapper batteryGroupMapper; |
| | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public int insertBatteryGroup(TBatteryGroup group) |
| | | { |
| | | validate(group, false); |
| | | group.setGroupName(StringUtils.trim(group.getGroupName())); |
| | | group.setCreateTime(new Date()); |
| | | if (StringUtils.isEmpty(group.getStatus())) |
| | | { |
| | | group.setStatus(ENABLED); |
| | | } |
| | | validate(group, false); |
| | | group.setGroupName(StringUtils.trim(group.getGroupName())); |
| | | group.setCreateTime(new Date()); |
| | | int rows = batteryGroupMapper.insertBatteryGroup(group); |
| | | replaceRelations(group); |
| | | return rows; |
| | |
| | | { |
| | | throw new ServiceException("电池组ID不能为空"); |
| | | } |
| | | if (batteryGroupMapper.selectBatteryGroupById(group.getGroupId()) == null) |
| | | TBatteryGroup current = batteryGroupMapper.selectBatteryGroupById(group.getGroupId()); |
| | | if (current == null) |
| | | { |
| | | throw new ServiceException("电池组不存在"); |
| | | } |
| | | if (StringUtils.isEmpty(group.getStatus())) |
| | | { |
| | | group.setStatus(current.getStatus()); |
| | | } |
| | | validateStatusChange(group, current.getStatus()); |
| | | validate(group, true); |
| | | group.setGroupName(StringUtils.trim(group.getGroupName())); |
| | | group.setUpdateTime(new Date()); |
| | |
| | | { |
| | | throw new ServiceException("电池组ID和目标状态不能为空"); |
| | | } |
| | | if (TESTING.equals(group.getStatus()) || !ENABLED.equals(group.getStatus()) && !"1".equals(group.getStatus())) |
| | | if (!ENABLED.equals(group.getStatus()) && !"1".equals(group.getStatus())) |
| | | { |
| | | throw new ServiceException("电池组状态不正确"); |
| | | } |
| | |
| | | { |
| | | throw new ServiceException("电池组型号不能为空"); |
| | | } |
| | | if (!ENABLED.equals(group.getStatus()) && !"1".equals(group.getStatus())) |
| | | { |
| | | throw new ServiceException("电池组状态不正确"); |
| | | } |
| | | TBatteryGroup existing = batteryGroupMapper.selectBatteryGroupByName(StringUtils.trim(group.getGroupName())); |
| | | if (existing != null && (group.getGroupId() == null || !group.getGroupId().equals(existing.getGroupId()))) |
| | | { |
| | |
| | | if (relation == null || relation.getBatteryGroupFieldId() == null || !fieldIds.add(relation.getBatteryGroupFieldId())) |
| | | { |
| | | throw new ServiceException("模板字段关系不正确"); |
| | | } |
| | | if (relation.getFieldValue() != null && relation.getFieldValue().length() > 500) |
| | | { |
| | | throw new ServiceException("模板字段值长度不能超过500个字符"); |
| | | } |
| | | } |
| | | } |
| | |
| | | } |
| | | batteryGroupMapper.batchInsertRelationFields(relations); |
| | | } |
| | | |
| | | private void validateStatusChange(TBatteryGroup group, String currentStatus) |
| | | { |
| | | if ("1".equals(group.getStatus()) && !"1".equals(currentStatus) |
| | | && batteryGroupMapper.countTestingByGroupId(group.getGroupId()) > 0) |
| | | { |
| | | throw new ServiceException("测试中的电池组不能禁用"); |
| | | } |
| | | } |
| | | } |
| | |
| | | update t_battery_group_field |
| | | set field_name = #{fieldName}, |
| | | field_type = #{fieldType}, |
| | | status = #{status}, |
| | | remark = #{remark}, |
| | | update_by = #{updateBy}, |
| | | update_time = #{updateTime} |
| | |
| | | <id property="relationId" column="relation_id" /><result property="groupId" column="group_id" /> |
| | | <result property="templateId" column="template_id" /><result property="batteryGroupFieldId" column="battery_group_field_id" /> |
| | | <result property="fieldName" column="field_name" /><result property="fieldType" column="field_type" /> |
| | | <result property="fieldValue" column="field_value" /> |
| | | <result property="delFlag" column="del_flag" /> |
| | | </resultMap> |
| | | <sql id="groupColumns">select group_id, group_name, model, purpose, z, h, f, a, b, status, create_by, create_time, update_by, update_time, remark, del_flag from t_battery_group</sql> |
| | |
| | | <select id="selectBatteryGroupById" parameterType="Long" resultMap="TBatteryGroupResult"><include refid="groupColumns"/> where group_id = #{groupId} and del_flag = '0'</select> |
| | | <select id="selectBatteryGroupByName" resultMap="TBatteryGroupResult"> <include refid="groupColumns"/> where group_name = #{groupName} and del_flag = '0' limit 1</select> |
| | | <select id="selectBatteries" parameterType="Long" resultMap="TBatteryGroupBatteryResult">select relation_id, group_id, battery_no, sort_order, del_flag from t_battery_group_battery where group_id = #{groupId} and del_flag = '0' order by sort_order, relation_id</select> |
| | | <select id="selectRelationFields" parameterType="Long" resultMap="TBatteryGroupRelationFieldResult">select r.relation_id, r.group_id, r.template_id, r.battery_group_field_id, f.field_name, f.field_type, r.del_flag from t_battery_group_relation_field r inner join t_battery_group_field f on f.field_id = r.battery_group_field_id and f.del_flag = '0' where r.group_id = #{groupId} and r.del_flag = '0' order by r.relation_id</select> |
| | | <select id="selectRelationFields" parameterType="Long" resultMap="TBatteryGroupRelationFieldResult">select r.relation_id, r.group_id, r.template_id, r.battery_group_field_id, f.field_name, f.field_type, r.field_value, r.del_flag from t_battery_group_relation_field r inner join t_battery_group_field f on f.field_id = r.battery_group_field_id and f.del_flag = '0' where r.group_id = #{groupId} and r.del_flag = '0' order by r.relation_id</select> |
| | | <select id="selectTemplate" resultType="TBatteryGroup">select template_id, template_name from t_battery_group_template where template_id = #{templateId}</select> |
| | | <select id="selectTemplateFieldIds" resultType="Long">select field_id from t_battery_group_template_field where template_id = #{templateId} and del_flag = '0' order by sort_order, relation_id</select> |
| | | <select id="selectValidTemplateFields" resultMap="TBatteryGroupRelationFieldResult">select r.relation_id, r.template_id, r.field_id as battery_group_field_id, f.field_name, f.field_type, '0' as del_flag from t_battery_group_template_field r inner join t_battery_group_field f on f.field_id = r.field_id and f.del_flag = '0' where r.template_id = #{templateId} and r.del_flag = '0' and r.field_id in <foreach collection="fieldIds" item="fieldId" open="(" separator="," close=")">#{fieldId}</foreach></select> |
| | | <select id="selectBatteryByNo" resultMap="TBatteryGroupBatteryResult">select relation_id, group_id, battery_no, sort_order, del_flag from t_battery_group_battery where battery_no = #{batteryNo} and del_flag = '0' limit 1</select> |
| | | <insert id="insertBatteryGroup" parameterType="TBatteryGroup" useGeneratedKeys="true" keyProperty="groupId">insert into t_battery_group (group_name, model, purpose, z, h, f, a, b, status, create_by, create_time, remark, del_flag) values (#{groupName}, #{model}, #{purpose}, #{z}, #{h}, #{f}, #{a}, #{b}, #{status}, #{createBy}, sysdate(), #{remark}, '0')</insert> |
| | | <update id="updateBatteryGroup" parameterType="TBatteryGroup">update t_battery_group set group_name=#{groupName}, model=#{model}, purpose=#{purpose}, z=#{z}, h=#{h}, f=#{f}, a=#{a}, b=#{b}, update_by=#{updateBy}, update_time=sysdate(), remark=#{remark} where group_id=#{groupId} and del_flag='0'</update> |
| | | <update id="updateBatteryGroup" parameterType="TBatteryGroup">update t_battery_group set group_name=#{groupName}, model=#{model}, purpose=#{purpose}, z=#{z}, h=#{h}, f=#{f}, a=#{a}, b=#{b}, status=#{status}, update_by=#{updateBy}, update_time=sysdate(), remark=#{remark} where group_id=#{groupId} and del_flag='0'</update> |
| | | <update id="updateBatteryGroupStatus" parameterType="TBatteryGroup">update t_battery_group set status=#{status}, update_by=#{updateBy}, update_time=sysdate() where group_id=#{groupId} and del_flag='0'</update> |
| | | <update id="deleteBatteryGroup">update t_battery_group set del_flag=cast(group_id as char), update_by=#{updateBy}, update_time=sysdate() where group_id=#{groupId} and del_flag='0'</update> |
| | | <update id="deleteBatteries">update t_battery_group_battery set del_flag=cast(relation_id as char) where group_id=#{groupId} and del_flag='0'</update> |
| | | <update id="deleteRelationFields">update t_battery_group_relation_field set del_flag=cast(relation_id as char) where group_id=#{groupId} and del_flag='0'</update> |
| | | <insert id="batchInsertBatteries">insert into t_battery_group_battery (group_id,battery_no,sort_order,del_flag) values <foreach collection="items" item="item" separator=",">(#{item.groupId},#{item.batteryNo},#{item.sortOrder},#{item.delFlag})</foreach></insert> |
| | | <insert id="batchInsertRelationFields">insert into t_battery_group_relation_field (group_id,template_id,battery_group_field_id,del_flag) values <foreach collection="items" item="item" separator=",">(#{item.groupId},#{item.templateId},#{item.batteryGroupFieldId},#{item.delFlag})</foreach></insert> |
| | | <select id="countTestingByGroupId" resultType="int">select count(1) from t_test_battery b inner join t_test t on t.test_id = b.test_id and t.del_flag = '0' where b.group_id=#{groupId} and b.del_flag='0' and t.status='1'</select> |
| | | <insert id="batchInsertRelationFields">insert into t_battery_group_relation_field (group_id,template_id,battery_group_field_id,field_value,del_flag) values <foreach collection="items" item="item" separator=",">(#{item.groupId},#{item.templateId},#{item.batteryGroupFieldId},#{item.fieldValue},#{item.delFlag})</foreach></insert> |
| | | <select id="countTestingByGroupId" resultType="int">select count(1) from t_test_battery b inner join t_test t on t.test_id = b.test_id and t.del_flag = 0 where b.group_id=#{groupId} and b.del_flag=0 and t.test_status not in ('COMPLETED', 'TERMINATED')</select> |
| | | </mapper> |