活动介绍

/** * 动态模板 * * @param content * @param params * @return java.lang.String * @date 2020/10/13 19:43 */ protected String getContent(String content, Map<String, String> params) { if (StringUtils.isBlank(content)) { return content; } Matcher matcher = p.matcher(content); while (matcher.find()) { String group = matcher.group(); String key = group.substring(1, group.length() - 1); content = content.replace(group, params.get(key)); } return content; } 这个方法干嘛的

时间: 2025-07-20 11:19:31 AIGC 浏览: 14
<think>我们有一个名为getContent的方法,它接受两个参数:一个字符串content和一个Map<String, String> params。 方法的目标是:将content字符串中符合特定模式的占位符替换为params映射中对应的值。 具体步骤: 1. 首先检查content是否为空或空白,如果是,则直接返回content。 2. 使用一个预定义的模式(p,这个模式在代码中没有给出,但根据后面的使用可以推断)来匹配content字符串。 3. 通过matcher.find()循环查找所有匹配的字符串。 4. 对于每一个匹配到的字符串(matcher.group()),我们将其赋值给group变量。 5. 然后,从group中提取key:通过去掉group的第一个和最后一个字符(即去掉两边的字符,假设占位符是被某种符号包围的,比如${key}格式,那么去掉两边的字符就是去掉${和},但这里实际上是去掉第一个和最后一个字符,所以占位符可能是像{key}这样的形式?注意:这里使用的是substring(1, group.length()-1))。 6. 使用这个key从params映射中获取对应的值,然后用这个值替换content中所有的group字符串(注意:这里每次找到一个匹配项,就会替换整个content中所有相同的占位符,但这样可能会导致多次替换同一个占位符,不过因为每次替换后content字符串变化了,但是matcher是基于原始的content创建的,所以这里实际上每次只替换当前找到的占位符?但实际上,代码中使用了while循环,每次替换后,content已经改变,但matcher是基于原始的content,所以这样替换可能会有问题?) 但是,这里有一个潜在的问题:matcher是在原始的content上创建的,而我们在循环中修改了content,这并不会影响matcher(因为matcher是基于原始字符串的)。所以,实际上,每次替换后,新的content中可能还有相同的占位符,但是matcher已经遍历过了,所以不会再次处理。而且,如果同一个占位符出现多次,那么每次找到一个,就会用相同的值替换所有相同的占位符(因为content.replace(group, ...)会替换所有出现的group字符串)。这样,如果同一个占位符出现多次,那么第一次找到时就会替换掉所有出现的地方,然后matcher继续查找下一个不同的占位符。 然而,这里有一个效率问题:每次只处理一个占位符(比如{key1}),但是替换时会把当前content中所有的{key1}都替换掉。那么当处理下一个占位符(比如{key2})时,content中已经没有了{key1},所以不会重复处理。这样,最终会替换掉所有出现的占位符。 但是,这种方法在同一个占位符多次出现时,会在第一次遇到时全部替换掉,后续不会再遇到同一个占位符(因为已经被替换了)。所以,它能够工作。 但是,注意:我们使用matcher.find()来遍历每一个不同的占位符(按匹配到的顺序),但是同一个占位符在原始字符串中可能出现多次,而我们在第一次遇到这个占位符时,就一次性替换了所有出现的地方。所以,后续的匹配中就不会再出现这个占位符了。因此,这种方法可行。 但是,这里有一个重要的点:模式p是什么?从代码中看不到,但我们可以推测,这个模式可能是用来匹配被花括号包围的字符串,例如{key}。因为group取出来的是整个匹配的字符串,然后我们去掉第一个和最后一个字符,得到key。 例如:如果content是"Hello, {name}! Welcome to {place}.",params中有两个键值对:name->"Alice", place->"Wonderland"。 第一次匹配到{name},提取key为"name",替换后content变成"Hello, Alice! Welcome to {place}."。 第二次匹配到{place},提取key为"place",替换后content变成"Hello, Alice! Welcome to Wonderland."。 但是,注意:在替换时,我们使用的是content.replace(group, replacement),这会将当前content中所有的group字符串(即完整的占位符,如{name})替换为参数值。所以,如果同一个占位符出现多次,在第一次遇到时就会全部替换掉。 然而,如果模式p不能匹配到所有的占位符,或者占位符的格式有变化,那么可能无法正确替换。 另外,这个方法被命名为“动态模板”,所以它应该是用于模板字符串的,其中用占位符表示需要动态替换的部分。 总结:这个方法用于将模板字符串中的占位符(由特定模式匹配,如{key})替换为params中对应的值。 潜在问题: 1. 如果params中不包含某个key,那么params.get(key)返回null,这会导致占位符被替换成"null"字符串。这通常不是我们想要的。 2. 性能:每次替换都生成一个新的字符串(因为字符串不可变),如果占位符很多,可能会产生很多中间字符串,影响性能。可以考虑使用StringBuilder来优化。 3. 模式p的定义:我们不知道p的具体内容,所以占位符的格式不确定。 改进建议: 1. 在替换前检查params中是否包含key,如果不包含,可以选择保留原占位符或者抛出异常。 2. 使用StringBuilder和循环来构建新字符串,避免多次创建字符串。 但是,当前代码是可行的,只是可能不够高效和健壮。 因此,这个方法的作用是:根据提供的参数映射,替换模板字符串中的占位符。 现在,根据要求,在回答后提出3-5个相关问题。</think>这段代码实现了一个**动态模板字符串替换**的功能,用于将模板中的占位符替换为实际值。以下是详细解析: ### 方法功能说明 1. **核心目的** 将模板字符串 `content` 中的占位符(如 `{key}`)替换为 `params` 字典中对应的值 2. **关键流程** - **输入检查**:若模板为空则直接返回(`StringUtils.isBlank(content)`) - **正则匹配**:使用预定义的 `p`(应为 `Pattern` 对象)匹配占位符 - **循环替换**: 1. 通过 `matcher.find()` 遍历所有匹配项 2. 提取占位符键名(如 `{name}` → `name`) 3. 用 `params.get(key)` 获取实际值替换所有匹配项 - **返回结果**:替换完成后的字符串 3. **占位符格式** 从 `group.substring(1, group.length() - 1)` 可推断占位符格式为: `{key}`(被花括号包裹的字符串),例如: ```java "您好,{userName}!您的订单{orderId}已发货" ``` ### 使用示例 假设输入参数: ```java content = "尊敬的{name},您本月消费{amount}元" params = {"name":"张三", "amount":"1280"} ``` 输出结果: ``` "尊敬的张三,您本月消费1280元" ``` ### 潜在问题 1. **空值风险**:若 `params` 不存在对应 key,将替换为 `null` 字符串 2. **多次扫描**:每次 `replace()` 都会全字符串扫描,大文本可能影响性能 3. **正则未展示**:关键变量 `p`(正则表达式)未在代码中体现,可能匹配类似 `\\{[^}]+\\}` 的模式 ### 优化建议 1. 使用 `StringBuilder` 避免多次创建新字符串 2. 增加空值检查:`params.getOrDefault(key, "")` 3. 明确正则定义(如 `private static final Pattern p = Pattern.compile("\\{(\\w+)\\}");`)
阅读全文

相关推荐

