From bc7d9c98bc1b23ef6ef29ed4a6313753f6900640 Mon Sep 17 00:00:00 2001 From: value <z1292839451@163.com> Date: 星期二, 14 五月 2024 02:25:01 +0800 Subject: [PATCH] 费用统计查询修复; 检验下单字段调整; 下单进度查询修改; 执行优化去掉多余模板内容; 报告生成删除试验方法; 修改报告表格列宽; 修改报告删除列的统计方法 --- /dev/null | 0 inspect-server/src/main/java/com/yuanchu/mom/mapper/InsReportMapper.java | 6 +- inspect-server/src/main/java/com/yuanchu/mom/vo/InsOrderPlanVO.java | 2 cnas-server/src/main/java/com/yuanchu/mom/pojo/Seal.java | 1 inspect-server/src/main/resources/mapper/InsOrderMapper.xml | 17 ++++- inspect-server/src/main/resources/mapper/InsReportMapper.xml | 8 ++ inspect-server/src/main/java/com/yuanchu/mom/pojo/InsOrder.java | 17 +---- inspect-server/src/main/resources/static/report-template.docx | 0 inspect-server/src/main/java/com/yuanchu/mom/service/impl/InsReportServiceImpl.java | 32 +++++++++- inspect-server/src/main/java/com/yuanchu/mom/service/impl/InsOrderPlanServiceImpl.java | 54 +++++++++++------- inspect-server/src/main/java/com/yuanchu/mom/dto/SampleOrderDto.java | 10 ++- inspect-server/src/main/java/com/yuanchu/mom/mapper/InsOrderMapper.java | 2 12 files changed, 99 insertions(+), 50 deletions(-) diff --git a/cnas-server/src/main/java/com/yuanchu/mom/pojo/Seal.java b/cnas-server/src/main/java/com/yuanchu/mom/pojo/Seal.java index 59c8ed3..13127c0 100644 --- a/cnas-server/src/main/java/com/yuanchu/mom/pojo/Seal.java +++ b/cnas-server/src/main/java/com/yuanchu/mom/pojo/Seal.java @@ -39,6 +39,7 @@ @TableField(fill = FieldFill.INSERT) private Integer createUser; + @TableField(fill = FieldFill.INSERT) @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime createTime; } diff --git a/inspect-server/src/main/java/com/yuanchu/mom/dto/SampleOrderDto.java b/inspect-server/src/main/java/com/yuanchu/mom/dto/SampleOrderDto.java index b9e8424..48ef8d5 100644 --- a/inspect-server/src/main/java/com/yuanchu/mom/dto/SampleOrderDto.java +++ b/inspect-server/src/main/java/com/yuanchu/mom/dto/SampleOrderDto.java @@ -14,19 +14,21 @@ @Data public class SampleOrderDto extends InsOrder { - @ValueTableShow(5) @ApiModelProperty("妫�楠屽璞�") private String sampleType; - @ValueTableShow(6) + @ValueTableShow(3) @ApiModelProperty("鏍峰搧鍚嶇О") private String sampleName; - @ValueTableShow(6) + @ValueTableShow(4) @ApiModelProperty("鏍峰搧鍨嬪彿") private String sampleModel; - @ValueTableShow(6) + @ValueTableShow(5) + @ApiModelProperty("鏍峰搧鏁伴噺") + private Integer sampleNum; + @ApiModelProperty("鏍峰搧缂栧彿") private String sampleCode; diff --git a/inspect-server/src/main/java/com/yuanchu/mom/mapper/InsOrderMapper.java b/inspect-server/src/main/java/com/yuanchu/mom/mapper/InsOrderMapper.java index 446f7b9..623ecc0 100644 --- a/inspect-server/src/main/java/com/yuanchu/mom/mapper/InsOrderMapper.java +++ b/inspect-server/src/main/java/com/yuanchu/mom/mapper/InsOrderMapper.java @@ -40,6 +40,8 @@ Long getCount(String inspectionItems, String orderNumber); String getEnumLabelByValue(String value); + + String getStandardMethodCode(Integer id); } diff --git a/inspect-server/src/main/java/com/yuanchu/mom/mapper/InsReportMapper.java b/inspect-server/src/main/java/com/yuanchu/mom/mapper/InsReportMapper.java index 657494d..73a7ce1 100644 --- a/inspect-server/src/main/java/com/yuanchu/mom/mapper/InsReportMapper.java +++ b/inspect-server/src/main/java/com/yuanchu/mom/mapper/InsReportMapper.java @@ -1,12 +1,10 @@ package com.yuanchu.mom.mapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.yuanchu.mom.dto.ReportPageDto; -import com.yuanchu.mom.dto.SampleOrderDto; -import com.yuanchu.mom.pojo.InsOrder; import com.yuanchu.mom.pojo.InsReport; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * @author Administrator @@ -18,6 +16,8 @@ IPage<ReportPageDto> pageInsReport(IPage<ReportPageDto> page, QueryWrapper<ReportPageDto> ew); + String getLaboratoryByName(String name); + } diff --git a/inspect-server/src/main/java/com/yuanchu/mom/pojo/InsOrder.java b/inspect-server/src/main/java/com/yuanchu/mom/pojo/InsOrder.java index 537af85..5021f6a 100644 --- a/inspect-server/src/main/java/com/yuanchu/mom/pojo/InsOrder.java +++ b/inspect-server/src/main/java/com/yuanchu/mom/pojo/InsOrder.java @@ -28,13 +28,12 @@ /** * 濮旀墭缂栧彿 */ - @ValueTableShow(2) + @ValueTableShow(1) @Size(max= 255,message="缂栫爜闀垮害涓嶈兘瓒呰繃255") @ApiModelProperty(value = "濮旀墭缂栧彿") @Length(max= 255,message="缂栫爜闀垮害涓嶈兘瓒呰繃255") private String entrustCode; - @ValueTableShow(2) @Size(max= 255,message="缂栫爜闀垮害涓嶈兘瓒呰繃255") @ApiModelProperty(value = "妫�楠岀被鍒�") @Length(max= 255,message="缂栫爜闀垮害涓嶈兘瓒呰繃255") @@ -43,22 +42,21 @@ /** * 涓嬪崟瀹㈡埛 */ - @ValueTableShow(3) + @ValueTableShow(5) @Size(max= 255,message="缂栫爜闀垮害涓嶈兘瓒呰繃255") - @ApiModelProperty("濮旀墭瀹㈡埛") + @ApiModelProperty("濮旀墭浜�") @Length(max= 255,message="缂栫爜闀垮害涓嶈兘瓒呰繃255") private String custom; /** * 涓嬪崟鍗曚綅 */ - @ValueTableShow(4) + @ValueTableShow(2) @Size(max= 255,message="缂栫爜闀垮害涓嶈兘瓒呰繃255") @ApiModelProperty("濮旀墭鍗曚綅") @Length(max= 255,message="缂栫爜闀垮害涓嶈兘瓒呰繃255") private String company; - @ValueTableShow(4) @Size(max= 255,message="缂栫爜闀垮害涓嶈兘瓒呰繃255") @ApiModelProperty("鑱旂郴鏂瑰紡") @Length(max= 255,message="缂栫爜闀垮害涓嶈兘瓒呰繃255") @@ -88,15 +86,12 @@ @JsonFormat(pattern = "yyyy-MM-dd") private LocalDate appointed; -// @ValueTableShow(7) @ApiModelProperty("妫�楠岀粨鏋�") private Integer insResult; - @ValueTableShow(7) @ApiModelProperty("鏍峰搧澶勭悊鏂瑰紡") private Integer processing; - @ValueTableShow(7) @ApiModelProperty("鏄惁鐣欐牱") private Integer isLeave; @@ -109,7 +104,7 @@ /** * 澶囨敞 */ - @ValueTableShow(9) + @ValueTableShow(6) @Size(max= 255,message="缂栫爜闀垮害涓嶈兘瓒呰繃255") @ApiModelProperty("澶囨敞") @Length(max= 255,message="缂栫爜闀垮害涓嶈兘瓒呰繃255") @@ -191,10 +186,8 @@ @ValueTableShow(value = 14, name = "閫�鍥炵悊鐢�") private String tell; - @ValueTableShow(value = 15, name = "鎶ュ憡鍙戦�佹柟寮�") private Integer send; - @ValueTableShow(value = 16, name = "鏉ユ牱鏂瑰紡") private String formType; @ApiModelProperty("瀹℃牳鏃堕棿") diff --git a/inspect-server/src/main/java/com/yuanchu/mom/service/impl/InsOrderPlanServiceImpl.java b/inspect-server/src/main/java/com/yuanchu/mom/service/impl/InsOrderPlanServiceImpl.java index b1d8c72..84b4fc7 100644 --- a/inspect-server/src/main/java/com/yuanchu/mom/service/impl/InsOrderPlanServiceImpl.java +++ b/inspect-server/src/main/java/com/yuanchu/mom/service/impl/InsOrderPlanServiceImpl.java @@ -98,6 +98,12 @@ @Resource private InformationNotificationService informationNotificationService; + @Resource + private UserMapper userMapper; + + @Value("${file.path}") + private String imgUrl; + @Override public Map<String, Object> selectInsOrderPlanList(Page page, InsOrderPlanDTO insOrderPlanDTO) { Map<String, Object> map = new HashMap<>(); @@ -295,7 +301,6 @@ Map<String, String> user = insProductMapper.selectUserById(insOrder.getUserId()); List<SampleProductDto> samples = insSampleMapper.selectSampleProductListByOrderId(orderId); InsReport insReport = new InsReport(); -// insReport.setCode(giveCode.giveCode("JCZX/TX-", "ins_report", "-", "yyMMdd")); insReport.setCode(insOrder.getEntrustCode()); insReport.setInsOrderId(orderId); Set<Integer> set = new HashSet<>(); @@ -308,10 +313,10 @@ String[] monthNames = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; samples.forEach(a -> { models.add(a.getModel()); + standardMethod.add(baseMapper.getStandardMethodCode(a.getStandardMethodListId())); Set<String> templateSet = new HashSet<>(); getTemplateThing(set, map2, a); for (InsProduct b : a.getInsProduct()) { - standardMethod.add(b.getMethodS()); productSize.set(productSize.get() + 1); if (b.getInsProductResult() != null) { List<JSONObject> jsonObjects = JSON.parseArray(b.getInsProductResult().getEquipValue(), JSONObject.class); @@ -320,6 +325,9 @@ deviceSet.add(jsonObject.get("v") + ""); } } + } + if(b.getTemplate()==null){ + continue; } if (b.getTemplate().size() == 0) { continue; @@ -365,10 +373,15 @@ Set<String> delRSet = new HashSet<>(); for (JSONObject jo1 : temp) { JSONObject v = JSON.parseObject(JSON.toJSONString(jo1.get("v"))); + if(Integer.parseInt(jo1.get("c")+"") > 9){ + delSet.add(jo1.get("c") + ""); + continue; + } if (v.get("ps") != null) { int count3 = 0; String str = ""; - if ("妫�楠屽��".equals(JSON.parseObject(JSON.toJSONString(v.get("ps"))).get("value") + "") || "璁惧鍚嶇О".equals(JSON.parseObject(JSON.toJSONString(v.get("ps"))).get("value") + "") || "璁惧缂栫爜".equals(JSON.parseObject(JSON.toJSONString(v.get("ps"))).get("value") + "")) { + String s = JSON.parseObject(JSON.toJSONString(v.get("ps"))).get("value") + ""; + if ("妫�楠屽��".equals(s) || "璁惧鍚嶇О".equals(s) || "璁惧缂栫爜".equals(s) || "璇曢獙鏂规硶".equals(s) || "妫�娴嬫柟娉�".equals(s)) { delSet.add(jo1.get("c") + ""); continue; } @@ -513,22 +526,11 @@ } TableRenderData tableRenderData = new TableRenderData(); tableRenderData.setRows(rows); + System.out.println(rows.size()); TableStyle tableStyle = new TableStyle(); - /*JSONObject styleJo = JSON.parseObject(JSON.toJSONString(b.getStyle().get("columnlen"))); - List<Integer> colLen = new ArrayList<>(); - int colSize = rows.get(0).getCells().size(); - int colOne = Integer.parseInt(b.getTemplate().get(0).get("r") + ""); - for (int i = colOne; i < colSize + colOne; i++) { - if (styleJo.get(i + "") == null) { - colLen.add(pxToCm(100)); - } else { - int i1 = Integer.parseInt(styleJo.get(i + "") + ""); - colLen.add(pxToCm(i1)); - } - }*/ -// tableStyle.setColWidths(ArrayUtils.toPrimitive(colLen.toArray(new Integer[0]))); + tableStyle.setColWidths(new int[]{650,1600,2000,750,2800,1100,1100}); + tableStyle.setWidth("10000"); tableStyle.setAlign(TableRowAlign.CENTER); - tableStyle.setWidth(XWPFTable.DEFAULT_PERCENTAGE_WIDTH); BorderStyle borderStyle = new BorderStyle(); borderStyle.setColor("000000"); borderStyle.setType(XWPFTable.XWPFBorderType.THICK); @@ -590,6 +592,13 @@ ConfigureBuilder builder = Configure.builder(); builder.useSpringEL(true); List<Map<String, String>> finalDeviceList = deviceList; + Integer userId = insSampleUserMapper.selectOne(Wrappers.<InsSampleUser>lambdaQuery().eq(InsSampleUser::getInsSampleId, orderId).orderByDesc(InsSampleUser::getCreateTime).last("limit 1")).getUserId(); + String signatureUrl; + try { + signatureUrl = userMapper.selectById(userId).getSignatureUrl(); + }catch (Exception e){ + throw new ErrorException("鎵句笉鍒版楠屼汉鐨勭鍚�"); + } XWPFTemplate template = XWPFTemplate.compile(url, builder.build()).render( new HashMap<String, Object>() {{ put("order", insOrder); @@ -608,20 +617,21 @@ put("insTime", insOrder.getInsTime().format(DateTimeFormatter.ofPattern("yyyy骞碝M鏈坉d鏃�"))); put("insTimeEn", monthNames[insOrder.getInsTime().getMonthValue() - 1] + " " + now.getDayOfMonth() + ", " + now.getYear()); put("writeUrl", null); - put("insUrl", null); + put("insUrl", Pictures.ofLocal(imgUrl+"/"+signatureUrl).create()); put("examineUrl", null); put("ratifyUrl", null); put("sampleEn", sampleEn); put("orderType", orderType); put("getTime", insOrder.getExamineTime().format(DateTimeFormatter.ofPattern("yyyy骞碝M鏈坉d鏃�"))); put("getTimeEn", monthNames[insOrder.getExamineTime().getMonthValue() - 1] + " " + insOrder.getExamineTime().getDayOfMonth() + ", " + insOrder.getExamineTime().getYear()); + put("seal1", null); + put("seal2", null); }}); try { String name = insReport.getCode().replace("/", "") + ".docx"; template.writeAndClose(Files.newOutputStream(Paths.get(wordUrl + "/" + name))); insReport.setUrl("/word/" + name); insReportMapper.insert(insReport); -// insOrder.setState(4); insOrder.setInsState(5); insOrderMapper.updateById(insOrder); } catch (IOException e) { @@ -698,10 +708,12 @@ product.setTemplate(new ArrayList<>()); continue; } + String thing = null; if (product.getTemplateId() != null && set.add(product.getTemplateId())) { map2.put(product.getTemplateId(), standardTemplateService.getStandTempThingById(product.getTemplateId()) + ""); + thing = map2.get(product.getTemplateId()); } - String thing = map2.get(product.getTemplateId()); +// thing = map2.get(product.getTemplateId()); if (StrUtil.isNotEmpty(thing)) { JSONObject sheet = JSON.parseObject(JSON.toJSONString(JSON.parseArray(JSON.toJSONString(JSON.parseObject(thing).get("data"))).get(0))); JSONObject config = JSON.parseObject(JSON.toJSONString(sheet.get("config"))); @@ -740,7 +752,7 @@ info.setViewStatus(false); info.setJumpPath("b1-inspect-order-plan"); informationNotificationService.addInformationNotification(info); - upPlanUser(userId, orderId); + upPlanUser(verifyUser, orderId); return 1; } diff --git a/inspect-server/src/main/java/com/yuanchu/mom/service/impl/InsReportServiceImpl.java b/inspect-server/src/main/java/com/yuanchu/mom/service/impl/InsReportServiceImpl.java index 52eded4..4d1bf27 100644 --- a/inspect-server/src/main/java/com/yuanchu/mom/service/impl/InsReportServiceImpl.java +++ b/inspect-server/src/main/java/com/yuanchu/mom/service/impl/InsReportServiceImpl.java @@ -95,7 +95,12 @@ insReport.setWriteUserId(getLook.selectPowerByMethodAndUserId(null).get("userId"));//鎻愪氦浜� insReport.setWriteTime(LocalDateTime.now());//鎻愪氦鏃堕棿 //鑾峰彇鎻愪氦浜虹殑绛惧悕鍦板潃 - String signatureUrl = userMapper.selectById(insReport.getWriteUserId()).getSignatureUrl(); + String signatureUrl; + try { + signatureUrl = userMapper.selectById(insReport.getWriteUserId()).getSignatureUrl(); + }catch (Exception e){ + throw new ErrorException("鎵句笉鍒扮紪鍒朵汉鐨勭鍚�"); + } //绯荤粺鐢熸垚鎶ュ憡鍦板潃 String url = insReport.getUrl(); //鎵嬪姩涓婁紶鎶ュ憡鍦板潃 @@ -122,7 +127,12 @@ return insReportMapper.updateById(insReport); } //鑾峰彇瀹℃牳浜虹殑绛惧悕鍦板潃 - String signatureUrl = userMapper.selectById(insReport.getExamineUserId()).getSignatureUrl(); + String signatureUrl; + try { + signatureUrl = userMapper.selectById(insReport.getExamineUserId()).getSignatureUrl(); + }catch (Exception e){ + throw new ErrorException("鎵句笉鍒板鏍镐汉鐨勭鍚�"); + } //绯荤粺鐢熸垚鎶ュ憡鍦板潃 String url = insReport.getUrl(); //鎵嬪姩涓婁紶鎶ュ憡鍦板潃 @@ -149,14 +159,28 @@ insReport.setState(0);//鎻愪氦鐘舵�佹敼涓哄緟鎻愪氦 return insReportMapper.updateById(insReport); } - //鑾峰彇瀹℃牳浜虹殑绛惧悕鍦板潃 - String signatureUrl = userMapper.selectById(insReport.getRatifyUserId()).getSignatureUrl(); + //鑾峰彇鎵瑰噯浜虹殑绛惧悕鍦板潃 + String signatureUrl; + try { + signatureUrl = userMapper.selectById(insReport.getRatifyUserId()).getSignatureUrl(); + }catch (Exception e){ + throw new ErrorException("鎵句笉鍒版壒鍑嗕汉鐨勭鍚�"); + } + String sealUrl; + try { + String laboratory = insOrderMapper.selectById(insReport.getInsOrderId()).getLaboratory(); + sealUrl = insReportMapper.getLaboratoryByName(laboratory); + }catch (Exception e){ + throw new ErrorException("鎵句笉鍒版姤鍛婄珷鍗�"); + } //绯荤粺鐢熸垚鎶ュ憡鍦板潃 String url = insReport.getUrl(); //鎵嬪姩涓婁紶鎶ュ憡鍦板潃 String urlS = insReport.getUrlS(); wordInsertUrl(new HashMap<String, Object>(){{ put("ratifyUrl", Pictures.ofLocal(imgUrl+"/"+signatureUrl).create()); + put("seal1", Pictures.ofLocal(imgUrl+"/"+sealUrl).create()); + put("seal2", Pictures.ofLocal(imgUrl+"/"+sealUrl).create()); }}, (urlS==null?url:urlS).replace("/word", wordUrl)); wordToPdf((urlS == null ? url : urlS).replace("/word", wordUrl)); InsOrder insOrder = new InsOrder(); diff --git a/inspect-server/src/main/java/com/yuanchu/mom/vo/InsOrderPlanVO.java b/inspect-server/src/main/java/com/yuanchu/mom/vo/InsOrderPlanVO.java index c3cb406..c91ca7b 100644 --- a/inspect-server/src/main/java/com/yuanchu/mom/vo/InsOrderPlanVO.java +++ b/inspect-server/src/main/java/com/yuanchu/mom/vo/InsOrderPlanVO.java @@ -13,7 +13,7 @@ @ValueTableShow(value = 1,name = "濮旀墭缂栧彿") private String entrustCode; - @ValueTableShow(value = 2,name = "鏍峰搧绫诲瀷") + @ValueTableShow(value = 2,name = "妫�楠屽璞�") private String sampleType; @ValueTableShow(value = 4,name = "绱ф�ョ▼搴�") diff --git a/inspect-server/src/main/resources/mapper/InsOrderMapper.xml b/inspect-server/src/main/resources/mapper/InsOrderMapper.xml index b520a2e..5355d6b 100644 --- a/inspect-server/src/main/resources/mapper/InsOrderMapper.xml +++ b/inspect-server/src/main/resources/mapper/InsOrderMapper.xml @@ -95,9 +95,11 @@ ir.id report_id, ir.url, ir.url_s, - concat(ROUND((select count(*) from ins_sample isa2 - where ins_state = 5 and isa2.ins_order_id = io.id) / (select count(*) from ins_sample isa2 - where isa2.ins_order_id = io.id) * 100, 2), '%') insProgress, + (select count(*) from ins_sample isa2 + where isa2.ins_order_id = io.id) sample_num, + concat(ROUND((select count(*) from ins_order_state ios + where ins_state = 5 and ios.ins_order_id = io.id) / (select count(*) from ins_order_state ios2 + where ios2.ins_order_id = io.id) * 100, 2), '%') insProgress, group_concat(distinct isa.sample_code,' ') sample_code, group_concat(distinct isa.sample,' ') sample_name, group_concat(distinct isa.model,' ') sample_model @@ -152,9 +154,10 @@ ins_order i LEFT JOIN ins_sample isa ON isa.ins_order_id = i.id LEFT JOIN `user` u ON u.id = i.user_id - LEFT JOIN (select SUM(b.price) price, sum(b.man_hour) cost,b.ins_sample_id,GROUP_CONCAT(b.inspection_item + LEFT JOIN (select SUM(b.price) price, sum(b.man_hour) cost,b.ins_sample_id,GROUP_CONCAT(b.inspection_item2 SEPARATOR ',') - inspection_item from (select * from ins_product where state = 1 GROUP BY ins_sample_id,man_hour_group) b GROUP + inspection_item from (select *,GROUP_CONCAT(inspection_item + SEPARATOR ',') inspection_item2 from ins_product where state = 1 GROUP BY ins_sample_id,man_hour_group) b GROUP BY b.ins_sample_id) c ON c.ins_sample_id = isa.id ) a <if test="ew.customSqlSegment != null and ew.customSqlSegment != ''"> @@ -238,4 +241,8 @@ select label from `center-lims`.enums where value = #{value} </select> + <select id="getStandardMethodCode" resultType="java.lang.String"> + select code from `center-lims`.standard_method + where id = #{id} + </select> </mapper> diff --git a/inspect-server/src/main/resources/mapper/InsReportMapper.xml b/inspect-server/src/main/resources/mapper/InsReportMapper.xml index 3b27479..d479c7c 100644 --- a/inspect-server/src/main/resources/mapper/InsReportMapper.xml +++ b/inspect-server/src/main/resources/mapper/InsReportMapper.xml @@ -34,4 +34,12 @@ ${ew.customSqlSegment} </if> </select> + <select id="getLaboratoryByName" resultType="java.lang.String"> + select s.address from `center-lims`.seal s + left join `center-lims`.laboratory l on s.lab_id = l.id + where l.laboratory_name = #{name} + and s.type = '鎶ュ憡绫诲瀷' + order by l.create_time desc + limit 1 + </select> </mapper> diff --git a/inspect-server/src/main/resources/static/report-template.docx b/inspect-server/src/main/resources/static/report-template.docx index c495275..51a2237 100644 --- a/inspect-server/src/main/resources/static/report-template.docx +++ b/inspect-server/src/main/resources/static/report-template.docx Binary files differ diff --git a/inspect-server/src/main/resources/static/template-table.docx b/inspect-server/src/main/resources/static/template-table.docx deleted file mode 100644 index 771a667..0000000 --- a/inspect-server/src/main/resources/static/template-table.docx +++ /dev/null Binary files differ -- Gitblit v1.9.3