fuliqi
2024-09-30 3351ade035a3a027d38e0372e496a5669cd7bde2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
package com.ycl.handler;
 
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.util.BooleanUtils;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.ycl.platform.domain.entity.CalculateRule;
import enumeration.ContractRule;
import enumeration.general.RuleDeductCategoryEnum;
import lombok.Data;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xssf.usermodel.XSSFDataValidation;
import org.springframework.util.CollectionUtils;
 
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
 
public class CustomCellWriteHandler implements CellWriteHandler {
 
    /**
     * 这个处理器其实可以做很多事情
     * 他能拿到整个 Excel 的 WorkHook
     * 也能拿到当前的 sheet
     * 也能拿到当前的 cell
     * 所以能做的自定义操作很丰富
     */
    // static class CustomCellWriteHandler {
    @Override
    public void afterCellDispose(CellWriteHandlerContext context) {
        // 当前的 cell
        Cell cell = context.getCell();
        // 这里可以对cell进行任何操作
        // 这里就要考虑 你要针对哪一列进行个性化处理  一定要记得加判断  因为每个 cell 都会执行到这里 所以要记得区分
        if (BooleanUtils.isTrue(context.getHead()) && cell.getColumnIndex() == 1) {
            // 1 表示 省那一列 要对省市区进行联动下拉处理
            ExcelAreaUtil.writeAreaInfo(context, 0);
        }
    }
    // }
 
    public static class ExcelAreaUtil {
 
        @Data
        static class AreaInfo {
 
            private String name;
 
            private List<String> areaList;
 
 
            public AreaInfo(String name, List<String> areaList) {
                this.name = name;
                this.areaList = areaList;
            }
        }
 
 
        /**
         * @param context    handler 的 上下文
         * @param startIndex 省的列号  从0开始的
         */
        public static void writeAreaInfo(CellWriteHandlerContext context, int startIndex) {
            // 获取到当前的 sheet 后续要给省市区列加下拉
            WriteSheetHolder writeSheetHolder = context.getWriteSheetHolder();
            Sheet sheet = writeSheetHolder.getSheet();
 
            // 省市区挨着的列号
            int cIndex = startIndex + 1;
            int aIndex = cIndex + 1;
 
            // 准备点数据
            List<AreaInfo> provinceList = new ArrayList<>();
            for (ContractRule contractRule : ContractRule.getParent()) {
                provinceList.add(new AreaInfo(contractRule.getName(), CollectionUtils.isEmpty(ContractRule.getChildren(contractRule)) ? null : ContractRule.getChildren(contractRule).stream().map(ContractRule::getName).toList()));
            }
 
            // 获取到当前的 excel 因为要创建隐藏的 sheet 也就是省市区的实际内容都来自于隐藏的 sheet
            Workbook workbook = context.getWriteSheetHolder().getParentWriteWorkbookHolder().getWorkbook();
            Sheet hideSheet = workbook.createSheet("area");
            workbook.setSheetHidden(workbook.getSheetIndex(hideSheet), true);
            // 写隐藏 sheet 的省市区信息
            int rowId = 0;
            // 设置第一行,存省的信息
            Row provinceRow = hideSheet.createRow(rowId++);
            provinceRow.createCell(0).setCellValue("省列表");
            for (int i = 0; i < provinceList.size(); i++) {
                Cell provinceCell = provinceRow.createCell(i + 1);
                provinceCell.setCellValue(provinceList.get(i).getName());
            }
 
            for (AreaInfo pro : provinceList) {
                String pName = pro.getName();
                List<String> cList = pro.getAreaList();
                if (!CollectionUtils.isEmpty(cList)) {
                    Row cRow = hideSheet.createRow(rowId++);
                    cRow.createCell(0).setCellValue(pName);
                    for (int j = 0; j < cList.size(); j++) {
                        String cInfo = cList.get(j);
                        Cell cCell = cRow.createCell(j + 1);
                        cCell.setCellValue(cInfo);
                    }
 
                    // 添加名称管理器
                    String range = getRange(1, rowId, cList.size());
                    Name name = workbook.createName();
                    // key不可重复
                    name.setNameName(pName);
                    String formula = "area!" + range;
                    name.setRefersToFormula(formula);
                }
            }
            // 给省添加下拉
            int lastRow = 100;
            setDataValidation(sheet, 1, lastRow, startIndex, startIndex);
 
            // 给市 区 添加下拉
            // 设置第二列和第三列的有效性
            for (int i = 2; i < lastRow; i++) {
                setDataValidation(CellReference.convertNumToColString(startIndex), sheet, i - 1, i - 1, cIndex, cIndex);
                setDataValidation(CellReference.convertNumToColString(cIndex), sheet, i - 1, i - 1, aIndex, aIndex);
            }
        }
 