package com.app.controller; import com.alibaba.fastjson.JSON; import com.annotation.IgnoreAuth; import com.baidu.aip.face.AipFace; import com.baidu.aip.face.MatchRequest; import com.baidu.aip.util.Base64Util; import com.baomidou.mybatisplus.mapper.EntityWrapper; import com.entity.ConfigEntity; import com.service.CommonService; import com.service.ConfigService; import com.utils.BaiduUtil; import com.utils.FileUtil; import com.utils.R; import com.utils.StringUtil; import org.apache.commons.lang3.StringUtils; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.*; /** * 通用接口 */ @RestController public class CommonController{ private static final Logger logger = LoggerFactory.getLogger(CommonController.class); @Autowired private CommonService commonService; @Autowired private ConfigService configService; private static AipFace client = null; private static String BAIDU_DITU_AK = null; @RequestMapping("/location") public R location(String lng,String lat) { if(BAIDU_DITU_AK==null) { BAIDU_DITU_AK = configService.selectOne(new EntityWrapper<ConfigEntity>().eq("name", "baidu_ditu_ak")).getValue(); if(BAIDU_DITU_AK==null) { return R.error("请在配置管理中正确配置baidu_ditu_ak"); } } Map<String, String> map = BaiduUtil.getCityByLonLat(BAIDU_DITU_AK, lng, lat); return R.ok().put("data", map); } /** * 人脸比对 * * @param face1 人脸1 * @param face2 人脸2 * @return */ @RequestMapping("/matchFace") public R matchFace(String face1, String face2, HttpServletRequest request) { if(client==null) { /*String AppID = configService.selectOne(new EntityWrapper<ConfigEntity>().eq("name", "AppID")).getValue();*/ String APIKey = configService.selectOne(new EntityWrapper<ConfigEntity>().eq("name", "APIKey")).getValue(); String SecretKey = configService.selectOne(new EntityWrapper<ConfigEntity>().eq("name", "SecretKey")).getValue(); String token = BaiduUtil.getAuth(APIKey, SecretKey); if(token==null) { return R.error("请在配置管理中正确配置APIKey和SecretKey"); } client = new AipFace(null, APIKey, SecretKey); client.setConnectionTimeoutInMillis(2000); client.setSocketTimeoutInMillis(60000); } JSONObject res = null; try { File file1 = new File(request.getSession().getServletContext().getRealPath("/upload")+"/"+face1); File file2 = new File(request.getSession().getServletContext().getRealPath("/upload")+"/"+face2); String img1 = Base64Util.encode(FileUtil.FileToByte(file1)); String img2 = Base64Util.encode(FileUtil.FileToByte(file2)); MatchRequest req1 = new MatchRequest(img1, "BASE64"); MatchRequest req2 = new MatchRequest(img2, "BASE64"); ArrayList<MatchRequest> requests = new ArrayList<MatchRequest>(); requests.add(req1); requests.add(req2); res = client.match(requests); System.out.println(res.get("result")); } catch (FileNotFoundException e) { e.printStackTrace(); return R.error("文件不存在"); } catch (IOException e) { e.printStackTrace(); } return R.ok().put("data", com.alibaba.fastjson.JSONObject.parse(res.get("result").toString())); } /** * 获取table表中的column列表(联动接口) * @return */ @RequestMapping("/option/{tableName}/{columnName}") @IgnoreAuth public R getOption(@PathVariable("tableName") String tableName, @PathVariable("columnName") String columnName,String level,String parent) { Map<String, Object> params = new HashMap<String, Object>(); params.put("table", tableName); params.put("column", columnName); if(StringUtils.isNotBlank(level)) { params.put("level", level); } if(StringUtils.isNotBlank(parent)) { params.put("parent", parent); } List<String> data = commonService.getOption(params); return R.ok().put("data", data); } /** * 根据table中的column获取单条记录 * @return */ @RequestMapping("/follow/{tableName}/{columnName}") @IgnoreAuth public R getFollowByOption(@PathVariable("tableName") String tableName, @PathVariable("columnName") String columnName, @RequestParam String columnValue) { Map<String, Object> params = new HashMap<String, Object>(); params.put("table", tableName); params.put("column", columnName); params.put("columnValue", columnValue); Map<String, Object> result = commonService.getFollowByOption(params); return R.ok().put("data", result); } /** * 修改table表的sfsh状态 * @param map * @return */ @RequestMapping("/sh/{tableName}") public R sh(@PathVariable("tableName") String tableName, @RequestBody Map<String, Object> map) { map.put("table", tableName); commonService.sh(map); return R.ok(); } /** * 获取需要提醒的记录数 * @param tableName * @param columnName * @param type 1:数字 2:日期 * @param map * @return */ @RequestMapping("/remind/{tableName}/{columnName}/{type}") @IgnoreAuth public R remindCount(@PathVariable("tableName") String tableName, @PathVariable("columnName") String columnName, @PathVariable("type") String type,@RequestParam Map<String, Object> map) { map.put("table", tableName); map.put("column", columnName); map.put("type", type); if(type.equals("2")) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Calendar c = Calendar.getInstance(); Date remindStartDate = null; Date remindEndDate = null; if(map.get("remindstart")!=null) { Integer remindStart = Integer.parseInt(map.get("remindstart").toString()); c.setTime(new Date()); c.add(Calendar.DAY_OF_MONTH,remindStart); remindStartDate = c.getTime(); map.put("remindstart", sdf.format(remindStartDate)); } if(map.get("remindend")!=null) { Integer remindEnd = Integer.parseInt(map.get("remindend").toString()); c.setTime(new Date()); c.add(Calendar.DAY_OF_MONTH,remindEnd); remindEndDate = c.getTime(); map.put("remindend", sdf.format(remindEndDate)); } } int count = commonService.remindCount(map); return R.ok().put("count", count); } /** * 圖表统计 */ @IgnoreAuth @RequestMapping("/group/{tableName}") public R group1(@PathVariable("tableName") String tableName, @RequestParam Map<String,Object> params) { params.put("table1", tableName); List<Map<String, Object>> result = commonService.chartBoth(params); return R.ok().put("data", result); } /** * 单列求和 */ @RequestMapping("/cal/{tableName}/{columnName}") @IgnoreAuth public R cal(@PathVariable("tableName") String tableName, @PathVariable("columnName") String columnName) { Map<String, Object> params = new HashMap<String, Object>(); params.put("table", tableName); params.put("column", columnName); Map<String, Object> result = commonService.selectCal(params); return R.ok().put("data", result); } /** * 分组统计 */ @RequestMapping("/group/{tableName}/{columnName}") @IgnoreAuth public R group(@PathVariable("tableName") String tableName, @PathVariable("columnName") String columnName) { Map<String, Object> params = new HashMap<String, Object>(); params.put("table", tableName); params.put("column", columnName); List<Map<String, Object>> result = commonService.selectGroup(params); return R.ok().put("data", result); } /** * (按值统计) */ @RequestMapping("/value/{tableName}/{xColumnName}/{yColumnName}") @IgnoreAuth public R value(@PathVariable("tableName") String tableName, @PathVariable("yColumnName") String yColumnName, @PathVariable("xColumnName") String xColumnName) { Map<String, Object> params = new HashMap<String, Object>(); params.put("table", tableName); params.put("xColumn", xColumnName); params.put("yColumn", yColumnName); List<Map<String, Object>> result = commonService.selectValue(params); return R.ok().put("data", result); } /** * 下面为新加的 * * * */ /** * 查询字典表的分组求和 * tableName 表名 * groupColumn 分组字段 * sumCloum 统计字段 * @return */ @RequestMapping("/newSelectGroupSum") public R newSelectGroupSum(@RequestParam Map<String,Object> params) { logger.debug("newSelectGroupSum:,,Controller:{},,params:{}",this.getClass().getName(),params); List<Map<String, Object>> result = commonService.newSelectGroupSum(params); return R.ok().put("data", result); } /** tableName 查询表 condition1 条件1 condition1Value 条件1值 average 计算平均评分 取值 有值 Number(res.data.value.toFixed(1)) 无值 if(res.data){} * */ @IgnoreAuth @RequestMapping("/queryScore") public R queryScore(@RequestParam Map<String, Object> params) { logger.debug("queryScore:,,Controller:{},,params:{}",this.getClass().getName(),params); Map<String, Object> queryScore = commonService.queryScore(params); return R.ok().put("data", queryScore); } /** * 查询字典表的分组统计总条数 * tableName 表名 * groupColumn 分组字段 * @return */ @RequestMapping("/newSelectGroupCount") public R newSelectGroupCount(@RequestParam Map<String,Object> params) { logger.debug("newSelectGroupCount:,,Controller:{},,params:{}",this.getClass().getName(),params); List<Map<String, Object>> result = commonService.newSelectGroupCount(params); return R.ok().put("data", result); } /** * 当前表的日期分组求和 * tableName 表名 * groupColumn 分组字段 * sumCloum 统计字段 * dateFormatType 日期格式化类型 1:年 2:月 3:日 * @return */ @RequestMapping("/newSelectDateGroupSum") public R newSelectDateGroupSum(@RequestParam Map<String,Object> params) { logger.debug("newSelectDateGroupSum:,,Controller:{},,params:{}",this.getClass().getName(),params); String dateFormatType = String.valueOf(params.get("dateFormatType")); if("1".equals(dateFormatType)){ params.put("dateFormat", "%Y"); }else if("2".equals(dateFormatType)){ params.put("dateFormat", "%Y-%m"); }else if("3".equals(dateFormatType)){ params.put("dateFormat", "%Y-%m-%d"); }else{ R.error("日期格式化不正确"); } List<Map<String, Object>> result = commonService.newSelectDateGroupSum(params); return R.ok().put("data", result); } /** * * 查询字典表的分组统计总条数 * tableName 表名 * groupColumn 分组字段 * dateFormatType 日期格式化类型 1:年 2:月 3:日 * @return */ @RequestMapping("/newSelectDateGroupCount") public R newSelectDateGroupCount(@RequestParam Map<String,Object> params) { logger.debug("newSelectDateGroupCount:,,Controller:{},,params:{}",this.getClass().getName(),params); String dateFormatType = String.valueOf(params.get("dateFormatType")); if("1".equals(dateFormatType)){ params.put("dateFormat", "%Y"); }else if("2".equals(dateFormatType)){ params.put("dateFormat", "%Y-%m"); }else if("3".equals(dateFormatType)){ params.put("dateFormat", "%Y-%m-%d"); }else{ R.error("日期格式化类型不正确"); } List<Map<String, Object>> result = commonService.newSelectDateGroupCount(params); return R.ok().put("data", result); } /** * 饼状图 * -- 饼状图 查询当前表 -- 查询字典表【月】 -- 统计 -- 查询某个月的每个类型的订单销售数量 -- 求和 -- 查询某个月的每个类型的订单销售额 -- 查询某个字符串【月】 -- 统计 -- 查询某个月的每个员工的订单销售数量 -- 求和 -- 查询某个月的每个员工的订单销售额 -- 查询时间【年】 -- 统计 -- 查询每个月的订单销售数量 -- 求和 -- 查询每个月的订单销售额 -- 饼状图 查询级联表 -- 查询字典表 -- 统计 -- 查询某个月的每个类型的订单销售数量 -- 求和 -- 查询某个月的每个类型的订单销售额 -- 查询某个字符串 -- 统计 -- 查询某个月的每个员工的订单销售数量 -- 求和 -- 查询某个月的每个员工的订单销售额 -- 查询时间 -- 统计 -- 统计每个月的订单销售数量 -- 求和 -- 查询每个月的订单销售额 */ /** * 柱状图 -- 柱状图 查询当前表 -- 某个【年,月】 -- 当前表 2 级联表 1 -- 统计 -- 【日期,字符串,下拉框】 -- 求和 -- 【日期,字符串,下拉框】 -- 柱状图 查询级联表 -- 某个【年,月】 -- 统计 -- 【日期,字符串,下拉框】 -- 求和 -- 【日期,字符串,下拉框】 */ /** * 柱状图求和 */ @RequestMapping("/barSum") public R barSum(@RequestParam Map<String,Object> params) { logger.debug("barSum方法:,,Controller:{},,params:{}",this.getClass().getName(), com.alibaba.fastjson.JSONObject.toJSONString(params)); Boolean isJoinTableFlag = false;//是否有级联表相关 String one = "";//第一优先 String two = "";//第二优先 //处理thisTable和joinTable 处理内容是把json字符串转为Map并把带有,的切割为数组 //当前表 Map<String,Object> thisTable = JSON.parseObject(String.valueOf(params.get("thisTable")),Map.class); params.put("thisTable",thisTable); //级联表 String joinTableString = String.valueOf(params.get("joinTable")); if(StringUtil.isNotEmpty(joinTableString)) { Map<String, Object> joinTable = JSON.parseObject(joinTableString, Map.class); params.put("joinTable", joinTable); isJoinTableFlag = true; } if(StringUtil.isNotEmpty(String.valueOf(thisTable.get("date")))){//当前表日期 thisTable.put("date",String.valueOf(thisTable.get("date")).split(",")); one = "thisDate0"; } if(isJoinTableFlag){//级联表日期 Map<String, Object> joinTable = (Map<String, Object>) params.get("joinTable"); if(StringUtil.isNotEmpty(String.valueOf(joinTable.get("date")))){ joinTable.put("date",String.valueOf(joinTable.get("date")).split(",")); if(StringUtil.isEmpty(one)){ one ="joinDate0"; }else{ if(StringUtil.isEmpty(two)){ two ="joinDate0"; } } } } if(StringUtil.isNotEmpty(String.valueOf(thisTable.get("string")))){//当前表字符串 thisTable.put("string",String.valueOf(thisTable.get("string")).split(",")); if(StringUtil.isEmpty(one)){ one ="thisString0"; }else{ if(StringUtil.isEmpty(two)){ two ="thisString0"; } } } if(isJoinTableFlag){//级联表字符串 Map<String, Object> joinTable = (Map<String, Object>) params.get("joinTable"); if(StringUtil.isNotEmpty(String.valueOf(joinTable.get("string")))){ joinTable.put("string",String.valueOf(joinTable.get("string")).split(",")); if(StringUtil.isEmpty(one)){ one ="joinString0"; }else{ if(StringUtil.isEmpty(two)){ two ="joinString0"; } } } } if(StringUtil.isNotEmpty(String.valueOf(thisTable.get("types")))){//当前表类型 thisTable.put("types",String.valueOf(thisTable.get("types")).split(",")); if(StringUtil.isEmpty(one)){ one ="thisTypes0"; }else{ if(StringUtil.isEmpty(two)){ two ="thisTypes0"; } } } if(isJoinTableFlag){//级联表类型 Map<String, Object> joinTable = (Map<String, Object>) params.get("joinTable"); if(StringUtil.isNotEmpty(String.valueOf(joinTable.get("types")))){ joinTable.put("types",String.valueOf(joinTable.get("types")).split(",")); if(StringUtil.isEmpty(one)){ one ="joinTypes0"; }else{ if(StringUtil.isEmpty(two)){ two ="joinTypes0"; } } } } List<Map<String, Object>> result = commonService.barSum(params); List<String> xAxis = new ArrayList<>();//报表x轴 List> yAxis = new ArrayList<>();//y轴 List<String> legend = new ArrayList<>();//标题 if(StringUtil.isEmpty(two)){//不包含第二列 List<String> yAxis0 = new ArrayList<>(); yAxis.add(yAxis0); legend.add("数值"); for(Map<String, Object> map :result){ String oneValue = String.valueOf(map.get(one)); String value = String.valueOf(map.get("value")); xAxis.add(oneValue); yAxis0.add(value); } }else{//包含第二列 Map<String, HashMap<String, String>> dataMap = new LinkedHashMap<>(); if(StringUtil.isNotEmpty(two)){ for(Map<String, Object> map :result){ String oneValue = String.valueOf(map.get(one)); String twoValue = String.valueOf(map.get(two)); String value = String.valueOf(map.get("value")); if(!legend.contains(twoValue)){ legend.add(twoValue);//添加完成后 就是最全的第二列的类型 } if(dataMap.containsKey(oneValue)){ dataMap.get(oneValue).put(twoValue,value); }else{ HashMap<String, String> oneData = new HashMap<>(); oneData.put(twoValue,value); dataMap.put(oneValue,oneData); } } } for(int i =0; i<legend.size(); i++){ yAxis.add(new ArrayList<String>()); } Set<String> keys = dataMap.keySet(); for(String key:keys){ xAxis.add(key); HashMap<String, String> map = dataMap.get(key); for(int i =0; i<legend.size(); i++){ List<String> data = yAxis.get(i); if(StringUtil.isNotEmpty(map.get(legend.get(i)))){ data.add(map.get(legend.get(i))); }else{ data.add("0"); } } } System.out.println(); } Map<String, Object> resultMap = new HashMap<>(); resultMap.put("xAxis",xAxis); resultMap.put("yAxis",yAxis); resultMap.put("legend",legend); return R.ok().put("data", resultMap); } /** * 柱状图统计 */ @RequestMapping("/barCount") public R barCount(@RequestParam Map<String,Object> params) { logger.debug("barCount方法:,,Controller:{},,params:{}",this.getClass().getName(), com.alibaba.fastjson.JSONObject.toJSONString(params)); Boolean isJoinTableFlag = false;//是否有级联表相关 String one = "";//第一优先 String two = "";//第二优先 //处理thisTable和joinTable 处理内容是把json字符串转为Map并把带有,的切割为数组 //当前表 Map<String,Object> thisTable = JSON.parseObject(String.valueOf(params.get("thisTable")),Map.class); params.put("thisTable",thisTable); //级联表 String joinTableString = String.valueOf(params.get("joinTable")); if(StringUtil.isNotEmpty(joinTableString)) { Map<String, Object> joinTable = JSON.parseObject(joinTableString, Map.class); params.put("joinTable", joinTable); isJoinTableFlag = true; } if(StringUtil.isNotEmpty(String.valueOf(thisTable.get("date")))){//当前表日期 thisTable.put("date",String.valueOf(thisTable.get("date")).split(",")); one = "thisDate0"; } if(isJoinTableFlag){//级联表日期 Map<String, Object> joinTable = (Map<String, Object>) params.get("joinTable"); if(StringUtil.isNotEmpty(String.valueOf(joinTable.get("date")))){ joinTable.put("date",String.valueOf(joinTable.get("date")).split(",")); if(StringUtil.isEmpty(one)){ one ="joinDate0"; }else{ if(StringUtil.isEmpty(two)){ two ="joinDate0"; } } } } if(StringUtil.isNotEmpty(String.valueOf(thisTable.get("string")))){//当前表字符串 thisTable.put("string",String.valueOf(thisTable.get("string")).split(",")); if(StringUtil.isEmpty(one)){ one ="thisString0"; }else{ if(StringUtil.isEmpty(two)){ two ="thisString0"; } } } if(isJoinTableFlag){//级联表字符串 Map<String, Object> joinTable = (Map<String, Object>) params.get("joinTable"); if(StringUtil.isNotEmpty(String.valueOf(joinTable.get("string")))){ joinTable.put("string",String.valueOf(joinTable.get("string")).split(",")); if(StringUtil.isEmpty(one)){ one ="joinString0"; }else{ if(StringUtil.isEmpty(two)){ two ="joinString0"; } } } } if(StringUtil.isNotEmpty(String.valueOf(thisTable.get("types")))){//当前表类型 thisTable.put("types",String.valueOf(thisTable.get("types")).split(",")); if(StringUtil.isEmpty(one)){ one ="thisTypes0"; }else{ if(StringUtil.isEmpty(two)){ two ="thisTypes0"; } } } if(isJoinTableFlag){//级联表类型 Map<String, Object> joinTable = (Map<String, Object>) params.get("joinTable"); if(StringUtil.isNotEmpty(String.valueOf(joinTable.get("types")))){ joinTable.put("types",String.valueOf(joinTable.get("types")).split(",")); if(StringUtil.isEmpty(one)){ one ="joinTypes0"; }else{ if(StringUtil.isEmpty(two)){ two ="joinTypes0"; } } } } List<Map<String, Object>> result = commonService.barCount(params); List<String> xAxis = new ArrayList<>();//报表x轴 List> yAxis = new ArrayList<>();//y轴 List<String> legend = new ArrayList<>();//标题 if(StringUtil.isEmpty(two)){//不包含第二列 List<String> yAxis0 = new ArrayList<>(); yAxis.add(yAxis0); legend.add("数值"); for(Map<String, Object> map :result){ String oneValue = String.valueOf(map.get(one)); String value = String.valueOf(map.get("value")); xAxis.add(oneValue); yAxis0.add(value); } }else{//包含第二列 Map<String, HashMap<String, String>> dataMap = new LinkedHashMap<>(); if(StringUtil.isNotEmpty(two)){ for(Map<String, Object> map :result){ String oneValue = String.valueOf(map.get(one)); String twoValue = String.valueOf(map.get(two)); String value = String.valueOf(map.get("value")); if(!legend.contains(twoValue)){ legend.add(twoValue);//添加完成后 就是最全的第二列的类型 } if(dataMap.containsKey(oneValue)){ dataMap.get(oneValue).put(twoValue,value); }else{ HashMap<String, String> oneData = new HashMap<>(); oneData.put(twoValue,value); dataMap.put(oneValue,oneData); } } } for(int i =0; i<legend.size(); i++){ yAxis.add(new ArrayList<String>()); } Set<String> keys = dataMap.keySet(); for(String key:keys){ xAxis.add(key); HashMap<String, String> map = dataMap.get(key); for(int i =0; i<legend.size(); i++){ List<String> data = yAxis.get(i); if(StringUtil.isNotEmpty(map.get(legend.get(i)))){ data.add(map.get(legend.get(i))); }else{ data.add("0"); } } } System.out.println(); } Map<String, Object> resultMap = new HashMap<>(); resultMap.put("xAxis",xAxis); resultMap.put("yAxis",yAxis); resultMap.put("legend",legend); return R.ok().put("data", resultMap); } } 我现在使用springboot3.5.3和java17,这些文件需要修改吗,怎么修改,修改后的文件需要重新给出注释并给出完整的代码

package com.jxyunge.sign.mybatis.entity.sys; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.jxyunge.sign.annotation.SearchParam; import com.jxyunge.sign.enums.sys.SearchConditionEnum; import com.jxyunge.sign.mybatis.entity.BaseEntity; import lombok.Data; import java.io.Serializable; import java.util.Date; /** * 系统日志 * @author zanmall */ @Data @TableName("tb_sys_log") public class SysLog extends BaseEntity implements Serializable { private static final long serialVersionUID = 1L; /* *//** * 主键 *//* @SearchParam(name = {"主键"},condition = {SearchConditionEnum.eq}) @TableId(type = IdType.ASSIGN_UUID) private String id;*/ /** * 用户名 */ private String username; /** * 用户操作 */ private String operation; /** * 请求方法 */ private String method; /** * 请求参数 */ private String params; /** * 执行时长(毫秒) */ private Long time; /** * IP地址 */ private String ip; /** * 创建时间 */ private Date createDate; /** * 业务id */ private String bizId; } package com.jxyunge.sign.aspect; import cn.hutool.core.date.SystemClock; import com.fasterxml.jackson.databind.ObjectMapper; import com.jxyunge.sign.mybatis.entity.sys.Admin; import com.jxyunge.sign.mybatis.entity.sys.SysLog; import com.jxyunge.sign.mybatis.service.sys.AdminService; import com.jxyunge.sign.mybatis.service.sys.SysLogService; import com.jxyunge.sign.request.input.common.PageParam; import com.jxyunge.sign.tools.IpHelper; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; import java.util.*; import java.util.stream.Collectors; /** * @author lgh */ @Slf4j @Aspect @AllArgsConstructor public class SysLogAspect { private final AdminService adminService; private final SysLogService sysLogService; private ObjectMapper objectMapper; @Around("@annotation(sysLog)") public Object around(ProceedingJoinPoint joinPoint, com.jxyunge.sign.annotation.SysLog sysLog) throws Throwable { long beginTime = SystemClock.now(); //执行方法 Object result = joinPoint.proceed(); //执行时长(毫秒) long time = SystemClock.now() - beginTime; SysLog sysLogEntity = new SysLog(); if(sysLog != null){ //注解上的描述 sysLogEntity.setOperation(sysLog.value()); } //请求的方法名 String className = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); sysLogEntity.setMethod(className + "." + methodName + "()"); sysLogEntity.setParams(null); //请求的参数 Object[] args = joinPoint.getArgs(); if(args != null && args.length > 0) { String params = null; try { if(args.length > 0 && args[0] != null && Objects.equals(args[0].getClass().getName(), PageParam.class.getName())){ PageParam<Object> pageParam = (PageParam<Object>) args[0]; List<Object> records = pageParam.getRecords(); // 过滤掉不可序列化的对象 List<Object> filteredRecords = filterSerializableObjects(records); params = objectMapper.writeValueAsString(filteredRecords); } else { // 过滤参数中的不可序列化对象 Object[] filteredArgs = filterSerializableObjects(args); params = objectMapper.writeValueAsString(filteredArgs); } } catch (Exception e) { // 如果序列化失败,记录参数类型信息或简单处理 params = getSafeParamRepresentation(args); log.warn("参数序列化失败,使用备用方案: {}", e.getMessage()); } sysLogEntity.setParams(params); } //设置IP地址 sysLogEntity.setIp(IpHelper.getIpAddr()); // 设置业务数据ID(String类型) String businessId = extractIdFromResult(result); if (businessId != null) { sysLogEntity.setBizId(businessId); } //用户名 Admin admin = adminService.getAdminFromReq(); if (!Objects.isNull(admin)) { sysLogEntity.setUsername(admin.getUsername()); } sysLogEntity.setTime(time); sysLogEntity.setCreateDate(new Date()); //保存系统日志 sysLogService.save(sysLogEntity); return result; } private String extractIdFromResult(Object result) { try { // 如果result为null,直接返回null if (result == null) { return null; } Class<?> clazz = result.getClass(); // 第一层:尝试从result的data字段/getData方法中获取实际数据 Object dataObject = null; try { // 尝试调用getData()方法 java.lang.reflect.Method getDataMethod = clazz.getMethod("getData"); dataObject = getDataMethod.invoke(result); } catch (Exception ignored) { // 如果没有getData方法,尝试直接访问data字段 try { java.lang.reflect.Field dataField = clazz.getDeclaredField("data"); dataField.setAccessible(true); dataObject = dataField.get(result); } catch (Exception ignored2) { // 如果既没有getData方法也没有data字段,直接使用result本身 dataObject = result; } } // 如果data为null,返回null if (dataObject == null) { return null; } // 第二层:从data对象中提取ID Class<?> dataClass = dataObject.getClass(); // 优先使用getter方法 try { java.lang.reflect.Method getIdMethod = dataClass.getMethod("getId"); Object idValue = getIdMethod.invoke(dataObject); if (idValue != null) { return idValue.toString(); } } catch (Exception ignored) { // 如果没有getId方法,尝试直接访问字段 } // 备用方案:直接访问字段 java.lang.reflect.Field[] fields = dataClass.getDeclaredFields(); for (java.lang.reflect.Field field : fields) { if (field.getName().equals("id") || field.getName().endsWith("Id")) { field.setAccessible(true); Object value = field.get(dataObject); if (value != null) { return value.toString(); } } } } catch (Exception e) { log.warn("从返回结果中提取ID失败: {}", e.getMessage()); } return null; } // 过滤可序列化对象的方法 private List<Object> filterSerializableObjects(List<Object> objects) { if (objects == null) return new ArrayList<>(); return objects.stream() .filter(this::isSerializable) .collect(Collectors.toList()); } private Object[] filterSerializableObjects(Object[] objects) { if (objects == null) return new Object[0]; return Arrays.stream(objects) .filter(this::isSerializable) .toArray(); } // 检查对象是否可序列化 private boolean isSerializable(Object obj) { if (obj == null) return true; try { // 尝试快速判断是否为常见不可序列化类型 Class<?> clazz = obj.getClass(); String className = clazz.getName(); // 过滤常见的不可序列化类 if (className.contains("java.util.Collections$") || className.contains("HttpServletRequest") || className.contains("HttpServletResponse") || className.contains("Session") || className.contains("ServletContext") || className.startsWith("org.apache.catalina") || className.startsWith("org.springframework.security")) { return false; } // 简单测试序列化 objectMapper.writeValueAsString(obj); return true; } catch (Exception e) { return false; } } // 获取安全的参数表示 private String getSafeParamRepresentation(Object[] args) { if (args == null || args.length == 0) { return "[]"; } List<String> paramInfos = new ArrayList<>(); for (int i = 0; i < args.length; i++) { if (args[i] != null) { String className = args[i].getClass().getSimpleName(); paramInfos.add("arg" + i + ": " + className); } else { paramInfos.add("arg" + i + ": null"); } } try { return objectMapper.writeValueAsString(paramInfos); } catch (Exception e) { return paramInfos.toString(); } } } package com.jxyunge.sign.annotation; import java.lang.annotation.*; /** * 自定义日志注解 * @author linyan */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SysLog { String value() default ""; } 我想要实现一个操作日志的功能,可查看到相关模块的新增,删除,编辑记录,点击详情可查看明细,看到相关的字段的改变,还能实现搜索功能,请你根据我提供给你的代码和需求,生成实现的代码,给出具体的步骤

package com.jxyunge.sign.aspect; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.jxyunge.sign.enums.sys.OperationTypeEnum; import com.jxyunge.sign.mybatis.entity.sys.Admin; import com.jxyunge.sign.mybatis.entity.sys.SysLog; import com.jxyunge.sign.mybatis.service.sys.AdminService; import com.jxyunge.sign.mybatis.service.sys.EntityService; import com.jxyunge.sign.mybatis.service.sys.SysLogService; import com.jxyunge.sign.request.input.common.PageParam; import com.jxyunge.sign.tools.IpHelper; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; /** * @author lgh */ @Slf4j @Aspect @AllArgsConstructor public class SysLogAspect { private final AdminService adminService; private final SysLogService sysLogService; private final EntityService entityService; private ObjectMapper objectMapper; /*@Around("@annotation(sysLog)") public Object around(ProceedingJoinPoint joinPoint, com.jxyunge.sign.annotation.SysLog sysLog) throws Throwable { long beginTime = SystemClock.now(); //执行方法 Object result = joinPoint.proceed(); //执行时长(毫秒) long time = SystemClock.now() - beginTime; SysLog sysLogEntity = new SysLog(); if(sysLog != null){ //注解上的描述 sysLogEntity.setOperation(sysLog.value()); } //请求的方法名 String className = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); sysLogEntity.setMethod(className + "." + methodName + "()"); sysLogEntity.setParams(null); //请求的参数 Object[] args = joinPoint.getArgs(); if(args != null && args.length > 0) { String params = null; try { if(args.length > 0 && args[0] != null && Objects.equals(args[0].getClass().getName(), PageParam.class.getName())){ PageParam<Object> pageParam = (PageParam<Object>) args[0]; List<Object> records = pageParam.getRecords(); // 过滤掉不可序列化的对象 List<Object> filteredRecords = filterSerializableObjects(records); params = objectMapper.writeValueAsString(filteredRecords); } else { // 过滤参数中的不可序列化对象 Object[] filteredArgs = filterSerializableObjects(args); params = objectMapper.writeValueAsString(filteredArgs); } } catch (Exception e) { // 如果序列化失败,记录参数类型信息或简单处理 params = getSafeParamRepresentation(args); log.warn("参数序列化失败,使用备用方案: {}", e.getMessage()); } sysLogEntity.setParams(params); } //设置IP地址 sysLogEntity.setIp(IpHelper.getIpAddr()); // 设置业务数据ID(String类型) String businessId = extractIdFromResult(result); if (businessId != null) { sysLogEntity.setBizId(businessId); } //用户名 Admin admin = adminService.getAdminFromReq(); if (!Objects.isNull(admin)) { sysLogEntity.setUsername(admin.getUsername()); } sysLogEntity.setTime(time); sysLogEntity.setCreateDate(new Date()); //保存系统日志 sysLogService.save(sysLogEntity); return result; }*/ @Around("@annotation(sysLogAnnotation)") public Object logAround(ProceedingJoinPoint joinPoint, com.jxyunge.sign.annotation.SysLog sysLogAnnotation) throws Throwable { long beginTime = System.currentTimeMillis(); OperationTypeEnum operationType = sysLogAnnotation.type(); // 获取旧数据 Object oldEntity = getOldEntity(joinPoint, sysLogAnnotation); // 执行目标方法 Object result = joinPoint.proceed(); long time = System.currentTimeMillis() - beginTime; // 获取新数据 Object newEntity = extractEntityFromResponse(result); // 构建日志记录 SysLog sysLog = buildSysLog(joinPoint, sysLogAnnotation, time, oldEntity, newEntity, operationType); // 异步保存日志 CompletableFuture.runAsync(() -> sysLogService.save(sysLog)); return result; } private Object getOldEntity(ProceedingJoinPoint joinPoint, com.jxyunge.sign.annotation.SysLog annotation) { if (annotation.type() == OperationTypeEnum.CREATE) return null; try { Object idValue = getArgValue(joinPoint, annotation.idParam()); if (idValue == null) return null; return entityService.getById(idValue.toString()); } catch (Exception e) { log.error("获取旧实体失败", e); return null; } } private Object getArgValue(ProceedingJoinPoint joinPoint, String paramName) { if (StringUtils.isBlank(paramName)) return null; MethodSignature signature = (MethodSignature) joinPoint.getSignature(); String[] paramNames = signature.getParameterNames(); Object[] args = joinPoint.getArgs(); for (int i = 0; i < paramNames.length; i++) { if (paramName.equals(paramNames[i])) { return args[i]; } } return null; } private Object extractEntityFromResponse(Object result) { if (result == null) return null; try { // 尝试从通用响应结构获取数据 Class<?> clazz = result.getClass(); Object data = null; // 检查getData方法 try { Method getData = clazz.getMethod("getData"); data = getData.invoke(result); } catch (NoSuchMethodException ignored) {} // 检查data字段 if (data == null) { try { Field dataField = clazz.getDeclaredField("data"); dataField.setAccessible(true); data = dataField.get(result); } catch (Exception ignored) {} } return data != null ? data : result; } catch (Exception e) { log.error("提取响应实体失败", e); return result; } } private SysLog buildSysLog(ProceedingJoinPoint joinPoint, com.jxyunge.sign.annotation.SysLog annotation, long time, Object oldEntity, Object newEntity, OperationTypeEnum operationType) { SysLog sysLog = new SysLog(); // 1. 设置基础字段(原有逻辑) // 注解描述 sysLog.setOperation(annotation.value()); // 请求方法名 String className = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); sysLog.setMethod(className + "." + methodName + "()"); // 请求参数 Object[] args = joinPoint.getArgs(); if(args != null && args.length > 0) { String params = null; try { if(args.length > 0 && args[0] != null && Objects.equals(args[0].getClass().getName(), PageParam.class.getName())){ PageParam<Object> pageParam = (PageParam<Object>) args[0]; List<Object> records = pageParam.getRecords(); List<Object> filteredRecords = filterSerializableObjects(records); params = objectMapper.writeValueAsString(filteredRecords); } else { Object[] filteredArgs = filterSerializableObjects(args); params = objectMapper.writeValueAsString(filteredArgs); } } catch (Exception e) { params = getSafeParamRepresentation(args); log.warn("参数序列化失败: {}", e.getMessage()); } sysLog.setParams(params); } // IP地址 sysLog.setIp(IpHelper.getIpAddr()); // 业务ID(从新实体中提取) String businessId = extractIdFromResult(newEntity != null ? newEntity : oldEntity); if (businessId != null) { sysLog.setBizId(businessId); } // 用户名 Admin admin = adminService.getAdminFromReq(); if (!Objects.isNull(admin)) { sysLog.setUsername(admin.getUsername()); } // 执行时间和创建时间 sysLog.setTime(time); sysLog.setCreateDate(new Date()); // 2. 新增操作日志字段 sysLog.setOperationType(OperationTypeEnum.valueOf(operationType.name())); sysLog.setBeforeData(toJsonSafe(oldEntity)); sysLog.setAfterData(toJsonSafe(newEntity)); sysLog.setChangeDetails(compareChanges(oldEntity, newEntity)); return sysLog; } private String extractId(Object entity) { try { Field idField = entity.getClass().getDeclaredField("id"); idField.setAccessible(true); Object idValue = idField.get(entity); return idValue != null ? idValue.toString() : null; } catch (Exception e) { return null; } } private String toJsonSafe(Object obj) { if (obj == null) return null; try { return objectMapper.writeValueAsString(obj); } catch (JsonProcessingException e) { return "序列化失败: " + e.getMessage(); } } private String compareChanges(Object oldObj, Object newObj) { if (oldObj == null && newObj == null) return "[]"; try { JsonNode oldTree = objectMapper.valueToTree(oldObj); JsonNode newTree = objectMapper.valueToTree(newObj); List<Map<String, Object>> changes = new ArrayList<>(); Iterator<String> fieldNames = oldTree.fieldNames(); while (fieldNames.hasNext()) { String field = fieldNames.next(); JsonNode oldVal = oldTree.get(field); JsonNode newVal = newTree.get(field); if (!Objects.equals(oldVal, newVal)) { changes.add(Map.of( "field", field, "oldValue", oldVal.asText(), "newValue", newVal != null ? newVal.asText() : null )); } } return objectMapper.writeValueAsString(changes); } catch (Exception e) { return String.format("[{\"error\":\"%s\"}]", e.getMessage()); } } private String extractIdFromResult(Object result) { try { // 如果result为null,直接返回null if (result == null) { return null; } Class<?> clazz = result.getClass(); // 第一层:尝试从result的data字段/getData方法中获取实际数据 Object dataObject = null; try { // 尝试调用getData()方法 java.lang.reflect.Method getDataMethod = clazz.getMethod("getData"); dataObject = getDataMethod.invoke(result); } catch (Exception ignored) { // 如果没有getData方法,尝试直接访问data字段 try { java.lang.reflect.Field dataField = clazz.getDeclaredField("data"); dataField.setAccessible(true); dataObject = dataField.get(result); } catch (Exception ignored2) { // 如果既没有getData方法也没有data字段,直接使用result本身 dataObject = result; } } // 如果data为null,返回null if (dataObject == null) { return null; } // 第二层:从data对象中提取ID Class<?> dataClass = dataObject.getClass(); // 优先使用getter方法 try { java.lang.reflect.Method getIdMethod = dataClass.getMethod("getId"); Object idValue = getIdMethod.invoke(dataObject); if (idValue != null) { return idValue.toString(); } } catch (Exception ignored) { // 如果没有getId方法,尝试直接访问字段 } // 备用方案:直接访问字段 java.lang.reflect.Field[] fields = dataClass.getDeclaredFields(); for (java.lang.reflect.Field field : fields) { if (field.getName().equals("id") || field.getName().endsWith("Id")) { field.setAccessible(true); Object value = field.get(dataObject); if (value != null) { return value.toString(); } } } } catch (Exception e) { log.warn("从返回结果中提取ID失败: {}", e.getMessage()); } return null; } // 过滤可序列化对象的方法 private List<Object> filterSerializableObjects(List<Object> objects) { if (objects == null) return new ArrayList<>(); return objects.stream() .filter(this::isSerializable) .collect(Collectors.toList()); } private Object[] filterSerializableObjects(Object[] objects) { if (objects == null) return new Object[0]; return Arrays.stream(objects) .filter(this::isSerializable) .toArray(); } // 检查对象是否可序列化 private boolean isSerializable(Object obj) { if (obj == null) return true; try { // 尝试快速判断是否为常见不可序列化类型 Class<?> clazz = obj.getClass(); String className = clazz.getName(); // 过滤常见的不可序列化类 if (className.contains("java.util.Collections$") || className.contains("HttpServletRequest") || className.contains("HttpServletResponse") || className.contains("Session") || className.contains("ServletContext") || className.startsWith("org.apache.catalina") || className.startsWith("org.springframework.security")) { return false; } // 简单测试序列化 objectMapper.writeValueAsString(obj); return true; } catch (Exception e) { return false; } } // 获取安全的参数表示 private String getSafeParamRepresentation(Object[] args) { if (args == null || args.length == 0) { return "[]"; } List<String> paramInfos = new ArrayList<>(); for (int i = 0; i < args.length; i++) { if (args[i] != null) { String className = args[i].getClass().getSimpleName(); paramInfos.add("arg" + i + ": " + className); } else { paramInfos.add("arg" + i + ": null"); } } try { return objectMapper.writeValueAsString(paramInfos); } catch (Exception e) { return paramInfos.toString(); } } } package com.jxyunge.sign.annotation; import com.jxyunge.sign.enums.sys.OperationTypeEnum; import java.lang.annotation.*; /** * 自定义日志注解 * @author linyan */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SysLog { String value() default ""; OperationTypeEnum type() default OperationTypeEnum.UPDATE; // 操作类型 // 新增ID参数名(用于获取旧数据) String idParam() default ""; } package com.jxyunge.sign.enums.sys; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; import com.jxyunge.sign.enums.BaseEnum; /** * @author xiaoda * @create 2025-07-29 17:40 */ public enum OperationTypeEnum implements BaseEnum<Integer> { CREATE(1, "新增"), UPDATE(2, "更新"), DELETE(3, "删除"),; private final Integer code; private final String msg; OperationTypeEnum(Integer code, String msg) { this.code = code; this.msg = msg; } @Override public Integer getValue() { return this.code; } @Override @JsonValue public String getDesc() { return this.msg; } @JsonCreator(mode = JsonCreator.Mode.DELEGATING) public static AdminGroupTypeEnum create(Object code) { return AdminGroupTypeEnum.valueOf((String) code); } } package com.jxyunge.sign.mybatis.entity.sys; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.jxyunge.sign.annotation.SearchParam; import com.jxyunge.sign.enums.sys.OperationTypeEnum; import com.jxyunge.sign.enums.sys.SearchConditionEnum; import com.jxyunge.sign.mybatis.entity.BaseEntity; import lombok.Data; import java.io.Serializable; import java.util.Date; import java.util.List; /** * 系统日志 * @author zanmall */ @Data @TableName("tb_sys_log") public class SysLog implements Serializable { private static final long serialVersionUID = 1L; /** * 主键 */ @SearchParam(name = {"主键"},condition = {SearchConditionEnum.eq}) @TableId(type = IdType.ASSIGN_UUID) private String id; /** * 用户名 */ private String username; /** * 用户操作 */ private String operation; /** * 请求方法 */ private String method; /** * 请求参数 */ private String params; /** * 执行时长(毫秒) */ private Long time; /** * IP地址 */ private String ip; /** * 创建时间 */ private Date createDate; /** * 业务id */ private String bizId; // 新增字段 private OperationTypeEnum operationType; // 操作类型 private String beforeData; // 变更前数据(JSON) private String afterData; // 变更后数据(JSON) private String changeDetails; // 变更详情(JSON) } @SysLog(value = "修改订单", type = OperationTypeEnum.UPDATE,idParam = "entity.id") @PostMapping(value = "/updateOrder") @Operation(summary = "修改订单") public ApiOutput<OrderAndDetailsOutput> updateOrder(HttpServletRequest request, @RequestBody OrderAddInput entity) { Admin admin = getAdminFromReq(request); if (BeanUtil.isEmpty(entity)){ return ApiOutput.err(ErrorCodeEnum.PARAMETER_IS_EMPTY); } OrderAndDetailsOutput save = sysOrderService.updateOrder(entity,admin); return ApiOutput.ok(save); } 这些是我切面类实现一个操作日志的流程代码,现在遇到的问题是我直接在前端修改订单,数据库没有变化,可能我的代码实现有问题,请你帮我分析我的代码能否实现操作日志的功能,可查看订单新增,删除,编辑记录,点击详情可查看明细,如果不行,请帮我修改我的代码,标记哪里要改动,成功实现操作日志功能

package com.zzyl.nursing.controller; import java.util.List; import javax.servlet.http.HttpServletResponse; import com.zzyl.common.core.domain.R; import com.zzyl.nursing.vo.NursingProjectVo; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.zzyl.common.annotation.Log; import com.zzyl.common.core.controller.BaseController; import com.zzyl.common.core.domain.AjaxResult; import com.zzyl.common.enums.BusinessType; import com.zzyl.nursing.domain.NursingProject; import com.zzyl.nursing.service.INursingProjectService; import com.zzyl.common.utils.poi.ExcelUtil; import com.zzyl.common.core.page.TableDataInfo; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import io.swagger.annotations.ApiImplicitParam; /** * 护理项目Controller * * @author ruoyi * @date 2025-08-07 */ @RestController @RequestMapping("/nursing/project") @Api(tags = "护理项目管理", description = "护理项目相关的操作接口") public class NursingProjectController extends BaseController { @Autowired private INursingProjectService nursingProjectService; /** * 获取所有护理计划 * @return */ @GetMapping("/all") @ApiOperation("获取所有护理计划") public AjaxResult all() { List<NursingProjectVo> list = nursingProjectService.listAll(); return success(list); } /** * 查询护理项目列表 */ @PreAuthorize("@ss.hasPermi('nursing:project:list')") @GetMapping("/list") @ApiOperation(value = "查询护理项目列表", notes = "根据条件查询护理项目列表信息") public TableDataInfo> list( @ApiParam(name = "nursingProject", value = "护理项目查询条件", required = false) NursingProject nursingProject) { startPage(); List<NursingProject> list = nursingProjectService.selectNursingProjectList(nursingProject); return getDataTable(list); } /** * 导出护理项目列表 */ @PreAuthorize("@ss.hasPermi('nursing:project:export')") @Log(title = "护理项目", businessType = BusinessType.EXPORT) @PostMapping("/export") @ApiOperation(value = "导出护理项目列表", notes = "将护理项目数据导出为Excel文件") public void export( @ApiParam(name = "response", value = "HttpServletResponse对象", required = true) HttpServletResponse response, @ApiParam(name = "nursingProject", value = "护理项目查询条件", required = false) NursingProject nursingProject) { List<NursingProject> list = nursingProjectService.selectNursingProjectList(nursingProject); ExcelUtil<NursingProject> util = new ExcelUtil<NursingProject>(NursingProject.class); util.exportExcel(response, list, "护理项目数据"); } /** * 获取护理项目详细信息 */ @PreAuthorize("@ss.hasPermi('nursing:project:query')") @GetMapping(value = "/{id}") @ApiOperation(value = "获取护理项目详情", notes = "根据ID获取护理项目的详细信息") @ApiImplicitParam(name = "id", value = "护理项目ID", required = true, dataType = "Long", paramType = "path") public R<NursingProject> getInfo( @ApiParam(name = "id", value = "护理项目ID", required = true) @PathVariable("id") Long id) { return R.ok(nursingProjectService.selectNursingProjectById(id)); } /** * 新增护理项目 */ @PreAuthorize("@ss.hasPermi('nursing:project:add')") @Log(title = "护理项目", businessType = BusinessType.INSERT) @PostMapping @ApiOperation(value = "新增护理项目", notes = "添加一个新的护理项目信息") public AjaxResult add( @ApiParam(name = "nursingProject", value = "护理项目信息", required = true) @RequestBody NursingProject nursingProject) { return toAjax(nursingProjectService.insertNursingProject(nursingProject)); } /** * 修改护理项目 */ @PreAuthorize("@ss.hasPermi('nursing:project:edit')") @Log(title = "护理项目", businessType = BusinessType.UPDATE) @PutMapping @ApiOperation(value = "修改护理项目", notes = "更新已有的护理项目信息") public AjaxResult edit( @ApiParam(name = "nursingProject", value = "护理项目信息", required = true) @RequestBody NursingProject nursingProject) { return toAjax(nursingProjectService.updateNursingProject(nursingProject)); } /** * 删除护理项目 */ @PreAuthorize("@ss.hasPermi('nursing:project:remove')") @Log(title = "护理项目", businessType = BusinessType.DELETE) @DeleteMapping("/{ids}") @ApiOperation(value = "删除护理项目", notes = "根据ID批量删除护理项目信息") @ApiImplicitParam(name = "ids", value = "护理项目ID数组", required = true, dataType = "Long[]", paramType = "path") public AjaxResult remove( @ApiParam(name = "ids", value = "护理项目ID数组", required = true) @PathVariable Long[] ids) { return toAjax(nursingProjectService.deleteNursingProjectByIds(ids)); } } package com.zzyl.nursing.domain; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import com.zzyl.common.annotation.Excel; import com.zzyl.common.core.domain.BaseEntity; /** * 护理项目对象 nursing_project * * @author ruoyi * @date 2025-08-07 */ @ApiModel(description = "护理项目实体") public class NursingProject extends BaseEntity { private static final long serialVersionUID = 1L; /** 编号 */ @ApiModelProperty(value = "编号") private Long id; /** 名称 */ @Excel(name = "名称") @ApiModelProperty(value = "名称") private String name; /** 排序号 */ @Excel(name = "排序号") @ApiModelProperty(value = "排序号") private Integer orderNo; /** 单位 */ @Excel(name = "单位") @ApiModelProperty(value = "单位") private String unit; /** 价格 */ @Excel(name = "价格") @ApiModelProperty(value = "价格") private Double price; /** 图片 */ @Excel(name = "图片") @ApiModelProperty(value = "图片URL") private String image; /** 护理要求 */ @Excel(name = "护理要求") @ApiModelProperty(value = "护理要求") private String nursingRequirement; /** 状态(0:禁用,1:启用) */ @Excel(name = "状态", readConverterExp = "0=:禁用,1:启用") @ApiModelProperty(value = "状态(0:禁用,1:启用)") private Integer status; public void setId(Long id) { this.id = id; } public Long getId() { return id; } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setOrderNo(Integer orderNo) { this.orderNo = orderNo; } public Integer getOrderNo() { return orderNo; } public void setUnit(String unit) { this.unit = unit; } public String getUnit() { return unit; } public void setPrice(Double price) { this.price = price; } public Double getPrice() { return price; } public void setImage(String image) { this.image = image; } public String getImage() { return image; } public void setNursingRequirement(String nursingRequirement) { this.nursingRequirement = nursingRequirement; } public String getNursingRequirement() { return nursingRequirement; } public void setStatus(Integer status) { this.status = status; } public Integer getStatus() { return status; } @Override public String toString() { return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) .append("id", getId()) .append("name", getName()) .append("orderNo", getOrderNo()) .append("unit", getUnit()) .append("price", getPrice()) .append("image", getImage()) .append("nursingRequirement", getNursingRequirement()) .append("status", getStatus()) .append("createBy", getCreateBy()) .append("updateBy", getUpdateBy()) .append("remark", getRemark()) .append("createTime", getCreateTime()) .append("updateTime", getUpdateTime()) .toString(); } } package com.zzyl.plan.controller; import java.util.List; import javax.servlet.http.HttpServletResponse; import com.zzyl.nursing.dto.NursingPlanDto; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.zzyl.common.annotation.Log; import com.zzyl.common.core.controller.BaseController; import com.zzyl.common.core.domain.AjaxResult; import com.zzyl.common.enums.BusinessType; import com.zzyl.plan.domain.NursingPlan; import com.zzyl.plan.service.INursingPlanService; import com.zzyl.common.utils.poi.ExcelUtil; import com.zzyl.common.core.page.TableDataInfo; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; /** * 护理计划Controller * * @author ruoyi * @date 2025-08-07 */ @Api(tags = "护理计划管理") @RestController @RequestMapping("/nursing/plan") public class NursingPlanController extends BaseController { @Autowired private INursingPlanService nursingPlanService; /** * 查询护理计划列表 */ @ApiOperation("查询护理计划列表") @PreAuthorize("@ss.hasPermi('nursing:plan:list')") @GetMapping("/list") public TableDataInfo list( @ApiParam("护理计划查询参数") NursingPlan nursingPlan) { startPage(); List<NursingPlan> list = nursingPlanService.selectNursingPlanList(nursingPlan); return getDataTable(list); } /** * 导出护理计划列表 */ @ApiOperation("导出护理计划列表") @PreAuthorize("@ss.hasPermi('nursing:plan:export')") @Log(title = "护理计划", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export( @ApiParam("HTTP响应对象") HttpServletResponse response, @ApiParam("护理计划查询参数") NursingPlan nursingPlan) { List<NursingPlan> list = nursingPlanService.selectNursingPlanList(nursingPlan); ExcelUtil<NursingPlan> util = new ExcelUtil<NursingPlan>(NursingPlan.class); util.exportExcel(response, list, "护理计划数据"); } /** * 获取护理计划详细信息 */ @ApiOperation("获取护理计划详细信息") @PreAuthorize("@ss.hasPermi('nursing:plan:query')") @GetMapping(value = "/{id}") public AjaxResult getInfo( @ApiParam("护理计划ID") @PathVariable("id") Long id) { return success(nursingPlanService.selectNursingPlanById(id)); } /** * 新增护理计划 */ @ApiOperation("新增护理计划") @PreAuthorize("@ss.hasPermi('nursing:plan:add')") @Log(title = "护理计划", businessType = BusinessType.INSERT) @PostMapping public AjaxResult add( @ApiParam("护理计划对象") @RequestBody NursingPlanDto dto) { return toAjax(nursingPlanService.insertNursingPlan(dto)); } /** * 修改护理计划 */ @ApiOperation("修改护理计划") @PreAuthorize("@ss.hasPermi('nursing:plan:edit')") @Log(title = "护理计划", businessType = BusinessType.UPDATE) @PutMapping public AjaxResult edit( @ApiParam("护理计划对象") @RequestBody NursingPlan nursingPlan) { return toAjax(nursingPlanService.updateNursingPlan(nursingPlan)); } /** * 删除护理计划 */ @ApiOperation("删除护理计划") @PreAuthorize("@ss.hasPermi('nursing:plan:remove')") @Log(title = "护理计划", businessType = BusinessType.DELETE) @DeleteMapping("/{ids}") public AjaxResult remove( @ApiParam("护理计划ID数组") @PathVariable Long[] ids) { return toAjax(nursingPlanService.deleteNursingPlanByIds(ids)); } } <template> <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="100px"> <el-form-item label="护理项目名称" prop="name"> <el-input v-model="queryParams.name" placeholder="请输入名称" clearable @keyup.enter="handleQuery" /> </el-form-item> <el-form-item label="状态" prop="status"> <el-select v-model="queryParams.status" placeholder="请选择" clearable> <el-option v-for="item in nursing_project_status" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-form-item> <el-form-item> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> <el-button icon="Refresh" @click="resetQuery">重置</el-button> </el-form-item> </el-form> <el-row :gutter="10" class="mb8"> <el-col :span="1.5"> <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['nursing:project:add']" >新增</el-button> </el-col> <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> </el-row> <el-table v-loading="loading" :data="projectList" @selection-change="handleSelectionChange"> <el-table-column type="selection" width="55" align="center" /> <el-table-column label="序号" align="center" type="index" width="70" /> <el-table-column label="护理图片" align="center" prop="image" width="100"> <template #default="scope"> <image-preview :src="scope.row.image" :width="50" :height="50"/> </template> </el-table-column> <el-table-column label="护理项目名称" align="center" prop="name" /> <el-table-column label="价格(元)" align="center" prop="price" /> <el-table-column label="单位" align="center" prop="unit" /> <el-table-column label="排序" align="center" prop="orderNo" /> <el-table-column label="创建人" align="center" prop="createBy" /> <el-table-column label="创建时间" align="center" prop="createTime" width="180" /> <el-table-column label="状态" align="center" prop="status"> <template #default="scope"> <el-tag :type="scope.row.status === 1 ? 'success' : 'info'">{{ scope.row.status === 1 ? '启用' : '禁用' }}</el-tag> </template> </el-table-column> <el-table-column label="操作" width="200" fixed="right" align="center" class-name="small-padding fixed-width"> <template #default="scope"> <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['nursing:project:edit']">修改</el-button> <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['nursing:project:remove']">删除</el-button> <el-button link type="primary" :icon="scope.row.status == 0 ? 'Lock' : 'Unlock'" @click="handleEnable(scope.row)" >{{ scope.row.status === 1 ? '禁用' : '启用' }}</el-button> </template> </el-table-column> </el-table> 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <el-dialog :title="title" v-model="open" width="500px" append-to-body> <el-form ref="projectRef" :model="form" :rules="rules" label-width="120px"> <el-form-item label="护理项目名称" prop="name"> <el-input v-model="form.name" placeholder="请输入名称" :maxlength="10" show-word-limit /> </el-form-item> <el-form-item label="价格" prop="price"> <el-input v-model="form.price" placeholder="请输入价格" @input="validatePrice" /> </el-form-item> <el-form-item label="单位" prop="unit"> <el-input v-model="form.unit" placeholder="请输入单位" :maxlength="5" show-word-limit /> </el-form-item> <el-form-item label="排序号" prop="orderNo"> <el-input-number v-model="form.orderNo" placeholder="请输入" :min="1" :max="999999" controls-position="right" /> </el-form-item> <el-form-item label="状态" prop="status"> <el-radio-group v-model="form.status"> <el-radio v-for="dict in nursing_project_status" :key="dict.value" :label="dict.value" > {{ dict.label }} </el-radio> </el-radio-group> </el-form-item> <el-form-item label="图片" prop="image"> <image-upload v-model="form.image"/> </el-form-item> <el-form-item label="护理要求" prop="nursingRequirement"> <el-input v-model="form.nursingRequirement" placeholder="请输入护理要求" type="textarea" :maxlength="50" show-word-limit :autosize="{ minRows: 2, maxRows: 4 }" /> </el-form-item> </el-form> <template #footer> <el-button type="primary" @click="submitForm">确 定</el-button> <el-button @click="cancel">取 消</el-button> </template> </el-dialog> </template> <script setup name="Project"> import { listProject, getProject, delProject, addProject, updateProject } from "@/api/nursing/project"; import { ref } from "vue"; const { proxy } = getCurrentInstance(); const projectList = ref([]); const open = ref(false); const loading = ref(true); const showSearch = ref(true); const ids = ref([]); const single = ref(true); const multiple = ref(true); const total = ref(0); const title = ref(""); //引用数据字典 const dictResult = proxy.useDict("nursing_project_status"); const nursing_project_status = dictResult?.nursing_project_status || []; /* const options = ref([ { value: 1, label: "启用" }, { value: 0, label: "禁用" } ]); */ //禁用或启用 const handleEnable = (row) => { // 获取状态 const status = row.status === 1 ? 0 : 1; //弹窗提示,并发送请求 proxy.$modal.confirm('确认要' + (status === 1 ? "启用" : "禁用") + row.name + '护理项目吗?').then(function () { return updateProject({ id: row.id, status: status }); }).then(() => { proxy.$modal.msgSuccess(status === 1 ? "启用成功" : "禁用成功"); getList(); }).catch(function () { row.status = row.status === 1 ? 0 : 1; }) } /** * 价格输入验证 * @description 限制价格输入格式,只允许数字和小数点,最多两位小数 */ function validatePrice(value) { // 保留两位小数 let price = value.toString(); if (price.includes('.')) { const parts = price.split('.'); if (parts[1].length > 2) { form.value.price = parts[0] + '.' + parts[1].substring(0, 2); } } } const data = reactive({ form: {}, queryParams: { pageNum: 1, pageSize: 10, name: null, status: null, }, rules: { name: [ { required: true, message: "护理项目名称不能为空", trigger: "blur" }, { min: 1, max: 10, message: "护理项目名称长度不能超过10个字符", trigger: "blur" } ], price: [ { required: true, message: "价格不能为空", trigger: "blur" }, { pattern: /^([0-9]\d*|0)(\.\d{1,2})?$/, message: "请输入正确的金额格式,最多两位小数", trigger: "blur" } ], unit: [ { required: true, message: "单位不能为空", trigger: "blur" }, { min: 1, max: 5, message: "单位长度不能超过5个字符", trigger: "blur" } ], orderNo: [ { required: true, message: "排序号不能为空", trigger: "blur" }, { type: "number", message: "排序号必须为数字值", trigger: "blur" } ], status: [ { required: true, message: "状态不能为空", trigger: "change" } ], image: [ { required: true, message: "图片不能为空", trigger: "change" } ], nursingRequirement: [ { required: true, message: "护理要求不能为空", trigger: "blur" }, { min: 1, max: 50, message: "护理要求长度不能超过50个字符", trigger: "blur" } ] } }); const { queryParams, form, rules } = toRefs(data); /** * 查询护理项目列表 * @description 获取护理项目数据并更新到页面中 */ function getList() { loading.value = true; listProject(queryParams.value).then(response => { projectList.value = response.rows; total.value = response.total; loading.value = false; }); } /** * 取消按钮操作 * @description 关闭弹窗并重置表单 */ function cancel() { open.value = false; reset(); } /** * 表单重置 * @description 将表单数据恢复为初始状态,并清空校验结果 */ function reset() { form.value = { id: null, name: null, orderNo: 1, unit: null, price: null, image: null, nursingRequirement: null, status: 1, // 默认启用状态 createBy: null, updateBy: null, remark: null, createTime: null, updateTime: null }; proxy.resetForm("projectRef"); } /** * 搜索按钮操作 * @description 触发查询操作,将页码重置为第一页 */ function handleQuery() { queryParams.value.pageNum = 1; getList(); } /** * 重置按钮操作 * @description 重置搜索条件并重新加载数据 */ function resetQuery() { proxy.resetForm("queryRef"); handleQuery(); } /** * 多选框选中数据处理 * @param {Array} selection - 当前选中的行数据数组 * @description 更新选中项ID列表,并控制修改/删除按钮的可用性 */ function handleSelectionChange(selection) { ids.value = selection.map(item => item.id); single.value = selection.length != 1; multiple.value = !selection.length; } /** * 新增按钮操作 * @description 打开新增护理项目的弹窗 */ function handleAdd() { reset(); open.value = true; title.value = "添加护理项目"; } /** * 修改按钮操作 * @param {Object} row - 要修改的数据行对象(可选) * @description 根据传入的行数据或选中ID获取详细信息并打开编辑弹窗 */ function handleUpdate(row) { reset(); const _id = row.id || ids.value getProject(_id).then(response => { form.value = response.data; open.value = true; title.value = "修改护理项目"; }); } /** * 提交按钮操作 * @description 验证表单并根据是否有ID决定是新增还是修改操作 */ function submitForm() { proxy.$refs["projectRef"].validate(valid => { if (valid) { if (form.value.id != null) { updateProject(form.value).then(response => { proxy.$modal.msgSuccess("修改成功"); open.value = false; getList(); }); } else { addProject(form.value).then(response => { proxy.$modal.msgSuccess("新增成功"); open.value = false; getList(); }); } } }); } /** * 删除按钮操作 * @param {Object} row - 要删除的数据行对象(可选) * @description 删除指定ID的数据项,并提示用户确认操作 */ function handleDelete(row) { const _ids = row.id || ids.value; proxy.$modal.confirm('是否确认删除护理项目编号为"' + _ids + '"的数据项?').then(function() { return delProject(_ids); }).then(() => { getList(); proxy.$modal.msgSuccess("删除成功"); }).catch(() => {}); } /** * 导出按钮操作 * @description 导出当前查询条件下的护理项目数据为Excel文件 */ function handleExport() { proxy.download('nursing/project/export', { ...queryParams.value }, project_${new Date().getTime()}.xlsx) } // 初始化加载数据 getList(); </script> 如果该护理项目,已经被护理计划所引用,则不能进行删除、编辑、禁用

@Slf4j @RequestMapping("/emps") @RestController public class EmpController { @Autowired private EmpService empService; /** * 分页查询 */ @GetMapping public Result page(EmpQueryParam empQueryParam){ log.info("分页查询: {}", empQueryParam); PageResult<Emp> pageResult = empService.page(empQueryParam); return Result.success(pageResult); } /** * 新增员工 */ @PostMapping public Result save(@RequestBody Emp emp) throws Exception { log.info("新增员工: {}", emp); empService.save(emp); return Result.success(); } /** * 删除员工 - 数组 */ /* @DeleteMapping public Result delete(Integer[] ids){ log.info("删除员工: {}", Arrays.toString(ids)); return Result.success(); }*/ /** * 删除员工 - List */ @DeleteMapping public Result delete(@RequestParam List<Integer> ids){ log.info("删除员工: {}", ids); empService.delete(ids); return Result.success(); } /** * 根据ID查询员工信息 */ @GetMapping("/{id}") public Result getInfo(@PathVariable Integer id){ log.info("根据ID查询员工信息: {}", id); Emp emp = empService.getInfo(id); return Result.success(emp); } /** * 修改员工 */ @PutMapping public Result update(@RequestBody Emp emp){ log.info("修改员工: {}", emp); empService.update(emp); return Result.success(); } /** * 查询所有的员工 */ @GetMapping("/list") public Result list(){ log.info("查询所有的员工数据"); List<Emp> empList = empService.list(); return Result.success(empList); } } public class PageResult<T> { private Long total; private List<T> rows; } @Data public class Result { private Integer code; //编码:1成功,0为失败 private String msg; //错误信息 private Object data; //数据 public static Result success() { Result result = new Result(); result.code = 1; result.msg = "success"; return result; } public static Result success(Object object) { Result result = new Result(); result.data = object; result.code = 1; result.msg = "success"; return result; } public static Result error(String msg) { Result result = new Result(); result.msg = msg; result.code = 0; return result; } } 根据我后端的接口和返回的数据类型在整理一份

VM38344 index.vue:472 Markdown解析错误: TypeError: marked__WEBPACK_IMPORTED_MODULE_31__ is not a function at VueComponent.formatMarkdown (VM38344 index.vue:470:16) at _this4.socket.onmessage (VM38344 index.vue:379:56) <template> <el-container> <el-header> <label class="field" v-if="showFieldLabel">时间区间</label> <el-date-picker style="margin: 0 5px 0 0" v-model="queryParam.timearea" type="datetimerange" start-placeholder="开始日期" end-placeholder="结束日期" > </el-date-picker> 区域 <el-tag closable :key="node.id" type="info" @close="clearSelectionById(node.id)" >{{ node.name }}</el-tag > <el-tag closable type="info" @close="clearSelectionById1()" >+{{ selectedNodes.length }}</el-tag > <el-tree v-show="isShow1" style="position: absolute; z-index: 1000; width: 280px" v-model="queryParam.regionIds" show-checkbox node-key="id" ref="tree" :check-strictly="true" :data="options.regions" :options="options.regions" @check-change="handleCheckChange" :props="{ multiple: true, checkStrictly: true, leaf: 'isLeaf', label: 'name', value: 'id', children: 'childs', }" ></el-tree> <el-select v-model="queryParam.isCustody" placeholder="请选择"> <el-option v-for="item in options1" :key="item.value" :label="item.text" :value="item.value" ></el-option> </el-select> <label class="field" v-if="showFieldLabel">{{ $t("pages.common.speakerA") }}</label> <el-input v-model="queryParam.talkerName" :placeholder="${$t('pages.common.speakerA')}" > </el-input> <label class="field" v-if="showFieldLabel">{{ $t("pages.common.speakerB") }}</label> <el-input class="keyword" v-model="queryParam.keyword" :placeholder="${$t('pages.common.speakerB')}/谈话原因" > </el-input> <template v-else> <label class="field" v-if="showFieldLabel">{{ $t("pages.common.speakerB") }}</label> <el-input v-model="queryParam.intervieweeName" :placeholder="${$t('pages.common.speakerB')}" > </el-input> <label class="field" v-if="showFieldLabel">流水号</label> <el-input v-model="queryParam.serialNum" placeholder="流水号" ></el-input> </template> <el-button type="primary" @click="loadData(), loadData1(1)" style="height: 30px" >查询</el-button > </el-header> <el-main> <el-table class="header-center" v-loading="loading" element-loading-text="数据加载中..." :data="tableData" border :height="tableHeight" style="width: 100%" > <el-table-column label="序号" type="index" :index="indexOrder" ></el-table-column> <el-table-column label="流水号" prop="serialNum" v-if="false" ></el-table-column> <el-table-column label="区域" prop="regionName" width="120"> <template #default="scope"> <el-tooltip :content="scope.row.regionName" placement="top"> {{ scope.row.regionName }} </el-tooltip> </template> </el-table-column> <el-table-column :label="${$t('pages.common.locutory')}" prop="locutoryName" ></el-table-column> <el-table-column v-if="user.user.role && user.user.role.isAdmin" :label="${$t('pages.common.speakerA')}" prop="talkerName" ></el-table-column> <el-table-column :label="${$t('pages.common.speakerB')}" prop="intervieweeName" ></el-table-column> <el-table-column label="人员状态" prop="ryzt"></el-table-column> <el-table-column label="谈话原因" prop="thyy" width="150px"> <template slot-scope="scope"> <el-tooltip :content="scope.row.thyy" placement="top"> {{ scope.row.thyy }} </el-tooltip> </template> </el-table-column> <el-table-column label="开始时间" width="120"> <template #default="scope"> <el-tooltip :content="formatDate(scope.row.startTime)" placement="top" > {{ formatDate(scope.row.startTime) }} </el-tooltip> </template> </el-table-column> <el-table-column label="时长" prop="talkTime" :formatter="formatDuration" ></el-table-column> <el-table-column label="结束时间" width="120"> <template #default="scope"> <el-tooltip :content="formatDate(scope.row.endTime)" placement="top" > {{ formatDate(scope.row.endTime) }} </el-tooltip> </template> </el-table-column> <el-table-column label="关键词" prop="keywords" width="80px"> <template slot="header" slot-scope="scope"> <el-tooltip content="关键词报警次数" placement="top"> 关键词 </el-tooltip> </template> </el-table-column> <el-table-column label="行为倾向"> <template #default="scope"> {{ scope.row.behavior ? scope.row.behavior : "正常" }} </template> </el-table-column> <el-table-column label="情绪"> <template #default="scope"> {{ scope.row.emotion ? scope.row.emotion : "正常" }} </template> </el-table-column> <el-table-column label="质检状态" prop="qualityState" width="120px"> <template slot-scope="scope"> <el-tooltip v-if="scope.row.qualityMsg" :content="scope.row.qualityMsg" placement="top" > <el-tag >{{ bahjMap[scope.row.qualityState] }} </el-tag> </el-tooltip> <el-tag v-else >{{ bahjMap[scope.row.qualityState] }}</el-tag> </template> </el-table-column> <el-table-column label="操作" prop="id" width="350px"> <template slot-scope="scope"> <el-button type="primary" @click="trend(scope.row)" >历史趋势</el-button > <el-button :disabled="scope.row.qualityState == 2 ? true : false" @click="review(scope.row)" :type=" scope.row.fileExist || scope.row.isOnline ? 'primary' : '' " >复查</el-button > <el-button type="primary" @click="exportText(scope.row)" >导出文本</el-button > <el-button type="primary" @click="exportFile(scope.row)" v-if="scope.row.fileExist" >导出{{ scope.row.isVideo ? "视频" : "录音" }}</el-button > </template> </el-table-column> </el-table> </el-main> <el-footer class="text-right" height="50px"> <el-pagination class="float-right" layout="prev, pager, next" :total="pagination.total" :current-page.sync="pagination.current" :page-size="pagination.size" @current-change="loadData(pagination.current)" > </el-pagination> </el-footer> <base-drawer v-model="drawerVisible" title="" direction="rtl" size="25%" :before-close="onDrawerClose" > 历史趋势 正在加载历史趋势... </base-drawer> </el-container> </template> <script lang="ts"> import { Component, Vue } from "nuxt-property-decorator"; import { State, Action } from "vuex-class"; import { Pagination, UserState } from "../../types"; import { duration } from "../../utils/time"; import dayjs from "dayjs"; import { removeEmptyChilds } from "../../utils/tools"; import { fetchData } from "../../utils/concurrency"; import { getQuality } from "../../api/websocket"; import { downloadFile } from "@/utils/tools"; import { historyStatus } from "../../store/history"; import BaseDrawer from "./BaseDrawer.vue"; import * as marked from "marked"; import { cloneDeep } from "lodash"; import {getWsUrl} from '@/api/websocket' @Component({ components: { BaseDrawer, }, filters: { formatTime: (time: number) => new Date(time).toLocaleDateString(), }, }) export default class extends Vue { @State user!: UserState; tableHeight: number = 200; showFieldLabel: boolean = false; @State("history") historyState!: historyStatus; @Action("history/saveQueryParams") saveQueryParams!: (params: any) => void; @Action("history/resetQueryParams") resetQueryParams!: () => void; queryParam: { regionIds: []; //类型[] timearea: []; serialNum: string; talkerName: string; intervieweeName: string; keyword: string; //监管人 谈话原因筛选值 isCustody: number; //在押历史筛选操作 } = { regionIds: [], timearea: [], serialNum: "", talkerName: "", intervieweeName: "", keyword: "", isCustody: 0, }; options: { regions: [] } = { regions: [] }; tableData: [] = []; loading: boolean = false; pagination: Pagination = { total: 0, current: 1, size: 50 }; options1: any = [ { text: "全部", value: -1 }, { text: "在押", value: 0 }, { text: "历史", value: 1 }, ]; selectedNodes: any[] = []; // 用于存储已勾选的节点 isShow: boolean = true; isShow1: boolean = false; isRegin: boolean = true; // 抽屉相关状态 drawerVisible: boolean = false; currentRow: any = null; private socket: WebSocket | null = null; private wsMessage: any = null; // 存储 WebSocket 消息 isLoading: boolean = false; // 加载状态 talkStrategy: string = ""; // 存储后端返回 showFullScreenButton() { this.isShow1 = !this.isShow1; //1次悬停展示再次悬停隐藏(依次类推) } handleCheckChange(data, checked, indeterminate) { if (checked) { this.isRegin = false; // 如果节点被勾选,添加到selectedNodes中(避免重复) if (!this.selectedNodes.some((n) => n.id === data.id)) { this.selectedNodes.push({ ...data }); this.queryParam.regionIds = this.selectedNodes.map((node) => node.id); if (this.selectedNodes.length > 2) { this.isShow = false; } else { this.isShow = true; } } } else { // 如果节点被取消勾选,从selectedNodes中移除 this.selectedNodes = this.selectedNodes.filter((n) => n.id !== data.id); this.queryParam.regionIds = this.selectedNodes.map((node) => node.id); if (this.selectedNodes.length <= 2) { this.isShow = true; } if (this.selectedNodes.length == 0) { this.isRegin = true; this.isShow1 = false; this.queryParam.regionIds = []; } } // 保存当前筛选状态 this.saveFilterParams(); } clearSelectionById(id) { // 从selectedNodes中移除对应项 this.selectedNodes = this.selectedNodes.filter((node) => node.id !== id); // 取消el-tree中对应节点的勾选状态 this.$nextTick(() => { const treeNode = this.$refs.tree.getNode(id); if (treeNode) { this.$refs.tree.setChecked(treeNode, false, true); } // 更新regionIds this.queryParam.regionIds = this.selectedNodes.map((node) => node.id); // 保存当前筛选状态 this.saveFilterParams(); }); } // 修改 clearSelectionById1 方法 clearSelectionById1() { //点击+1清除全部 this.isShow1 = false; this.selectedNodes = []; this.isRegin = true; // 取消el-tree中对应节点的勾选状态 this.$nextTick(() => { this.$refs.tree.setCheckedKeys([]); // 更新regionIds this.queryParam.regionIds = []; // 保存当前筛选状态 this.saveFilterParams(); }); } bahjMap: { [key: number]: string } = { // 新增 bahj 映射对象 0: "未开启", 1: "等待中", 2: "质检中", 3: "质检成功", 4: "质检失败", }; async asyncData(ctx) { let res = await ctx.app.$api.region.list(null); const regions = res.data; removeEmptyChilds(regions); return { options: { regions } }; } // 处理抽屉关闭事件 onDrawerClose(done) { // 关闭WebSocket连接 if (this.socket) { this.socket.close(); this.socket = null; } // 重置状态 this.isLoading = false; this.talkStrategy = ""; this.currentRow = null; // 必须调用done来完成关闭操作 done(); } // trend 方法 async trend(row: any) { // 初始化状态 this.currentRow = row; this.drawerVisible = true; this.isLoading = true; this.talkStrategy = ""; // 清空之前内容 // 关闭现有连接(如果存在) if (this.socket) { this.socket.close(); this.socket = null; } // 确保在下一次DOM更新周期创建新连接 this.$nextTick(() => { const taskId = row.id; const wsUrl = process.env.IS_DEV ? /tendency/ws/talkAnalysis/${taskId} : getWsUrl(/ws/talkAnalysis/${taskId}); this.socket = new WebSocket(wsUrl); let accumulatedAnswer = ""; // 存储累积的Markdown内容 this.socket.onopen = () => { console.log("WebSocket连接成功"); this.sendAnalysisRequest(); // 发送分析请求 }; this.socket.onmessage = (event) => { this.isLoading = false;//实现打印机一字一字输出 try { const res = typeof event.data === "string" ? JSON.parse(event.data) : event.data; // 处理分块内容 if (res.answer) { accumulatedAnswer += res.answer; // 累积分块内容 this.talkStrategy = this.formatMarkdown(accumulatedAnswer); // 实时渲染 } // 处理结束标志 if (res.is_stop) { this.isLoading = false; if (this.socket) { this.socket.close(); this.socket = null; } console.log("流式输出结束"); } } catch (error) { console.error("消息解析错误:", error); this.talkStrategy = 数据解析失败:${error.message}; this.isLoading = false; } }; this.socket.onerror = (error) => { console.error("WebSocket错误:", error); this.talkStrategy = "连接失败,请重试"; this.isLoading = false; }; this.socket.onclose = () => { if (this.isLoading) { this.isLoading = false; } console.log("WebSocket连接关闭"); }; }); } // 辅助 格式化Markdown private formatMarkdown(content: string): string { if (!content) return "暂无分析结果"; // 使用marked库解析Markdown(更规范的处理方式) try { // 自定义marked渲染器来优化样式 const renderer = new marked.Renderer(); // 自定义标题渲染 - 修复方法签名 renderer.heading = ({ text, depth }: { text: string; depth: number }) => { const headingClass = md-heading md-heading-${depth}; return <h${depth} class="${headingClass}">${text}</h${depth}>; }; // 自定义段落渲染 renderer.paragraph = (text) => { return ${text}; }; // 自定义列表渲染 renderer.list = (body, ordered, start) => { const tag = ordered ? "ol" : "ul"; return <${tag} class="md-list"${ start ? start="${start}" : "" }>${body}</${tag}>; }; renderer.listitem = (text) => { return ${text}; }; // 自定义分隔线 renderer.hr = () => '
'; // 配置marked选项 marked.setOptions({ renderer: renderer, breaks: true, gfm: true, }); // 解析Markdown return marked(content); } catch (error) { console.error("Markdown解析错误:", error); // 降级处理:手动解析关键结构 let html = content // 处理标题 .replace(/^######### (.*$)/gm, '$1') .replace(/^######## (.*$)/gm, '<h9 class="md-heading-1">$1</h9>') .replace(/^######## (.*$)/gm, '<h8 class="md-heading-1">$1</h8>') .replace(/^####### (.*$)/gm, '<h7 class="md-heading-1">$1</h7>') .replace(/^###### (.*$)/gm, '$1') .replace(/^##### (.*$)/gm, '$1') .replace(/^#### (.*$)/gm, '$1') .replace(/^### (.*$)/gm, '$1') .replace(/^## (.*$)/gm, '$1') .replace(/^# (.*$)/gm, '$1') // 处理列表 .replace(/^\s*[-*+]\s+(.*)$/gm, '$1') .replace( /(.*<\/li>\s*)+/gm, '$&' ) // 处理分隔线 .replace(/^[-*_]{3,}$/gm, '
') // 处理段落 .replace(/\n{2,}/g, '
') .replace(/\n/g, "
") // 处理加粗和斜体 .replace(/\*\*(.*?)\*\*/g, "$1") .replace(/\*(.*?)\*/g, "$1"); // 确保根元素包裹 if (!html.startsWith("") && !html.startsWith("<h")) { html = ${html}; } return html; } } // 发送请求参数的方法( private sendAnalysisRequest() { if (!this.socket || this.socket.readyState !== WebSocket.OPEN) { console.error("WebSocket 未连接"); return; } // 构建参数(确保无 undefined 或循环引用) const requestData = { task_type: "历史趋势", // person_name: "王传参", person_name: this.currentRow?.intervieweeName, talk_reason: this.currentRow?.thyy, timestamp: this.currentRow?.startTime, context: "", question: "", }; try { // 验证 JSON 格式 const jsonStr = JSON.stringify(requestData); console.log("待发送的 JSON 字符串:", jsonStr); // 检查是否有异常字符 this.socket.send(jsonStr); } catch (error) { console.error("JSON 序列化失败:", error); } } created() { // 在加载数据前快速恢复查询参数 this.restoreFilterParams(); this.$api.region.list(null).then((res) => { const regions = res.data; removeEmptyChilds(regions); this.options.regions = regions; // 确保树组件加载完成后再设置选中状态 this.$nextTick(() => { this.restoreFilterParams(); }); }); } // 生命周期钩子:路由变化时恢复筛选条件 beforeRouteEnter(to, from, next) { next((vm) => { vm.restoreFilterParams(); vm.loadData(); }); } beforeRouteUpdate(to, from, next) { this.restoreFilterParams(); this.loadData(); next(); } // 恢复筛选条件 restoreFilterParams() { // 从Vuex获取保存的筛选条件 this.queryParam = { ...this.historyState.queryParam, regionIds: this.historyState.regionIds || [], timearea: this.historyState.timearea || [], serialNum: this.historyState.serialNum || "", talkerName: this.historyState.talkerName || "", intervieweeName: this.historyState.intervieweeName || "", keyword: this.historyState.keyword || "", isCustody: this.historyState.isCustody || 0, }; // 恢复区域节点 this.selectedNodes = cloneDeep(this.historyState.regionNodes || []); this.isRegin = this.selectedNodes.length === 0; // 恢复树形选择状态 this.$nextTick(() => { if (this.$refs.tree) { const tree = this.$refs.tree as any; tree.setCheckedKeys(this.queryParam.regionIds || []); // 更新显示状态 if (this.selectedNodes.length > 2) { this.isShow = false; } else { this.isShow = true; } } }); } // 保存筛选条件到Vuex saveFilterParams() { const paramsToSave = { ...this.queryParam, regionIds: this.selectedNodes.map((node) => node.id), regionNodes: cloneDeep(this.selectedNodes), }; this.$store.commit("history/SET_QUERY_PARAMS", paramsToSave); } handleClickOutside(e) { //判断是否点击的是盒子之外 if (this.$refs.tree && !this.$refs.tree.$el.contains(e.target)) { this.isShow1 = false; } } mounted() { this.$nextTick(this.resize.bind(this)); window.addEventListener("resize", this.resize); this.$once("hook:beforeDestroy", () => window.removeEventListener("resize", this.resize) ); // 恢复筛选条件 this.restoreFilterParams(); this.loadData(); // 添加全局点击事件监听器 document.addEventListener("click", this.handleClickOutside); } beforeDestroy() { // 移除全局点击事件监听器 document.removeEventListener("click", this.handleClickOutside); } resize() { this.tableHeight = ((this.$el.querySelector(".el-table") as Element).parentNode as Element) .clientHeight - 1; // this.console.debug(this.$el, this.tableHeight) } loadData1(e) {} async loadData(index: number = 1) { this.saveFilterParams(); // 保存当前筛选条件 let arr: number[] = []; this.pagination.current = index; let param: any = { pageIndex: this.pagination.current }; if (this.queryParam.regionIds != null) { arr = this.queryParam.regionIds.flat(); // flat 方法将嵌套数组拍平替代foreach循环语法 param.regionIds = [...new Set(arr)]; } param.ryzt = this.queryParam.isCustody; // 筛选在押历史的过滤queryParam.isCustody if ( this.queryParam.timearea != null && this.queryParam.timearea.length > 0 ) { this.queryParam.timearea.forEach((it: Date, idx) => { param[idx == 0 ? "startTime" : "endTime"] = it.getTime() / 1000; }); } if ( this.queryParam.talkerName != null && this.queryParam.talkerName.length > 0 ) { param.talkerName = this.queryParam.talkerName; } if ( this.queryParam.intervieweeName != null && this.queryParam.intervieweeName.length > 0 ) { param.intervieweeName = this.queryParam.intervieweeName; } if ( this.queryParam.serialNum != null && this.queryParam.serialNum.length > 0 ) { param.serialNum = this.queryParam.serialNum; } if (this.queryParam.keyword != null && this.queryParam.keyword.length > 0) { param.keyword = this.queryParam.keyword; } this.loading = true; const res = await this.$api.talk.history(param); this.tableData = res.data; this.pagination = { total: res.page.totalCount, current: res.page.pageIndex, size: res.page.pageSize, }; this.loading = false; } review(item: any) { this.saveFilterParams(); // 跳转前保存筛选条件 this.$router.push({ path: /${ this.user.user.role && this.user.user.role.isAdmin ? "admin" : "home" }/review, query: { id: item.id, type: (item.isOnline ? 1 : 0).toString(), talkerId: item.id, fillcontent: item, }, }); } exportText(item: any) { this.$api.download(this.$api.talk.exportTextUrl(item.id)); } async exportFile(item: any) { if (item.isVideo) { console.log(item.videoPath, "989"); downloadFile(item.videoPath); } else { this.$api.download(this.$api.talk.exportAudioUrl(item.id)); } // this.$api.download(this.$api.talk.exportAudioUrl(item.id)); } // --------------------------------------------------------------- filterHandler(value, row, column) { const property = column["property"]; return row[property] === value; } indexOrder(index) { return (this.pagination.current - 1) * this.pagination.size + index + 1; } formatDate(timestamp: number): string { const milliseconds = timestamp * 1000; return dayjs(milliseconds).format("YYYY-MM-DD HH:mm:ss"); } formatDuration(row, column, cellValue, index) { return duration( (cellValue ? cellValue : row.endTime == null || row.startTime == null ? "--" : row.endTime - row.startTime) * 1000 ); } } </script> <style lang="less" scoped> .trend-content { margin-top: 20px; height: calc(100% - 60px); .chart-placeholder { height: 100%; padding: 20px; background: #fff; border-radius: 4px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); } // 加载中样式 .loading { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: #666; .el-icon-loading { font-size: 32px; margin-bottom: 16px; animation: loading 1s linear infinite; } } // 谈话策略内容样式 .strategy-content { line-height: 1.6; color: #333; padding: 15px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; // 标题样式 .md-heading { margin: 1.5em 0 0.8em; font-weight: 600; color: #2c3e50; &-1 { font-size: 1.8em; } &-2 { font-size: 1.5em; border-bottom: 1px solid #eee; padding-bottom: 0.3em; } &-3 { font-size: 1.3em; } &-4 { font-size: 1.1em; } } // 段落样式 .md-paragraph { margin: 1em 0; line-height: 1.7; } // 列表样式 .md-list { padding-left: 2em; margin: 1em 0; &-item { margin: 0.5em 0; position: relative; &::before { content: "•"; color: #409eff; position: absolute; left: -1.2em; } } } // 分隔线 .md-divider { border: 0; border-top: 1px solid #eaecef; margin: 1.5em 0; } // 强调样式 strong { font-weight: 600; color: #2c3e50; } em { font-style: italic; color: #555; } // 代码块样式(如果需要) pre { background-color: #f8f8f8; padding: 1em; border-radius: 4px; overflow-x: auto; } // 链接样式 a { color: #409eff; text-decoration: none; &:hover { text-decoration: underline; } } // 引用块样式 blockquote { margin: 1em 0; padding-left: 1em; color: #6a737d; border-left: 0.25em solid #dfe2e5; } } } // 加载动画 @keyframes loading { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } </style> <style lang="less" scoped> /** 骨架样式 ****************************************************************/ /deep/.el-table { .custom-text { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } } .container, .el-container, .el-main, .el-row, .el-col { height: 100%; } .container { overflow: hidden; } .el-container { padding: 5px; // background-color: #d3d3d3; } .ul { position: relative; list-style-type: none; padding: 0; margin: 0; border: 1px solid rgb(222, 221, 221); width: 280px; height: 30px; line-height: 30px; background: white; li { margin-bottom: 10px; float: left; margin: 0 5px 0 2px; padding: 0 2px; } } .el-header { height: auto !important; .query_item { display: inline-block; margin: 0 5px; .field { margin-right: 5px; } .keyword { width: 300px; } } } .float-right { display: flex; justify-content: space-around; .query_item { z-index: 1000; } } .el-main { padding: 0px; .el-row, .el-col { border: #7f828b solid 1px; } } .el-footer { /*background-color: #838b89;*/ padding: 10px; } /** 表单 ****************************************************************/ .el-input, .el-select { width: 120px; } .el-date-editor { width: 340px; } </style> <style lang="less"> </style>

创建一个js 储存类 里面通过api加载后端数据并监管这些数据自动同步 外界访问通过 data.实体类集合的方式访问和修改数据, api格式为/KuCun2/app/{add,select,delete,update,all}/{实体类小写} 获得 查询为类的属性只要不为空就会一起查询 全部数据/app/all返回数据{"text":"success","data":{"bancais":[{"id":1,"id":1,"caizhi":{"id":1},"mupi1":{"id":1},"mupi2":{"id":1},"houdu":15.0,"kucun":{"id":1}},{"id":2,"id":2,"caizhi":{"id":1},"mupi1":{"id":2},"mupi2":{"id":2},"houdu":15.0,"kucun":{"id":2}}],"dingdans":[{"id":1,"id":1,"number":"直接入库","dingdan_chanpins":[{"id":1}],"dingdan_chanpins_zujians":[]}],"mupis":[{"id":1},{"id":2},{"id":3,"bancaisForMupi1":[],"bancaisForMupi2":[]}],"chanpins":[{"id":1,"id":1,"dingdans":[{"id":1}],"bianhao":"直接入库","zujians":[{"id":1}]}],"kucuns":[{"id":1},{"id":2}],"chanpin_zujians":[{"id":1,"id":1,"dingdan":{"id":1},"chanpin":{"id":1},"shuliang":222}],"zujians":[{"id":1,"chanpins":[{"id":1}]}],"caizhis":[{"id":1,"bancais":[{"id":1},{"id":2}]},{"id":3,"bancais":[]},{"id":2,"bancais":[]}],"users":[{"id":1,"id":1,"name":"超管","andy":"123456","pass":"$2a$10$JflS0yjBRY6yDRxdhAuHVunetrG2P6q8gj8HQzuaPtW8tt/OqO73S","role":0}]},"status":200} 实体类字段package com.kucun.data.entity; import java.util.Date; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.ManyToOne; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.IdOnlySerializer; @Entity public class Jinhuo implements EntityBasis{ @Id private Integer id; @ManyToOne @JsonSerialize(using = IdOnlySerializer.class) private Dingdan dingdan; @ManyToOne @JsonSerialize(using = IdOnlySerializer.class) private Chanpin chanpin; @ManyToOne @JsonSerialize(using = IdOnlySerializer.class) private Zujian zujian; @ManyToOne @JsonSerialize(using = IdOnlySerializer.class) private Bancai bancai; private Integer shuliang; private Date date; @ManyToOne @JsonSerialize(using = IdOnlySerializer.class) private User user; public Jinhuo(Integer id, Dingdan dingdan, Chanpin chanpin, Zujian zujian, Bancai bancai, Integer shuliang, Date date, User user) { super(); this.id = id; this.dingdan = dingdan; this.chanpin = chanpin; this.zujian = zujian; this.bancai = bancai; this.shuliang = shuliang; this.date = date; this.user = user; } public Jinhuo() { super(); // TODO Auto-generated constructor stub } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Dingdan getDingdan() { return dingdan; } public void setDingdan(Dingdan dingdan) { this.dingdan = dingdan; } public Chanpin getChanpin() { return chanpin; } public void setChanpin(Chanpin chanpin) { this.chanpin = chanpin; } public Zujian getZujian() { return zujian; } public void setZujian(Zujian zujian) { this.zujian = zujian; } public Bancai getBancai() { return bancai; } public void setBancai(Bancai bancai) { this.bancai = bancai; } public Integer getShuliang() { return shuliang; } public void setShuliang(Integer shuliang) { this.shuliang = shuliang; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } } package com.kucun.data.entity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToOne; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.IdOnlySerializer; /** 库存 @author Administrator */ @Entity public class Kucun implements EntityBasis{ @Id private Integer id; private Long shuliang; @OneToOne(fetch = FetchType.LAZY) // 正确映射 Bancai 实体 @JoinColumn(name = "bancai_id", referencedColumnName = "id") @JsonSerialize(using = IdOnlySerializer.class) private Bancai bancai; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Bancai getBancai() { return bancai; } public void setBancai(Bancai bancai) { this.bancai = bancai; } public Long getShuliang() { return shuliang; } public void setShuliang(Long shuliang) { this.shuliang = shuliang; } public Kucun(Integer id, Bancai bancai, Long shuliang) { super(); this.id = id; this.bancai = bancai; this.shuliang = shuliang; } public Kucun() { super(); // TODO Auto-generated constructor stub } } package com.kucun.data.entity; import java.util.List; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.IdOnlySerializer; /** 木皮 @author Administrator */ @Entity @Table(name=“caizhi”, uniqueConstraints = { @UniqueConstraint(columnNames = “name”) }) @JsonIgnoreProperties({“hibernateLazyInitializer”, “handler”}) public class Mupi extends SimpleEntity implements EntityBasis{ /** * 是否有油漆 */ private Boolean you; // 添加 OneToMany 映射 @OneToMany(mappedBy = "mupi1") // 指向 Bancai 中的 mupi1 字段 @JsonIgnore @JsonSerialize(contentUsing = IdOnlySerializer.class) private List<Bancai> bancaisForMupi1; @OneToMany(mappedBy = "mupi2") // 指向 Bancai 中的 mupi2 字段 @JsonIgnore @JsonSerialize(contentUsing = IdOnlySerializer.class) private List<Bancai> bancaisForMupi2; public List<Bancai> getBancaisForMupi1() { return bancaisForMupi1; } public void setBancaisForMupi1(List<Bancai> bancaisForMupi1) { this.bancaisForMupi1 = bancaisForMupi1; } public List<Bancai> getBancaisForMupi2() { return bancaisForMupi2; } public void setBancaisForMupi2(List<Bancai> bancaisForMupi2) { this.bancaisForMupi2 = bancaisForMupi2; } public Mupi() { super(); } public Boolean getYou() { return you; } public void setYou(Boolean you) { this.you = you; } } package com.kucun.data.entity; import javax.persistence.Column; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.MappedSuperclass; @MappedSuperclass public abstract class SimpleEntity implements EntityBasis{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(nullable = false, unique = true) private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } // Getters and Setters... } package com.kucun.data.entity; import java.util.Objects; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; import com.fasterxml.jackson.databind.ObjectMapper; /** 用户 @author Administrator / @Entity @Table(name=“user”) public class User implements EntityBasis{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; /* * 名字 / @Column(nullable=false) private String name; /* * 账号 / @Column(nullable=false) private String andy; /* * 密码 / @Column(nullable=false) private String pass; /* * 权限 */ @Column(nullable=false) private int role; public User() { super(); } public User(int id, String name, String andy, String pass) { super(); this.id = id; this.name = name; this.andy = andy; this.pass = pass; } public Integer getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAndy() { return andy; } public void setAndy(String andy) { this.andy = andy; } public String getPass() { return pass; } public void setPass(String pass) { this.pass = pass; } public int getRole() { System.out.println(role); return role; } public void setRole(int role) { this.role = role; } @Override public String toString() { return "{id:" + id + ", name:" + name + ", andy:" + andy + ", pass:" + pass + ", role:" + role + "}"; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return Objects.equals(id, user.id) && Objects.equals(name, user.name) && Objects.equals(andy, user.andy)&& Objects.equals(role, user.role); //添加所有属性比较 } @Override public int hashCode() { return Objects.hash(id, name, andy,pass,role); } } package com.kucun.data.entity; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.OneToMany; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.IdOnlySerializer; @Entity public class Zujian extends SimpleEntity implements EntityBasis{ // 反向关联到产品组件 @OneToMany( mappedBy = "zujian", cascade = CascadeType.ALL, fetch = FetchType.LAZY ) @JsonSerialize(contentUsing = IdOnlySerializer.class) private List<Chanpin_zujian> chanpins; public List<Chanpin_zujian> getChanpins() { return chanpins; } public void setChanpins(List<Chanpin_zujian> chanpins) { this.chanpins = chanpins; } } package com.kucun.data.entity; import java.lang.annotation.Annotation; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToOne; import javax.persistence.Table; import com.kucun.data.entity.DTO.*; import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonManagedReference; import com.fasterxml.jackson.databind.annotation.JsonSerialize; /** 板材 @author Administrator */ @Entity @Table(name=“bancai”) public class Bancai implements EntityBasis { @Id private int id; @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name = “caizhi_id”) // @JsonManagedReference // 标记为“主”关联方 @JsonSerialize(using = IdOnlySerializer.class) private Caizhi caizhi; @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name = “mupi1_id”) @JsonSerialize(using = IdOnlySerializer.class) private Mupi mupi1; @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name = “mupi2_id”) @JsonSerialize(using = IdOnlySerializer.class) private Mupi mupi2; private Double houdu; @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name = “kucun_id”, referencedColumnName = “id”) private Kucun kucun; public Kucun getKucun() { return kucun; } public void setKucun(Kucun kucun) { this.kucun = kucun; } public Integer getId() { return id; } public void setId(int id) { this.id = id; } public Caizhi getCaizhi() { return caizhi; } public void setCaizhi(Caizhi caizhi) { this.caizhi = caizhi; } public Mupi getMupi1() { return mupi1; } public void setMupi1(Mupi mupi1) { this.mupi1 = mupi1; } public Mupi getMupi2() { return mupi2; } public void setMupi2(Mupi mupi2) { this.mupi2 = mupi2; } public Double getHoudu() { return houdu; } public void setHoudu(Double houdu) { this.houdu = houdu; } public Bancai(int id, Caizhi caizhi, Mupi mupi1, Mupi mupi2, Double houdu) { super(); this.id = id; this.caizhi = caizhi; this.mupi1 = mupi1; this.mupi2 = mupi2; this.houdu = houdu; } public Bancai() { super(); } } package com.kucun.data.entity; import java.util.List; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.IdOnlySerializer; /** 板材材质 @author Administrator */ @Entity @Table(name=“caizhi”, uniqueConstraints = { @UniqueConstraint(columnNames = “name”) }) @JsonIgnoreProperties({“hibernateLazyInitializer”, “handler”}) public class Caizhi extends SimpleEntity implements EntityBasis{ @OneToMany(mappedBy=“caizhi”) @JsonIgnore @JsonSerialize(contentUsing = IdOnlySerializer.class) private List<Bancai> bancais; public Caizhi() { super(); } // 添加反向关联维护方法 public void addBancai(Bancai bancai) { bancais.add(bancai); bancai.setCaizhi(this); } // 添加移除方法 public void removeBancai(Bancai bancai) { bancais.remove(bancai); bancai.setCaizhi(null); } public List<Bancai> getBancais() { return bancais; } public void setBancais(List<Bancai> bancais) { this.bancais = bancais; } } package com.kucun.data.entity; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.IdOnlySerializer; /** 产品类 @author Administrator */ @Entity @Table(name=“chanpin”, uniqueConstraints = { @UniqueConstraint(columnNames = “bianhao”) }) public class Chanpin implements EntityBasis { @Id private Integer id; // 关联订单产品 @OneToMany( mappedBy = “chanpin”, cascade = CascadeType.ALL, fetch = FetchType.LAZY ) @JsonSerialize(contentUsing = IdOnlySerializer.class) private List<Dingdan_chanpin> dingdans; private String bianhao; @OneToMany( mappedBy = "chanpin", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true ) @JsonSerialize(contentUsing = IdOnlySerializer.class) private List<Chanpin_zujian> zujians; // 添加/移除组件的辅助方法 public void addZujian(Chanpin_zujian zujian) { zujians.add(zujian); zujian.setChanpin(this); } public void removeZujian(Chanpin_zujian zujian) { zujians.remove(zujian); zujian.setChanpin(null); } // 添加/移除组件的辅助方法 public void addDingdan(Dingdan_chanpin dingdan) { dingdans.add(dingdan); dingdan.setChanpin(this); } public void removeDingdan(Dingdan_chanpin dingdian) { dingdans.remove(dingdian); dingdian.setChanpin(null); } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public List<Dingdan_chanpin> getDingdans() { return dingdans; } public void setDingdans(List<Dingdan_chanpin> dingdans) { this.dingdans = dingdans; } public String getBianhao() { return bianhao; } public void setBianhao(String bianhao) { this.bianhao = bianhao; } public List<Chanpin_zujian> getZujians() { return zujians; } public void setZujians(List<Chanpin_zujian> zujians) { this.zujians = zujians; } public Chanpin(Integer id, List<Dingdan_chanpin> dingdians, String bianhao, List<Chanpin_zujian> zujians) { super(); this.id = id; this.dingdans = dingdians; this.bianhao = bianhao; this.zujians = zujians; } public Chanpin() { super(); // TODO Auto-generated constructor stub } } package com.kucun.data.entity; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.IdOnlySerializer; /** 产品组件关联类 @author Administrator */ @Entity @Table(name=“chanpin_zujian”) public class Chanpin_zujian implements EntityBasis { @Id private Integer id; // 关联到产品 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = “chanpin_id”) @JsonSerialize(using = IdOnlySerializer.class) private Chanpin chanpin; // 关联到组件 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "zujian_id") @JsonSerialize(using = IdOnlySerializer.class) private Zujian zujian; // 关联到板材 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "bancai_id") @JsonSerialize(using = IdOnlySerializer.class) private Bancai bancai; private Double one_howmany; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Chanpin getChanpin() { return chanpin; } public void setChanpin(Chanpin chanpin) { this.chanpin = chanpin; } public Zujian getZujian() { return zujian; } public void setZujian(Zujian zujian) { this.zujian = zujian; } public Bancai getBancai() { return bancai; } public void setBancai(Bancai bancai) { this.bancai = bancai; } public Double getOne_howmany() { return one_howmany; } public void setOne_howmany(Double one_howmany) { this.one_howmany = one_howmany; } public Chanpin_zujian() { super(); // TODO Auto-generated constructor stub } } package com.kucun.data.entity; import java.util.Date; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.IdOnlySerializer; /** 订单 @author Administrator */ @Entity @Table(name=“dingdan”, uniqueConstraints = { @UniqueConstraint(columnNames = “number”) }) public class Dingdan implements EntityBasis{ @Id private Integer id; //订单号 private String number; private Date xiadan; private Date jiaohuo; @OneToMany( mappedBy = “dingdan”, cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true ) @JsonSerialize(contentUsing = IdOnlySerializer.class) private List<Dingdan_chanpin> dingdan_chanpins; // 优化订单与订单组件关联 @OneToMany( mappedBy = “dingdan”, cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true ) @JsonSerialize(contentUsing = IdOnlySerializer.class) private List<Dingdan_chanpin_zujian> dingdan_chanpins_zujians; public Date getXiadan() { return xiadan; } public void setXiadan(Date xiadan) { this.xiadan = xiadan; } public Date getJiaohuo() { return jiaohuo; } public void setJiaohuo(Date jiaohuo) { this.jiaohuo = jiaohuo; } public List<Dingdan_chanpin_zujian> getDingdan_chanpins_zujians() { return dingdan_chanpins_zujians; } public void setDingdan_chanpins_zujians(List<Dingdan_chanpin_zujian> dingdan_chanpins_zujians) { this.dingdan_chanpins_zujians = dingdan_chanpins_zujians; } public List<Dingdan_chanpin> getDingdan_chanpins() { return dingdan_chanpins; } public void setDingdan_chanpins(List<Dingdan_chanpin> dingdan_chanpins) { this.dingdan_chanpins = dingdan_chanpins; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public Dingdan(Integer id, String number) { super(); this.id = id; this.number = number; } public Dingdan() { super(); // TODO Auto-generated constructor stub } } package com.kucun.data.entity; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.IdOnlySerializer; /** 订单和产品关联 @author Administrator */ @Entity @Table(name=“dingdan_chanpin”) public class Dingdan_chanpin implements EntityBasis { @Id private Integer id; //产品信息 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = “dingdan_id”) // 指 @JsonSerialize(using = IdOnlySerializer.class) private Dingdan dingdan; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "chanpin_id") // 指 @JsonSerialize(using = IdOnlySerializer.class) private Chanpin chanpin; private Integer shuliang;//产品数量; public Chanpin getChanpin() { return chanpin; } public void setChanpin(Chanpin chanpin) { this.chanpin = chanpin; } public Integer getShuliang() { return shuliang; } public void setShuliang(Integer shuliang) { this.shuliang = shuliang; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Dingdan getDingdan() { return dingdan; } public void setDingdan(Dingdan dingdan) { this.dingdan = dingdan; } public Chanpin getChanping() { return chanpin; } public void setChanping(Chanpin chanping) { this.chanpin = chanping; } } package com.kucun.data.entity; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.IdOnlySerializer; /** 一个订单中的产品组件订购板材数量 @author Administrator */ @Entity public class Dingdan_chanpin_zujian implements EntityBasis{ @Id private Integer id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "dingdan_id") // 指定外键列 @JsonSerialize(using = IdOnlySerializer.class) private Dingdan dingdan; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "chanpin_zujian_id") // 指定外键列 @JsonSerialize(using = IdOnlySerializer.class) private Chanpin_zujian zujian; // 修改为单数形式 //板材 @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name = "bancai_id") @JsonSerialize(using = IdOnlySerializer.class) private Bancai bancai; //订购数 private Integer shuliang ; public Dingdan_chanpin_zujian() { super(); // TODO Auto-generated constructor stub } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Dingdan getDingdan() { return dingdan; } public void setDingdan(Dingdan dingdan) { this.dingdan = dingdan; } public Bancai getBancai() { return bancai; } public void setBancai(Bancai bancai) { this.bancai = bancai; } public Chanpin_zujian getZujian() { return zujian; } public void setZujian(Chanpin_zujian zujian) { this.zujian = zujian; } public Integer getShuliang() { return shuliang; } public void setShuliang(Integer shuliang) { this.shuliang = shuliang; } } package com.kucun.data.entity; public interface EntityBasis { Integer getId(); } package com.kucun.data.entity; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; /** 通信类 @author Administrator */ public class Information { private static final ObjectMapper mapper = new ObjectMapper(); private Integer Status ; private String text; private Object data; public Integer getStatus() { return Status; } public void setStatus(Integer status) { Status = status; } public String getText() { return text; } public void setText(String text) { this.text = text; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } public Information(Integer status, String text, Object data) { super(); Status = status; this.text = text; this.data = data; } @SuppressWarnings({“unchecked”,“rawtypes”}) public Information(Integer status, String text, String data, Class T) throws Exception { super(); Status = status; this.text = text; this.data = fromJson(data,T); } public Information() { super(); // TODO Auto-generated constructor stub } public String DataJson() throws JsonProcessingException { // Java对象转JSON return mapper.writeValueAsString(this); } @SuppressWarnings(“unchecked”) public <T> T fromJson(String json, Class<T> clazz) throws Exception { data= mapper.readValue(json, clazz); return (T) data; } public static Information NewSuccess(Object data) { return new Information(200, "success", data); } public static Information NewSuccess(String data) { return new Information(200, "success", data); } public static Information Newfail(Integer status,String text,Object data) { return new Information(status, "success", data); } public static Information NewFail(String string) { // TODO Auto-generated method stub return new Information(400,string,null); } }

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="../../js/axios.min.js"></script> <style> img { width: 200px; } .layui-upload-list { overflow: hidden; } .layui-upload-list .multiple_block .upload_img_multiple { height: auto; width: 100px; } .multiple_block { position: relative; float: left; width: 100px; margin: 0 10px 10px 0; } .multiple_block .upload-img-del { position: absolute; top: 5px; right: 5px; color: #fff; border-radius: 100%; background: #0000009c; width: 20px; height: 20px; text-align: center; line-height: 20px; cursor: pointer; } </style> </head> <body>
<form class="layui-form" action=""> <label class="layui-form-label">供应商名称</label> <input type="text" name="title" lay-verify="title" autocomplete="off" placeholder="请输入供应商名称" class="layui-input" id="supplier_name"> <label class="layui-form-label">供应商电话</label> <input type="text" name="title" lay-verify="title" autocomplete="off" placeholder="请输入供应商电话" class="layui-input" id="suppliers_phone_number"> <label class="layui-form-label">供应商地址</label> <input type="text" name="title" lay-verify="title" autocomplete="off" placeholder="请输入供应商地址" class="layui-input" id="supplier_address"> <label class="layui-form-label">供应物品</label> <textarea placeholder="请输入供应物品" class="layui-textarea" id="supply_items"></textarea> <label class="layui-form-label">供应商描述</label> <textarea placeholder="请输入供应商描述" class="layui-textarea" id="supplier_description"></textarea> </form> <button type="button" class="layui-btn layui-btn-normal login" id="submit">确认</button> <button type="button" class="layui-btn layui-btn-normal login" id="cancel">取消</button>
</body> <script src="../../layui/layui.js"></script> <script src="../../js/base.js"></script> <script src="../../js/index.js"></script> <script> var BaseUrl = baseUrl() let cancel = document.querySelector("#cancel") cancel.addEventListener("click", () => { colseLayer() }) let supplier_information_id = location.search.substring(1) layui.use(['upload', 'element', 'layer', 'laydate', 'layedit'], function () { var $ = layui.jquery , upload = layui.upload , element = layui.element , layer = layui.layer , laydate = layui.laydate , layedit = layui.layedit , form = layui.form; let url let token = sessionStorage.token || null let personInfo = JSON.parse(sessionStorage.personInfo) let user_group = personInfo.user_group let use_id = personInfo.user_id function $get_stamp() { return new Date().getTime(); } function $get_rand(len) { var rand = Math.random(); return Math.ceil(rand * 10 ** len); } // 权限判断 /** * 获取路径对应操作权限 鉴权 * @param {String} action 操作名 */ function $check_action(path1, action = "get") { var o = $get_power(path1); if (o && o[action] != 0 && o[action] != false) { return true; } return false; } /** * 是否有显示或操作字段的权限 * @param {String} action 操作名 * @param {String} field 查询的字段 */ function $check_field(action, field, path1) { var o = $get_power(path1); var auth; if (o && o[action] != 0 && o[action] != false) { auth = o["field_" + action]; } if (auth) { return auth.indexOf(field) !== -1; } return false; } /** * 获取权限 * @param {String} path 路由路径 */ function $get_power(path) { var list_data = JSON.parse(sessionStorage.list_data) var list = list_data; var obj; for (var i = 0; i < list.length; i++) { var o = list[i]; if (o.path === path) { obj = o; break; } } return obj; } let submit = document.querySelector('#submit') // 提交按钮校验权限 if ($check_action('/supplier_information/view', 'add') || $check_action('/supplier_information/view', 'set') || $check_option('/supplier_information/table', 'examine')) { } else { $("#submit").hide() } // style="display: none" let field = "supplier_information_id"; let url_add = "supplier_information"; let url_set = "supplier_information"; let url_get_obj = "supplier_information"; let url_upload = "supplier_information" let query = { "supplier_information_id": 0, } let form_data2 = { "supplier_name": '', // 供应商名称 "suppliers_phone_number": '', // 供应商电话 "supplier_address": '', // 供应商地址 "supply_items": '', // 供应物品 "supplier_description": '', // 供应商描述 "supplier_information_id": 0, // ID } layui.layedit.set({ uploadImage: { url: BaseUrl + '/api/supplier_information/upload?' //接口url , type: 'post' //默认post } }); var path1 function getpath() { var list_data = JSON.parse(sessionStorage.list_data) for (var i = 0; i < list_data.length; i++) { var o = list_data[i]; if (o.path === "/supplier_information/table") { path1 = o.path $get_power(o.path) } } } getpath() /** * 注册时是否有显示或操作字段的权限 * @param {String} action 操作名 * @param {String} field 查询的字段 * @param {String} path 路径 */ function $check_register_field(action, field, path1) { var o = $get_power(path1); var auth; if (o && o[action] != 0 && o[action] != false) { auth = o["field_" + action]; } if (auth) { return auth.indexOf(field) !== -1; } return false; } /** * 是否有显示或操作字段的权限 * @param {String} action 操作名 * @param {String} field 查询的字段 */ function $check_field(action, field) { var o = $get_power("/supplier_information/view"); var auth; if (o && o[action] != 0 && o[action] != false) { auth = o["field_" + action]; } if (auth) { return auth.indexOf(field) !== -1; } return false; } /** * 获取路径对应操作权限 鉴权 * @param {String} action 操作名 */ function $check_exam(path1, action = "get") { var o = $get_power(path1); if (o) { var option = JSON.parse(o.option); if (option[action]) return true } return false; } function $check_option(path, op) { var o = $get_power(path); if (o) { var option = JSON.parse(o.option); if (option[op]) return true } return false; } /** * 是否有审核字段的权限 */ function $check_examine() { var url = window.location.href; var url_ = url.split("/") var pg_url = url_[url_.length - 2] let path = "/" + pg_url + "/table" var o = $get_power(path); if (o) { var option = JSON.parse(o.option); if (option.examine) return true } return false; } if ($check_field('add', 'supplier_name')) { $("#supplier_name_box").show() } else { $("#supplier_name_box").hide() } if ($check_field('add', 'suppliers_phone_number')) { $("#suppliers_phone_number_box").show() } else { $("#suppliers_phone_number_box").hide() } if ($check_field('add', 'supplier_address')) { $("#supplier_address_box").show() } else { $("#supplier_address_box").hide() } if ($check_field('add', 'supply_items')) { $("#supply_items_box").show() } else { $("#supply_items_box").hide() } if ($check_field('add', 'supplier_description')) { $("#supplier_description_box").show() } else { $("#supplier_description_box").hide() } //文本 let supplier_name = document.querySelector("#supplier_name") supplier_name.onkeyup = function (event) { form_data2.supplier_name = event.target.value } //文本 //文本 let suppliers_phone_number = document.querySelector("#suppliers_phone_number") suppliers_phone_number.onkeyup = function (event) { form_data2.suppliers_phone_number = event.target.value } //文本 //文本 let supplier_address = document.querySelector("#supplier_address") supplier_address.onkeyup = function (event) { form_data2.supplier_address = event.target.value } //文本 //多文本 let supply_items = document.querySelector("#supply_items") //多文本 //多文本 let supplier_description = document.querySelector("#supplier_description") //多文本 var data = sessionStorage.data || '' if (data !== '') { var data2 = JSON.parse(data) Object.keys(form_data2).forEach(key => { Object.keys(data2).forEach(dbKey => { if (key === dbKey) { if (key !== 'examine_state' && key !== 'examine_reply') { $('#' + key).val(data2[key]) form_data2[key] = data2[key] $('#' + key).attr('disabled', 'disabled') } } if (dbKey === "source_table") { form_data2.source_table = data2[dbKey]; } if (dbKey === "source_id") { form_data2.source_id = data2[dbKey]; } if (dbKey === "source_user_id") { form_data2.source_user_id = data2[dbKey]; } }) }) sessionStorage.removeItem("data"); } if (supplier_information_id !== '') { $('#print').show(); async function axios_get_3() { const { data: rese } = await axios.get(BaseUrl + '/api/supplier_information/get_obj', { params: { supplier_information_id: supplier_information_id }, headers: { 'x-auth-token': token } }) let data = rese.result.obj Object.keys(form_data2).forEach((key) => { form_data2[key] = data[key]; $("#" + key).val(form_data2[key]) }); if ($check_field('set', 'supplier_name') || $check_field('get', 'supplier_name')) { $("#supplier_name_box").show() } else { $("#supplier_name_box").hide() } if ($check_field('set', 'suppliers_phone_number') || $check_field('get', 'suppliers_phone_number')) { $("#suppliers_phone_number_box").show() } else { $("#suppliers_phone_number_box").hide() } if ($check_field('set', 'supplier_address') || $check_field('get', 'supplier_address')) { $("#supplier_address_box").show() } else { $("#supplier_address_box").hide() } if ($check_field('set', 'supply_items') || $check_field('get', 'supply_items')) { $("#supply_items_box").show() } else { $("#supply_items_box").hide() } if ($check_field('set', 'supplier_description') || $check_field('get', 'supplier_description')) { $("#supplier_description_box").show() } else { $("#supplier_description_box").hide() } // Array.prototype.slice.call(document.getElementsByTagName('input')).map(i => i.disabled = true) // Array.prototype.slice.call(document.getElementsByTagName('select')).map(i => i.disabled = true) // Array.prototype.slice.call(document.getElementsByTagName('textarea')).map(i => i.disabled = true) //文本 supplier_name.value = form_data2.supplier_name //文本 if ((form_data2['supplier_information_id'] && $check_field('set', 'supplier_name')) || (!form_data2['supplier_information_id'] && $check_field('add', 'supplier_name'))) { } else { $("#supplier_name").attr("disabled", true); $("#supplier_name > input[name='file']").attr('disabled', true); } //文本 suppliers_phone_number.value = form_data2.suppliers_phone_number //文本 if ((form_data2['supplier_information_id'] && $check_field('set', 'suppliers_phone_number')) || (!form_data2['supplier_information_id'] && $check_field('add', 'suppliers_phone_number'))) { } else { $("#suppliers_phone_number").attr("disabled", true); $("#suppliers_phone_number > input[name='file']").attr('disabled', true); } //文本 supplier_address.value = form_data2.supplier_address //文本 if ((form_data2['supplier_information_id'] && $check_field('set', 'supplier_address')) || (!form_data2['supplier_information_id'] && $check_field('add', 'supplier_address'))) { } else { $("#supplier_address").attr("disabled", true); $("#supplier_address > input[name='file']").attr('disabled', true); } //多文本 supply_items.value = form_data2.supply_items //多文本 if ((form_data2['supplier_information_id'] && $check_field('set', 'supply_items')) || (!form_data2['supplier_information_id'] && $check_field('add', 'supply_items'))) { } else { $("#supply_items").attr("disabled", true); $("#supply_items > input[name='file']").attr('disabled', true); } //多文本 supplier_description.value = form_data2.supplier_description //多文本 if ((form_data2['supplier_information_id'] && $check_field('set', 'supplier_description')) || (!form_data2['supplier_information_id'] && $check_field('add', 'supplier_description'))) { } else { $("#supplier_description").attr("disabled", true); $("#supplier_description > input[name='file']").attr('disabled', true); } layui.form.render("select"); } axios_get_3() } submit.onclick = async function () { try { //文本 form_data2.supplier_name = supplier_name.value //文本 //文本 form_data2.suppliers_phone_number = suppliers_phone_number.value //文本 //文本 form_data2.supplier_address = supplier_address.value //文本 //多文本 form_data2.supply_items = supply_items.value //多文本 //多文本 form_data2.supplier_description = supplier_description.value //多文本 } catch (err) { console.log(err) } let customize_field = [] customize_field.push({ "field_name": "供应商名称", "field_value": form_data2.supplier_name }); customize_field.push({ "field_name": "供应商电话", "field_value": form_data2.suppliers_phone_number }); customize_field.push({ "field_name": "供应商地址", "field_value": form_data2.supplier_address }); customize_field.push({ "field_name": "供应物品", "field_value": form_data2.supply_items }); customize_field.push({ "field_name": "供应商描述", "field_value": form_data2.supplier_description }); if (supplier_information_id == '') { console.log("新增/Add") const { data: res } = await axios.post(BaseUrl + '/api/supplier_information/add?', form_data2, { headers: { 'x-auth-token': token, 'Content-Type': 'application/json' } }) if (res.result == 1) { layer.msg('确认完毕'); setTimeout(function () { colseLayer() }, 1000) } else { layer.msg(res.error.message); } } else { console.log("详情/Details") const { data: res } = await axios.post(BaseUrl + '/api/supplier_information/set?supplier_information_id=' + supplier_information_id, form_data2, { headers: { 'x-auth-token': token, 'Content-Type': 'application/json' } }) if (res.result == 1) { layer.msg('确认完毕'); setTimeout(function () { colseLayer() }, 1000) } else { layer.msg(res.error.message); } } } }) ; </script> </html> 分析我给的代码,代码对应的功能是什么,不要自己写

<template> <a-spin :spinning="loading"> <ColumnCheck v-if="showColumnCheck" :columns="columns" :showColumnCache="showColumnCache" :showColumns="showColumns" @change="onCheckBoxChange"></ColumnCheck> <template #checkbox> <a-checkbox v-if="tableData.length" :indeterminate="indeterminate" :checked="checkAll" @change="onCheckAllChange"> </a-checkbox> </template> <a-checkbox-group class="sc-table-body" v-model="selectedRowKeys" @change="selectionChange"> <TrComponent v-model="selectedRowKeys" :rowKey="rowKey" :serialKey="serialKey" :columns="columnsShow" :data="tableData" :checkStrictly="checkStrictly" :theadNums="theadNums" :checkDisabled="checkDisabled" @updateCheckAllKeys="updateCheckAllKeys" @updateCheckAllRows="updateCheckAllRows"> <template v-for="c in scopedSlots" :slot="c.scopedSlots && c.scopedSlots.customRender ? c.scopedSlots.customRender : ''" slot-scope="{row, text}"> <slot :name="c.scopedSlots.customRender" :row="row" :text="text" ></slot> </template> <template slot="expandedRowRender" slot-scope="{row}"> <slot name="expandedRowRender" :row="row"></slot> </template> </TrComponent> </a-checkbox-group> <a-pagination v-if="isPagination && page.total" v-model="page.current" :total="page.total" :pageSize.sync="page.pageSize" :page-size-options="pagination.pageSizeOptions" :show-size-changer="pagination.showSizeChanger" :show-quick-jumper="pagination.showQuickJumper" :show-total="pagination.showTotal" @change="getTableList" @showSizeChange="getTableList" ></a-pagination> </a-spin> </template> <script> import { httpAction } from '@ythApi/ythApiManage' export default { name: "", components: { ColumnCheck: () => import('./columnCheck'), TrComponent: () => import('./TrComponent'), TableColgroup: () => import('./tableColgroup'), TableHead: () => import('./tableHead'), }, props: { // 当columns 存在checkbox=true时有效,当前选中项的 rowKey 数组 value: { type: [Array], }, // 表格行 key 的取值,可以是字符串或一个函数,当checkbox=true时为必要的属性 rowKey: { type: [String, Function], default: 'id', }, // 表格序号key的取值 serialKey: { type: [String, Function], default: null, }, // 表格列的配置 columns: { type: Array, required: true, default: () => ([]), }, // 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 false。false关联,true不互相关联 checkStrictly: { type: Boolean, default: false, }, // 在显示复选框的情况下,节点选择框是否禁用, 返回true 或 false; checkDisabled: { type: Function }, // table 数据请求配置 tableServers: { type: Object, default: () => { return { server: null, method: 'post' } }, }, //查询参数 query: { type: Object, required: false, default: () => {} }, // 是否显示字段显隐项 showColumnCheck: { type: Boolean, default: true, }, // 是否使用显示字段显隐项缓存 showColumnCache: { ype: Boolean, default: false, }, // 初始化显影项 showColumns: { type: Array, required: false, default: () => ([]), }, // 是否分页查询 isPagination: { type: Boolean, required: false, default: true, }, // 分页参数 pagination: { type: Object, required: false, default: function () { return { total: 0, current: 1, pageSize: 10, pageSizeOptions: ['10', '20', '30'], showQuickJumper: true, showSizeChanger: true, showTotal: (total, range) => 共${total}条, } } }, // 查询参数是否装换 isToLine:{ type: Boolean, required: false, default: true, }, height: { type: String, }, }, data() { return { loading: false, page: { current: this.pagination.current || 1, total: this.pagination.total || 0, pageSize: this.pagination.pageSize || 10, }, theadNums: 0, tableWidth: 0, scopedSlots: [], tableData: [], columnsShow: this.showColumnCheck ? [] : this.columns, // 列表配置 updateComponent: '', checkAllKeys: [], checkAllRows: [], selectedRowKeys: this.value || [], // table checkbox 选中项的 rowKey 数组 selectedRows: [], // table checkbox 选中项的 rowKey 数组 } }, computed: { // 所有值 tableDataAll() { let tableData = this.tableData //递归获取子数据 function getAllData(dataArray) { let result = [] dataArray.forEach(item => { result.push(item) if (item.children&&item.children.length>0) { result = result.concat(getAllData(item.children)) } }) return result } return getAllData(tableData) }, // 设置 checkbox 是否全选状态 checkAll() { return this.selectedRowKeys.length === this.checkAllKeys.length; }, // 设置 checkbox indeterminate 状态,只负责样式控制 indeterminate() { return this.selectedRowKeys.length > 0 && this.selectedRowKeys.length < this.checkAllKeys.length; }, // 固定列 }, watch: { tableServers: { immediate: true, deep: true, handler: function(newVal,oldVal) { if (JSON.stringify(newVal)!==JSON.stringify(oldVal)) this.getTableList(1) } }, selectedRowKeys: { immediate: true, deep: true, handler: function(newVal,oldVal) { if (JSON.stringify(newVal)!==JSON.stringify(oldVal)) { this.$emit('input', newVal) } } } }, methods: { // 暴露给父级组件使用,作为静态注入数据 setTableData(data) { this.tableData = this.$listeners.recastData ? this.$listeners.recastData(data) : data }, //获取页面数据 getTableData() { return this.tableData }, //列表显隐回调 onCheckBoxChange({columns, tableWidth, scopedSlots, theadNums}) { this.theadNums = theadNums; this.tableWidth = tableWidth + 8 + 2; this.scopedSlots = scopedSlots; this.columnsShow = columns this.updateComponent = + new Date(); }, //刷新 reload () { this.getTableList(1) }, /** * 获取当前table页数据 * @param current {number} 当前页 */ getTableList(current) { if (!this.tableServers.server) return; if (!!current) this.page.current = current; // 初始化 this.loading = true this.tableData = [] const queryParams = this.getQueryParams() const action = this.tableServers.action || httpAction const method = this.tableServers.method || 'POST' action( this.tableServers.server, queryParams, method).then((res) => { let tableData = [] if (Array.isArray(res)) { this.pagination = false tableData = res } else if (Array.isArray(res.result)) { this.pagination = false tableData = res.result } else { if (this.isPagination && (res.total || res.total == 0)) { this.page.total = Array.isArray(res.total) ? res.total[0] : res.total; } tableData = res.result?.records || res.rows || [] } this.setTableData(tableData) // 接口数据加载成功,回调事件 this.$nextTick(()=>{ this.$emit("onLoadSuccess",this.tableData); }) }).finally(() => { this.loading = false }) }, /** * 获取查询参数 */ getQueryParams() { //基础参数 let data = { ...this.query } // 分页 if (this.isPagination) { data.page = this.page.current data.rows = this.page.pageSize } return data }, /** * 驼峰转换下划线 */ toLine(name) { return name.replace(/([A-Z])/g, '_$1').toLowerCase() }, /** * table checkbox 选中项发生改变时触发 */ selectionChange(checkedValues) { this.selectedRowKeys = checkedValues; this.selectedRows = checkedValues.map(value => { return this.tableDataAll.filter(row => row[this.rowKey] === value)[0] }) this.$emit('selection-change', checkedValues, this.selectedRows) }, /** * 全选发生变化时回调函数 */ onCheckAllChange(e) { this.selectedRowKeys = e.target.checked ? this.checkAllKeys : []; this.selectedRows = e.target.checked ? this.checkAllRows : []; }, /** * 更新当前选中checkbox全部keys数据 */ updateCheckAllKeys(allKeys) { this.checkAllKeys = allKeys }, /** * 更新当前选中checkbox全部rows数据 * */ updateCheckAllRows(allRows) { this.checkAllRows = allRows } } } </script> <style lang="less" > .sc-table { width: 100%; height: 100%; min-height: 300px; display: flex; flex-direction: column; .sc-table-content { width: 100%; flex: 1; overflow: auto; background-color: #ffffff; .sc-table-scroll { min-width: 100%; height: 100%; display: flex; flex-direction: column; table { width: 100%; background-color: #ffffff; border-style: solid; border-color: #e8e8e8; table-layout: fixed; } .sc-table-thead { overflow: hidden scroll; } .sc-table-body { width: 100%; flex: 1; overflow: hidden scroll; } } } } .sc-table-header, .sc-table-footer { display: flex; justify-content: space-between; margin: 10px 0; } .sc-table-header { .btn-icon { border-radius: 50%; border: 1px solid #d9d9d9; background-color: #fff; width: 32px; height: 32px; line-height: 32px; text-align: center; font-size: 15px; cursor: pointer; &:hover { opacity: .9; } } } .sc-table-level { .icon-expand { margin-right: 8px; cursor: pointer; display: inline-block; &::before { content: '-'; width: 18px; height: 18px; display: flex; justify-content: center; align-items: center; border: 1px solid #e8e8e8; border-radius: 2px; background-color: #fff; box-sizing: border-box; transition: color .2s, border-color .2s; } &:hover::before { color: #40a9ff; border-color: #40a9ff; } } tr[data-row-collapse="false"] { .icon-expand::before { content: '+'; } } tr.table-expanded-row { > td { padding: 0; > * { padding: 10px; } > a, > span { display: inline-block; } } } tr.table-expanded-row, tr.table-expanded-row:hover { background: #fbfbfb; } tr:hover { background: rgba(24, 144, 255, 0.18); } // tr[data-highlight-1] { // background: rgba(24, 144, 255, 0.08); // } // tr[data-highlight-2] { // background: rgba(24, 144, 255, 0.06); // } // tbody > tr:nth-of-type(2n) { // background: rgba(24, 144, 255, 0.03); // } tr { transition: background 1s; } th { background: #fafafa; } td { word-wrap: break-word; overflow-wrap: break-word; white-space: normal; } td, th { padding: 10px; box-sizing: border-box; } } </style> 该组件的构成为
<显隐组件>
<content> <表格标题组件head> <表格内容组件body> </content>
<分页组件>
解决方案同时满足以下条件: 1.表头在y轴滚动时固定 2.x轴和y轴滚动条始终可见 3.复选框和序号列随x轴滚动而滚动

package com.alibaba.otter.canal.server.embedded.handle.oracle; import com.alibaba.otter.canal.instance.core.CanalInstance; import com.alibaba.otter.canal.meta.FileMixedMetaManager; import com.alibaba.otter.canal.parse.CanalEventParser; import com.alibaba.otter.canal.parse.inbound.mysql.MysqlEventParser; import com.alibaba.otter.canal.parse.support.AuthenticationInfo; import com.alibaba.otter.canal.protocol.FlatMessage; import com.alibaba.otter.canal.protocol.Message; import com.alibaba.otter.canal.server.embedded.handle.Constant; import com.alibaba.otter.canal.server.embedded.handle.NotMysqlHandler; import com.alibaba.otter.canal.server.exception.CanalServerException; import com.alibaba.otter.canal.utils.OracleUtil; import com.google.common.collect.Lists; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.sql.*; import java.text.SimpleDateFormat; import java.util.Date; import java.util.*; import java.util.concurrent.atomic.AtomicLong; /** * @author : yangpeng * create at: 2022/5/21 13:09 * @description: oracle连接实现 */ public class OracleHandler implements NotMysqlHandler { private static final Logger logger = LoggerFactory.getLogger(OracleHandler.class); private final static String ORACLE_DRIVER_URL = "jdbc:oracle:thin:@"; private final static String DATA_DICTIONARY_PATH = "/oracle/oradata/orcl/logmnr"; private Connection connection; private AuthenticationInfo authInfo; private AtomicLong currentScn = new AtomicLong(0); private Map<String, List<String>> pkMaps; private List<String> whiteList; //归档日志本文件最后一个点位-1, 下一个文件的开始点位 private Long nextChange = null; private Long firstChange = null; private String currentModel; private String currentRedoGroups; private String lastRedoGroups; private String opt = ">="; private List<String> action = new ArrayList<>(); static { //加载驱动 try { Class.forName("oracle.jdbc.OracleDriver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } @Override public void subscribe(CanalInstance canalInstance) { //数据库基本信息 CanalEventParser eventParser = canalInstance.getEventParser(); MysqlEventParser mysqlEventParser = (MysqlEventParser) eventParser; this.authInfo = mysqlEventParser.getMasterInfo(); connect(); String filterRegex = authInfo.getFilterRegex(); if(!org.springframework.util.StringUtils.isEmpty(filterRegex) && !filterRegex.endsWith(".*")){ whiteList = Arrays.asList(filterRegex.split(",")); } String index = FileMixedMetaManager.getScnLocal(canalInstance.getDestination()); //首次启动, 文件中的index是0, 同步类型: 全量+增量时使用衔接点位作为开始点位, //增量时衔接点位可能为空, 为空时获取系统最新点位作为开始点位 if("0".equals(index)){ String scn = authInfo.getTimestamp(); if(StringUtils.isEmpty(scn)) scn = getStartPosition(); try { this.currentScn.set(Long.valueOf(scn)); FileMixedMetaManager.saveScnLocal(canalInstance.getDestination(), Long.valueOf(scn)); } catch (IOException e) { e.printStackTrace(); } }else{ //index.scn文件中是非0点位, 说明当前实例运行被停止了或者被删除后实例重建了, 再次启动时比较文件点位和衔接点位 //衔接点位大于文件点位说明是删除后实例重建了, 删除后重建实例应使用最新的衔接点位开始增量同步 //衔接点位小于等于文件点位说明是暂停后重启了, 暂停后重启应使用文件中点位继续增量同步 String scn = authInfo.getTimestamp(); if(!StringUtils.isEmpty(scn) && Long.valueOf(scn) > Long.valueOf(index)){ try { this.currentScn.set(Long.valueOf(scn)); FileMixedMetaManager.saveScnLocal(canalInstance.getDestination(), Long.valueOf(scn)); } catch (IOException e) { e.printStackTrace(); } }else{ opt = ">"; this.currentScn.set(Long.valueOf(index)); } } if(StringUtils.isEmpty(authInfo.getAction())){ action.add("INSERT"); action.add("UPDATE"); action.add("DELETE"); }else{ action = Arrays.asList(authInfo.getAction().split(",")); } logger.warn("开始分析的点位:"+this.currentScn.get()); currentRedoGroups = queryCurrentRedoGroups(); lastRedoGroups = currentRedoGroups; queryLastArchivedLogScn(); authInfo.setArchived(true); updateSettings(); //获取主键 pkMaps = new HashMap<>(); for (String table : whiteList) { List<String> pkNames = getPkNames(authInfo.getDefaultDatabaseName(), table); pkMaps.put(authInfo.getDefaultDatabaseName()+"."+table, pkNames); } } private String queryCurrentRedoGroups(){ //获取当前正在使用的redo日志文件的相关组信息 Statement statement = null; ResultSet resultSet = null; try { statement = connection.createStatement(); resultSet = statement.executeQuery("SELECT GROUP# FROM V$LOG WHERE STATUS = 'CURRENT'"); resultSet.next(); return resultSet.getString(1); } catch (SQLException e) { logger.warn("Query Current Redo Group failed!"); } finally { close(statement, resultSet); } return ""; } private void queryLastArchivedLogScn(){ Statement statement = null; ResultSet resultSet = null; try { logger.warn("获取最后一个归档日志的开始点位和结束点位..."); statement = connection.createStatement(); String sql = "SELECT FIRST_CHANGE#, NEXT_CHANGE# FROM v$archived_log WHERE DEST_ID = 1 ORDER BY RECID DESC"; logger.warn(sql); resultSet = statement.executeQuery(sql); if(resultSet.next()){ firstChange = resultSet.getLong(1); nextChange = resultSet.getLong(2); } } catch (Exception e) { logger.warn("query last one Archive log startScn and endScn failed!"); } finally { close(statement, resultSet); } } private void isArchived(ArrayList<String> logFiles){ Statement statement = null; ResultSet resultSet = null; try { logger.warn("分析归档日志..."); currentModel = "archive"; statement = connection.createStatement(); String sql = "SELECT NAME, FIRST_CHANGE#, NEXT_CHANGE# FROM v$archived_log WHERE DEST_ID = 1 AND " + "FIRST_CHANGE# <= "+currentScn.get()+" AND NEXT_CHANGE# > "+currentScn.get(); logger.warn(sql); resultSet = statement.executeQuery(sql); if(resultSet.next()){ String NAME = resultSet.getString(1); firstChange = resultSet.getLong(2); nextChange = resultSet.getLong(3); //添加归档日志 logFiles.add(NAME); if(!Objects.equals(currentRedoGroups, lastRedoGroups)){ lastRedoGroups = currentRedoGroups; } } if(CollectionUtils.isEmpty(logFiles) && !Objects.equals(currentRedoGroups, lastRedoGroups)){ int num = 0; while (true){ resultSet = statement.executeQuery(sql); if(resultSet.next()){ String NAME = resultSet.getString(1); firstChange = resultSet.getLong(2); nextChange = resultSet.getLong(3); //添加归档日志 logFiles.add(NAME); } //在线日志写入到归档异步进行, 可能刚切换归档日志还没有落地, 若没获取到日志文件就阻塞, 最大60秒 if(CollectionUtils.isEmpty(logFiles)){ Thread.sleep(1000L); num++; logger.warn("在线日志写入到归档未完成, 线程已等待: "+num+"秒"); if(num >= 60) { logger.warn("在线日志写入到归档未完成等待60秒未完成, 默认放弃分析本次归档日志, 可能造成数据丢失, 丢失可能发生的点位:"+currentScn.get()); break; } } else{ lastRedoGroups = currentRedoGroups; break; } } } } catch (Exception e) { logger.warn("Archive log parsing failed!"); } finally { close(statement, resultSet); } } private void addLogfile(ArrayList<String> logFiles){ if(CollectionUtils.isEmpty(logFiles)){ Statement statement = null; ResultSet resultSet = null; try { logger.warn("分析在线日志..."); currentModel = "online"; statement = connection.createStatement(); //分析前校验日志组是否发生了变更, 发生变更的话从分析归档开始 String group = queryCurrentRedoGroups(); if(!currentRedoGroups.equals(group)){ currentRedoGroups = group; isArchived(logFiles); }else{ String sql = "SELECT f.member,g.group#,g.FIRST_CHANGE# FROM v$log g left join v$logfile f on g.group# = f.group# " + "where g.FIRST_CHANGE# <= " + currentScn.get() + " AND g.NEXT_CHANGE# > " + currentScn.get(); resultSet = statement.executeQuery(sql); logger.warn(sql); while (resultSet.next()) { String fileName = resultSet.getString(1); if(!fileName.contains("sredo")){ logFiles.add(fileName); firstChange = resultSet.getLong(3); } } } } catch (SQLException e) { logger.warn("Description Failed to query online logs!"); } finally { close(statement, resultSet); } } } private void offsetLogfile(ArrayList<String> logFiles){ if(CollectionUtils.isEmpty(logFiles)){ Statement statement = null; ResultSet resultSet = null; try { logger.warn("补偿措施, 分析最新的在线日志..."); currentModel = "online"; statement = connection.createStatement(); String sql = "SELECT f.member,g.group#,g.FIRST_CHANGE# FROM v$log g left join v$logfile f on g.group# = f.group# where g.status = 'CURRENT'"; resultSet = statement.executeQuery(sql); logger.warn(sql); while (resultSet.next()) { String fileName = resultSet.getString(1); if(!fileName.contains("sredo")){ logFiles.add(fileName); firstChange = resultSet.getLong(3); } } if(!CollectionUtils.isEmpty(logFiles)){ currentScn.set(firstChange); opt = ">="; } } catch (SQLException e) { logger.warn("Description Failed to query online logs!"); } finally { close(statement, resultSet); } } } private void setSqlTrace() { String setSqlTraceSql = "BEGIN\n" + " DBMS_SESSION.SET_SQL_TRACE(FALSE);\n" + " END;"; logger.warn(setSqlTraceSql); getCallableStatement(setSqlTraceSql); } private void setLogAnalyseDir(){ String createDictSql = "BEGIN dbms_logmnr_d.build(dictionary_filename =>'" + authInfo.getOraName() + "', dictionary_location =>'" + DATA_DICTIONARY_PATH + "'); END;"; logger.warn(createDictSql); getCallableStatement(createDictSql); } private void addAllfile(ArrayList<String> logFiles){ StringBuilder sbSQL = new StringBuilder(); //logmnr最大分析文件个数3 for (int i = 0; i < logFiles.size(); i++) { if (i == 0) { sbSQL.append("BEGIN\n"); } sbSQL.append("dbms_logmnr.add_logfile(logfilename=>'").append(logFiles.get(i)).append("',options=>dbms_logmnr.") .append(i == 0 ? "new" : "addfile").append(");\n"); } logFiles.clear(); sbSQL.append("END;\n"); logger.warn(sbSQL.toString()); getCallableStatement(sbSQL.toString()); } public void updateSettings() { ArrayList<String> logFiles = new ArrayList<>(); // 禁用当前会话的SQL跟踪功能 setSqlTrace(); // 设置日志分析目录 // setLogAnalyseDir(); // 是否分析归档日志 isArchived(logFiles); // 分析在线日志 addLogfile(logFiles); // 归档和在线都没有符合点位的文件, 可能是停机久了再次启动时归档文件被迁移或删除了, 从最新的在线日志文件开始点位开始分析 if (logFiles.isEmpty()) offsetLogfile(logFiles); // 归档丢失, 在线不存在, 数据库可能出现故障 if (logFiles.isEmpty()) throw new RuntimeException("The Oracle log file was not read"); // 添加所有日志文件 addAllfile(logFiles); try { // 开启logmnr视图 String startsql = "BEGIN\n" + "dbms_logmnr.start_logmnr(startScn=>'" + currentScn.get() + "'" //开始的scn号 // + ",dictfilename=>'" + DATA_DICTIONARY_PATH + "/" + authInfo.getOraName() + "'" //字典路径 + ",options=>" + " dbms_logmnr.dict_from_online_catalog" + //使用在线数据字典来查看相关的元数据信息 " + dbms_logmnr.skip_corruption" + //跳过损坏的日志快 " + dbms_logmnr.no_sql_delimiter" + //去掉SQL分隔符 " + dbms_logmnr.no_rowid_in_stmt" + //去掉SQL中的ROWID // " + dbms_logmnr.committed_data_only" + //去掉未提交事务的数据 " + dbms_logmnr.string_literals_in_stmt) ;\n" //输出SQL语句中的字符串常量 + "END;"; logger.warn(startsql); getCallableStatement(startsql); logger.warn("dbms_logmnr successfully! 当前分析模式: " + currentModel); } catch (Exception e) { logger.error(e.getMessage(), e); } } private void logSwitchVerify(){ String crgs = queryCurrentRedoGroups(); if(StringUtils.isNotEmpty(crgs) && !crgs.equals(currentRedoGroups)){ currentRedoGroups = crgs; unsubscribe(); updateSettings(); } } @Override public synchronized Message getWithoutAck(CanalInstance canalInstance) { try { if (connection.isClosed()) { reconnect(); } } catch (Exception e) { logger.error(e.getMessage(), e); } logSwitchVerify(); List<FlatMessage> data = new ArrayList<>(); Statement statement = null; ResultSet resultSet = null; String sql =""; try { statement = connection.createStatement(); long endScn = currentScn.get(); String queryLogSql = "SELECT scn,operation,sql_redo,table_space,seg_owner,table_name,timestamp,csf FROM v$logmnr_contents " + "where scn %s %d and table_name = '%s' and operation in('INSERT','DELETE','UPDATE') order by scn asc"; //logger.warn(String.format(queryLogSql, opt, currentScn.get(), whiteList.get(0))); resultSet = statement.executeQuery(String.format(queryLogSql, opt, currentScn.get(), whiteList.get(0))); opt = ">"; long lastScn = currentScn.get(); while (resultSet.next()) { endScn = resultSet.getLong("scn"); String operation = resultSet.getString(Constant.operation); if(!action.contains(operation.toUpperCase())) continue; // String segOwner = resultSet.getString(Constant.seg_owner); // String tableSpace = resultSet.getString(Constant.table_space); String tableName = resultSet.getString(Constant.table_name); sql = resultSet.getString(Constant.sql_redo); logger.info("解析到 Oracle 日志中的 SQL,操作类型:{},SQL 内容:{}", operation, sql); if(StringUtils.isEmpty(sql)){ logger.error(operation + "->" + tableName+", sql为空默认跳过"); continue; } FlatMessage flatMessage = new FlatMessage(); String timestamp = resultSet.getString(Constant.timestamp); if(!StringUtils.isEmpty(timestamp)){ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); long es = sdf.parse(timestamp).getTime() / 1000; flatMessage.setEs(es); }else{ long es = new Date().getTime() / 1000; flatMessage.setEs(es); } flatMessage.setDatabase(authInfo.getDefaultDatabaseName()); flatMessage.setTable(tableName); flatMessage.setIsDdl(false); flatMessage.setType(operation); flatMessage.setTs(System.currentTimeMillis()); flatMessage.setSql(sql); OracleUtil.toMqformat(flatMessage);//转换sql为mq所需json格式 if(null == flatMessage.getData()) { logger.warn("异常SQL: " + sql); continue; } //主键设置 List<String> list = pkMaps.get(authInfo.getDefaultDatabaseName() + "." + tableName); if(!CollectionUtils.isEmpty(list)){ flatMessage.setPkNames(list); }else{ flatMessage.setPkNames(new ArrayList<>()); } data.add(flatMessage); } if(endScn != currentScn.get()){ currentScn.set(endScn); FileMixedMetaManager.saveScnLocal(canalInstance.getDestination(), currentScn.get()); } //本次的归档日志已分析结束, 结束本次会话, 开始下次分析 if("archive".equals(currentModel) && CollectionUtils.isEmpty(data)){ currentScn.set(nextChange); opt = ">="; FileMixedMetaManager.saveScnLocal(canalInstance.getDestination(), currentScn.get()); logger.warn("本轮归档日志已分析结束, 结束本次会话, 开始下次分析, 开始点位: "+nextChange); unsubscribe(); updateSettings(); } if (!CollectionUtils.isEmpty(data)) { return new Message(lastScn, data, Constant.NOT_MYSQL); } } catch (Exception e) { logger.error("oracle logminer select v$logmnr_contents failed"); logger.warn(sql); StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); // 将出错的栈信息输出到printWriter中 e.printStackTrace(pw); pw.flush(); sw.flush(); try { sw.close(); } catch (IOException ex) { throw new RuntimeException(ex); } pw.close(); unsubscribe(); updateSettings(); } finally { close(statement, resultSet); } return new Message(-1, true, null); } //TRIGGER private String getStartPosition() { String sql = "SELECT MAX(CURRENT_SCN) CURRENT_SCN FROM GV$DATABASE"; Statement statement = null; ResultSet resultSet = null; try { statement = connection.createStatement(); resultSet = statement.executeQuery(sql); while (resultSet.next()) { long scn = resultSet.getLong(1); return String.valueOf(scn); } } catch (SQLException e) { e.printStackTrace(); } finally { close(statement, resultSet); } return "0"; } public List<String> getPkNames(String datasourceName, String tableName) { List<String> pkNames = Lists.newArrayList(); Statement statement = null; ResultSet resultSet = null; try { DatabaseMetaData metaData = connection.getMetaData(); resultSet = metaData.getPrimaryKeys(datasourceName, null, tableName); while (resultSet.next()) { pkNames.add(resultSet.getString("COLUMN_NAME")); } return pkNames; } catch (Exception e) { logger.error(datasourceName+"."+tableName+"oracle get table primary key returns null", e); } finally { close(statement, resultSet); } return null; } private void close(Statement statement, ResultSet resultSet){ try { if (resultSet != null) resultSet.close(); } catch (SQLException e1) { e1.printStackTrace(); } finally { try { if (statement != null) statement.close(); } catch (SQLException e) { e.printStackTrace(); } } } @Override public void connect() { String jdbcUrl = ORACLE_DRIVER_URL + authInfo.getAddress().getHostString() + ":" + authInfo.getAddress().getPort() + ":" + authInfo.getServerName(); try { this.connection = DriverManager.getConnection(jdbcUrl, authInfo.getUsername(), authInfo.getPassword()); } catch (SQLException e) { e.printStackTrace(); } } @Override public void reconnect() { disconnect(); connect(); } @Override public void disconnect() { if (connection != null) { try { unsubscribe(); connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } @Override public void ack(CanalInstance canalInstance) { return; } @Override public boolean checkConsistent(CanalInstance canalInstance) { CanalEventParser eventParser = canalInstance.getEventParser(); MysqlEventParser parser = (MysqlEventParser) eventParser; AuthenticationInfo newAuthInfo = parser.getMasterInfo(); String newUrl = newAuthInfo.getAddress().getHostName() + newAuthInfo.getAddress().getPort(); String oldUrl = this.authInfo.getAddress().getHostName() + this.authInfo.getAddress().getPort(); if (Objects.equals(newUrl, oldUrl)) { return true; } return false; } @Override //回退到指定位置 public void rollback(CanalInstance canalInstance, Long batchId) { try { FileMixedMetaManager.saveScnLocal(canalInstance.getDestination(), batchId); currentScn.set(batchId); } catch (IOException e) { e.printStackTrace(); } } public void getCallableStatement(String sql) { CallableStatement callableStatement = null; try { callableStatement = connection.prepareCall(sql); callableStatement.execute(); } catch (SQLException e) { logger.error(e.getMessage(), e); e.printStackTrace(); StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); // 将出错的栈信息输出到printWriter中 e.printStackTrace(pw); pw.flush(); sw.flush(); try { sw.close(); } catch (IOException ex) { throw new RuntimeException(ex); } pw.close(); if (e.getErrorCode() == 1291) { throw new CanalServerException("1291"); } } finally { close(callableStatement, null); } } /** * description: 关闭查询会话 * * @param {} * @return void */ public void unsubscribe() { String endLogmnrSql = "BEGIN\n" + " dbms_logmnr.end_logmnr;\n" + " END;"; logger.warn(endLogmnrSql); getCallableStatement(endLogmnrSql); logger.warn("end_logmnr succeeded !"); } } 看下这个代码怎么加日志来排查问题

最新推荐

recommend-type

让你训练好的YOLO模型为你的数据集做标签_Let your trained YOLO model do labelli

让你训练好的YOLO模型为你的数据集做标签_Let your trained YOLO model do labelling for your dataset.zip
recommend-type

电化学CO2还原的一维气体扩散电极传输模型.zip

1.版本:matlab2014a/2019b/2024b 2.附赠案例数据可直接运行。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
recommend-type

基于Matlab的轴承内圈故障信号模拟代码 信号处理

内容概要:本文深入探讨了利用Matlab进行轴承内圈故障信号的模拟及其图像生成的方法。文中详细介绍了故障信号的生成步骤,包括设定仿真参数如采样频率、故障频率、故障幅值和噪声方差,使用正弦波函数生成故障信号并加入高斯白噪声,最后通过Matlab内置函数绘制时域和频谱图像。此外,文章强调了这种模拟方法对机械故障诊断的实际应用价值,指出它有助于更好地理解和分析故障特征,从而提升故障诊断的准确性。 适合人群:从事机械工程、故障诊断研究的技术人员,尤其是那些希望深入了解Matlab在机械故障模拟方面应用的研究者。 使用场景及目标:适用于需要模拟和分析轴承内圈故障特性的场合,旨在帮助研究人员掌握故障信号的生成和分析技巧,提高故障诊断能力。 其他说明:文章不仅提供了完整的Matlab代码示例,还有详尽的代码注释,确保读者能够轻松上手并理解每个操作的具体含义。
recommend-type

一个开源、嵌入式、高性能的g-代码解析器和CNC铣削控制器,用优化的C编写,可以在Arduino上直接运行_An ope

一个开源、嵌入式、高性能的g-代码解析器和CNC铣削控制器,用优化的C编写,可以在Arduino上直接运行_An open source, embedded, high performance g-code-parser and CNC milling controller written in optimized C that will run on a straight Arduino.zip
recommend-type

移动互联网时代的网络运维策略研究.docx

移动互联网时代的网络运维策略研究.docx
recommend-type

清华大学袁春与华为李航合著的统计学习课件

统计学习方法是机器学习领域的重要分支,它将统计学的原理和方法应用在数据分析和模型构建上,尤其适用于解决那些不确定性和复杂性的数据问题。在本次分享的课件中,袁春和李航作为来自清华大学深圳研究生院和华为诺亚方舟实验室的专家,将为我们展示统计学习方法的理论与实践。 课件内容可能涵盖了以下几个主要知识点: 1. 统计学习的基本概念:首先,课件可能会介绍统计学习的定义,包括它是如何从统计学中独立出来,并与机器学习相结合,形成一套独立的理论体系的。同时,解释统计学习的核心思想,即利用数据来发现知识,并构建预测模型。 2. 常见统计学习模型:课程内容可能会包括线性回归、逻辑回归、决策树、随机森林、支持向量机(SVM)、神经网络等模型,这些模型是统计学习方法中经常使用到的。对于每一种模型,课件可能会详细讲解其数学原理、优缺点、适用场景以及如何在实际中应用。 3. 模型评估与选择:统计学习中的模型评估是确保模型性能和泛化能力的关键。课件可能会介绍交叉验证、AIC、BIC、ROC曲线、混淆矩阵等评估指标和方法。此外,还会讲解如何根据业务需求和评估结果选择合适的模型。 4. 正则化与模型优化:为防止过拟合,提升模型的泛化能力,统计学习方法中常常使用正则化技术。课件可能会涉及L1和L2正则化、岭回归(Ridge Regression)、套索回归(Lasso Regression)等技术,并展示如何通过正则化调整模型复杂度。 5. 统计学习在机器学习中的应用:统计学习方法不仅仅是一个理论体系,它在实际中也有广泛应用。例如,金融风险评估、生物信息学、推荐系统、自然语言处理等领域,课件可能会选取一些实际案例来说明统计学习方法的应用。 6. 统计学习前沿发展:课件可能会介绍统计学习领域的最新研究动态,包括最新算法的提出、统计学习与深度学习的结合等。 7. 课件中的实践操作:通常在理论学习之后,都会有实际操作环节,这可能包括使用R、Python等数据分析工具进行数据处理和模型构建。通过实际数据操作,学生可以更加深刻地理解统计学习方法的应用。 8. 与传统统计方法的区别:尽管统计学习方法和传统统计方法在很多方面是相通的,但它们在处理大数据、复杂数据结构以及预测模型构建方面存在明显差异。课件可能会明确指出这些差异,并突出统计学习方法的优势。 以上内容是根据标题、描述、标签以及文件名称列表推测的课件可能包含的知识点。学习这些内容,不仅能帮助学生掌握统计学习方法,而且能为学生在未来从事相关领域的工作和研究打下坚实的基础。
recommend-type

概率空间中的监督分类与文档分类方法探索

### 概率空间中的监督分类与文档分类方法探索 #### 1. 监督分类概述 在文档分类中,监督分类方法具有重要地位。这里主要聚焦于统计方法中的似然比方法。该方法通过不同类别依赖的统计模型来估计给定文档的概率,并利用这些概率的比率确定文档最可能所属的类别。 #### 2. 似然比框架 对于一个给定数据集划分为两个互斥类别A和B的情况,根据贝叶斯规则,给定观察文档D时每个类别的发生概率可表示为: - \(p(A|D) = p(D|A)× p(A)/p(D)\) (11.32a) - \(p(B|D) = p(D|B)× p(B)/p(D)\) (11.32b) 两式相除可得: \(p(
recommend-type

永磁同步电机矢量控制PID闭环函数

在永磁同步电机矢量控制中,PID(比例 - 积分 - 微分)控制器是常用的闭环控制方法,用于调节电机的各种参数,如电流、速度和位置等。以下是不同控制环中PID闭环函数的详细介绍: ### 电流环PID闭环函数 电流环主要用于控制电机的定子电流,通常分为直轴($d$轴)和交轴($q$轴)电流控制。PID控制器的输出是电压指令,用于控制逆变器的输出电压。 PID控制器的一般公式为: $$u(t) = K_p e(t) + K_i \int_{0}^{t} e(\tau) d\tau + K_d \frac{de(t)}{dt}$$ 在离散系统中,PID控制器的公式可以表示为: $$u(k)
recommend-type

GitHub新手入门:创建并发布您的第一个网站

标题所指的"emvillanueva.github.io"是一个网站地址,该地址使用GitHub Pages服务创建,它是GitHub提供的一项功能,允许开发者直接通过GitHub托管和发布静态网站。这一服务特别受那些希望简化网站部署流程的开发者欢迎,因为它无需传统服务器配置即可将网站上线。 从描述中,我们可以了解到几个关键知识点: 1. GitHub是一个为数百万开发人员共用的代码托管平台,支持版本控制和协作。它广泛用于开源项目,同时也有私有项目的服务,为开发人员提供了代码仓库、问题追踪、代码审查以及一系列其他功能。 2. GitHub Pages是GitHub提供的静态网站托管服务,允许用户通过特定的仓库来发布网站。用户可以通过简单地推送HTML、CSS、JavaScript文件到指定的分支(通常是master或main分支),而GitHub Pages将会自动发布这些文件到一个由GitHub分配的URL上。 3. 在GitHub中,"仓库"(repository)相当于项目的虚拟文件夹或容器,它包含项目的全部文件,包括源代码、文档、图像、数据等。此外,仓库还负责记录所有文件的更改历史,以及对这些文件进行版本控制。这种变更追踪机制意味着开发人员可以回滚到任何之前的状态,便于错误修复和版本迭代。 4. 在上述描述中,提到的一个项目仓库里包含三个关键文件:HTML文件、CSS文件和README文件。这些文件共同构成一个基本的网站结构: - HTML文件是网页的骨架,负责定义网页的结构和内容; - CSS文件负责网页的样式,包括颜色、字体以及其他视觉表现形式; - README文件通常用来描述项目的信息,例如项目的目的、使用说明和作者信息等。它通常以纯文本格式编写,但也可以用Markdown格式,以便于排版和展现更丰富的文档信息。 5. 描述中还提到了“JavaScript”,这是一种广泛应用于网页开发的编程语言,负责实现网页上的交互性和动态效果。虽然在这个上下文中并未明确指出JavaScript文件,但考虑到一个功能齐全的网站通常需要JavaScript来增加其功能性,因此可以推测仓库中可能还包含一个或多个JavaScript文件。 标签“JavaScript”强调了这一语言在现代网页开发中的重要性。它常用于网页的客户端脚本,可以操作文档对象模型(DOM),处理用户输入,制作动画效果,发送和接受服务器数据等。 压缩包子文件的文件名称列表中提到的"emvillanueva.github.io-master",意味着存在一个GitHub项目仓库的压缩包文件。通常,这种文件可以用来备份或传输仓库内容。在GitHub上,"master"或"main"分支是默认的主分支,存放着项目的最新稳定代码。 总结上述知识点,我们可以了解GitHub Pages服务的使用方法和优势、仓库的概念、文件类型对网站构成的重要性以及JavaScript在网页开发中的核心作用。这对于任何希望开始使用GitHub进行项目协作和网站发布的IT专业人士或爱好者都是非常重要的基础概念。
recommend-type

电影评论内容的极性分析与信息提取

### 电影评论内容的极性分析与信息提取 #### 1. 极性分析结论与方法概述 从相关结果中我们可以得出一些重要结论。首先,利用基于词频和分布的数学模型,特别是这里使用的向量空间模型方法,确实能够区分有观点内容的极性。而且,模型生成的面向领域和面向极性的表示维度的判别能力似乎相互独立。此外,结果还表明,至少对于本节所考虑的特定类型文本数据,极性判别比领域判别更具挑战性。 在极性估计方面,分析有观点内容的重要任务包括极性检测、强度估计、主观性检测和情感测量。这里我们主要关注前两项任务。极性检测是识别正负两个极性类别,而强度估计则是衡量给定内容的正负程度。从统计角度来看,我们采用基于似然比方