From 2022c1612e9952c2bf7082ab7f6a090ac4f09804 Mon Sep 17 00:00:00 2001 From: wxl Date: Wed, 3 Jun 2026 17:44:35 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8A=9F=E8=83=BD=EF=BC=9A?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E7=AE=A1=E7=90=86-=E5=8E=9F=E5=A7=8B?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E6=A8=A1=E6=9D=BF=E3=80=81AB=E8=A7=92?= =?UTF-8?q?=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/granter/SocialTokenGranter.java | 2 +- .../auth/service/BladeUserDetails.java | 7 +- .../service/BladeUserDetailsServiceImpl.java | 3 +- .../auth/support/BladeJwtTokenEnhancer.java | 1 + .../org/springblade/auth/utils/TokenUtil.java | 1 + .../springblade/lims/entry/ExamineItem.java | 7 ++ .../lims/entry/OriginalRecordTemplate.java | 32 +++++ .../springblade/lims/entry/TemplateField.java | 29 +++++ .../lims/entry/TemplateFieldMapping.java | 33 +++++ .../springblade/system/user/entity/User.java | 13 ++ .../OriginalRecordTemplateController.java | 45 +++++++ .../controller/ReagentFormulaController.java | 46 +++++-- .../controller/TemplateFieldController.java | 55 +++++++++ .../mapper/OriginalRecordTemplateMapper.java | 8 ++ .../lims/mapper/TemplateFieldMapper.java | 13 ++ .../mapper/TemplateFieldMappingMapper.java | 13 ++ .../IOriginalRecordTemplateService.java | 21 ++++ .../lims/service/ITemplateFieldService.java | 64 ++++++++++ .../OriginalRecordTemplateServiceImpl.java | 57 +++++++++ .../impl/TemplateFieldServiceImpl.java | 99 +++++++++++++++ .../labuser/init/AdminAccountInitializer.java | 116 ++++++++++++++++++ .../system/user/UserApplication.java | 2 + .../user/controller/UserController.java | 12 ++ .../system/user/service/IUserService.java | 9 ++ .../user/service/impl/UserServiceImpl.java | 7 ++ .../user/util/RedisKeyExpirationListener.java | 9 +- 26 files changed, 691 insertions(+), 13 deletions(-) create mode 100644 lab-service-api/lab-lims-api/src/main/java/org/springblade/lims/entry/OriginalRecordTemplate.java create mode 100644 lab-service-api/lab-lims-api/src/main/java/org/springblade/lims/entry/TemplateField.java create mode 100644 lab-service-api/lab-lims-api/src/main/java/org/springblade/lims/entry/TemplateFieldMapping.java create mode 100644 lab-service/lab-lims/src/main/java/org/springblade/lims/controller/OriginalRecordTemplateController.java create mode 100644 lab-service/lab-lims/src/main/java/org/springblade/lims/controller/TemplateFieldController.java create mode 100644 lab-service/lab-lims/src/main/java/org/springblade/lims/mapper/OriginalRecordTemplateMapper.java create mode 100644 lab-service/lab-lims/src/main/java/org/springblade/lims/mapper/TemplateFieldMapper.java create mode 100644 lab-service/lab-lims/src/main/java/org/springblade/lims/mapper/TemplateFieldMappingMapper.java create mode 100644 lab-service/lab-lims/src/main/java/org/springblade/lims/service/IOriginalRecordTemplateService.java create mode 100644 lab-service/lab-lims/src/main/java/org/springblade/lims/service/ITemplateFieldService.java create mode 100644 lab-service/lab-lims/src/main/java/org/springblade/lims/service/impl/OriginalRecordTemplateServiceImpl.java create mode 100644 lab-service/lab-lims/src/main/java/org/springblade/lims/service/impl/TemplateFieldServiceImpl.java create mode 100644 lab-service/lab-user/src/main/java/org/springblade/labuser/init/AdminAccountInitializer.java diff --git a/lab-auth/src/main/java/org/springblade/auth/granter/SocialTokenGranter.java b/lab-auth/src/main/java/org/springblade/auth/granter/SocialTokenGranter.java index 13af9b7..7cefec0 100644 --- a/lab-auth/src/main/java/org/springblade/auth/granter/SocialTokenGranter.java +++ b/lab-auth/src/main/java/org/springblade/auth/granter/SocialTokenGranter.java @@ -97,7 +97,7 @@ public class SocialTokenGranter extends AbstractTokenGranter { } bladeUserDetails = new BladeUserDetails(user.getId(), tenantId, result.getData().getOauthId(), user.getName(), user.getRealName(), user.getDeptId(), user.getPostId(), user.getRoleId(), Func.join(result.getData().getRoles()), Func.toStr(userOauth.getAvatar(), TokenUtil.DEFAULT_AVATAR), - userOauth.getUsername(), AuthConstant.ENCRYPT + user.getPassword(), detail, true, true, true, true, + userOauth.getUsername(), AuthConstant.ENCRYPT + user.getPassword(), detail, false, true, true, true, true, AuthorityUtils.commaSeparatedStringToAuthorityList(Func.join(result.getData().getRoles()))); } else { throw new InvalidGrantException("social grant failure, feign client return error"); diff --git a/lab-auth/src/main/java/org/springblade/auth/service/BladeUserDetails.java b/lab-auth/src/main/java/org/springblade/auth/service/BladeUserDetails.java index 65b0f0a..b741562 100644 --- a/lab-auth/src/main/java/org/springblade/auth/service/BladeUserDetails.java +++ b/lab-auth/src/main/java/org/springblade/auth/service/BladeUserDetails.java @@ -64,8 +64,12 @@ public class BladeUserDetails extends User { * 用户详情 */ private final Kv detail; + /** + * 强制修改密码 + */ + private final boolean forcePasswordChange; - public BladeUserDetails(Long userId, String tenantId, String oauthId, String name, String realName, String deptId, String postId, String roleId, String roleName, String avatar, String username, String password, Kv detail, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection authorities) { + public BladeUserDetails(Long userId, String tenantId, String oauthId, String name, String realName, String deptId, String postId, String roleId, String roleName, String avatar, String username, String password, Kv detail, boolean forcePasswordChange, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection authorities) { super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); this.userId = userId; this.tenantId = tenantId; @@ -79,6 +83,7 @@ public class BladeUserDetails extends User { this.roleName = roleName; this.avatar = avatar; this.detail = detail; + this.forcePasswordChange = forcePasswordChange; } } diff --git a/lab-auth/src/main/java/org/springblade/auth/service/BladeUserDetailsServiceImpl.java b/lab-auth/src/main/java/org/springblade/auth/service/BladeUserDetailsServiceImpl.java index 94c3283..21b2c2a 100644 --- a/lab-auth/src/main/java/org/springblade/auth/service/BladeUserDetailsServiceImpl.java +++ b/lab-auth/src/main/java/org/springblade/auth/service/BladeUserDetailsServiceImpl.java @@ -83,9 +83,10 @@ public class BladeUserDetailsServiceImpl implements UserDetailsService { if (Func.isEmpty(userInfo.getRoles())) { throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_ROLE); } + boolean forcePasswordChange = Func.toInt(user.getForcePasswordChange()) == 1; return new BladeUserDetails(user.getId(), user.getTenantId(), StringPool.EMPTY, user.getName(), user.getRealName(), user.getDeptId(), user.getPostId(), user.getRoleId(), Func.join(result.getData().getRoles()), Func.toStr(user.getAvatar(), TokenUtil.DEFAULT_AVATAR), - username, AuthConstant.ENCRYPT + user.getPassword(), userInfo.getDetail(), true, true, true, true, + username, AuthConstant.ENCRYPT + user.getPassword(), userInfo.getDetail(), forcePasswordChange, true, true, true, true, AuthorityUtils.commaSeparatedStringToAuthorityList(Func.join(result.getData().getRoles()))); } else { throw new UsernameNotFoundException(result.getMsg()); diff --git a/lab-auth/src/main/java/org/springblade/auth/support/BladeJwtTokenEnhancer.java b/lab-auth/src/main/java/org/springblade/auth/support/BladeJwtTokenEnhancer.java index c3fbe9b..8c36ee2 100644 --- a/lab-auth/src/main/java/org/springblade/auth/support/BladeJwtTokenEnhancer.java +++ b/lab-auth/src/main/java/org/springblade/auth/support/BladeJwtTokenEnhancer.java @@ -48,6 +48,7 @@ public class BladeJwtTokenEnhancer implements TokenEnhancer { info.put(TokenUtil.AVATAR, principal.getAvatar()); info.put(TokenUtil.DETAIL, principal.getDetail()); info.put(TokenUtil.LICENSE, TokenUtil.LICENSE_NAME); + info.put(TokenUtil.FORCE_PASSWORD_CHANGE, principal.isForcePasswordChange()); ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info); //token状态设置 diff --git a/lab-auth/src/main/java/org/springblade/auth/utils/TokenUtil.java b/lab-auth/src/main/java/org/springblade/auth/utils/TokenUtil.java index f50acf4..5b0ca03 100644 --- a/lab-auth/src/main/java/org/springblade/auth/utils/TokenUtil.java +++ b/lab-auth/src/main/java/org/springblade/auth/utils/TokenUtil.java @@ -40,6 +40,7 @@ public class TokenUtil { public final static String DETAIL = TokenConstant.DETAIL; public final static String LICENSE = TokenConstant.LICENSE; public final static String LICENSE_NAME = TokenConstant.LICENSE_NAME; + public final static String FORCE_PASSWORD_CHANGE = "forcePasswordChange"; public final static String CAPTCHA_HEADER_KEY = "Captcha-Key"; public final static String CAPTCHA_HEADER_CODE = "Captcha-Code"; diff --git a/lab-service-api/lab-lims-api/src/main/java/org/springblade/lims/entry/ExamineItem.java b/lab-service-api/lab-lims-api/src/main/java/org/springblade/lims/entry/ExamineItem.java index e714833..5db77e9 100644 --- a/lab-service-api/lab-lims-api/src/main/java/org/springblade/lims/entry/ExamineItem.java +++ b/lab-service-api/lab-lims-api/src/main/java/org/springblade/lims/entry/ExamineItem.java @@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.NullSerializer; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.springblade.core.mp.base.BaseEntity; import org.springframework.data.annotation.Id; @@ -93,6 +94,12 @@ public class ExamineItem extends BaseEntity implements Serializable { */ private String inputMode; + /** + * 原始记录模板ID + */ + @ApiModelProperty("原始记录模板ID") + private Long templateId; + /** * 使用次数 */ diff --git a/lab-service-api/lab-lims-api/src/main/java/org/springblade/lims/entry/OriginalRecordTemplate.java b/lab-service-api/lab-lims-api/src/main/java/org/springblade/lims/entry/OriginalRecordTemplate.java new file mode 100644 index 0000000..b838e6c --- /dev/null +++ b/lab-service-api/lab-lims-api/src/main/java/org/springblade/lims/entry/OriginalRecordTemplate.java @@ -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; +} diff --git a/lab-service-api/lab-lims-api/src/main/java/org/springblade/lims/entry/TemplateField.java b/lab-service-api/lab-lims-api/src/main/java/org/springblade/lims/entry/TemplateField.java new file mode 100644 index 0000000..2a237f5 --- /dev/null +++ b/lab-service-api/lab-lims-api/src/main/java/org/springblade/lims/entry/TemplateField.java @@ -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; +} diff --git a/lab-service-api/lab-lims-api/src/main/java/org/springblade/lims/entry/TemplateFieldMapping.java b/lab-service-api/lab-lims-api/src/main/java/org/springblade/lims/entry/TemplateFieldMapping.java new file mode 100644 index 0000000..ea52388 --- /dev/null +++ b/lab-service-api/lab-lims-api/src/main/java/org/springblade/lims/entry/TemplateFieldMapping.java @@ -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; +} diff --git a/lab-service-api/lab-user-api/src/main/java/org/springblade/system/user/entity/User.java b/lab-service-api/lab-user-api/src/main/java/org/springblade/system/user/entity/User.java index 8d73559..af33428 100644 --- a/lab-service-api/lab-user-api/src/main/java/org/springblade/system/user/entity/User.java +++ b/lab-service-api/lab-user-api/src/main/java/org/springblade/system/user/entity/User.java @@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; import org.springblade.core.tenant.mp.TenantEntity; +import io.swagger.annotations.ApiModelProperty; import java.util.Date; @@ -101,4 +102,16 @@ public class User extends TenantEntity { */ private Integer accountStatus; + /** + * 是否管理员 + */ + @ApiModelProperty(value = "是否管理员 0-否 1-是") + private Integer is_admin; + + /** + * 首次登录强制修改密码 + */ + @ApiModelProperty(value = "首次登录强制修改密码 0-否 1-是") + private Integer forcePasswordChange; + } diff --git a/lab-service/lab-lims/src/main/java/org/springblade/lims/controller/OriginalRecordTemplateController.java b/lab-service/lab-lims/src/main/java/org/springblade/lims/controller/OriginalRecordTemplateController.java new file mode 100644 index 0000000..34de9fd --- /dev/null +++ b/lab-service/lab-lims/src/main/java/org/springblade/lims/controller/OriginalRecordTemplateController.java @@ -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() { + return R.data(originalRecordTemplateService.listWithFieldCount()); + } + + @PostMapping("/save") + public R save(@RequestBody OriginalRecordTemplate template) { + originalRecordTemplateService.save(template); + return R.data(template); + } + + @PostMapping("/update") + public R 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))); + } +} diff --git a/lab-service/lab-lims/src/main/java/org/springblade/lims/controller/ReagentFormulaController.java b/lab-service/lab-lims/src/main/java/org/springblade/lims/controller/ReagentFormulaController.java index 589fbda..4c5e2f8 100644 --- a/lab-service/lab-lims/src/main/java/org/springblade/lims/controller/ReagentFormulaController.java +++ b/lab-service/lab-lims/src/main/java/org/springblade/lims/controller/ReagentFormulaController.java @@ -11,10 +11,14 @@ import org.springblade.core.mp.support.Condition; import org.springblade.core.mp.support.Query; import org.springblade.core.tool.api.R; import org.springblade.core.tool.utils.Func; +import org.springblade.lims.entry.ExamineItem; import org.springblade.lims.entry.Reagent; import org.springblade.lims.entry.ReagentFormula; +import org.springblade.lims.entry.TemplateField; +import org.springblade.lims.service.IExamineItemService; import org.springblade.lims.service.IReagentFormulaService; import org.springblade.lims.service.IReagentService; +import org.springblade.lims.service.ITemplateFieldService; import org.springblade.lims.utils.FormulaValidationTool; import org.springframework.web.bind.annotation.*; @@ -39,6 +43,8 @@ public class ReagentFormulaController extends BladeController { private final IReagentFormulaService service; private final IReagentService reagentService; + private final IExamineItemService examineItemService; + private final ITemplateFieldService templateFieldService; /** * 分页查询 @@ -120,20 +126,44 @@ public class ReagentFormulaController extends BladeController { /** * 获取试剂关联的可用变量 - * 从 Reagent.resultDeterminationMethod 中语义提取 + * 优先从检验项模板字段中获取,无模板时回退到 Reagent.resultDeterminationMethod 解析 */ @GetMapping("/variables") @ApiOperation(value = "获取可用变量", notes = "根据试剂ID获取公式可用变量") - public R getVariables(@ApiParam(value = "试剂ID") @RequestParam Long reagentId) { - Reagent reagent = reagentService.getById(reagentId); - if (reagent == null) { - return R.data(new ArrayList<>()); + public R> getVariables(@ApiParam(value = "试剂ID") @RequestParam Long reagentId) { + // 1. 查找所有使用该试剂的检验项(reagentId 为逗号分隔字符串,使用 LIKE 匹配) + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.like(ExamineItem::getReagentId, reagentId.toString()); + List items = examineItemService.list(wrapper); + + // 2. 从有模板的检验项中收集模板字段名 + Set variables = new LinkedHashSet<>(); + boolean hasTemplateFields = false; + for (ExamineItem item : items) { + if (item.getTemplateId() != null) { + List fields = templateFieldService.getFieldsByTemplateId(item.getTemplateId()); + if (fields != null && !fields.isEmpty()) { + hasTemplateFields = true; + for (TemplateField field : fields) { + variables.add(field.getFieldName()); + } + } + } } - String method = reagent.getResultDeterminationMethod(); - List> variables = extractVariables(method); + // 3. 无模板字段时回退到旧逻辑:从 resultDeterminationMethod 解析 + if (!hasTemplateFields) { + Reagent reagent = reagentService.getById(reagentId); + if (reagent != null) { + String method = reagent.getResultDeterminationMethod(); + List> oldVars = extractVariables(method); + for (Map v : oldVars) { + variables.add((String) v.get("name")); + } + } + } - return R.data(variables); + return R.data(new ArrayList<>(variables)); } /** diff --git a/lab-service/lab-lims/src/main/java/org/springblade/lims/controller/TemplateFieldController.java b/lab-service/lab-lims/src/main/java/org/springblade/lims/controller/TemplateFieldController.java new file mode 100644 index 0000000..02ed237 --- /dev/null +++ b/lab-service/lab-lims/src/main/java/org/springblade/lims/controller/TemplateFieldController.java @@ -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(Long templateId) { + return R.data(templateFieldService.getFieldsByTemplateId(templateId)); + } + + @PostMapping("/save") + public R 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 fields) { + return R.status(templateFieldService.reorderFields(fields)); + } + + @PostMapping("/bindTemplate") + public R bindTemplate( + @ApiParam(value = "模板ID", required = true) @RequestParam Long templateId, + @RequestBody List fieldIds) { + return R.status(templateFieldService.bindTemplateFields(templateId, fieldIds)); + } +} diff --git a/lab-service/lab-lims/src/main/java/org/springblade/lims/mapper/OriginalRecordTemplateMapper.java b/lab-service/lab-lims/src/main/java/org/springblade/lims/mapper/OriginalRecordTemplateMapper.java new file mode 100644 index 0000000..9dc716c --- /dev/null +++ b/lab-service/lab-lims/src/main/java/org/springblade/lims/mapper/OriginalRecordTemplateMapper.java @@ -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 { + +} diff --git a/lab-service/lab-lims/src/main/java/org/springblade/lims/mapper/TemplateFieldMapper.java b/lab-service/lab-lims/src/main/java/org/springblade/lims/mapper/TemplateFieldMapper.java new file mode 100644 index 0000000..ec65397 --- /dev/null +++ b/lab-service/lab-lims/src/main/java/org/springblade/lims/mapper/TemplateFieldMapper.java @@ -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 { + +} diff --git a/lab-service/lab-lims/src/main/java/org/springblade/lims/mapper/TemplateFieldMappingMapper.java b/lab-service/lab-lims/src/main/java/org/springblade/lims/mapper/TemplateFieldMappingMapper.java new file mode 100644 index 0000000..cf5296a --- /dev/null +++ b/lab-service/lab-lims/src/main/java/org/springblade/lims/mapper/TemplateFieldMappingMapper.java @@ -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 { +} diff --git a/lab-service/lab-lims/src/main/java/org/springblade/lims/service/IOriginalRecordTemplateService.java b/lab-service/lab-lims/src/main/java/org/springblade/lims/service/IOriginalRecordTemplateService.java new file mode 100644 index 0000000..c077118 --- /dev/null +++ b/lab-service/lab-lims/src/main/java/org/springblade/lims/service/IOriginalRecordTemplateService.java @@ -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 { + + /** + * 查询所有原始记录模板(含字段数量) + * + * @return 模板列表(含 fieldCount) + */ + List> listWithFieldCount(); +} diff --git a/lab-service/lab-lims/src/main/java/org/springblade/lims/service/ITemplateFieldService.java b/lab-service/lab-lims/src/main/java/org/springblade/lims/service/ITemplateFieldService.java new file mode 100644 index 0000000..d97c60b --- /dev/null +++ b/lab-service/lab-lims/src/main/java/org/springblade/lims/service/ITemplateFieldService.java @@ -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 { + + /** + * 根据模板ID获取字段列表 + * + * @param templateId 模板ID + * @return 字段列表 + */ + List 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 fields); + + /** + * 绑定字段到模板 + * + * @param templateId 模板ID + * @param fieldIds 字段ID列表 + * @return 是否成功 + */ + boolean bindTemplateFields(Long templateId, List fieldIds); +} diff --git a/lab-service/lab-lims/src/main/java/org/springblade/lims/service/impl/OriginalRecordTemplateServiceImpl.java b/lab-service/lab-lims/src/main/java/org/springblade/lims/service/impl/OriginalRecordTemplateServiceImpl.java new file mode 100644 index 0000000..a4ac504 --- /dev/null +++ b/lab-service/lab-lims/src/main/java/org/springblade/lims/service/impl/OriginalRecordTemplateServiceImpl.java @@ -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 implements IOriginalRecordTemplateService { + + private final OriginalRecordTemplateMapper originalRecordTemplateMapper; + private final TemplateFieldMapper templateFieldMapper; + + @Override + public List> listWithFieldCount() { + // Query all non-deleted templates + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(OriginalRecordTemplate::getIsDeleted, 0) + .orderByAsc(OriginalRecordTemplate::getCreateTime); + List templates = originalRecordTemplateMapper.selectList(queryWrapper); + + // For each template, return basic info with real field count + List> result = new ArrayList<>(); + for (OriginalRecordTemplate tpl : templates) { + Map 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() + .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; + } +} diff --git a/lab-service/lab-lims/src/main/java/org/springblade/lims/service/impl/TemplateFieldServiceImpl.java b/lab-service/lab-lims/src/main/java/org/springblade/lims/service/impl/TemplateFieldServiceImpl.java new file mode 100644 index 0000000..174bf4c --- /dev/null +++ b/lab-service/lab-lims/src/main/java/org/springblade/lims/service/impl/TemplateFieldServiceImpl.java @@ -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 implements ITemplateFieldService { + + private final TemplateFieldMappingMapper templateFieldMappingMapper; + + @Override + public List getFieldsByTemplateId(Long templateId) { + if (templateId == null) { + // 字段库:返回所有未删除的字段(多对多设计下,一个字段可同时属于多个模板) + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TemplateField::getIsDeleted, 0); + return baseMapper.selectList(queryWrapper); + } + // 按模板查询:通过关联表 JOIN 获取字段列表 + LambdaQueryWrapper 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 clearMapping = new LambdaUpdateWrapper<>(); + clearMapping.eq(TemplateFieldMapping::getFieldId, id); + templateFieldMappingMapper.delete(clearMapping); + return baseMapper.updateById(field) > 0; + } + + @Override + public boolean reorderFields(List fields) { + // 排序逻辑已移至关联表(t_template_field_mapping.sort_order), + // 此方法保留以保持向后兼容,前端暂未使用 + return true; + } + + @Override + public boolean bindTemplateFields(Long templateId, List fieldIds) { + // 1. 清除该模板下的所有字段关联 + LambdaUpdateWrapper clearWrapper = new LambdaUpdateWrapper<>(); + clearWrapper.eq(TemplateFieldMapping::getTemplateId, templateId); + templateFieldMappingMapper.delete(clearWrapper); + + // 2. 绑定新字段(按 fieldIds 数组顺序生成 sort_order) + if (fieldIds != null && !fieldIds.isEmpty()) { + List 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; + } +} diff --git a/lab-service/lab-user/src/main/java/org/springblade/labuser/init/AdminAccountInitializer.java b/lab-service/lab-user/src/main/java/org/springblade/labuser/init/AdminAccountInitializer.java new file mode 100644 index 0000000..d178d51 --- /dev/null +++ b/lab-service/lab-user/src/main/java/org/springblade/labuser/init/AdminAccountInitializer.java @@ -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; + +/** + * 应用启动时自动检查并创建备用管理员账户 + *

+ * 当 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 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.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.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(); + } + +} diff --git a/lab-service/lab-user/src/main/java/org/springblade/system/user/UserApplication.java b/lab-service/lab-user/src/main/java/org/springblade/system/user/UserApplication.java index 834be4d..88adb13 100644 --- a/lab-service/lab-user/src/main/java/org/springblade/system/user/UserApplication.java +++ b/lab-service/lab-user/src/main/java/org/springblade/system/user/UserApplication.java @@ -5,6 +5,7 @@ import org.springblade.core.cloud.feign.EnableBladeFeign; import org.springblade.core.launch.BladeApplication; import org.springblade.core.launch.constant.AppConstant; import org.springframework.cloud.client.SpringCloudApplication; +import org.springframework.context.annotation.ComponentScan; import org.springframework.scheduling.annotation.EnableAsync; /** @@ -15,6 +16,7 @@ import org.springframework.scheduling.annotation.EnableAsync; @EnableBladeFeign @SpringCloudApplication @EnableAsync +@ComponentScan({"org.springblade.system.user", "org.springblade.labuser"}) public class UserApplication { public static void main(String[] args) { diff --git a/lab-service/lab-user/src/main/java/org/springblade/system/user/controller/UserController.java b/lab-service/lab-user/src/main/java/org/springblade/system/user/controller/UserController.java index 4e1b5d2..eb66ec8 100644 --- a/lab-service/lab-user/src/main/java/org/springblade/system/user/controller/UserController.java +++ b/lab-service/lab-user/src/main/java/org/springblade/system/user/controller/UserController.java @@ -113,6 +113,18 @@ public class UserController { return R.data(UserWrapper.build().pageVO(pages)); } + /** + * 管理员列表 + */ + @GetMapping("/admin/list") + @ApiOperationSupport(order = 3) + @ApiOperation(value = "管理员列表", notes = "查询管理员用户") + //@PreAuth(RoleConstant.HAS_ROLE_ADMIN) + public R> adminList(User user, Query query) { + IPage pages = userService.getAdminList(Condition.getPage(query), user); + return R.data(pages); + } + /** * 获取部门下角色用户列表 */ diff --git a/lab-service/lab-user/src/main/java/org/springblade/system/user/service/IUserService.java b/lab-service/lab-user/src/main/java/org/springblade/system/user/service/IUserService.java index 77e6b5b..a91cd13 100644 --- a/lab-service/lab-user/src/main/java/org/springblade/system/user/service/IUserService.java +++ b/lab-service/lab-user/src/main/java/org/springblade/system/user/service/IUserService.java @@ -214,4 +214,13 @@ public interface IUserService extends BaseService { * @return */ public List listRolebyId(String rolename); + + /** + * 管理员列表(分页) + * + * @param page + * @param user + * @return + */ + IPage getAdminList(IPage page, User user); } diff --git a/lab-service/lab-user/src/main/java/org/springblade/system/user/service/impl/UserServiceImpl.java b/lab-service/lab-user/src/main/java/org/springblade/system/user/service/impl/UserServiceImpl.java index 654010f..b058265 100644 --- a/lab-service/lab-user/src/main/java/org/springblade/system/user/service/impl/UserServiceImpl.java +++ b/lab-service/lab-user/src/main/java/org/springblade/system/user/service/impl/UserServiceImpl.java @@ -544,5 +544,12 @@ public class UserServiceImpl extends BaseServiceImpl implement return null; } + @Override + public IPage getAdminList(IPage page, User user) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(User::getIs_admin, 1); + return baseMapper.selectPage(page, queryWrapper); + } + } diff --git a/lab-service/lab-user/src/main/java/org/springblade/system/user/util/RedisKeyExpirationListener.java b/lab-service/lab-user/src/main/java/org/springblade/system/user/util/RedisKeyExpirationListener.java index 5d020b3..d943685 100644 --- a/lab-service/lab-user/src/main/java/org/springblade/system/user/util/RedisKeyExpirationListener.java +++ b/lab-service/lab-user/src/main/java/org/springblade/system/user/util/RedisKeyExpirationListener.java @@ -46,9 +46,11 @@ public class RedisKeyExpirationListener extends KeyExpirationEventMessageListene String expiredKey = message.toString(); // System.out.println("缓存过期的key为:" + expiredKey); LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(Train::getId,expiredKey); + queryWrapper.eq(Train::getId, expiredKey); Train train = trainService.getOne(queryWrapper); - System.out.println(train.getName()); + if (train == null) { + return; + } //这里可以根据培训的id查询到培训的具体信息,然后再实现通知功能 //获取培训人员信息并发消息 LambdaQueryWrapper personWrapper = new LambdaQueryWrapper<>(); @@ -62,6 +64,9 @@ public class RedisKeyExpirationListener extends KeyExpirationEventMessageListene LambdaQueryWrapper speakWrapper = new LambdaQueryWrapper<>(); speakWrapper.eq(TrainSpeak::getTrainId, train.getId()); TrainSpeak teacher = trainSpeakService.getOne(speakWrapper); + if (teacher == null) { + return; + } messageClient.event(SysTypeEnum.INFORM.getValue(), "会议提醒", "您有新的会议将在" + train.getDuration() + "分钟后开始,请准时参加!", 1, 5, teacher.getSpeakName(), "/train/project"); }