        public static void setDataValidation(Sheet sheetPro, int firstRow, int lastRow, int firstCol, int lastCol) {
            DataValidationHelper dvHelper = sheetPro.getDataValidationHelper();
            String formula = getFormula(1, 1, 1, 34);
            // 省规则
            DataValidationConstraint provConstraint = dvHelper.createFormulaListConstraint(formula);
            // 四个参数分别是:起始行、终止行、起始列、终止列
            CellRangeAddressList provRangeAddressList = new CellRangeAddressList(firstRow, lastRow, firstCol, lastCol);
            DataValidation provinceDataValidation = dvHelper.createValidation(provConstraint, provRangeAddressList);
            // 验证
            provinceDataValidation.createErrorBox("error", "请选择正确的规则");
            provinceDataValidation.setShowErrorBox(true);
            provinceDataValidation.setSuppressDropDownArrow(true);
            sheetPro.addValidationData(provinceDataValidation);
        }
 
        public static void setDataValidation(String offset, Sheet sheet, int firstRow, int lastRow, int firstCol, int lastCol) {
            DataValidationHelper dvHelper = sheet.getDataValidationHelper();
            String formulaString = String.format("INDIRECT($%s%s)", offset, firstRow + 1);
            DataValidation dataValidation = getDataValidationByFormula(formulaString, dvHelper, firstRow, lastRow, firstCol, lastCol);
            sheet.addValidationData(dataValidation);
        }
 
        private static DataValidation getDataValidationByFormula(String formulaString, DataValidationHelper dvHelper,
                                                                 int firstRow, int lastRow, int firstCol, int lastCol) {
            // 加载下拉列表内容
            // 举例:若formulaString = "INDIRECT($A$2)" 表示规则数据会从名称管理器中获取key与单元格 A2 值相同的数据,
            // 如果A2是江苏省,那么此处就是江苏省下的市信息。
            DataValidationConstraint dvConstraint = dvHelper.createFormulaListConstraint(formulaString);
            // 设置数据有效性加载在哪个单元格上。
            // 四个参数分别是:起始行、终止行、起始列、终止列
            CellRangeAddressList regions = new CellRangeAddressList(firstRow, lastRow, firstCol, lastCol);
            // 绑定 数据有效性对象
            XSSFDataValidation dataValidation = (XSSFDataValidation) dvHelper.createValidation(dvConstraint, regions);
            dataValidation.setEmptyCellAllowed(true);
            dataValidation.setSuppressDropDownArrow(true);
            dataValidation.setShowErrorBox(true);
            // 设置输入错误提示信息
            dataValidation.createErrorBox("选择错误提示", "你输入的值未在备选列表中,请下拉选择合适的值!");
            return dataValidation;
        }
 
        private static String getFormula(int firstRow, int lastRow, int firstCol, int lastCol) {
            String ss = String.format("$%s$%s:$%s$%s", CellReference.convertNumToColString(firstCol), firstRow,
                    CellReference.convertNumToColString(lastCol), lastRow);
            return String.format("=%s!%s", "area", ss);
        }
 
        private static String getRange(int offset, int rowId, int colCount) {
            String columnLetter1 = CellReference.convertNumToColString(offset);
            String columnLetter2 = CellReference.convertNumToColString(offset + colCount - 1);
            return String.format("$%s$%s:$%s$%s", columnLetter1, rowId, columnLetter2, rowId);
        }
 
    }
 
 
    public static void main(String[] args) {
        EasyExcel.write("test.xlsx", CalculateRule.class)
                // 自适应列宽
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                // 下拉框
                .registerWriteHandler(new CustomSheetWriteHandler(Arrays.asList("扣指定分数", "分数乘以数量", "除以数量后乘以分数")))
                .registerWriteHandler(new CustomCellWriteHandler())
                // 标注
                .registerWriteHandler(new CommentWriteHandler())
                .sheet("合同导入模板")
                .doWrite(getExcelData());
    }
 
