f744d3f8 张雷

设备台账自定义功能开发

1 个父辈 f6c10862
...@@ -2,6 +2,7 @@ package com.skua.modules.equip.controller; ...@@ -2,6 +2,7 @@ package com.skua.modules.equip.controller;
2 2
3 import com.skua.core.api.vo.Result; 3 import com.skua.core.api.vo.Result;
4 import com.skua.core.aspect.annotation.AutoLog; 4 import com.skua.core.aspect.annotation.AutoLog;
5 import com.skua.modules.equip.entity.SysEquipField;
5 import com.skua.modules.equip.service.IFmEquipInfoService; 6 import com.skua.modules.equip.service.IFmEquipInfoService;
6 import com.skua.modules.equip.vo.FmEquipResult; 7 import com.skua.modules.equip.vo.FmEquipResult;
7 import io.swagger.annotations.Api; 8 import io.swagger.annotations.Api;
...@@ -54,4 +55,21 @@ public class FmEquipInfoController { ...@@ -54,4 +55,21 @@ public class FmEquipInfoController {
54 return result; 55 return result;
55 } 56 }
56 57
58 /**
59 * 添加设备字段
60 * @param equipField
61 * @return
62 */
63 @AutoLog(value = "添加设备字段")
64 @ApiOperation(value="添加设备字段", notes="添加设备字段")
65 @PostMapping(value = "/fieldAdd")
66 public Result<?> planAdd(@RequestBody SysEquipField equipField) {
67 boolean ok = fmEquipInfoService.equipFieldAdd(equipField);
68 if(ok){
69 return Result.ok("添加成功!");
70 }else{
71 return Result.error("添加失败!");
72 }
73 }
74
57 } 75 }
......
1 package com.skua.modules.equip.controller;
2
3 import java.util.Arrays;
4 import java.util.List;
5 import java.util.Map;
6 import java.io.IOException;
7 import java.io.UnsupportedEncodingException;
8 import java.net.URLDecoder;
9 import javax.servlet.http.HttpServletRequest;
10 import javax.servlet.http.HttpServletResponse;
11 import com.skua.core.aspect.annotation.AutoLog;
12 import com.skua.core.api.vo.Result;
13 import com.skua.core.query.QueryGenerator;
14 import com.skua.core.util.ConvertUtils;
15 import com.skua.modules.equip.entity.SysEquipField;
16 import com.skua.modules.equip.service.ISysEquipFieldService;
17 import java.util.Date;
18 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
19 import com.baomidou.mybatisplus.core.metadata.IPage;
20 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
21 import lombok.extern.slf4j.Slf4j;
22
23 import org.jeecgframework.poi.excel.ExcelImportUtil;
24 import org.jeecgframework.poi.excel.def.NormalExcelConstants;
25 import org.jeecgframework.poi.excel.entity.ExportParams;
26 import org.jeecgframework.poi.excel.entity.ImportParams;
27 import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
28
29 import org.springframework.beans.factory.annotation.Autowired;
30 import org.springframework.web.bind.annotation.*;
31 import org.springframework.web.multipart.MultipartFile;
32 import org.springframework.web.multipart.MultipartHttpServletRequest;
33 import org.springframework.web.servlet.ModelAndView;
34 import com.alibaba.fastjson.JSON;
35 import io.swagger.annotations.Api;
36 import io.swagger.annotations.ApiOperation;
37
38 /**
39 * 自定义设备字段
40 */
41 @Slf4j
42 @Api(tags="自定义设备字段")
43 @RestController
44 @RequestMapping("/equip/sysEquipField")
45 public class SysEquipFieldController {
46 @Autowired
47 private ISysEquipFieldService sysEquipFieldService;
48
49 /**
50 * 分页列表查询
51 * @param sysEquipField
52 * @param pageNo
53 * @param pageSize
54 * @param req
55 * @return
56 */
57 @AutoLog(value = "自定义设备字段-分页列表查询")
58 @ApiOperation(value="自定义设备字段-分页列表查询", notes="自定义设备字段-分页列表查询")
59 @GetMapping(value = "/list")
60 public Result<IPage<SysEquipField>> queryPageList(SysEquipField sysEquipField,
61 @RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
62 @RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
63 HttpServletRequest req) {
64 Result<IPage<SysEquipField>> result = new Result<IPage<SysEquipField>>();
65 QueryWrapper<SysEquipField> queryWrapper = QueryGenerator.initQueryWrapper(sysEquipField, req.getParameterMap());
66 Page<SysEquipField> page = new Page<SysEquipField>(pageNo, pageSize);
67 IPage<SysEquipField> pageList = sysEquipFieldService.page(page, queryWrapper);
68 result.setSuccess(true);
69 result.setResult(pageList);
70 return result;
71 }
72
73 /**
74 * 添加
75 * @param sysEquipField
76 * @return
77 */
78 @AutoLog(value = "自定义设备字段-添加")
79 @ApiOperation(value="自定义设备字段-添加", notes="自定义设备字段-添加")
80 @PostMapping(value = "/add")
81 public Result<SysEquipField> add(@RequestBody SysEquipField sysEquipField) {
82 Result<SysEquipField> result = new Result<SysEquipField>();
83 try {
84 sysEquipFieldService.save(sysEquipField);
85 result.success("添加成功!");
86 } catch (Exception e) {
87 log.error(e.getMessage(),e);
88 result.error500("操作失败");
89 }
90 return result;
91 }
92
93 /**
94 * 编辑
95 * @param sysEquipField
96 * @return
97 */
98 @AutoLog(value = "自定义设备字段-编辑")
99 @ApiOperation(value="自定义设备字段-编辑", notes="自定义设备字段-编辑")
100 @PutMapping(value = "/edit")
101 public Result<SysEquipField> edit(@RequestBody SysEquipField sysEquipField) {
102 Result<SysEquipField> result = new Result<SysEquipField>();
103 SysEquipField sysEquipFieldEntity = sysEquipFieldService.getById(sysEquipField.getId());
104 if(sysEquipFieldEntity==null) {
105 result.error500("未找到对应实体");
106 }else {
107 boolean ok = sysEquipFieldService.updateById(sysEquipField);
108 //TODO 返回false说明什么?
109 if(ok) {
110 result.success("修改成功!");
111 }
112 }
113
114 return result;
115 }
116
117 /**
118 * 通过id删除
119 * @param id
120 * @return
121 */
122 @AutoLog(value = "自定义设备字段-通过id删除")
123 @ApiOperation(value="自定义设备字段-通过id删除", notes="自定义设备字段-通过id删除")
124 @DeleteMapping(value = "/delete")
125 public Result<?> delete(@RequestParam(name="id",required=true) String id) {
126 try {
127 sysEquipFieldService.removeById(id);
128 } catch (Exception e) {
129 log.error("删除失败",e.getMessage());
130 return Result.error("删除失败!");
131 }
132 return Result.ok("删除成功!");
133 }
134
135 /**
136 * 批量删除
137 * @param ids
138 * @return
139 */
140 @AutoLog(value = "自定义设备字段-批量删除")
141 @ApiOperation(value="自定义设备字段-批量删除", notes="自定义设备字段-批量删除")
142 @DeleteMapping(value = "/deleteBatch")
143 public Result<SysEquipField> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
144 Result<SysEquipField> result = new Result<SysEquipField>();
145 if(ids==null || "".equals(ids.trim())) {
146 result.error500("参数不识别!");
147 }else {
148 this.sysEquipFieldService.removeByIds(Arrays.asList(ids.split(",")));
149 result.success("删除成功!");
150 }
151 return result;
152 }
153
154 /**
155 * 通过id查询
156 * @param id
157 * @return
158 */
159 @AutoLog(value = "自定义设备字段-通过id查询")
160 @ApiOperation(value="自定义设备字段-通过id查询", notes="自定义设备字段-通过id查询")
161 @GetMapping(value = "/queryById")
162 public Result<SysEquipField> queryById(@RequestParam(name="id",required=true) String id) {
163 Result<SysEquipField> result = new Result<SysEquipField>();
164 SysEquipField sysEquipField = sysEquipFieldService.getById(id);
165 if(sysEquipField==null) {
166 result.error500("未找到对应实体");
167 }else {
168 result.setResult(sysEquipField);
169 result.setSuccess(true);
170 }
171 return result;
172 }
173
174 /**
175 * 导出excel
176 *
177 * @param request
178 * @param response
179 */
180 @RequestMapping(value = "/exportXls")
181 public ModelAndView exportXls(HttpServletRequest request, HttpServletResponse response) {
182 // Step.1 组装查询条件
183 QueryWrapper<SysEquipField> queryWrapper = null;
184 try {
185 String paramsStr = request.getParameter("paramsStr");
186 if (ConvertUtils.isNotEmpty(paramsStr)) {
187 String deString = URLDecoder.decode(paramsStr, "UTF-8");
188 SysEquipField sysEquipField = JSON.parseObject(deString, SysEquipField.class);
189 queryWrapper = QueryGenerator.initQueryWrapper(sysEquipField, request.getParameterMap());
190 }
191 } catch (UnsupportedEncodingException e) {
192 e.printStackTrace();
193 }
194
195 //Step.2 AutoPoi 导出Excel
196 ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
197 List<SysEquipField> pageList = sysEquipFieldService.list(queryWrapper);
198 //导出文件名称
199 mv.addObject(NormalExcelConstants.FILE_NAME, "自定义设备字段列表");
200 mv.addObject(NormalExcelConstants.CLASS, SysEquipField.class);
201 mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("自定义设备字段列表数据", "导出人:Jeecg", "导出信息"));
202 mv.addObject(NormalExcelConstants.DATA_LIST, pageList);
203 return mv;
204 }
205
206 /**
207 * 通过excel导入数据
208 *
209 * @param request
210 * @param response
211 * @return
212 */
213 @RequestMapping(value = "/importExcel", method = RequestMethod.POST)
214 public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
215 MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
216 Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
217 for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
218 MultipartFile file = entity.getValue();// 获取上传文件对象
219 ImportParams params = new ImportParams();
220 params.setTitleRows(2);
221 params.setHeadRows(1);
222 params.setNeedSave(true);
223 try {
224 List<SysEquipField> listSysEquipFields = ExcelImportUtil.importExcel(file.getInputStream(), SysEquipField.class, params);
225 sysEquipFieldService.saveBatch(listSysEquipFields);
226 return Result.ok("文件导入成功!数据行数:" + listSysEquipFields.size());
227 } catch (Exception e) {
228 log.error(e.getMessage(),e);
229 return Result.error("文件导入失败:"+e.getMessage());
230 } finally {
231 try {
232 file.getInputStream().close();
233 } catch (IOException e) {
234 e.printStackTrace();
235 }
236 }
237 }
238 return Result.ok("文件导入失败!");
239 }
240
241 }
1 package com.skua.modules.equip.entity;
2
3 import java.io.Serializable;
4 import java.util.Date;
5 import com.baomidou.mybatisplus.annotation.IdType;
6 import com.baomidou.mybatisplus.annotation.TableId;
7 import com.baomidou.mybatisplus.annotation.TableName;
8 import com.baomidou.mybatisplus.annotation.TableField;
9 import io.swagger.annotations.ApiModel;
10 import io.swagger.annotations.ApiModelProperty;
11 import lombok.Data;
12 import lombok.EqualsAndHashCode;
13 import lombok.experimental.Accessors;
14 import com.fasterxml.jackson.annotation.JsonFormat;
15 import org.springframework.format.annotation.DateTimeFormat;
16 import org.jeecgframework.poi.excel.annotation.Excel;
17
18 /**
19 * 自定义设备字段
20 */
21 @Data
22 @TableName("sys_equip_field")
23 @EqualsAndHashCode(callSuper = false)
24 @Accessors(chain = true)
25 @ApiModel(value="sys_equip_field对象", description="自定义设备字段")
26 public class SysEquipField {
27
28 /**主键id*/
29 @TableId(type = IdType.UUID)
30 @ApiModelProperty(value = "主键id")
31 private java.lang.String id;
32 /**数据项别名*/
33 @Excel(name = "数据项别名", width = 15)
34 @ApiModelProperty(value = "数据项别名")
35 private java.lang.String itemAlias;
36 /**数据项名称*/
37 @Excel(name = "数据项名称", width = 15)
38 @ApiModelProperty(value = "数据项名称")
39 private java.lang.String itemCode;
40 /**数据项单位*/
41 @Excel(name = "数据项单位", width = 15)
42 @ApiModelProperty(value = "数据项单位")
43 private java.lang.String itemUnit;
44 /**文本框类型*/
45 @Excel(name = "文本框类型", width = 15)
46 @ApiModelProperty(value = "文本框类型")
47 private java.lang.String textType;
48 /**字典CODE/时间格式/正则*/
49 @Excel(name = "字典CODE/时间格式/正则", width = 15)
50 @ApiModelProperty(value = "字典CODE/时间格式/正则")
51 private java.lang.String typeInit;
52 /**是否必填项*/
53 @Excel(name = "是否必填项", width = 15)
54 @ApiModelProperty(value = "是否必填项")
55 private java.lang.String isMust;
56 /**备注*/
57 @Excel(name = "备注", width = 15)
58 @ApiModelProperty(value = "备注")
59 private java.lang.String remark;
60 /**排序序号*/
61 @Excel(name = "排序序号", width = 15)
62 @ApiModelProperty(value = "排序序号")
63 private java.lang.Integer sortNum;
64 /**所属项目*/
65 @Excel(name = "所属项目", width = 15)
66 @ApiModelProperty(value = "所属项目")
67 private java.lang.String projectId;
68 /**创建人Id*/
69 @Excel(name = "创建人Id", width = 15)
70 @ApiModelProperty(value = "创建人Id")
71 private java.lang.String createBy;
72 /**创建时间*/
73 @Excel(name = "创建时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
74 @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
75 @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
76 @ApiModelProperty(value = "创建时间")
77 private java.util.Date createTime;
78 /**修改人Id*/
79 @Excel(name = "修改人Id", width = 15)
80 @ApiModelProperty(value = "修改人Id")
81 private java.lang.String updateBy;
82 /**修改时间*/
83 @Excel(name = "修改时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
84 @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
85 @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
86 @ApiModelProperty(value = "修改时间")
87 private java.util.Date updateTime;
88 /**所属部门*/
89 @Excel(name = "所属部门", width = 15)
90 @ApiModelProperty(value = "所属部门")
91 private java.lang.String createDept;
92 /**所属公司*/
93 @Excel(name = "所属公司", width = 15)
94 @ApiModelProperty(value = "所属公司")
95 private java.lang.String createCmpy;
96 /**删除标识*/
97 @Excel(name = "删除标识", width = 15)
98 @ApiModelProperty(value = "删除标识")
99 private java.lang.Integer delFlag;
100 }
...@@ -13,4 +13,8 @@ public interface FmEquipInfoMapper { ...@@ -13,4 +13,8 @@ public interface FmEquipInfoMapper {
13 List<FmEquipResult> queryListForView(@Param("projectId") String projectId); 13 List<FmEquipResult> queryListForView(@Param("projectId") String projectId);
14 14
15 FmEquipResult getDataById(@Param("equipId") String equipId); 15 FmEquipResult getDataById(@Param("equipId") String equipId);
16
17 void createView(@Param("createViewSql") String createViewSql,@Param("viewName") String viewName);
18
19 void deleteView(@Param("viewName") String viewName);
16 } 20 }
......
1 package com.skua.modules.equip.mapper;
2
3 import java.util.List;
4
5 import org.apache.ibatis.annotations.Param;
6 import com.skua.modules.equip.entity.SysEquipField;
7 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
8
9 /**
10 * 自定义设备字段
11 */
12 public interface SysEquipFieldMapper extends BaseMapper<SysEquipField> {
13
14 }
...@@ -10,4 +10,13 @@ ...@@ -10,4 +10,13 @@
10 select * from view_equipment where equip_id = #{equipId} limit 1 10 select * from view_equipment where equip_id = #{equipId} limit 1
11 </select> 11 </select>
12 12
13 <!-- 删除视图 -->
14 <select id="deleteView">
15 drop view if exists ${viewName}
16 </select>
17
18 <!-- 创建视图 -->
19 <select id="createView">
20 create view ${viewName} as ${createViewSql}
21 </select>
13 </mapper> 22 </mapper>
...\ No newline at end of file ...\ No newline at end of file
......
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3 <mapper namespace="com.skua.modules.equip.mapper.SysEquipFieldMapper">
4
5 </mapper>
...\ No newline at end of file ...\ No newline at end of file
1 package com.skua.modules.equip.service; 1 package com.skua.modules.equip.service;
2 2
3 import com.skua.modules.equip.entity.SysEquipField;
3 import com.skua.modules.equip.vo.FmEquipResult; 4 import com.skua.modules.equip.vo.FmEquipResult;
4 5
5 import java.util.List; 6 import java.util.List;
...@@ -12,4 +13,6 @@ public interface IFmEquipInfoService { ...@@ -12,4 +13,6 @@ public interface IFmEquipInfoService {
12 List<FmEquipResult> queryList(String projectId); 13 List<FmEquipResult> queryList(String projectId);
13 14
14 FmEquipResult getDataById(String equipId); 15 FmEquipResult getDataById(String equipId);
16
17 boolean equipFieldAdd(SysEquipField equipField);
15 } 18 }
......
1 package com.skua.modules.equip.service;
2
3 import com.skua.modules.equip.entity.SysEquipField;
4 import com.baomidou.mybatisplus.extension.service.IService;
5
6 /**
7 * 自定义设备字段
8 */
9 public interface ISysEquipFieldService extends IService<SysEquipField> {
10
11 }
1 package com.skua.modules.equip.service.impl; 1 package com.skua.modules.equip.service.impl;
2 2
3 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
4 import com.skua.modules.equip.entity.SysEquipField;
3 import com.skua.modules.equip.mapper.FmEquipInfoMapper; 5 import com.skua.modules.equip.mapper.FmEquipInfoMapper;
4 import com.skua.modules.equip.service.IFmEquipInfoService; 6 import com.skua.modules.equip.service.IFmEquipInfoService;
7 import com.skua.modules.equip.service.ISysEquipFieldService;
5 import com.skua.modules.equip.vo.FmEquipResult; 8 import com.skua.modules.equip.vo.FmEquipResult;
6 import org.springframework.beans.factory.annotation.Autowired; 9 import org.springframework.beans.factory.annotation.Autowired;
7 import org.springframework.stereotype.Service; 10 import org.springframework.stereotype.Service;
8 11
12 import java.beans.BeanInfo;
13 import java.beans.Introspector;
14 import java.beans.PropertyDescriptor;
15 import java.lang.reflect.Method;
16 import java.util.HashMap;
9 import java.util.List; 17 import java.util.List;
18 import java.util.Map;
10 19
11 /** 20 /**
12 * 设备管理 21 * 设备管理
...@@ -16,6 +25,8 @@ public class FmEquipInfoServiceImpl implements IFmEquipInfoService { ...@@ -16,6 +25,8 @@ public class FmEquipInfoServiceImpl implements IFmEquipInfoService {
16 25
17 @Autowired 26 @Autowired
18 private FmEquipInfoMapper fmEquipInfoMapper; 27 private FmEquipInfoMapper fmEquipInfoMapper;
28 @Autowired
29 private ISysEquipFieldService sysEquipFieldService;
19 30
20 @Override 31 @Override
21 public List<FmEquipResult> queryList(String projectId) { 32 public List<FmEquipResult> queryList(String projectId) {
...@@ -27,4 +38,67 @@ public class FmEquipInfoServiceImpl implements IFmEquipInfoService { ...@@ -27,4 +38,67 @@ public class FmEquipInfoServiceImpl implements IFmEquipInfoService {
27 public FmEquipResult getDataById(String equipId) { 38 public FmEquipResult getDataById(String equipId) {
28 return fmEquipInfoMapper.getDataById(equipId); 39 return fmEquipInfoMapper.getDataById(equipId);
29 } 40 }
41
42 @Override
43 public boolean equipFieldAdd(SysEquipField equipField) {
44 QueryWrapper<SysEquipField> queryWrapper = new QueryWrapper<SysEquipField>();
45 queryWrapper.eq("project_id",equipField.getProjectId());
46 queryWrapper.eq("item_code",equipField.getItemCode());
47 List<SysEquipField> list = sysEquipFieldService.list(queryWrapper);
48 if(list.size()>0){
49 return false;
50 }else{
51 boolean ok = sysEquipFieldService.save(equipField);
52 if(ok){
53 //获取所有设备信息字段
54 QueryWrapper<SysEquipField> newQueryWrapper = new QueryWrapper<SysEquipField>();
55 newQueryWrapper.eq("project_id",equipField.getProjectId());
56 List<SysEquipField> newList = sysEquipFieldService.list(newQueryWrapper);
57 //修改设备自定义字段的视图
58 updateEquipView(newList,equipField.getProjectId());
59 return true;
60 }else{
61 return false;
62 }
63 }
64
65
66 }
67
68 /**
69 * 修改设备自定义字段的视图
70 * @param list
71 */
72 private void updateEquipView(List<SysEquipField> list,String projectId) {
73 StringBuffer sb = new StringBuffer();
74 fmEquipInfoMapper.deleteView("view_equip_"+projectId);//删除旧视图
75 sb.append("SELECT ");
76 for(SysEquipField equipField : list) {
77 sb.append(" MAX( CASE a.item_code WHEN '"+equipField.getItemCode()+"' THEN a.item_value ELSE '' END ) AS '"+equipField.getItemCode()+"', ");
78 }
79 sb.append(" a.data_id " +
80 " FROM " +
81 " view_equip_field a " +
82 " WHERE a.project_id = '"+projectId+"' " +
83 " GROUP BY a.data_id");
84 //创建自定义设备属性视图
85 fmEquipInfoMapper.createView(sb.toString(),"view_equip_"+projectId);
86 }
87
88 public static Map<String, Object> entityToMap(Object bean) throws Exception {
89 Class type = bean.getClass();
90 Map<String,Object> returnMap = new HashMap<>();
91 BeanInfo beanInfo = Introspector.getBeanInfo(type);
92 PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
93 for (PropertyDescriptor descriptor : propertyDescriptors) {
94 String propertyName = descriptor.getName();//name
95 if (!propertyName.equals("class")) {
96 Method readMethod = descriptor.getReadMethod();
97 Object result = readMethod.invoke(bean);//value
98 String typeName = descriptor.getPropertyType().getName();//type
99 }
100 }
101 return returnMap;
102 }
103
30 } 104 }
......
1 package com.skua.modules.equip.service.impl;
2
3 import com.skua.modules.equip.entity.SysEquipField;
4 import com.skua.modules.equip.mapper.SysEquipFieldMapper;
5 import com.skua.modules.equip.service.ISysEquipFieldService;
6 import org.springframework.stereotype.Service;
7
8 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
9
10 /**
11 * 自定义设备字段
12 */
13 @Service
14 public class SysEquipFieldServiceImpl extends ServiceImpl<SysEquipFieldMapper, SysEquipField> implements ISysEquipFieldService {
15
16 }
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!