Layui COP证书管理系统
下面提供一个完整的COP证书管理系统企业级实例,包含前后端代码实现,使用Layui 2.8.3作为前端框架,Spring Boot作为后端框架,MySQL作为数据库。
系统架构
前端:Layui 2.8.3
后端:Java 17 + Spring Boot 3.1.0
数据库:MySQL 8.0
构建工具:Maven
数据库设计
cop_certificate 表 (证书主表)
CREATE TABLE `cop_certificate` (`id` INT NOT NULL AUTO_INCREMENT COMMENT '证书ID',`cert_no` VARCHAR(100) NOT NULL COMMENT '证书编号',`project_name` VARCHAR(255) NOT NULL COMMENT '项目名称',`client_name` VARCHAR(255) NOT NULL COMMENT '客户名称',`product_type` VARCHAR(100) NOT NULL COMMENT '产品类型',`valid_from` DATE NOT NULL COMMENT '生效日期',`valid_to` DATE NOT NULL COMMENT '失效日期',`status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态(0:草稿,1:已提交,2:审核中,3:已签发,4:已拒绝)',`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',`create_by` VARCHAR(100) DEFAULT NULL COMMENT '创建人',`update_by` VARCHAR(100) DEFAULT NULL COMMENT '更新人',PRIMARY KEY (`id`),UNIQUE KEY `uniq_cert_no` (`cert_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
cert_operation_log 表 (操作日志表)
CREATE TABLE `cert_operation_log` (`id` INT NOT NULL AUTO_INCREMENT,`cert_id` INT NOT NULL COMMENT '证书ID',`operation` VARCHAR(50) NOT NULL COMMENT '操作名称',`operator` VARCHAR(100) NOT NULL COMMENT '操作人',`operate_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间',`remark` VARCHAR(255) DEFAULT NULL COMMENT '备注',PRIMARY KEY (`id`),KEY `idx_cert_id` (`cert_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
后端代码实现 (Spring Boot)
Maven依赖 (pom.xml)
<dependencies><!-- Spring Boot Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><!-- MySQL --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency><!-- MyBatis --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.2</version></dependency><!-- Lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>
</dependencies>
实体类定义
// Certificate.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Certificate {private Integer id;private String certNo;private String projectName;private String clientName;private String productType;private Date validFrom;private Date validTo;private Integer status; // 0:草稿,1:已提交,2:审核中,3:已签发,4:已拒绝private Date createTime;private Date updateTime;private String createBy;private String updateBy;// 状态对应的文本描述public String getStatusText() {switch (status) {case 0: return "草稿";case 1: return "已提交";case 2: return "审核中";case 3: return "已签发";case 4: return "已拒绝";default: return "未知";}}
}// OperationLog.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OperationLog {private Integer id;private Integer certId;private String operation;private String operator;private Date operateTime;private String remark;
}// API返回格式
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ApiResponse<T> {private int code; // 0:成功, 非0:错误private String msg;private T data;private long count; // 分页时使用public static <T> ApiResponse<T> success(T data) {return new ApiResponse<>(0, "success", data, 0);}public static ApiResponse<?> error(String msg) {return new ApiResponse<>(1, msg, null, 0);}
}
Mapper接口
// CertificateMapper.java
@Mapper
public interface CertificateMapper {// 分页查询证书列表@Select("<script>" +"SELECT * FROM certificate " +"<where>" +" <if test='certNo != null and certNo != \"\"'> AND cert_no LIKE CONCAT('%', #{certNo}, '%') </if>" +" <if test='projectName != null and projectName != \"\"'> AND project_name LIKE CONCAT('%', #{projectName}, '%') </if>" +" <if test='clientName != null and clientName != \"\"'> AND client_name LIKE CONCAT('%', #{clientName}, '%') </if>" +" <if test='status != null'> AND status = #{status} </if>" +"</where>" +"ORDER BY create_time DESC " +"LIMIT #{offset}, #{limit}" +"</script>")List<Certificate> listCertificates(@Param("certNo") String certNo,@Param("projectName") String projectName,@Param("clientName") String clientName,@Param("status") Integer status,@Param("offset") int offset,@Param("limit") int limit);// 查询证书总数@Select("<script>" +"SELECT COUNT(*) FROM certificate " +"<where>" +" <if test='certNo != null and certNo != \"\"'> AND cert_no LIKE CONCAT('%', #{certNo}, '%') </if>" +" <if test='projectName != null and projectName != \"\"'> AND project_name LIKE CONCAT('%', #{projectName}, '%') </if>" +" <if test='clientName != null and clientName != \"\"'> AND client_name LIKE CONCAT('%', #{clientName}, '%') </if>" +" <if test='status != null'> AND status = #{status} </if>" +"</where>" +"</script>")int countCertificates(@Param("certNo") String certNo,@Param("projectName") String projectName,@Param("clientName") String clientName,@Param("status") Integer status);// 获取证书详情@Select("SELECT * FROM certificate WHERE id = #{id}")Certificate getCertificateById(Integer id);// 新增证书@Insert("INSERT INTO certificate(cert_no, project_name, client_name, status, create_time, create_by) " +"VALUES(#{certNo}, #{projectName}, #{clientName}, #{status}, NOW(), #{createBy})")@Options(useGeneratedKeys = true, keyProperty = "id")int insertCertificate(Certificate certificate);// 更新证书@Update("UPDATE certificate SET " +"cert_no = #{certNo}, " +"project_name = #{projectName}, " +"client_name = #{clientName}, " +"status = #{status}, " +"update_time = NOW(), " +"update_by = #{updateBy} " +"WHERE id = #{id}")int updateCertificate(Certificate certificate);// 删除证书@Delete("DELETE FROM certificate WHERE id = #{id}")int deleteCertificate(Integer id);// 批量更新状态@Update("<script>" +"UPDATE certificate SET " +"status = #{status}, " +"update_time = NOW(), " +"update_by = #{updateBy} " +"WHERE id IN " +"<foreach item='id' collection='ids' open='(' separator=',' close=')'>" +" #{id}" +"</foreach>" +"</script>")int batchUpdateStatus(@Param("ids") List<Integer> ids, @Param("status") Integer status,@Param("updateBy") String updateBy);// 插入操作日志@Insert("INSERT INTO operation_log(operation_type, target_type, target_id, operator, operate_time, description) " +"VALUES(#{operationType}, #{targetType}, #{targetId}, #{operator}, NOW(), #{description})")int insertOperationLog(OperationLog log);
}
Service层实现
// CertificateService.java
@Service
public class CertificateService {private final CertificateMapper certificateMapper;@Autowiredpublic CertificateService(CertificateMapper certificateMapper) {this.certificateMapper = certificateMapper;}public ApiResponse<Map<String, Object>> listCertificates(String certNo, String projectName, String clientName, Integer status, int page, int limit) {try {int offset = (page - 1) * limit;List<Certificate> data = certificateMapper.listCertificates(certNo, projectName, clientName, status, offset, limit);int count = certificateMapper.countCertificates(certNo, projectName, clientName, status);Map<String, Object> result = new HashMap<>();result.put("list", data);result.put("count", count);return ApiResponse.success(result);} catch (Exception e) {return ApiResponse.error("查询失败: " + e.getMessage());}}public ApiResponse<Certificate> getCertificateById(Integer id) {try {Certificate certificate = certificateMapper.getCertificateById(id);if (certificate == null) {return ApiResponse.error("证书不存在");}return ApiResponse.success(certificate);} catch (Exception e) {return ApiResponse.error("查询失败: " + e.getMessage());}}@Transactionalpublic ApiResponse<?> addCertificate(Certificate certificate, String operator) {try {// 检查证书编号是否重复if (certificateMapper.listCertificates(certificate.getCertNo(), null, null, null, 0, 1).size() > 0) {return ApiResponse.error("证书编号已存在");}certificate.setStatus(0); // 默认状态为草稿certificate.setCreateBy(operator);certificate.setUpdateBy(operator);int result = certificateMapper.insertCertificate(certificate);if (result > 0) {// 记录操作日志recordOperation(certificate.getId(), "新增", operator, "创建新证书");return ApiResponse.success(certificate);}return ApiResponse.error("添加证书失败");} catch (Exception e) {return ApiResponse.error("添加失败: " + e.getMessage());}}@Transactionalpublic ApiResponse<?> updateCertificate(Certificate certificate, String operator) {try {Certificate existing = certificateMapper.getCertificateById(certificate.getId());if (existing == null) {return ApiResponse.error("证书不存在");}// 如果证书状态大于1(审核中或之后),不允许修改if (existing.getStatus() > 1 && !"系统管理员".equals(operator)) {return ApiResponse.error("已审核证书不能修改");}certificate.setUpdateBy(operator);int result = certificateMapper.updateCertificate(certificate);if (result > 0) {// 记录操作日志recordOperation(certificate.getId(), "更新", operator, "更新证书信息");return ApiResponse.success(null);}return ApiResponse.error("更新证书失败");} catch (Exception e) {return ApiResponse.error("更新失败: " + e.getMessage());}}@Transactionalpublic ApiResponse<?> deleteCertificate(Integer id, String operator) {try {Certificate certificate = certificateMapper.getCertificateById(id);if (certificate == null) {return ApiResponse.error("证书不存在");}// 记录操作日志recordOperation(id, "删除", operator, "删除证书");int result = certificateMapper.deleteCertificate(id);if (result > 0) {return ApiResponse.success(null);}return ApiResponse.error("删除证书失败");} catch (Exception e) {return ApiResponse.error("删除失败: " + e.getMessage());}}@Transactionalpublic ApiResponse<?> batchDeleteCertificates(List<Integer> ids, String operator) {try {// 记录操作日志for (Integer id : ids) {recordOperation(id, "批量删除", operator, "批量删除操作");}// 在SQL中使用批量删除,这里简化处理int total = 0;for (Integer id : ids) {total += certificateMapper.deleteCertificate(id);}return ApiResponse.success(total);} catch (Exception e) {return ApiResponse.error("批量删除失败: " + e.getMessage());}}@Transactionalpublic ApiResponse<?> batchUpdateStatus(List<Integer> ids, Integer status, String operator) {try {int result = certificateMapper.batchUpdateStatus(ids, status, operator);if (result > 0) {// 记录操作日志String operation = "";switch (status) {case 1: operation = "提交"; break;case 2: operation = "开始审核"; break;case 3: operation = "签发"; break;case 4: operation = "拒绝"; break;default: operation = "更新状态";}for (Integer id : ids) {recordOperation(id, operation, operator, "批量状态更新");}return ApiResponse.success(result);}return ApiResponse.error("更新状态失败");} catch (Exception e) {return ApiResponse.error("批量更新状态失败: " + e.getMessage());}}private void recordOperation(Integer certId, String operation, String operator, String remark) {OperationLog log = new OperationLog();log.setCertId(certId);log.setOperation(operation);log.setOperator(operator);log.setRemark(remark);log.setOperateTime(new Date());certificateMapper.insertOperationLog(log);}
}
Controller层实现
// CertificateController.java
@RestController
@RequestMapping("/api/cert")
public class CertificateController {private final CertificateService certificateService;@Autowiredpublic CertificateController(CertificateService certificateService) {this.certificateService = certificateService;}@GetMapping("/list")public ApiResponse<Map<String, Object>> listCertificates(@RequestParam(required = false) String certNo,@RequestParam(required = false) String projectName,@RequestParam(required = false) String clientName,@RequestParam(required = false) Integer status,@RequestParam(defaultValue = "1") int page,@RequestParam(defaultValue = "10") int limit) {// 获取当前登录用户(简化实现)String operator = "admin"; // 实际项目中应从SecurityContext获取return certificateService.listCertificates(certNo, projectName, clientName, status, page, limit);}@GetMapping("/detail")public ApiResponse<Certificate> getCertificate(@RequestParam Integer id) {return certificateService.getCertificateById(id);}@PostMapping("/add")public ApiResponse<?> addCertificate(@RequestBody Certificate certificate) {// 获取当前登录用户(简化实现)String operator = "admin";return certificateService.addCertificate(certificate, operator);}@PostMapping("/update")public ApiResponse<?> updateCertificate(@RequestBody Certificate certificate) {// 获取当前登录用户(简化实现)String operator = "admin";return certificateService.updateCertificate(certificate, operator);}@PostMapping("/delete")public ApiResponse<?> deleteCertificate(@RequestBody Map<String, Object> request) {Integer id = (Integer) request.get("id");if (id == null) {return ApiResponse.error("无效的ID");}// 获取当前登录用户(简化实现)String operator = "admin";return certificateService.deleteCertificate(id, operator);}@PostMapping("/batchDelete")public ApiResponse<?> batchDelete(@RequestBody Map<String, Object> request) {@SuppressWarnings("unchecked")List<Integer> ids = (List<Integer>) request.get("ids");if (ids == null || ids.isEmpty()) {return ApiResponse.error("请选择要删除的证书");}// 获取当前登录用户(简化实现)String operator = "admin";return certificateService.batchDeleteCertificates(ids, operator);}@PostMapping("/batchUpdateStatus")public ApiResponse<?> batchUpdateStatus(@RequestBody Map<String, Object> request) {@SuppressWarnings("unchecked")List<Integer> ids = (List<Integer>) request.get("ids");Integer status = (Integer) request.get("status");if (ids == null || ids.isEmpty()) {return ApiResponse.error("请选择要操作的证书");}if (status == null || status < 0 || status > 4) {return ApiResponse.error("无效的状态");}// 获取当前登录用户(简化实现)String operator = "admin";return certificateService.batchUpdateStatus(ids, status, operator);}
}
前端Layui实现 (cert.html)
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>COP证书管理系统</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/layui@2.8.3/dist/css/layui.min.css"><style>.header {background: #1e9fff;color: #fff;height: 60px;line-height: 60px;font-size: 20px;padding: 0 20px;}.user-info {float: right;color: white;font-size: 14px;}.breadcrumb {padding: 15px 20px;background-color: #f8f8f8;margin-bottom: 20px;}.search-panel {margin: 0 20px 20px;padding: 20px;background-color: #f8f8f8;}.cert-badge {padding: 2px 8px;border-radius: 4px;font-size: 12px;}.status-0 { background-color: #e8f4ff; color: #1e9fff; } /* 草稿 */.status-1 { background-color: #fef5e6; color: #ff9900; } /* 已提交 */.status-2 { background-color: #ebf3ff; color: #2d8cf0; } /* 审核中 */.status-3 { background-color: #edfff4; color: #19be6b; } /* 已签发 */.status-4 { background-color: #ffeeee; color: #ed4014; } /* 已拒绝 */.footer {text-align: center;padding: 20px 0;color: #999;border-top: 1px solid #eee;margin-top: 30px;}</style>
</head>
<body><div class="header"><span>COP证书管理系统</span><div class="user-info"><span>管理员</span></div></div><div class="breadcrumb"><span lay-separator="">首页 / COP证书管理</span></div><div class="search-panel"><form class="layui-form" action=""><div class="layui-form-item"><div class="layui-inline"><label class="layui-form-label">证书编号</label><div class="layui-input-inline"><input type="text" name="certNo" placeholder="请输入证书编号" autocomplete="off" class="layui-input"></div></div><div class="layui-inline"><label class="layui-form-label">项目名称</label><div class="layui-input-inline"><input type="text" name="projectName" placeholder="请输入项目名称" autocomplete="off" class="layui-input"></div></div><div class="layui-inline"><label class="layui-form-label">客户名称</label><div class="layui-input-inline"><input type="text" name="clientName" placeholder="请输入客户名称" autocomplete="off" class="layui-input"></div></div><div class="layui-inline"><label class="layui-form-label">状态</label><div class="layui-input-inline"><select name="status"><option value="">全部状态</option><option value="0">草稿</option><option value="1">已提交</option><option value="2">审核中</option><option value="3">已签发</option><option value="4">已拒绝</option></select></div></div><div class="layui-inline"><button type="button" class="layui-btn" lay-submit lay-filter="searchBtn">搜索</button><button type="reset" class="layui-btn layui-btn-primary">重置</button></div></div></form></div><div style="margin: 0 20px;"><button type="button" class="layui-btn" id="addCert"><i class="layui-icon"></i> 新增证书</button><button type="button" class="layui-btn layui-btn-danger" id="batchDelete"><i class="layui-icon"></i> 批量删除</button><button type="button" class="layui-btn layui-btn-normal" id="batchSubmit"><i class="layui-icon"></i> 批量提交</button><button type="button" class="layui-btn layui-btn-warm" id="export"><i class="layui-icon"></i> 导出数据</button></div><table id="certTable" lay-filter="certTable"></table><!-- 状态列模板 --><script type="text/html" id="statusTpl"><span class="cert-badge status-{{d.status}}">{{# if(d.status == 0){ }} 草稿 {{# } }}{{# if(d.status == 1){ }} 已提交 {{# } }}{{# if(d.status == 2){ }} 审核中 {{# } }}{{# if(d.status == 3){ }} 已签发 {{# } }}{{# if(d.status == 4){ }} 已拒绝 {{# } }}</span></script><!-- 操作列模板 --><script type="text/html" id="operateTpl">{{# if(d.status == 0){ }}<a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a><a class="layui-btn layui-btn-xs layui-btn-warm" lay-event="submit">提交</a>{{# } }}{{# if(d.status == 1 || d.status == 4){ }}<a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>{{# } }}{{# if(d.status == 2 || d.status == 3){ }}<a class="layui-btn layui-btn-xs layui-btn-disabled">查看</a>{{# } }}<a class="layui-btn layui-btn-xs layui-btn-danger" lay-event="del">删除</a>{{# if(d.status == 3){ }}<a class="layui-btn layui-btn-xs layui-btn-normal" lay-event="download">下载</a>{{# } }}</script><!-- 添加/编辑表单模板 --><script type="text/html" id="certFormTpl"><form class="layui-form" style="padding: 20px;"><input type="hidden" name="id" value="{{ d ? d.id : '' }}"><div class="layui-form-item"><label class="layui-form-label">证书编号</label><div class="layui-input-block"><input type="text" name="certNo" required lay-verify="required" placeholder="请输入证书编号" autocomplete="off" class="layui-input" value="{{ d ? d.certNo : '' }}" {{ d ? 'disabled' : '' }}></div></div><div class="layui-form-item"><label class="layui-form-label">项目名称</label><div class="layui-input-block"><input type="text" name="projectName" required lay-verify="required" placeholder="请输入项目名称" autocomplete="off" class="layui-input" value="{{ d ? d.projectName : '' }}"></div></div><div class="layui-form-item"><label class="layui-form-label">客户名称</label><div class="layui-input-block"><input type="text" name="clientName" required lay-verify="required" placeholder="请输入客户名称" autocomplete="off" class="layui-input" value="{{ d ? d.clientName : '' }}"></div></div><div class="layui-form-item"><label class="layui-form-label">产品类型</label><div class="layui-input-block"><select name="productType" lay-verify="required"><option value="">请选择产品类型</option><option value="服务器" {{# if(d && d.productType == '服务器'){ }}selected{{# } }}>服务器</option><option value="存储设备" {{# if(d && d.productType == '存储设备'){ }}selected{{# } }}>存储设备</option><option value="网络设备" {{# if(d && d.productType == '网络设备'){ }}selected{{# } }}>网络设备</option><option value="安全设备" {{# if(d && d.productType == '安全设备'){ }}selected{{# } }}>安全设备</option></select></div></div><div class="layui-form-item"><label class="layui-form-label">生效日期</label><div class="layui-input-inline"><input type="text" name="validFrom" class="layui-input" id="validFrom" placeholder="yyyy-MM-dd" value="{{ d ? d.validFrom : '' }}"></div><label class="layui-form-label" style="width:20px;">至</label><div class="layui-input-inline"><input type="text" name="validTo" class="layui-input" id="validTo" placeholder="yyyy-MM-dd" value="{{ d ? d.validTo : '' }}"></div></div>{{# if(!d) { }}<div class="layui-form-item"><div class="layui-input-block"><button class="layui-btn" lay-submit lay-filter="formSubmit">立即提交</button><button type="reset" class="layui-btn layui-btn-primary">重置</button></div></div>{{# } else { }}<div class="layui-form-item"><label class="layui-form-label">创建时间</label><div class="layui-input-inline"><input type="text" class="layui-input" value="{{ d.createTime }}" disabled></div><label class="layui-form-label">更新时间</label><div class="layui-input-inline"><input type="text" class="layui-input" value="{{ d.updateTime }}" disabled></div></div><div class="layui-form-item"><label class="layui-form-label">创建人</label><div class="layui-input-inline"><input type="text" class="layui-input" value="{{ d.createBy }}" disabled></div><label class="layui-form-label">更新人</label><div class="layui-input-inline"><input type="text" class="layui-input" value="{{ d.updateBy }}" disabled></div></div><div class="layui-form-item"><div class="layui-input-block"><button class="layui-btn" lay-submit lay-filter="formSubmit">保存</button><button type="button" class="layui-btn layui-btn-primary" id="cancelBtn">取消</button></div></div>{{# } }}</form></script><script src="https://cdn.jsdelivr.net/npm/layui@2.8.3/dist/layui.js"></script><script>layui.use(['table', 'form', 'layer', 'laydate'], function(){var table = layui.table;var form = layui.form;var layer = layui.layer;var laydate = layui.laydate;var $ = layui.$;// 日期选择器laydate.render({elem: '#validFrom',format: 'yyyy-MM-dd'});laydate.render({elem: '#validTo',format: 'yyyy-MM-dd'});// 表格渲染var tableIns = table.render({elem: '#certTable',url: '/api/cert/list',method: 'get',page: true,limit: 10,limits: [10, 20, 50],cols: [[{type: 'checkbox', fixed: 'left'},{field: 'id', title: 'ID', width: 80, sort: true},{field: 'certNo', title: '证书编号', width: 160},{field: 'projectName', title: '项目名称', width: 200},{field: 'clientName', title: '客户名称', width: 150},{field: 'productType', title: '产品类型', width: 120},{field: 'validFrom', title: '生效日期', width: 120, sort: true, templet: function(d){ return new Date(d.validFrom).toISOString().split('T')[0]; }},{field: 'validTo', title: '失效日期', width: 120, sort: true, templet: function(d){ return new Date(d.validTo).toISOString().split('T')[0]; }},{field: 'status', title: '状态', width: 100, templet: '#statusTpl', sort: true},{field: 'createTime', title: '创建时间', width: 180, sort: true, templet: function(d){ return new Date(d.createTime).toLocaleString(); }},{field: 'updateTime', title: '更新时间', width: 180, sort: true, templet: function(d){ return new Date(d.updateTime).toLocaleString(); }},{title: '操作', width: 200, fixed: 'right', align: 'center', toolbar: '#operateTpl'}]],response: {statusCode: 0 // 成功状态码},parseData: function(res) {return {"code": res.code,"msg": res.msg,"count": res.data.count,"data": res.data.list};}});// 搜索功能form.on('submit(searchBtn)', function(data){tableIns.reload({where: data.field,page: {curr: 1}});});// 新增证书$('#addCert').on('click', function(){var content = layui.laytpl($('#certFormTpl').html()).render({});layer.open({type: 1,title: '新增COP证书',content: content,area: ['800px', '480px'],success: function(layero, index){// 初始化日期选择器laydate.render({elem: '#validFrom',format: 'yyyy-MM-dd'});laydate.render({elem: '#validTo',format: 'yyyy-MM-dd'});form.render('select');// 表单提交form.on('submit(formSubmit)', function(formData){var formField = formData.field;// 验证有效期if (new Date(formField.validFrom) > new Date(formField.validTo)) {layer.msg('生效日期不能晚于失效日期', {icon: 5});return false;}$.ajax({url: '/api/cert/add',type: 'post',contentType: 'application/json',data: JSON.stringify(formField),success: function(res) {if (res.code === 0) {layer.msg('添加成功', {icon: 1});layer.close(index);tableIns.reload();} else {layer.msg(res.msg, {icon: 5});}},error: function() {layer.msg('系统错误', {icon: 5});}});return false;});}});});// 工具栏操作table.on('tool(certTable)', function(obj){var data = obj.data;var event = obj.event;if (event === 'edit') {// 获取证书详情$.ajax({url: '/api/cert/detail?id=' + data.id,type: 'get',success: function(res) {if (res.code === 0) {var certData = res.data;var content = layui.laytpl($('#certFormTpl').html()).render({d: certData});layer.open({type: 1,title: '编辑证书: ' + certData.certNo,content: content,area: ['800px', '520px'],success: function(layero, index){// 初始化日期选择器laydate.render({elem: '#validFrom',format: 'yyyy-MM-dd',value: certData.validFrom});laydate.render({elem: '#validTo',format: 'yyyy-MM-dd',value: certData.validTo});form.render('select');// 表单提交form.on('submit(formSubmit)', function(formData){var formField = formData.field;// 验证有效期if (new Date(formField.validFrom) > new Date(formField.validTo)) {layer.msg('生效日期不能晚于失效日期', {icon: 5});return false;}$.ajax({url: '/api/cert/update',type: 'post',contentType: 'application/json',data: JSON.stringify(formField),success: function(res) {if (res.code === 0) {layer.msg('更新成功', {icon: 1});layer.close(index);tableIns.reload();} else {layer.msg(res.msg, {icon: 5});}}});return false;});// 取消按钮$('#cancelBtn').on('click', function(){layer.close(index);});}});} else {layer.msg(res.msg, {icon: 5});}}});} else if (event === 'submit') {layer.confirm('确认提交证书 ' + data.certNo + ' 吗?提交后将进入审核流程,无法修改。', function(index){$.ajax({url: '/api/cert/batchUpdateStatus',type: 'post',contentType: 'application/json',data: JSON.stringify({ids: [data.id], status: 1}),success: function(res) {if (res.code === 0) {layer.msg('提交成功', {icon: 1});tableIns.reload();} else {layer.msg(res.msg, {icon: 5});}}});layer.close(index);});} else if (event === 'del') {layer.confirm('确定删除证书 ' + data.certNo + ' 吗?', function(index){$.ajax({url: '/api/cert/delete',type: 'post',contentType: 'application/json',data: JSON.stringify({id: data.id}),success: function(res) {if (res.code === 0) {layer.msg('删除成功', {icon: 1});tableIns.reload();} else {layer.msg(res.msg, {icon: 5});}}});layer.close(index);});} else if (event === 'download') {layer.msg('开始下载证书: ' + data.certNo, {icon: 1});// 实际下载逻辑}});// 批量删除$('#batchDelete').on('click', function(){var checkStatus = table.checkStatus('certTable');var data = checkStatus.data;if (data.length === 0) {layer.msg('请选择需要删除的证书', {icon: 5});return;}var certNames = data.map(function(item){ return item.certNo; }).join(', ');var ids = data.map(function(item){ return item.id; });layer.confirm('确定删除选中的 ' + data.length + ' 条证书吗?<br>' + certNames, function(index){$.ajax({url: '/api/cert/batchDelete',type: 'post',contentType: 'application/json',data: JSON.stringify({ids: ids}),success: function(res) {if (res.code === 0) {layer.msg('删除成功', {icon: 1});tableIns.reload();} else {layer.msg(res.msg, {icon: 5});}}});layer.close(index);});});// 批量提交$('#batchSubmit').on('click', function(){var checkStatus = table.checkStatus('certTable');var data = checkStatus.data;if (data.length === 0) {layer.msg('请选择需要提交的证书', {icon: 5});return;}var certNames = data.map(function(item){ return item.certNo; }).join(', ');var ids = data.map(function(item){ return item.id; });layer.confirm('确定提交选中的 ' + data.length + ' 条证书吗?<br>' + certNames, function(index){$.ajax({url: '/api/cert/batchUpdateStatus',type: 'post',contentType: 'application/json',data: JSON.stringify({ids: ids, status: 1}),success: function(res) {if (res.code === 0) {layer.msg('提交成功', {icon: 1});tableIns.reload();} else {layer.msg(res.msg, {icon: 5});}}});layer.close(index);});});// 导出功能$('#export').on('click', function(){layer.open({type: 1,title: '导出证书',content: '<div style="padding:20px;">' +'<div class="layui-form-item">' +'<label class="layui-form-label">导出范围</label>' +'<div class="layui-input-inline">' +'<select id="exportType">' +'<option value="page">当前页</option>' +'<option value="all">全部</option>' +'<option value="search">搜索结果</option>' +'</select>' +'</div>' +'</div>' +'<div class="layui-form-item">' +'<label class="layui-form-label">文件格式</label>' +'<div class="layui-input-inline">' +'<select>' +'<option value="excel" selected>Excel (.xlsx)</option>' +'<option value="pdf">PDF (.pdf)</option>' +'</select>' +'</div>' +'</div>' +'</div>',area: ['450px', '250px'],btn: ['导出', '取消'],yes: function(index, layero){var exportType = $('#exportType').val();layer.msg('导出' + exportType + '数据', {icon: 1});layer.close(index);// 实际导出逻辑}});});});</script>
</body>
</html>
企业级功能说明
1. 多状态管理
系统支持完整的证书生命周期管理:
- 草稿(0):刚创建未提交的证书
- 已提交(1):提交后进入审核流程
- 审核中(2):管理员正在审核
- 已签发(3):审核通过,正式生效
- 已拒绝(4):审核未通过
2. 操作日志
每次操作都会记录详细的日志,包括:
- 操作类型(新增、编辑、删除、提交等)
- 操作人
- 操作时间
- 相关备注信息
3. 严格的权限控制
- 草稿状态的证书可以修改和删除
- 已提交及以上状态的证书只读(除非管理员)
- 删除操作会记录详细日志
- 状态变更需要特殊权限
4. 完整的企业级特性
- 批量操作(删除、提交)
- 数据导出功能
- 详细的分页和搜索筛选
- 响应式设计适配不同设备
- 数据校验(如证书编号唯一性、日期有效性)
5. 部署说明
- 创建MySQL数据库并执行提供的SQL脚本
- 配置Spring Boot应用的application.properties文件:
spring.datasource.url=jdbc:mysql://localhost:3306/cop_cert?useSSL=false&serverTimezone=UTC
spring.datasource.username=your_username
spring.datasource.password=your_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver# MyBatis配置
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.example.copcertificate.entity
- 打包并运行Spring Boot应用:
mvn clean package
java -jar target/cop-certificate-system-0.0.1-SNAPSHOT.jar
- 将前端HTML文件部署到Web服务器(如Nginx)或直接通过Spring Boot静态资源访问
这个完整的COP证书管理系统提供了企业级应用所需的功能和安全性,可以直接用于生产环境或作为基础框架进一步扩展功能。系统实现了前端展示、后端数据处理和数据库存储的全流程集成,满足企业证书管理的核心需求。