    private static List<CalculateRule> getExcelData() {
        ArrayList<CalculateRule> list = new ArrayList<>();
 
        setTemplateRule(list,1,"设备平均在线率","≥98%",null,98D,RuleDeductCategoryEnum.DEDUCT_POINTS,0.00);
        setTemplateRule(list,null,"设备平均在线率","95%≤设备平均在线率<98%",97D,95D,RuleDeductCategoryEnum.DEDUCT_POINTS,5.00);
        setTemplateRule(list,null,"设备平均在线率","90%≤设备平均在线率<95%",94D,90D,RuleDeductCategoryEnum.DEDUCT_POINTS,10.00);
        setTemplateRule(list,null,"设备平均在线率","<90%",89D,null,RuleDeductCategoryEnum.DEDUCT_POINTS,20.00);
 
        setTemplateRule(list,2,"前端感知源治理工作","时钟同步(超过±3秒为不合格)",null,null,RuleDeductCategoryEnum.MULTIPLY_POINTS_BY_QUANTITY,0.1);
        setTemplateRule(list,null,"前端感知源治理工作","OSD标识",null,null,RuleDeductCategoryEnum.MULTIPLY_POINTS_BY_QUANTITY,0.1);
        setTemplateRule(list,null,"前端感知源治理工作","一机一档",null,null,RuleDeductCategoryEnum.MULTIPLY_POINTS_BY_QUANTITY,0.1);
 
        setTemplateRule(list,3,"存储故障","因存储设备、云存储软件等引起平台不能正常查看历史图像,单次故障时长在24小时以内的",24D,null,RuleDeductCategoryEnum.MULTIPLY_POINTS_BY_QUANTITY,0.5);
        setTemplateRule(list,null,"存储故障","因存储设备、云存储软件等引起平台不能正常查看历史图像,单次故障时长若超出24小时以上。",null,24D,RuleDeductCategoryEnum.MULTIPLY_POINTS_BY_QUANTITY,1.0);
        setTemplateRule(list,null,"存储故障","因视频或者图片丢失导致重要案事件不能回放或查看",null,null,RuleDeductCategoryEnum.MULTIPLY_POINTS_BY_QUANTITY,2.0);
 
        setTemplateRule(list,4,"对于前端点位异常情况的处理","镜头故障或污染或树枝遮挡或枪机视角偏移正常角度或补光灯应亮未亮,24小时后未修复的",null,24D,RuleDeductCategoryEnum.MULTIPLY_POINTS_BY_QUANTITY,0.5);
        setTemplateRule(list,null,"对于前端点位异常情况的处理","镜头故障或污染或树枝遮挡或枪机视角偏移正常角度或补光灯应亮未亮,48小时后未修复的",null,48D,RuleDeductCategoryEnum.MULTIPLY_POINTS_BY_QUANTITY,1.0);
 
        setTemplateRule(list,5,"确保录像完整不定期对所有点位录像完整性抽查","每路视频累计丢失10分钟以内",10D,null,RuleDeductCategoryEnum.MULTIPLY_POINTS_BY_QUANTITY,0.2);
        setTemplateRule(list,null,"确保录像完整不定期对所有点位录像完整性抽查","丢失10-60 分钟",10D,60D,RuleDeductCategoryEnum.MULTIPLY_POINTS_BY_QUANTITY,0.5);
        setTemplateRule(list,null,"确保录像完整不定期对所有点位录像完整性抽查","丢失1 小时-4 小时(含)",60D,240D,RuleDeductCategoryEnum.MULTIPLY_POINTS_BY_QUANTITY,1.0);
        setTemplateRule(list,null,"确保录像完整不定期对所有点位录像完整性抽查","丢失4 小时-12 小时(含)",240D,720D,RuleDeductCategoryEnum.MULTIPLY_POINTS_BY_QUANTITY,1.5);
        setTemplateRule(list,null,"确保录像完整不定期对所有点位录像完整性抽查","丢失12 小时以上",null,720D,RuleDeductCategoryEnum.MULTIPLY_POINTS_BY_QUANTITY,2.0);
 
        setTemplateRule(list,6,"确保图片完整不定期对所有人脸车辆以及智能前端抓拍的图片完整性抽查","发现后台存储不能调取前端设备图片",null,null,RuleDeductCategoryEnum.MULTIPLY_POINTS_BY_QUANTITY,2.0);
 
 
        return list;
    }
 
    private static void setTemplateRule(ArrayList<CalculateRule> list,Integer id,String ruleName,String condition,Double max,Double min,RuleDeductCategoryEnum deductCategoryEnum,Double calcFraction) {
        CalculateRule calculateRule = new CalculateRule();
        calculateRule.setId(id);
        calculateRule.setRuleName(ruleName);
        calculateRule.setRuleCondition(condition);
        calculateRule.setMax(max);
        calculateRule.setMin(min);
        calculateRule.setDeductCategory(deductCategoryEnum);
        calculateRule.setCalcFraction(calcFraction);
        list.add(calculateRule);
    }
 
}