Index: ssts-recyclingapplication/src/main/java/com/forgon/disinfectsystem/recyclingapplication/dto/TousseItemDto.java =================================================================== diff -u --- ssts-recyclingapplication/src/main/java/com/forgon/disinfectsystem/recyclingapplication/dto/TousseItemDto.java (revision 0) +++ ssts-recyclingapplication/src/main/java/com/forgon/disinfectsystem/recyclingapplication/dto/TousseItemDto.java (revision 18606) @@ -0,0 +1,46 @@ +package com.forgon.disinfectsystem.recyclingapplication.dto; + + +/** + * @author zhonghaowen + * @apiNote 用来存放科室申领物品修改记录 + * @since 2017-06-29 + */ +public class TousseItemDto { + + private String name; + + private Integer amount; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getAmount() { + return amount; + } + + public void setAmount(Integer amount) { + this.amount = amount; + } + + public TousseItemDto() { + } + + public TousseItemDto(String name, Integer amount) { + this.name = name; + this.amount = amount; + } + + @Override + public String toString() { + return "TousseItemDto{" + + "name='" + name + '\'' + + ", amount=" + amount + + '}'; + } +} Index: ssts-recyclingapplication/src/main/java/com/forgon/disinfectsystem/recyclingapplication/service/RecyclingApplicationManagerImpl.java =================================================================== diff -u -r18560 -r18606 --- ssts-recyclingapplication/src/main/java/com/forgon/disinfectsystem/recyclingapplication/service/RecyclingApplicationManagerImpl.java (.../RecyclingApplicationManagerImpl.java) (revision 18560) +++ ssts-recyclingapplication/src/main/java/com/forgon/disinfectsystem/recyclingapplication/service/RecyclingApplicationManagerImpl.java (.../RecyclingApplicationManagerImpl.java) (revision 18606) @@ -21,6 +21,8 @@ import java.util.Set; import java.util.stream.Collectors; +import com.forgon.disinfectsystem.recyclingapplication.updateLog.ApplicationLogManager; +import com.forgon.log.enums.ApplicationLogStatusEnum; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import net.sf.json.JsonConfig; @@ -146,7 +148,13 @@ private InvoicePlanManager invoicePlanManager; private DiposableGoodBatchStockManager diposableGoodBatchStockManager; - + + private ApplicationLogManager applicationLogManager; + + public void setApplicationLogManager(ApplicationLogManager applicationLogManager) { + this.applicationLogManager = applicationLogManager; + } + public void setGoodsStockManager(GoodsStockManager goodsStockManager) { this.goodsStockManager = goodsStockManager; } @@ -426,6 +434,7 @@ } else { delete(invoicePlan); } + applicationLogManager.saveApplicationLog(invoicePlan, "申请单删除", ApplicationLogStatusEnum.DELETE); return true; } else { return false; Index: ssts-recyclingapplication/src/main/java/com/forgon/disinfectsystem/recyclingapplication/updateLog/ApplicationLogManager.java =================================================================== diff -u --- ssts-recyclingapplication/src/main/java/com/forgon/disinfectsystem/recyclingapplication/updateLog/ApplicationLogManager.java (revision 0) +++ ssts-recyclingapplication/src/main/java/com/forgon/disinfectsystem/recyclingapplication/updateLog/ApplicationLogManager.java (revision 18606) @@ -0,0 +1,35 @@ +package com.forgon.disinfectsystem.recyclingapplication.updateLog; + +import com.forgon.disinfectsystem.entity.invoicemanager.InvoicePlan; +import com.forgon.disinfectsystem.entity.recyclingapplication.RecyclingApplication; +import com.forgon.entity.PageEntity; +import com.forgon.log.enums.ApplicationLogStatusEnum; +import net.sf.json.JSONObject; + +/** + * @author zhonghaowen + * @apiNote 申请单修改记录服务层 + * @since 2017-07-03 + */ +public interface ApplicationLogManager { + + /** + * 保存申请单修改日志 + * + * @param recyclingApplication 对应的申请单 + * @param compareResult 日志的内容 + * @param applicationLogStatusEnum {@link ApplicationLogStatusEnum} + */ + void saveApplicationLog(InvoicePlan recyclingApplication, String compareResult, ApplicationLogStatusEnum applicationLogStatusEnum); + + + /** + * 读取申请单修改的历史记录 + * + * @param recyclingApplicationId 申请单的id + * @param pagePara 分页的对象 + */ + JSONObject loadRecyclingApplicationUpdateDetail(Long recyclingApplicationId, PageEntity pagePara); + + +} Index: ssts-recyclingapplication/src/main/java/com/forgon/disinfectsystem/recyclingapplication/service/RecyclingApplicationComparator.java =================================================================== diff -u --- ssts-recyclingapplication/src/main/java/com/forgon/disinfectsystem/recyclingapplication/service/RecyclingApplicationComparator.java (revision 0) +++ ssts-recyclingapplication/src/main/java/com/forgon/disinfectsystem/recyclingapplication/service/RecyclingApplicationComparator.java (revision 18606) @@ -0,0 +1,162 @@ +package com.forgon.disinfectsystem.recyclingapplication.service; + +import com.forgon.disinfectsystem.entity.assestmanagement.DisposableGoods; +import com.forgon.disinfectsystem.entity.recyclingapplication.RecyclingApplication; +import com.forgon.disinfectsystem.entity.tousseitem.TousseItem; +import com.forgon.disinfectsystem.recyclingapplication.dto.RecyclingApplicationDto; +import com.forgon.disinfectsystem.recyclingapplication.dto.TousseItemDto; +import com.forgon.disinfectsystem.vo.TousseItemVo; +import com.forgon.tools.compare.ObjectComparator; +import com.forgon.tools.util.ObjectUtil; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author zhonghaowen + * @apiNote 申请单比较类 + * @since 2017-06-29 + */ +public class RecyclingApplicationComparator extends ObjectComparator { + + + + /** + * 比较申请单对应的属性 + * + * @param oldRecyclingApplication 旧的申请单 + * @param newRecyclingApplication 新的申请单 + * @param newList 新申请单下的物品 + * @return 返回比较申请单的属性结果 + */ + public String doCompare(RecyclingApplication oldRecyclingApplication, RecyclingApplication newRecyclingApplication, Collection newList) { + String compareItemList = this.compareItemList(newList, oldRecyclingApplication.getApplicationItems()); + String compareRecyclingApplication = this.compareRecyclingApplication(oldRecyclingApplication, newRecyclingApplication); + if (compareRecyclingApplication.length() > 0){ + compareItemList += compareItemList.length() > 0 ? "," + compareRecyclingApplication : compareRecyclingApplication; + } + logger.warn("compareItemList:" + compareItemList); + return compareItemList; + } + + + /** + * 比较申请单 + * + * @param oldRecyclingApplication 旧的申请单 + * @param newRecyclingApplication 新的申请单 + * @return 返回比较后的结果 + */ + private String compareRecyclingApplication(RecyclingApplication oldRecyclingApplication, RecyclingApplication newRecyclingApplication) { + if (oldRecyclingApplication.getId() == null) { + return ""; + } + return this.compareDiff(oldRecyclingApplication, newRecyclingApplication); + } + + /** + * 比较申请单下的物品 + * + * @param newList 申请单下的新的物品 + * @param oldList 申请单下的旧的物品 + * @return 返回比较后的结果 + */ + private String compareItemList(Collection newList, List oldList) { + StringBuilder updateMsg = new StringBuilder(); + if (oldList == null) { + return updateMsg.toString(); + } + List newIds = newList.stream().map(TousseItemVo::getId).collect(Collectors.toList()); + List oldIds = oldList.stream().map(TousseItem::getId).collect(Collectors.toList()); + Collection deleteIds = CollectionUtils.subtract(new ArrayList<>(oldIds), new ArrayList<>(newIds)); + Map updateMap = new HashMap<>(); + Map addMap = new HashMap<>(); + Map oldMap = oldList.stream().collect(Collectors.toMap(TousseItem::getId, tousseItem -> new TousseItemDto(tousseItem.getTousseName(), tousseItem.getAmount()))); + for (TousseItemVo tousseItemVo : newList) { + Long id = tousseItemVo.getId(); + if (id != 0) { + updateMap.put(id, new TousseItemDto(tousseItemVo.getTousseName(), tousseItemVo.getAmount())); + continue; + } + Long goodId = DisposableGoods.TYPE_NAME.equals(tousseItemVo.getTousseType()) ? tousseItemVo.getDisposableGoodsId() : tousseItemVo.getTousseDefinitionID(); + //如果id是0表示是新增的 + addMap.put(goodId, new TousseItemDto(tousseItemVo.getTousseName(), tousseItemVo.getAmount())); + } + logger.debug("updateMap:" + updateMap); + logger.debug("addMap:" + addMap); + logger.debug("deleteIds:" + deleteIds); + this.handleItemListUpdate(updateMap, oldMap, updateMsg); + this.handleItemListDelete(deleteIds, oldMap, updateMsg); + this.handleItemListAdd(addMap, updateMsg); + String result = ObjectUtil.subStringLastBuilder(updateMsg); + logger.debug(result); + return result; + } + + + @Override + public Map toMap(RecyclingApplication recyclingApplication) { + Map map = new HashMap<>(); + map.put("处理科室", recyclingApplication.getHandleDepart()); + map.put("申请科室", recyclingApplication.getDepart()); + map.put("结算科室", recyclingApplication.getSettleAccountsDepart()); + map.put("备注", recyclingApplication.getRemark()); + return map; + } + + + /** + * 记录添加的TousseItem + * + * @param addMap 添加的TousseItemMap + * @param updateMsg 要记录的信息 + */ + private void handleItemListAdd(Map addMap, StringBuilder updateMsg) { + for (Map.Entry entry : addMap.entrySet()) { + TousseItemDto newTousseItemDto = entry.getValue(); + updateMsg.append(String.format("新增物品【%s】,数量为【%s】,", newTousseItemDto.getName(), newTousseItemDto.getAmount())); + } + } + + /** + * 记录删除的TousseItem + * + * @param deleteIds 删除的TousseItemList的id + * @param oldMap 原来的TousseItemMap + * @param updateMsg 要记录的信息 + */ + private void handleItemListDelete(Collection deleteIds, Map oldMap, StringBuilder updateMsg) { + for (Long deleteId : deleteIds) { + TousseItemDto oldTousseItemDto = MapUtils.getObject(oldMap, deleteId); + updateMsg.append(String.format("物品【%s】被删除,", oldTousseItemDto.getName())); + } + } + + /** + * 记录更新的TousseItem + * + * @param updateMap 更新的TousseItemMap + * @param oldMap 原来的TousseItemMap + * @param updateMsg 要记录的信息 + */ + private void handleItemListUpdate(Map updateMap, Map oldMap, StringBuilder updateMsg) { + for (Map.Entry entry : updateMap.entrySet()) { + TousseItemDto oldTousseItemDto = MapUtils.getObject(oldMap, entry.getKey()); + TousseItemDto newTousseItemDto = entry.getValue(); + Integer oldAmount = oldTousseItemDto.getAmount(); + Integer newAmount = newTousseItemDto.getAmount(); + if (!oldAmount.equals(newAmount)) { + updateMsg.append(String.format("物品【%s】从数量【%s】改成【%s】,", newTousseItemDto.getName(), oldAmount, newAmount)); + } + } + } + + +} Index: ssts-recyclingapplication/src/main/java/com/forgon/disinfectsystem/recyclingapplication/updateLog/ApplicationLogManagerImpl.java =================================================================== diff -u --- ssts-recyclingapplication/src/main/java/com/forgon/disinfectsystem/recyclingapplication/updateLog/ApplicationLogManagerImpl.java (revision 0) +++ ssts-recyclingapplication/src/main/java/com/forgon/disinfectsystem/recyclingapplication/updateLog/ApplicationLogManagerImpl.java (revision 18606) @@ -0,0 +1,96 @@ +package com.forgon.disinfectsystem.recyclingapplication.updateLog; + +import com.forgon.directory.acegi.tools.AcegiHelper; +import com.forgon.directory.vo.LoginUserData; +import com.forgon.disinfectsystem.entity.invoicemanager.InvoicePlan; +import com.forgon.disinfectsystem.entity.recyclingapplication.RecyclingApplication; +import com.forgon.disinfectsystem.entity.tousseitem.TousseItem; +import com.forgon.entity.PageEntity; +import com.forgon.log.enums.ApplicationLogStatusEnum; +import com.forgon.log.model.recyclingapplication.ApplicationLog; +import com.forgon.tools.hibernate.ObjectDao; +import com.forgon.tools.json.DateJsonValueProcessor; +import com.forgon.tools.util.ObjectUtil; +import net.sf.json.JSONArray; +import net.sf.json.JSONObject; +import net.sf.json.JsonConfig; +import org.apache.commons.lang3.StringUtils; +import org.apache.log4j.Logger; + +import java.util.Date; +import java.util.List; + +/** + * @author zhonghaowen + * @apiNote + * @since 2017-07-03 + */ +public class ApplicationLogManagerImpl implements ApplicationLogManager { + + protected Logger logger = Logger.getLogger(this.getClass()); + + private ObjectDao objectDao; + + public void setObjectDao(ObjectDao objectDao) { + this.objectDao = objectDao; + } + + @Override + public void saveApplicationLog(InvoicePlan recyclingApplication, String compareResult, ApplicationLogStatusEnum applicationLogStatusEnum) { + try { + LoginUserData loginUser = AcegiHelper.getLoginUser(); + String updateDetail = StringUtils.isBlank(compareResult) ? this.buildCreateLog(recyclingApplication) : compareResult; + ApplicationLog applicationLog = new ApplicationLog(updateDetail, recyclingApplication.getId(), loginUser, new Date(), applicationLogStatusEnum); + logger.debug("applicationLog:" + applicationLog); + objectDao.saveOrUpdate(applicationLog); + } + catch (Exception e) { + logger.error(e.getMessage()); + throw new RuntimeException(e); + } + } + + + @Override + public JSONObject loadRecyclingApplicationUpdateDetail(Long recyclingApplicationId, PageEntity pagePara) { + String hql = " where recyclingApplicationId = ? "; + @SuppressWarnings("unchecked") + List list = objectDao.findHqlByParam(" from ApplicationLog " + hql, new Object[]{recyclingApplicationId}, pagePara.getStart(), pagePara.getLimit(), ApplicationLog.class); + int size = objectDao.countObjectBySql(ApplicationLog.class.getSimpleName(), hql, new Object[]{recyclingApplicationId}); + return this.buildUpdateDetailResult(list, size); + } + + + /** + * 构造申请单修改记录详情返回页面结果详情 + * + * @param list 申请单修改记录列表 + */ + private JSONObject buildUpdateDetailResult(List list, int size) { + JSONObject result = new JSONObject(); + JsonConfig cfg = new JsonConfig(); + //这里要使用这个转换器转一下,因为修改时间在hibernate查询出来是timestamp类型,页面会识别不了 + cfg.registerJsonValueProcessor(Date.class, new DateJsonValueProcessor("yyyy-MM-dd HH:mm")); + JSONArray array = JSONArray.fromObject(list, cfg); + result.put("data", array); + result.put("size", size); + return result; + } + + /** + * 构造申请单修改记录(用于申请单的创建时) + * + * @param recyclingApplication 申请单对象 + * @return 返回构造好的修改记录 + */ + private String buildCreateLog(InvoicePlan recyclingApplication) { + StringBuilder result = new StringBuilder(); + result.append(String.format("%s创建,申请科室为【%s】,结算科室为【%s】,处理科室为【%s】,物品如下:", recyclingApplication.getType(), recyclingApplication.getDepart(), recyclingApplication.getSettleAccountsDepart(), recyclingApplication.getHandleDepart())); + List applicationItems = recyclingApplication.getApplicationItems(); + for (TousseItem applicationItem : applicationItems) { + result.append(String.format("【%s】数量为:%s,", applicationItem.getTousseName(), applicationItem.getAmount())); + } + return ObjectUtil.subStringLastBuilder(result); + } + +} Index: forgon-core/src/main/java/com/forgon/log/model/recyclingapplication/ApplicationLog.java =================================================================== diff -u --- forgon-core/src/main/java/com/forgon/log/model/recyclingapplication/ApplicationLog.java (revision 0) +++ forgon-core/src/main/java/com/forgon/log/model/recyclingapplication/ApplicationLog.java (revision 18606) @@ -0,0 +1,158 @@ +package com.forgon.log.model.recyclingapplication; + +import com.forgon.directory.vo.LoginUserData; +import com.forgon.log.enums.ApplicationLogStatusEnum; +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Lob; +import javax.persistence.Table; +import java.util.Date; + +/** + * @author zhonghaowen + * @apiNote 申请单修改记录日志 + * @since 2017-06-30 + */ +@Entity +@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) +@Table(name = "ApplicationLog") +public class ApplicationLog { + + /** 主键id */ + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @Lob + @Column(length = 30000) + /** 修改的详细内容 */ + private String updateDetail; + + /** 申请单id */ + private Long recyclingApplicationId; + + /** 创建时间 */ + private Date createTime; + + /** 上次修改的用户id */ + private Long updateUserId; + + /** 上次修改的用户名字 */ + private String updateUser; + + /** 上次修改的用户部门id */ + private String updateUserDepart; + + /** 上次修改的用户部门名字 */ + private String updateUserDepartName; + + /** 申请单状态 */ + private ApplicationLogStatusEnum applicationLogStatusEnum; + + public ApplicationLog() { + } + + public ApplicationLog(String updateDetail, Long recyclingApplicationId, LoginUserData loginUser, Date createTime, ApplicationLogStatusEnum applicationLogStatusEnum) { + this.createTime = createTime; + this.updateDetail = updateDetail; + this.recyclingApplicationId = recyclingApplicationId; + this.applicationLogStatusEnum = applicationLogStatusEnum; + this.updateUserId = loginUser.getUserId(); + this.updateUser = loginUser.getUserFullName(); + this.updateUserDepart = loginUser.getCurrentOrgUnitCode(); + this.updateUserDepartName = loginUser.getCurrentOrgUnitName(); + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getRecyclingApplicationId() { + return recyclingApplicationId; + } + + public void setRecyclingApplicationId(Long recyclingApplicationId) { + this.recyclingApplicationId = recyclingApplicationId; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Long getUpdateUserId() { + return updateUserId; + } + + public void setUpdateUserId(Long updateUserId) { + this.updateUserId = updateUserId; + } + + public String getUpdateUser() { + return updateUser; + } + + public void setUpdateUser(String updateUser) { + this.updateUser = updateUser; + } + + public String getUpdateUserDepart() { + return updateUserDepart; + } + + public void setUpdateUserDepart(String updateUserDepart) { + this.updateUserDepart = updateUserDepart; + } + + public String getUpdateUserDepartName() { + return updateUserDepartName; + } + + public void setUpdateUserDepartName(String updateUserDepartName) { + this.updateUserDepartName = updateUserDepartName; + } + + public String getUpdateDetail() { + return updateDetail; + } + + public void setUpdateDetail(String updateDetail) { + this.updateDetail = updateDetail; + } + + public ApplicationLogStatusEnum getApplicationLogStatusEnum() { + return applicationLogStatusEnum; + } + + public void setApplicationLogStatusEnum(ApplicationLogStatusEnum applicationLogStatusEnum) { + this.applicationLogStatusEnum = applicationLogStatusEnum; + } + + @Override + public String toString() { + return "ApplicationLog{" + + "id=" + id + + ", updateDetail='" + updateDetail + '\'' + + ", recyclingApplicationId=" + recyclingApplicationId + + ", createTime=" + createTime + + ", updateUserId=" + updateUserId + + ", updateUser='" + updateUser + '\'' + + ", updateUserDepart='" + updateUserDepart + '\'' + + ", updateUserDepartName='" + updateUserDepartName + '\'' + + ", applicationLogStatusEnum=" + applicationLogStatusEnum + + '}'; + } +} Index: ssts-recyclingapplication/src/main/java/com/forgon/disinfectsystem/recyclingapplication/action/RecyclingApplicationAction.java =================================================================== diff -u -r18570 -r18606 --- ssts-recyclingapplication/src/main/java/com/forgon/disinfectsystem/recyclingapplication/action/RecyclingApplicationAction.java (.../RecyclingApplicationAction.java) (revision 18570) +++ ssts-recyclingapplication/src/main/java/com/forgon/disinfectsystem/recyclingapplication/action/RecyclingApplicationAction.java (.../RecyclingApplicationAction.java) (revision 18606) @@ -18,12 +18,17 @@ import javax.servlet.http.HttpServletResponse; import com.forgon.disinfectsystem.recyclingapplication.result.LoadTousseLeaseItemsResultBuilder; +import com.forgon.disinfectsystem.recyclingapplication.service.RecyclingApplicationComparator; +import com.forgon.disinfectsystem.recyclingapplication.updateLog.ApplicationLogManager; +import com.forgon.log.enums.ApplicationLogStatusEnum; +import com.forgon.tools.util.PageUtil; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import net.sf.json.JsonConfig; import net.sf.json.util.CycleDetectionStrategy; import net.sf.json.util.PropertyFilter; +import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.Predicate; import org.apache.commons.collections4.Transformer; @@ -98,6 +103,8 @@ private RecyclingApplication recyclingApplication = new RecyclingApplication(); + private RecyclingApplication oldRecyclingApplication = new RecyclingApplication(); + private String ids; // 合并的时候使用它来标记要合并的单,此单被合并后将被删除 RecyclingApplication needToBeRemoved = null; @@ -127,6 +134,12 @@ private GoodFilterConfigManager goodFilterConfigManagerImpl; + private ApplicationLogManager applicationLogManager; + + public void setApplicationLogManager(ApplicationLogManager applicationLogManager) { + this.applicationLogManager = applicationLogManager; + } + public void setGoodFilterConfigManagerImpl(GoodFilterConfigManager goodFilterConfigManagerImpl) { this.goodFilterConfigManagerImpl = goodFilterConfigManagerImpl; } @@ -181,6 +194,12 @@ @Override public RecyclingApplication getModel() { + try { + BeanUtils.copyProperties(oldRecyclingApplication, recyclingApplication); + } + catch (Exception e) { + logger.error("复制属性的时候发生错误!" + e.getMessage()); + } return recyclingApplication; } @@ -481,14 +500,22 @@ boolean autoReturnTheBorrowingTousse = "true".equals(StrutsParamUtils .getPraramValue("autoReturnTheBorrowingTousse", "").trim()); - + + String compareResult = new RecyclingApplicationComparator().doCompare(oldRecyclingApplication, recyclingApplication, srcTousseItemVoList); + //如果比较结果不是空的,或者这张申请单id还没有值,则需要写到日志 + boolean isWriteToLog = StringUtils.isNotBlank(compareResult) || recyclingApplication.getId() == null; + if (!autoReturnTheBorrowingTousse) { JSONObject jsonObject = saveRecyclingApplication(recyclingApplication, srcTousseItemVoList); //如果申请的物品有加急物品,则清空UrgentGoodsWarning表 if (CssdUtils.getSystemSetConfigByNameBool("enableTousseVoluntarilyUrgent", false) && urgentItems.size() > 0) { objectDao.executeUpdate("delete from UrgentGoodsWarning"); } + //保存申请单后插入更新的日志 + if (isWriteToLog){ + applicationLogManager.saveApplicationLog(recyclingApplication, compareResult, ApplicationLogStatusEnum.SUBMIT); + } return jsonObject; } @@ -1709,4 +1736,13 @@ e.printStackTrace(); } } + + /** + * 获取申请单修改记录 + */ + public void loadRecyclingApplicationUpdateDetail(){ + Long id = StrutsParamUtils.getPraramLongValue("id", 0L); + JSONObject jsonObject = applicationLogManager.loadRecyclingApplicationUpdateDetail(id, PageUtil.getPagePara()); + StrutsResponseUtils.output(jsonObject); + } } Index: ssts-web/src/main/webapp/disinfectsystem/recyclingApplication/showUpdateDetail.js =================================================================== diff -u --- ssts-web/src/main/webapp/disinfectsystem/recyclingApplication/showUpdateDetail.js (revision 0) +++ ssts-web/src/main/webapp/disinfectsystem/recyclingApplication/showUpdateDetail.js (revision 18606) @@ -0,0 +1,82 @@ +// 申请单历史修改对象 +var UpdateDetailObj = function () { + + top.Ext4.define('UpdateDetailStoreModel', { + extend: 'Ext.data.Model', + fields: [ + {name: 'updateUser'}, + {name: 'createTime'}, + {name: 'updateUserDepartName'}, + {name: 'updateDetail'} + ] + }); + + var updateDetail = {}; + + updateDetail.getStore = function (id) { + return top.Ext4.create('Ext.data.Store', { + pageSize: 20, + storeId: 'updateDetailStore', + model: 'UpdateDetailStoreModel', + proxy: { + type: 'ajax', + url: WWWROOT + '/disinfectSystem/recyclingApplicationAction!loadRecyclingApplicationUpdateDetail.do?id=' + id, + reader: { + root: 'data', + totalProperty: 'size' + } + }, + autoLoad: true + }); + }; + + updateDetail.getPanel = function (id) { + var me = this; + var store = me.getStore(id); + return top.Ext4.create('Ext.grid.Panel', { + store: store, + columns: [ + {header: '修改用户', dataIndex: 'updateUser'}, + {header: '修改时间', dataIndex: 'createTime', width: 110}, + {header: '修改用户部门', dataIndex: 'updateUserDepartName'}, + { + dataIndex: 'updateDetail', text: '修改记录', width: 450, + renderer: function (value, meta, record) { + meta.style = 'overflow:auto;padding: 3px 6px;text-overflow: ellipsis;white-space: nowrap;white-space:normal;line-height:20px;'; + meta.tdAttr = 'data-qtip="' + value + '"'; + return value; + } + } + ], + dockedItems: [{ + xtype: 'pagingtoolbar', + store: store, + dock: 'bottom', + displayInfo: true + }], + autoExpandColumn: 'updateDetail' + }); + }; + + updateDetail.getWin = function (id) { + return top.Ext4.create('Ext.window.Window', { + id: 'recyclingApplicationUpdateWin', + title: '申请单修改记录', + width: 750, + height: 500, + resizable: false, + modal: true, + border: false, + plain: true, + layout: 'fit', + items: [updateDetail.getPanel(id)] + }); + } + + this.showWin = function (id) { + updateDetail.getWin(id).show(); + } +} + + + Index: ssts-web/src/main/webapp/disinfectsystem/recyclingApplication/goodsApplicationView.jsp =================================================================== diff -u -r18570 -r18606 --- ssts-web/src/main/webapp/disinfectsystem/recyclingApplication/goodsApplicationView.jsp (.../goodsApplicationView.jsp) (revision 18570) +++ ssts-web/src/main/webapp/disinfectsystem/recyclingApplication/goodsApplicationView.jsp (.../goodsApplicationView.jsp) (revision 18606) @@ -365,6 +365,7 @@ + Index: ssts-web/src/main/webapp/disinfectsystem/recyclingApplication/goodsTemplateApplicationView.js =================================================================== diff -u -r18570 -r18606 --- ssts-web/src/main/webapp/disinfectsystem/recyclingApplication/goodsTemplateApplicationView.js (.../goodsTemplateApplicationView.js) (revision 18570) +++ ssts-web/src/main/webapp/disinfectsystem/recyclingApplication/goodsTemplateApplicationView.js (.../goodsTemplateApplicationView.js) (revision 18606) @@ -46,6 +46,8 @@ } //表单的打印状态(1是未打印,2是已打印) var formPrinted = 1; +// 申请单历史修改对象 +var updateDetailObj = new UpdateDetailObj(); /** * 打开对应的表单 * @param form 对应的表单 @@ -2623,9 +2625,21 @@ thiz.selectText(); } } - }] - - }, { + } + , { + xtype : 'button', + iconCls: 'btn_ext_add_tousse', + text : '显示修改记录', + id : 'showUpdateDetail', + style: { + marginLeft: '30px' + }, + // iconCls : 'btn_ext_add', + handler : function() { + updateDetailObj.showWin(id); + } + }] + }, { xtype : 'container', width : 800, flex : 1, Index: forgon-tools/src/main/java/com/forgon/annotations/CompareProperty.java =================================================================== diff -u --- forgon-tools/src/main/java/com/forgon/annotations/CompareProperty.java (revision 0) +++ forgon-tools/src/main/java/com/forgon/annotations/CompareProperty.java (revision 18606) @@ -0,0 +1,23 @@ +package com.forgon.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author zhonghaowen + * @apiNote 两个对象比较注解 + * @since 2017-06-30 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD}) +@Documented +public @interface CompareProperty { + + /** + * @return 显示该属性的名称 + */ + String displayName(); +} Index: ssts-recyclingapplication/src/main/java/com/forgon/disinfectsystem/recyclingapplication/service/InvoicePlanManagerImpl.java =================================================================== diff -u -r18500 -r18606 --- ssts-recyclingapplication/src/main/java/com/forgon/disinfectsystem/recyclingapplication/service/InvoicePlanManagerImpl.java (.../InvoicePlanManagerImpl.java) (revision 18500) +++ ssts-recyclingapplication/src/main/java/com/forgon/disinfectsystem/recyclingapplication/service/InvoicePlanManagerImpl.java (.../InvoicePlanManagerImpl.java) (revision 18606) @@ -15,6 +15,8 @@ import java.util.Set; import java.util.stream.Collectors; +import com.forgon.disinfectsystem.recyclingapplication.updateLog.ApplicationLogManager; +import com.forgon.log.enums.ApplicationLogStatusEnum; import net.sf.json.JSONArray; import net.sf.json.JSONObject; @@ -126,6 +128,12 @@ private RecyclingApplicationManager recyclingApplicationManagerTarget; + private ApplicationLogManager applicationLogManager; + + public void setApplicationLogManager(ApplicationLogManager applicationLogManager) { + this.applicationLogManager = applicationLogManager; + } + private static final String FOREIGNTOUSSE_APP_AWAITRECEIVE = "(待接收)外来器械包申请单"; private static final String FOREIGNTOUSSE_APP_AWAITRECYCLE = "(待回收)外来器械包申请单"; @@ -1698,6 +1706,7 @@ plan.setApplicationEndTime(endTime); plan.setApplicationEnd(userName); CssdUtils.appendRemarkOfInvoicePlan(plan, remark); + applicationLogManager.saveApplicationLog(plan, remark, ApplicationLogStatusEnum.PART_TERMINATE); } terminateTousseItems(list); @@ -1751,6 +1760,7 @@ terminateTousseItems(applicationItems); setInvoicePlanToTerminateStatus(invoicePlan);// 某些情况下,计算状态的方法有问题 objectDao.saveOrUpdate(invoicePlan); + applicationLogManager.saveApplicationLog(invoicePlan, "申请单被终止,原因如下:" + endCause, ApplicationLogStatusEnum.TERMINATE); } /** @@ -1816,6 +1826,7 @@ List applicationItems = invoicePlan.getApplicationItems(); terminateTousseItems(applicationItems); objectDao.saveOrUpdate(invoicePlan); + applicationLogManager.saveApplicationLog(invoicePlan, "申请单被终止,原因如下:" + endCause, ApplicationLogStatusEnum.TERMINATE); } List endCauses = httpOptionManager.getHttpOptionTextById(HttpOption.SYSTEMSETTING_APPLICATION_END_CAUSE); Index: forgon-tools/src/main/java/com/forgon/tools/util/ObjectUtil.java =================================================================== diff -u -r17419 -r18606 --- forgon-tools/src/main/java/com/forgon/tools/util/ObjectUtil.java (.../ObjectUtil.java) (revision 17419) +++ forgon-tools/src/main/java/com/forgon/tools/util/ObjectUtil.java (.../ObjectUtil.java) (revision 18606) @@ -1,13 +1,21 @@ package com.forgon.tools.util; +import com.forgon.annotations.CompareProperty; import com.forgon.tools.StrutsParamUtils; import org.apache.log4j.Logger; import javax.servlet.http.HttpServletRequest; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Enumeration; +import java.util.HashMap; +import java.util.LinkedList; import java.util.List; +import java.util.Map; /** * Created by zhonghaowen on 2016/12/9. @@ -139,5 +147,99 @@ } } + /** + * 从开始截取到字符串最后一个字符 + * + * @param sb 要截取的字符串 + * @return 字符串a, b, c, 返回a,b,c + */ + public static String subStringLastBuilder(StringBuilder sb) { + int length = sb.length(); + return length == 0 ? "" : sb.substring(0, length - 1); + } + + /** + * 比较两个对象的所有属性 + */ + public static Map> compareObject(Object oldObj, Object newObj) throws Exception { + return compareObject(oldObj, newObj, new String[]{}); + } + + /** + * 比较两个对象的属性(白名单) + * + * @param oldObj 旧的对象 + * @param newObj 新的对象 + * @param whiteList 要比较的属性名字 + * @return 返回一个map以不同的属性名为key, value为一个list分别存obj1(旧值), obj2(新值)此属性名的值,格式:(科室名字, [外科,内科]) + */ + public static Map> compareObject(Object oldObj, Object newObj, String[] whiteList) throws Exception { + //只有两个对象都是同一类型的才有可比性 + if (oldObj.getClass() == newObj.getClass()) { + List white = whiteList.length > 0 ? Arrays.asList(whiteList) : new LinkedList<>(); + return compareAndSet(oldObj, newObj, white); + } + return null; + } + + /** + * 开始比较并且把结果集设置到map中 + * + * @param oldObj 旧的对象 + * @param newObj 新的对象 + * @param whiteList 要比较的属性名字 + */ + private static Map> compareAndSet(Object oldObj, Object newObj, List whiteList) throws Exception { + Map> map = new HashMap<>(); + Class clazz = oldObj.getClass(); + // 获取object的属性描述 + PropertyDescriptor[] pds = Introspector.getBeanInfo(clazz, Object.class).getPropertyDescriptors(); + //这里就是所有的属性了 + if (whiteList.size() == 0) { + for (PropertyDescriptor pd : pds) { + setDiffResult(oldObj, newObj, map, pd); + } + } + else { + for (PropertyDescriptor pd : pds) { + //当前属性的在白名单中才去比较 + if (whiteList.contains(pd.getName())) { + setDiffResult(oldObj, newObj, map, pd); + } + } + } + return map; + } + + /** + * 设置比较后的结果 + * + * @param oldObj 旧的对象 + * @param newObj 新的对象 + * @param map 结果集 + * @param pd {@link PropertyDescriptor} + */ + private static void setDiffResult(Object oldObj, Object newObj, Map> map, PropertyDescriptor pd) throws Exception { + //get方法 + Method readMethod = pd.getReadMethod(); + //在oldObj上调用get方法等同于获得oldObj的属性值 + Object o1 = readMethod.invoke(oldObj); + //在newObj上调用get方法等同于获得newObj的属性值 + Object o2 = readMethod.invoke(newObj); + //比较这两个值是否相等,不等就可以放入map了 + if (!o1.equals(o2)) { + List list = new ArrayList<>(2); + list.add(o1); + list.add(o2); + String name = pd.getName(); + CompareProperty annotation = oldObj.getClass().getDeclaredField(name).getAnnotation(CompareProperty.class); + if (annotation != null) { + //如果该属性有这个注解,map的key用这个名字 + name = annotation.displayName(); + } + map.put(name, list); + } + } + } Index: ssts-web/src/main/resources/spring/applicationContext-service.xml =================================================================== diff -u -r17182 -r18606 --- ssts-web/src/main/resources/spring/applicationContext-service.xml (.../applicationContext-service.xml) (revision 17182) +++ ssts-web/src/main/resources/spring/applicationContext-service.xml (.../applicationContext-service.xml) (revision 18606) @@ -538,6 +538,26 @@ + + + + + + + + + + + + + PROPAGATION_REQUIRED + + + + + + Index: forgon-tools/src/main/java/com/forgon/tools/compare/ObjectComparator.java =================================================================== diff -u --- forgon-tools/src/main/java/com/forgon/tools/compare/ObjectComparator.java (revision 0) +++ forgon-tools/src/main/java/com/forgon/tools/compare/ObjectComparator.java (revision 18606) @@ -0,0 +1,118 @@ +package com.forgon.tools.compare; + +import com.forgon.tools.util.ObjectUtil; +import org.apache.commons.beanutils.BeanUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.log4j.Logger; + +import java.util.List; +import java.util.Map; + +/** + * @author zhonghaowen + * @apiNote 比较类基类, 用于找出修改后和修改前的数据不同, 此类提供两种方法比较(这两个方法只能用来判定更新了什么, 不能判定新增或者删除)
+ * 比如一张申请单,这两个方法可以用来判定出他修改了的申请科室,备注等属性,但不能判定出这张单下的物品改动,这种一对多的修改的比较需要自己写方法实现
+ *

1.使用compareDiff来比较,这种需要重写toMap的方法

+ *

2.使用compareObjectByReflection来比较,这种需要自己定义dto类,要比较的属性要加上@CompareProperty的注解,使用方法如下:

+ *

+ *     {@literal @CompareProperty(displayName = "申请科室")
+ *      private String depart;}
+ * 
+ * 
+ * @since 2017-06-30 + */ +public abstract class ObjectComparator { + + protected Logger logger = Logger.getLogger(this.getClass()); + + private final static String updateMsg = "%s从【%s】改成【%s】,"; + + /** + * 将对象的属性放到map中,用于调用diff方法,每个子类需要重写这个方法(即需要比较的属性) + * + * @param obj 对用的对象 + * @return map格式:[属性名字,值] + * @see ObjectComparator#diff(Map, Map) + */ + public abstract Map toMap(T obj); + + /** + * 通过反射来比较两个对象 + * + * @param oldObj 旧对象 + * @param newObj 新对象 + * @param clazzDto 这个对象的dto(由于比较的对象有可能直接就是和数据库映射的pojo,所以他的属性比较多,因此需要通过dto来设定需要比较的属性,这样可以减少比较的属性从而提高效率) + * @param whiteList 由于每个业务需要比较的属性可能不同,所以这里可以设置一个白名单,根据每个业务来自己定义需要比较的属性 + * @return 比较后的结果 + */ + protected String compareObjectByReflection(T oldObj, T newObj, Class clazzDto, String[] whiteList) { + try { + Object oldDto = clazzDto.newInstance(); + Object newDto = clazzDto.newInstance(); + BeanUtils.copyProperties(oldDto, oldObj); + BeanUtils.copyProperties(newDto, newObj); + whiteList = whiteList == null ? new String[]{} : whiteList; + Map> map = ObjectUtil.compareObject(oldDto, newDto, whiteList); + StringBuilder sb = new StringBuilder(); + if (MapUtils.isEmpty(map)) { + return sb.toString(); + } + for (Map.Entry> entry : map.entrySet()) { + List value = entry.getValue(); + sb.append(String.format(updateMsg, entry.getKey(), value.get(0), value.get(1))); + } + return ObjectUtil.subStringLastBuilder(sb); + } + catch (Exception e) { + logger.error(e.getMessage(), e); + throw new RuntimeException(e); + } + } + + + /** + * @see ObjectComparator#compareObjectByReflection(Object, Object, Class, String[]) + */ + protected String compareObjectByReflection(T oldPojo, T newPojo, Class clazzDto) { + return compareObjectByReflection(oldPojo, newPojo, clazzDto, null); + } + + /** + * 比较两个对象的对应属性 + * + * @param oldObject 旧的对象 + * @param newObject 新的对象 + * @return 返回比较后的结果 + */ + protected String compareDiff(T oldObject, T newObject) { + Map oldMap = this.toMap(oldObject); + Map newMap = this.toMap(newObject); + String result = ObjectUtil.subStringLastBuilder(this.diff(oldMap, newMap)); + logger.debug(result); + return result; + } + + + /** + *

只适用于不同名字的比较,例如科室名字

+ * 比较两个map的不同,map的格式是:[属性名字, 值](例:map[科室名字,xx科室]) + * + * @param oldMap 原来的map,格式:[属性名字,值] + * @param newMap 新的map,格式:[属性名字,值] + * @return 返回比较好字符串 + */ + private StringBuilder diff(Map oldMap, Map newMap) { + StringBuilder result = new StringBuilder(); + if (oldMap.size() != newMap.size()) { + throw new RuntimeException("两个比较的map长度不同!"); + } + for (Map.Entry entry : oldMap.entrySet()) { + Object newValue = newMap.get(entry.getKey()); + if (!entry.getValue().equals(newValue)) { + result.append(String.format(updateMsg, entry.getKey(), entry.getValue(), newValue)); + } + } + return result; + } + +} Index: forgon-core/src/main/java/com/forgon/log/enums/ApplicationLogStatusEnum.java =================================================================== diff -u --- forgon-core/src/main/java/com/forgon/log/enums/ApplicationLogStatusEnum.java (revision 0) +++ forgon-core/src/main/java/com/forgon/log/enums/ApplicationLogStatusEnum.java (revision 18606) @@ -0,0 +1,34 @@ +package com.forgon.log.enums; + +/** + * @author zhonghaowen + * @apiNote 申请单状态枚举类,用来记录申请单的状态(删除,提交,终止),存到ApplicationLog里面 + * @since 2017-07-04 + */ +public enum ApplicationLogStatusEnum { + + DELETE(0, "删除"), + + SUBMIT(1, "提交"), + + PART_TERMINATE(2, "部分终止"), + + TERMINATE(3, "终止"); + + private Integer status; + + private String desc; + + ApplicationLogStatusEnum(Integer status, String desc) { + this.status = status; + this.desc = desc; + } + + public Integer getStatus() { + return status; + } + + public String getDesc() { + return desc; + } +}