second-state / chat-with-chatgpt

Chat with ChatGPT via GitHub issue comments.
111 stars 13 forks source link

springboot参数校验,两个参数不能同时为空 #330

Open ialianga opened 1 year ago

ialianga commented 1 year ago

springboot校验RequestBody, RequestBody中的字段只允许一个字段为空

alabulei1 commented 1 year ago

Hello, I am your assistant on GitHub, built by the Second State team. I am ready for your question now! Please go ahead and ask your question about Spring Boot parameter validation.

ialianga commented 1 year ago

springboot校验RequestBody, RequestBody中的字段只允许一个字段为空

alabulei1 commented 1 year ago

To validate a RequestBody in Spring Boot and enforce that only one field can be empty, you can use the javax.validation annotations along with a custom validation annotation.

Here's an example of how you can achieve this:

  1. Create a custom validation annotation, let's say OneFieldEmpty, to mark the fields that should allow only one to be empty.
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = OneFieldEmptyValidator.class)
public @interface OneFieldEmpty {

    String message() default "Only one field can be empty";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}
  1. Create a custom validator, OneFieldEmptyValidator, to implement the validation logic.
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class OneFieldEmptyValidator implements ConstraintValidator<OneFieldEmpty, YourRequestBodyClass> {

    @Override
    public boolean isValid(YourRequestBodyClass requestBody, ConstraintValidatorContext context) {
        // Check if only one field is empty
        // Replace the field1, field2, etc. with the actual field names in your request body
        int count = 0;
        if (requestBody.getField1() == null || requestBody.getField1().isEmpty()) {
            count++;
        }
        if (requestBody.getField2() == null || requestBody.getField2().isEmpty()) {
            count++;
        }
        // If the count is exactly 1, return true; otherwise, return false.
        return count == 1;
    }
}
  1. Apply the custom annotation to your RequestBody class.
@OneFieldEmpty
public class YourRequestBodyClass {
    // Your field declarations here.
}
  1. In your controller class, annotate the RequestBody parameter with @Valid to enable validation.
import javax.validation.Valid;

@RestController
public class YourController {

    @PostMapping("/your-endpoint")
    public ResponseEntity<?> yourEndpoint(@Valid @RequestBody YourRequestBodyClass requestBody) {
        // Your controller logic here.
    }
}

Now, when the request is made to the endpoint, the validation will be triggered and an error response will be returned if the validation fails.

Please note that you need to add the necessary dependencies, such as javax.validation, hibernate-validator, and spring-boot-starter-validation, to your project for this validation to work.

Hope this helps! Let me know if you have any further questions.

ialianga commented 1 year ago

requestBody 为object

alabulei1 commented 1 year ago

If your RequestBody is an object instead of a simple field, you can still apply the same validation logic by modifying the custom validator.

Let's assume your RequestBody class is called YourRequestBodyClass and it has two fields field1 and field2. Here's an example of how to modify the custom validator for validating an object:

  1. Modify the custom validator, OneFieldEmptyValidator, to implement the validation logic for the object fields.
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class OneFieldEmptyValidator implements ConstraintValidator<OneFieldEmpty, YourRequestBodyClass> {

    @Override
    public boolean isValid(YourRequestBodyClass requestBody, ConstraintValidatorContext context) {
        // Check if only one field is empty
        int count = 0;
        if (requestBody.getField1() == null || requestBody.getField1().isEmpty()) {
            count++;
        }
        if (requestBody.getField2() == null || requestBody.getField2().isEmpty()) {
            count++;
        }
        // If the count is exactly 1, return true; otherwise, return false.
        return count == 1;
    }
}
  1. Apply the custom annotation to your YourRequestBodyClass.
@OneFieldEmpty
public class YourRequestBodyClass {
    private String field1;
    private String field2;

    // Getters and setters
}
  1. In your controller class, annotate the @RequestBody parameter with @Valid to enable validation.
import javax.validation.Valid;

@RestController
public class YourController {

    @PostMapping("/your-endpoint")
    public ResponseEntity<?> yourEndpoint(@Valid @RequestBody YourRequestBodyClass requestBody) {
        // Your controller logic here
    }
}

Now, when the request is made to the endpoint, the validation will be triggered, and an error response will be returned if the validation fails.

Make sure to add the necessary dependencies, such as javax.validation, hibernate-validator, and spring-boot-starter-validation, to your project for this validation to work.

Let me know if you have any further questions!

ialianga commented 1 year ago

RequestBody is any type

alabulei1 commented 1 year ago

If your RequestBody can have any type, you can still perform the validation by using the @Validated annotation along with a method-level validation.

Here's an example of how you can achieve this:

  1. Annotate your controller class with @Validated to enable method-level validation.
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Validated
public class YourController {

    @PostMapping("/your-endpoint")
    public ResponseEntity<?> yourEndpoint(@RequestBody @Valid YourRequestBodyClass requestBody) {
        // Your controller logic here.
    }
}
  1. Create your YourRequestBodyClass with the required fields and annotate them with the appropriate validation annotations according to your needs.
public class YourRequestBodyClass {

    @NotBlank(message = "Field1 is required")
    private String field1;

    @NotBlank(message = "Field2 is required")
    private String field2;

    // Getters and setters.
}

In this example, I used the @NotBlank validation annotation from the javax.validation.constraints package to ensure that the fields are not empty or contain only whitespace.

Now, when the request is made to the endpoint, the validation will be triggered, and an error response will be returned if the validation fails.

Make sure to add the necessary dependencies, such as javax.validation, hibernate-validator, and spring-boot-starter-validation, to your project for this validation to work.

Feel free to modify the annotations based on your specific validation requirements.

Let me know if you have any further questions!

ialianga commented 1 year ago

根据上面代码写单元测试

alabulei1 commented 1 year ago

Certainly! Here's an example of how you can write a unit test for the controller method using the Spring Boot Test framework and Mockito:

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.HandlerMethod;

import javax.validation.ConstraintViolation;
import javax.validation.Path;
import javax.validation.Validation;
import java.util.Set;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
public class YourControllerTest {

    @Mock
    private YourService yourService;

    @InjectMocks
    private YourController yourController;

    private LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.setValidator(validator);
    }

    @Test
    public void testYourEndpoint_ValidationFailed() {
        // Arrange
        YourRequestBodyClass requestBody = new YourRequestBodyClass();
        requestBody.setField1("");
        requestBody.setField2("");

        validator.setProviderClass(Validation.class);
        validator.afterPropertiesSet();

        MethodArgumentNotValidException ex = new MethodArgumentNotValidException(
                new HandlerMethod(yourController, "yourEndpoint", YourRequestBodyClass.class),
                null, null);

        // Act
        ResponseEntity<?> responseEntity = yourController.yourEndpoint(requestBody, ex);

        // Assert
        assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode());
    }

    @Test
    public void testYourEndpoint_Success() {
        // Arrange
        YourRequestBodyClass requestBody = new YourRequestBodyClass();
        requestBody.setField1("value1");

        // Perform additional mocking and setup according to your use case

        // Act
        ResponseEntity<?> responseEntity = yourController.yourEndpoint(requestBody);

        // Assert
        assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
        // Perform additional assertions as needed
    }
}

Note that in this example, I assumed that YourService is the service class that will be called in the controller's endpoint. You can replace it with your actual service class.

In the testYourEndpoint_ValidationFailed test, I purposely set empty values for both field1 and field2 to trigger the validation failure.

In the testYourEndpoint_Success test, I demonstrated a successful endpoint call with valid input.

Make sure to include the necessary dependencies for testing, such as JUnit, Mockito, and Spring Boot Test, in your project.

Feel free to adjust the test cases and add additional assertions based on your specific requirements.

Let me know if you have any further questions!

ialianga commented 1 year ago

springboot自定义参数校验单元测试

alabulei1 commented 1 year ago

To unit test a custom parameter validation in a Spring Boot application, you can use the Spring Boot Test framework along with JUnit and Mockito. Here's an example of how you can write a unit test for a custom parameter validation:

  1. Create a test class and annotate it with the necessary annotations.
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(YourController.class)
public class YourControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void testYourEndpoint_CustomValidation() throws Exception {
        // Arrange
        MockHttpServletRequestBuilder requestBuilder = post("/your-endpoint")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"field\": \"invalid_value\"}");

        // Act & Assert
        mockMvc.perform(requestBuilder)
                .andExpect(status().isBadRequest())
                .andExpect(MockMvcResultMatchers.content().string("Invalid field value"));
    }
}

In this example, YourController is annotated with @WebMvcTest to limit the auto-configuration to only the web layer components. This allows you to focus the unit test on the controller without starting the full application context.

  1. Run the test case to validate the custom parameter validation.

Make sure to include the necessary dependencies (such as JUnit, Mockito, and Spring Boot Test) in your project for testing.

In the test case, mockMvc is autowired, and you can use it to perform HTTP requests and validate the responses using the fluent API provided by the MockMvcResultMatchers class. In this example, the test sends a POST request to the /your-endpoint endpoint with an invalid field value, and it expects a 400 Bad Request status and the response body to be "Invalid field value".

You may need to modify the request payload and response assertions based on your specific custom parameter validation implementation.

Let me know if you have any further questions!

ialianga commented 1 year ago

给一下代码写单元测试 `package com.huawei.cubeconfig.core.configchange.adapter.inband.rest;

import com.huawei.cubeconfig.common.adapter.model.BaseResponse; import com.huawei.cubeconfig.common.util.BeanUtils; import com.huawei.cubeconfig.core.configchange.adapter.inband.rest.model.CheckVersionReq; import com.huawei.cubeconfig.core.configchange.adapter.inband.rest.model.ConfigReleaseReq; import com.huawei.cubeconfig.core.configchange.adapter.inband.rest.model.RollbackVersionReq; import com.huawei.cubeconfig.core.configchange.adapter.inband.rest.model.UpdateItemReq; import com.huawei.cubeconfig.core.configchange.application.ClusterScene; import com.huawei.cubeconfig.core.configchange.application.ConfigChangeScene; import com.huawei.cubeconfig.core.configchange.domain.entity.UpdateItemEntity; import com.huawei.cubeconfig.core.configchange.infrastructure.repository.cluster.ClusterQueryCriteria; import com.huawei.cubeconfig.core.configchange.infrastructure.repository.configitem.ConfigItemQueryCriteria; import com.huawei.cubeconfig.core.configvisualization.adapter.inband.rest.model.req.ClusterReq; import com.huawei.cubeconfig.core.configvisualization.adapter.inband.rest.model.resp.ConfigItemInfo; import com.huawei.cubeconfig.core.configvisualization.adapter.inband.rest.model.resp.ConfigReleaseVo; import com.huawei.cubeconfig.core.configvisualization.application.ConfigScene; import com.huawei.cubeconfig.generic.auth.domain.model.annotation.NorthAuth; import com.huawei.cubeconfig.generic.auth.domain.model.enums.AuthRule; import com.huawei.cubeconfig.generic.proxy.infrastructure.apis.INorthApi; import com.huawei.cubeconfig.generic.util.PrivilegeUtil; import com.huawei.cubeconfig.supporting.infrastructure.QueryStringArgResolver; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController @RequiredArgsConstructor public class ConfigChangeController { private final ClusterScene clusterScene;

private final ConfigChangeScene configChangeScene;

private final ConfigScene configScene;

/**
 * 修改配置项
 *
 * @param req 修改参数
 * @return 配置项列表
 */
@NorthAuth(authRule = AuthRule.NORTH_OPERATOR)
@PostMapping(INorthApi.ConfigChange.UPDATE_ITEMS)
public BaseResponse<ConfigItemInfo> updateItems(@RequestBody UpdateItemReq req) {
    Integer clusterId = getClusterId(req);
    List<UpdateItemEntity> entities = req.transToEntity(clusterId);

    configChangeScene.changeItem(clusterId, entities);
    return new BaseResponse<>(configScene.queryConfigItemInfo(ConfigItemQueryCriteria.builder().clusterId(clusterId).build()));
}

/**
 * 校验配置版本
 *
 * @param req 版本参数
 * @return 配置项列表
 */
@NorthAuth(authRule = AuthRule.NORTH_OPERATOR)
@PostMapping(INorthApi.ConfigChange.CHECK_VERSION)
public BaseResponse<ConfigItemInfo> checkConfigVersion(@RequestBody CheckVersionReq req) {
    Integer clusterId = getClusterId(req);
    return new BaseResponse<>(configChangeScene.checkConfigVersion(clusterId, req.getConfigVersion()));
}

/**
 * 配置发布
 *
 * @param req 发布参数
 * @return 配置发布版本
 */
@NorthAuth(authRule = AuthRule.NORTH_OPERATOR)
@PostMapping(INorthApi.ConfigChange.RELEASE)
public BaseResponse<ConfigReleaseVo> configRelease(@RequestBody ConfigReleaseReq req) {
    Integer clusterId = getClusterId(req);
    return new BaseResponse<>(BeanUtils.copyProperties(configChangeScene.configRelease(clusterId,
        req.getConfigVersion(), req.getComment(), req.getOperator()), ConfigReleaseVo::new));
}

/**
 * 回滚发布版本
 *
 * @param req 回滚参数
 * @return 回滚配置项
 */
@NorthAuth(authRule = AuthRule.NORTH_OPERATOR)
@PostMapping(INorthApi.ConfigChange.ROLLBACK)
public BaseResponse<ConfigItemInfo> rollbackReleaseVersion(@RequestBody RollbackVersionReq req) {
    Integer clusterId = getClusterId(req);
    configChangeScene.rollbackVersion(clusterId, req.getConfigVersion(), req.getOperator());
    return new BaseResponse<>(configScene.queryConfigItemInfo(ConfigItemQueryCriteria.builder().clusterId(clusterId).build()));
}

/**
 * 下线集群,删除集群配置信息
 *
 * @param req 集群信息
 * @return 集群id
 */
@NorthAuth(authRule = AuthRule.NORTH_OPERATOR)
@DeleteMapping(INorthApi.ConfigChange.CLUSTER)
public BaseResponse<Integer> deleteCluster(@QueryStringArgResolver ClusterReq req) {
    return new BaseResponse<>(clusterScene.deleteCluster(BeanUtils.copyProperties(req, ClusterQueryCriteria::new)));
}

private Integer getClusterId(ClusterReq req) {
    return clusterScene.queryCluster(BeanUtils.copyProperties(req, ClusterQueryCriteria::new), PrivilegeUtil.getAuthUsername()).getId();
}

}`

alabulei1 commented 1 year ago

以下是针对ConfigChangeController类中的每个请求方法编写的单元测试代码:

package com.huawei.cubeconfig.core.configchange.adapter.inband.rest;

import com.huawei.cubeconfig.common.adapter.model.BaseResponse;
import com.huawei.cubeconfig.core.configchange.adapter.inband.rest.model.CheckVersionReq;
import com.huawei.cubeconfig.core.configchange.adapter.inband.rest.model.ConfigReleaseReq;
import com.huawei.cubeconfig.core.configchange.adapter.inband.rest.model.RollbackVersionReq;
import com.huawei.cubeconfig.core.configchange.adapter.inband.rest.model.UpdateItemReq;
import com.huawei.cubeconfig.core.configchange.application.ClusterScene;
import com.huawei.cubeconfig.core.configchange.application.ConfigChangeScene;
import com.huawei.cubeconfig.core.configvisualization.application.ConfigScene;
import com.huawei.cubeconfig.core.configvisualization.adapter.inband.rest.model.req.ClusterReq;
import com.huawei.cubeconfig.core.configvisualization.adapter.inband.rest.model.resp.ConfigItemInfo;
import com.huawei.cubeconfig.core.configvisualization.adapter.inband.rest.model.resp.ConfigReleaseVo;
import com.huawei.cubeconfig.generic.proxy.infrastructure.apis.INorthApi;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import java.util.Collections;

import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class ConfigChangeControllerTest {
    private MockMvc mockMvc;
    private ClusterScene clusterScene;
    private ConfigChangeScene configChangeScene;
    private ConfigScene configScene;
    private ConfigChangeController configChangeController;

    @BeforeEach
    public void setup() {
        clusterScene = mock(ClusterScene.class);
        configChangeScene = mock(ConfigChangeScene.class);
        configScene = mock(ConfigScene.class);

        configChangeController = new ConfigChangeController(clusterScene, configChangeScene, configScene);

        mockMvc = MockMvcBuilders.standaloneSetup(configChangeController).build();
    }

    @Test
    public void testUpdateItems() throws Exception {
        UpdateItemReq req = new UpdateItemReq();
        req.setClusterId(1);
        req.setItems(Collections.emptyList());

        when(configChangeScene.changeItem(anyInt(), anyList())).thenReturn(true);
        when(configScene.queryConfigItemInfo(any())).thenReturn(new ConfigItemInfo());

        mockMvc.perform(MockMvcRequestBuilders.post(INorthApi.ConfigChange.UPDATE_ITEMS)
                .contentType(MediaType.APPLICATION_JSON)
                .content("{ \"clusterId\": 1, \"items\": [] }")
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print());

        Mockito.verify(configChangeScene).changeItem(anyInt(), anyList());
        Mockito.verify(configScene).queryConfigItemInfo(any());
    }

    @Test
    public void testCheckConfigVersion() throws Exception {
        CheckVersionReq req = new CheckVersionReq();
        req.setClusterId(1);
        req.setConfigVersion("1.0.0");

        when(configChangeScene.checkConfigVersion(anyInt(), any())).thenReturn(new ConfigItemInfo());

        mockMvc.perform(MockMvcRequestBuilders.post(INorthApi.ConfigChange.CHECK_VERSION)
                .contentType(MediaType.APPLICATION_JSON)
                .content("{ \"clusterId\": 1, \"configVersion\": \"1.0.0\" }")
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print());

        Mockito.verify(configChangeScene).checkConfigVersion(anyInt(), any());
    }

    @Test
    public void testConfigRelease() throws Exception {
        ConfigReleaseReq req = new ConfigReleaseReq();
        req.setClusterId(1);
        req.setConfigVersion("1.0.0");
        req.setComment("Test");
        req.setOperator("Admin");

        when(configChangeScene.configRelease(anyInt(), any(), any(), any())).thenReturn(new ConfigReleaseVo());

        mockMvc.perform(MockMvcRequestBuilders.post(INorthApi.ConfigChange.RELEASE)
                .contentType(MediaType.APPLICATION_JSON)
                .content("{ \"clusterId\": 1, \"configVersion\": \"1.0.0\", \"comment\": \"Test\", \"operator\": \"Admin\" }")
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print());

        Mockito.verify(configChangeScene).configRelease(anyInt(), any(), any(), any());
    }

    @Test
    public void testRollbackReleaseVersion() throws Exception {
        RollbackVersionReq req = new RollbackVersionReq();
        req.setClusterId(1);
        req.setConfigVersion("1.0.0");
        req.setOperator("Admin");

        when(configChangeScene.rollbackVersion(anyInt(), any(), any())).thenReturn(true);
        when(configScene.queryConfigItemInfo(any())).thenReturn(new ConfigItemInfo());

        mockMvc.perform(MockMvcRequestBuilders.post(INorthApi.ConfigChange.ROLLBACK)
                .contentType(MediaType.APPLICATION_JSON)
                .content("{ \"clusterId\": 1, \"configVersion\": \"1.0.0\", \"operator\": \"Admin\" }")
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print());

        Mockito.verify(configChangeScene).rollbackVersion(anyInt(), any(), any());
        Mockito.verify(configScene).queryConfigItemInfo(any());
    }

    @Test
    public void testDeleteCluster() throws Exception {
        ClusterReq req = new ClusterReq();
        req.setClusterId(1);

        when(clusterScene.deleteCluster(any())).thenReturn(1);

        mockMvc.perform(MockMvcRequestBuilders.delete(INorthApi.ConfigChange.CLUSTER)
                .contentType(MediaType.APPLICATION_JSON)
                .param("clusterId", "1")
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print());

        Mockito.verify(clusterScene).deleteCluster(any());
    }
}

以上代码使用了Mockito框架,根据每个请求方法的逻辑编写了相应的测试方法。通过mock对象进行模拟,验证方法的参数和返回值是否符合预期。测试用例使用了MockMvc进行请求的模拟,并通过验证请求的返回结果来判断测试是否通过。

ialianga commented 1 year ago

如何忽略NorthAuth的校验

alabulei1 commented 1 year ago

要忽略NorthAuth的校验,你可以使用@MockBean注解来模拟NorthAuth类,并返回一个默认的对象。然后在测试方法上使用@WithMockUser注解来模拟已认证的用户,绕过权限校验。

首先,在ConfigChangeControllerTest类中添加以下代码:

import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.security.test.context.support.WithMockUser;

@AutoConfigureMockMvc
public class ConfigChangeControllerTest {
    // ...

    @MockBean
    private NorthAuth northAuth;

    // ...

    @WithMockUser
    @Test
    public void testUpdateItems() throws Exception {
        // Test case code
    }

    // ...
}

在测试方法上添加@WithMockUser注解可以模拟已认证的用户。也可以通过设置usernameroles属性指定特定的用户名和角色,如@WithMockUser(username = "admin", roles = {"USER", "ADMIN"})

然后,模拟NorthAuth类返回默认对象,在@BeforeEach方法中添加以下代码:

@BeforeEach
public void setup() {
    // ...

    northAuth = mock(NorthAuth.class);
    when(northAuth.isAccessAllowed(any())).thenReturn(true);

    // ...
}

这样,模拟的NorthAuth类就会返回true,表示对所有请求都放行。

通过以上操作,你就可以忽略NorthAuth的校验,并且在测试方法中模拟已认证的用户进行测试。

ialianga commented 1 year ago

springboot 注解满足特地条件才生效

alabulei1 commented 1 year ago

在Spring Boot中,可以使用条件注解来满足特定条件时才生效。条件注解是@Conditional注解的子类,可以通过实现Condition接口来自定义条件。

以下是一个示例,演示了如何使用条件注解来控制其他注解生效的时机:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Conditional(MyCondition.class)
public @interface MyAnnotation {
    // 注解的定义
}
public class MyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 满足条件时返回true,不满足条件时返回false
        // 可根据实际情况编写条件判断逻辑
        return true; // 这里示意条件满足的情况
    }
}

在上述示例中,@MyAnnotation注解会在自定义的MyCondition条件类中定义的条件满足时才会生效。条件的判断逻辑可以根据实际需求来编写。

然后,可以在需要使用@MyAnnotation注解的地方进行标注:

@MyAnnotation
public class MyService {
    // ...
}

当满足MyCondition中定义的条件时,MyService类上的@MyAnnotation注解就会生效。

这样,你就可以根据自定义的条件来控制注解生效的时机。

ialianga commented 1 year ago

写一份安全生产学习心得,120字左右

alabulei1 commented 1 year ago

在学习安全生产的过程中,我深刻认识到安全意识至关重要。安全生产不仅仅是一种规定和制度,更是一种思维和态度。只有全员参与,才能形成良好的安全文化。我学习了各种安全管理知识和技能,包括风险评估、应急预案等,也参与了模拟演练和实地考察。这个过程中,我深刻认识到安全事故对人员和社会的影响,更懂得如何做好安全防范和处理。通过学习安全生产,我将努力将安全意识贯穿于工作和生活中,为自己和他人的安全尽一份责任,保障生产的顺利进行。

ialianga commented 1 year ago

安全生产不仅仅是一种规定和制度,更是一种思维和态度。只有全员参与,才能形成良好的安全生产文化,在学习中,我了解到事故的可避免性和防范措施的必要性。作为员工,我们要时刻保持警觉,正确使用劳动工具,遵守安全规程,以确保自身安全和他人的生命财产安全。同时,定期参加培训和演练,不断提升安全意识和应急处理能力。安全生产是保障生命安全和企业发展的基础,我们每个人都应该时刻铭记,营造安全稳定的工作环境。

ialianga commented 1 year ago

sre安全生产学习心得,120字左右

alabulei1 commented 1 year ago

学习安全生产让我明白,安全不仅仅是一种规定和制度,更是一种思维和态度。安全生产需要全员参与,形成良好的安全文化。在学习过程中,我认识到事故是可以避免的,防范措施是必要的。作为员工,我们要保持警觉,正确使用劳动工具,遵守安全规程,确保自己和他人的安全。同时,参加培训和演练,提升安全意识和应急处理能力。安全生产是保障生命安全和企业发展的基础,每个人都应时刻铭记,并营造安全稳定的工作环境。

alabulei1 commented 1 year ago

在学习SRE(Site Reliability Engineering)安全生产的过程中,我深刻认识到安全是SRE工作的核心要素。作为SRE工程师,我们需要关注系统的可靠性和稳定性,同时也要时刻关注安全风险和威胁。我学习了各种安全工具和技术,包括监控和日志管理、漏洞扫描和安全审计等,也参与了安全演练和渗透测试。这个过程中,我深刻认识到安全对于企业的重要性,也学会了如何保护系统免受攻击。通过学习SRE安全生产,我将继续关注和提升系统的安全性,努力确保服务的高可用性和安全性。

idamiaoo commented 12 months ago

golang使用gomonkey为函数/方法打桩

alabulei1 commented 12 months ago

在Golang中,使用gomonkey库可以方便地对函数或方法进行打桩,以便在测试中进行模拟或替代。下面是一个简单的示例,展示了如何使用gomonkey来为函数打桩:

首先,确保已经安装了gomonkey库,可以使用以下命令进行安装:

go get github.com/agiledragon/gomonkey

接下来,假设有一个名为functionToStub的函数,需要在测试中进行打桩。 在测试函数中,可以按照以下步骤使用gomonkey:

  1. 导入必要的包

    import (
    "github.com/agiledragon/gomonkey"
    "testing"
    )
  2. 创建一个Monkey实例

    func TestMyFunction(t *testing.T) {
    p := gomonkey.NewPatch(MyPackage.functionToStub)
    defer p.Reset()
    
    // 打桩逻辑
    p.ApplyFunc(func() int {
        return 42 // 设置函数返回值为42
    })
    
    // 测试代码
    result := MyFunction()
    // 对于返回值为42的情况进行验证
    if result != 42 {
        t.Fatal("Unexpected result")
    }
    }
  3. 打桩后,测试代码将使用自定义的逻辑(返回42),而不是实际函数的逻辑。可以根据自己的需求修改打桩逻辑。

通过这样的方式,就可以使用gomonkey为函数或方法进行打桩,并在测试中轻松模拟所需的行为。

idamiaoo commented 12 months ago

golang单元测试实践

alabulei1 commented 12 months ago

在Golang中,单元测试是一种重要且常用的测试方法。以下是一些Golang单元测试的实践建议:

  1. 使用内置的testing包:Golang的testing包提供了丰富的测试功能和断言方法,是进行单元测试的基础。编写测试函数时,应该以Test开头,并接收一个*testing.T类型的参数。

  2. 分离测试和实现:保持测试代码和生产代码的分离,避免相互依赖。在测试文件中引入需要测试的包或文件,并在测试函数中调用相关函数或方法进行测试。

  3. 编写清晰的测试用例:测试函数应该覆盖多个场景和边界条件,确保被测试的函数或方法在各种情况下都能正确运行。每个测试用例应该具有明确的输入和期望输出,利用断言方法进行结果验证。

  4. 使用表格驱动测试:表格驱动测试是一种将多个测试用例数据放入表格中进行循环遍历测试的方式。通过这种方式,可以更直观地展示测试用例,减少冗余代码,并方便添加新的测试数据。

  5. 使用Mocking或Stubbing工具:在测试中,可能需要模拟外部依赖或者对函数进行打桩,以确保测试的独立性和可靠性。可以使用一些Mocking或Stubbing工具,如gomock或gomonkey,来完成这些任务。

  6. 运行并行测试:使用go test -p参数可以并行执行测试,提高测试的执行效率。合理设置并行度,可以根据测试机器的CPU核心数进行调整,以获得最佳性能。

  7. 执行测试覆盖率分析:使用go test -cover命令可以查看代码的测试覆盖率报告。测试覆盖率分析可以帮助发现测试用例的漏洞,并指导编写更全面的测试。

  8. 持续集成和自动化测试:将单元测试与持续集成(CI)流程结合起来,自动运行测试并提供测试报告。这样可以确保代码在每次更改后都经过了测试,并可以及早发现潜在问题。

通过以上实践,可以编写健壮、可靠的单元测试,提高代码质量和可维护性,减少潜在的bug和问题。

idamiaoo commented 12 months ago

使用 gomock 为接口打桩

alabulei1 commented 12 months ago

要使用gomock为接口打桩,可以按照以下步骤进行:

  1. 安装gomockmockgen工具:

    go get github.com/golang/mock/gomock
    go install github.com/golang/mock/mockgen
  2. 定义需要打桩的接口:

    package mypackage
    
    type MyInterface interface {
       DoSomething() string
    }
  3. 生成接口的Mock实现文件:

    mockgen -destination=./mocks/mock_myinterface.go -source=./mypackage/myinterface.go

    执行以上命令后,会在./mocks目录下生成mock_myinterface.go文件,其中包含了接口MyInterface的Mock实现。

  4. 在测试中使用生成的Mock实现:

    package mypackage_test
    
    import (
       "testing"
    
       "github.com/golang/mock/gomock"
       "github.com/your-username/your-repo/mocks"
    )
    
    func TestFoo(t *testing.T) {
       ctrl := gomock.NewController(t)
       defer ctrl.Finish()
    
       mockMyInterface := mocks.NewMockMyInterface(ctrl)
    
       // 设置打桩逻辑
       mockMyInterface.EXPECT().DoSomething().Return("mocked result")
    
       // 使用Mock对象进行测试
       result := CallSomeFunction(mockMyInterface)
       if result != "mocked result" {
           t.Errorf("unexpected result: %s", result)
       }
    }

    在测试函数中,我们使用gomockNewController创建一个控制器对象,然后使用NewMockMyInterface方法创建MyInterface的Mock对象。接着可以使用EXPECTReturn方法设置打桩逻辑。

    最后,在测试函数中使用Mock对象调用需要测试的函数或方法,并对结果进行断言。

通过这些步骤,就可以使用gomock为接口打桩,并在测试中模拟接口的行为。这样可以保证测试的独立性,同时确保被测试的代码在各种场景下都能正确运行。

idamiaoo commented 11 months ago

使用 gomonkey 为函数/方法打桩

alabulei1 commented 11 months ago

要使用gomonkey为函数或方法打桩,可以按照以下步骤进行:

  1. 安装gomonkey库:

    go get github.com/agiledragon/gomonkey
  2. 在测试文件中导入gomonkey和需要测试的包:

    package mypackage_test
    
    import (
       "testing"
    
       "github.com/agiledragon/gomonkey"
       "github.com/your-username/your-repo/mypackage"
    )
  3. 在测试函数中使用gomonkey创建一个Monkey实例:

    func TestFoo(t *testing.T) {
       // 创建Monkey实例
       p := gomonkey.NewPatch(mypackage.SomeFunction)
       defer p.Reset()
    
       // 设置打桩逻辑
       p.ApplyFunc(func() int {
           return 42 // 返回值设为42
       })
    
       // 使用打桩后的函数进行测试
       result := mypackage.SomeFunction()
       if result != 42 {
           t.Errorf("unexpected result: %d", result)
       }
    }

    在测试函数中,我们使用gomonkeyNewPatch方法创建了一个Monkey实例,然后使用ApplyFunc方法设置函数的打桩逻辑。在示例中,我们将函数的返回值设为了42。

    注意,defer p.Reset()用于在测试函数结束时重置打桩效果,以免对其他测试函数产生影响。

通过这些步骤,就可以使用gomonkey为函数或方法打桩,并在测试中模拟函数的行为。这样可以保证测试的独立性,同时确保被测试的代码在各种场景下都能正确运行。

idamiaoo commented 11 months ago

使用 httpmock mock http请求响应结果

alabulei1 commented 11 months ago

要使用httpmock库模拟HTTP请求和响应结果,可以按照以下步骤进行:

  1. 安装httpmock库:

    go get github.com/jarcoal/httpmock
  2. 在测试文件中导入httpmock和需要测试的包:

    package mypackage_test
    
    import (
       "testing"
    
       "github.com/jarcoal/httpmock"
       "github.com/your-username/your-repo/mypackage"
    )
  3. 在测试函数中使用httpmock.Activate()激活HTTP Mock:

    func TestFoo(t *testing.T) {
       // 激活HTTP Mock
       httpmock.Activate()
       defer httpmock.DeactivateAndReset()
    
       // 创建并注册模拟的响应
       response := `{"message": "success"}`
       httpmock.RegisterResponder("GET", "https://api.example.com",
           httpmock.NewStringResponder(200, response))
    
       // 测试代码,对请求进行测试
       result, err := mypackage.SendRequest()
       if err != nil {
           t.Fatal("Unexpected error:", err)
       }
    
       // 对响应结果进行验证
       expected := "success"
       if result != expected {
           t.Errorf("Unexpected result. Expected: %s, got: %s", expected, result)
       }
    }

    在测试函数中,我们使用httpmock.Activate()激活HTTP Mock,然后使用httpmock.RegisterResponder方法注册模拟的响应。在示例中,我们模拟了一个GET请求,并返回了一个状态码为200的响应。

    注意,httpmock.DeactivateAndReset()用于在测试函数结束时重置HTTP Mock的状态,以免对其他测试函数产生影响。

通过这些步骤,就可以使用httpmock来模拟HTTP请求和响应结果,以验证被测试的代码在不同情况下的行为。这样可以保证测试的独立性,并且可以在没有真实网络连接的情况下进行测试。

alabulei1 commented 11 months ago

非常抱歉,我之前的回答有误。httpmock库的正确地址是https://github.com/jarcoal/httpmock。以下是正确的步骤:

  1. 安装httpmock库:

    go get github.com/jarcoal/httpmock
  2. 在测试文件中导入httpmock和需要测试的包:

    package mypackage_test
    
    import (
       "testing"
    
       "github.com/jarcoal/httpmock"
       "github.com/your-username/your-repo/mypackage"
    )
  3. 在测试函数中使用httpmock.Activate()激活HTTP Mock:

    func TestFoo(t *testing.T) {
       // 激活HTTP Mock
       httpmock.Activate()
       defer httpmock.DeactivateAndReset()
    
       // 创建并注册模拟的响应
       response := `{"message": "success"}`
       httpmock.RegisterResponder("GET", "https://api.example.com",
           httpmock.NewStringResponder(200, response))
    
       // 测试代码,对请求进行测试
       result, err := mypackage.SendRequest()
       if err != nil {
           t.Fatal("Unexpected error:", err)
       }
    
       // 对响应结果进行验证
       expected := "success"
       if result != expected {
           t.Errorf("Unexpected result. Expected: %s, got: %s", expected, result)
       }
    }

    在测试函数中,我们使用httpmock.Activate()激活HTTP Mock,然后使用httpmock.RegisterResponder方法注册模拟的响应。在示例中,我们模拟了一个GET请求,并返回了一个状态码为200的响应。

    注意,httpmock.DeactivateAndReset()用于在测试函数结束时重置HTTP Mock的状态,以免对其他测试函数产生影响。

通过这些步骤,就可以使用httpmock来模拟HTTP请求和响应结果,以验证被测试的代码在不同情况下的行为。这样可以保证测试的独立性,并且可以在没有真实网络连接的情况下进行测试。非常抱歉之前给您带来的困扰。

idamiaoo commented 11 months ago

容器内怎么查看实际可用内存大小

alabulei1 commented 11 months ago

要查看容器内的实际可用内存大小,可以使用以下命令:

cat /sys/fs/cgroup/memory/memory.limit_in_bytes

该命令会返回容器的内存限制,单位为字节。这个值表示容器内可使用的最大内存大小。

另外,你也可以使用以下命令来查看容器的内存使用情况:

cat /sys/fs/cgroup/memory/memory.usage_in_bytes

该命令会返回容器当前实际使用的内存大小,单位为字节。

这些命令适用于多数 Linux 发行版。但请注意,这些路径和文件可能会因为不同的容器运行时或 Linux 发行版而有所变化。

idamiaoo commented 11 months ago

写 一个 Springboot 缓存类,缓存从外部获取的token

alabulei1 commented 11 months ago

下面是一个使用Spring Boot的缓存类的示例,用于从外部获取的token进行缓存。

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;

@Component
public class TokenCache {

    @Cacheable(value = "tokenCache", key = "#key")
    public String getToken(String key) {
        // 从外部获取Token的逻辑
        String token = "your-external-method-to-get-token";
        return token;
    }
}

上述示例中,我们定义了一个TokenCache类,并在getToken方法上使用@Cacheable注解。@Cacheable注解表示该方法的结果可以被缓存。

@Cacheable注解中,我们使用了value属性指定了缓存的名称,这里为"tokenCache"。同时,使用key属性指定了缓存的键,这里可以是获取Token的key。当多次调用getToken方法时,如果传入相同的key,会直接从缓存中获取结果而不再执行方法体中的业务逻辑。

为了使Spring Boot支持缓存,还需要在主配置类中添加@EnableCaching注解,如下所示:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

通过以上示例,您可以在Spring Boot中实现一个缓存类,用于缓存从外部获取的token。这样可以避免多次调用外部接口,提高性能和效率。

idamiaoo commented 11 months ago

写 一个 Springboot 缓存类,缓存从外部获取的token,每隔2小时清除缓存

alabulei1 commented 11 months ago

要实现每隔两小时清除缓存的功能,可以使用Spring Boot提供的定时任务功能。下面是一个修改后的示例:

import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class TokenCache {

    private String cachedToken;

    @Cacheable(value = "tokenCache", key = "'token'")
    public String getToken() {
        // 从外部获取Token的逻辑
        String token = "your-external-method-to-get-token";
        cachedToken = token;
        return token;
    }

    @CacheEvict(value = "tokenCache", key = "'token'")
    @Scheduled(fixedDelay = 7200000) // 每隔2小时清除缓存
    public void evictTokenCache() {
        cachedToken = null;
    }
}

在上述示例中,我们使用了@CacheEvict注解来标记清除缓存的方法evictTokenCache。该注解指示在方法执行后清除指定的缓存,value属性用于指定缓存的名称,key属性用于指定缓存的键。

同时,我们使用@Scheduled注解来指定定时任务的执行频率。fixedDelay属性设定了任务执行的间隔时间,这里设置为7200000毫秒(即2小时)。

getToken方法中,我们将从外部获取的Token缓存在cachedToken变量中,并使用@Cacheable注解进行缓存。这样,每次调用getToken方法时,如果缓存中存在有效的Token,则直接返回缓存值,避免重复调用外部接口。

通过以上示例,您可以实现一个Spring Boot缓存类,在获取Token时进行缓存,并且每隔两个小时清除缓存。这样可以提高性能,同时保持缓存的有效性。

idamiaoo commented 11 months ago

logback-spring.xml日志文件权限配置

alabulei1 commented 11 months ago

在 logback-spring.xml 文件中,你可以通过配置 RollingFileAppender 来设置日志文件的权限。下面是一个示例配置,用于设置日志文件的权限为 600(即只有文件所有者具有读写权限):

<configuration>
    <appender name="fileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>/path/to/logfile.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>/path/to/logfile.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="fileAppender" />
    </root>
</configuration>

在上述示例中,我们通过配置 <appender> 元素来定义 RollingFileAppender,并在其中指定日志文件的路径和名称。请将 "/path/to/logfile.log" 替换为实际的文件路径和名称。

<rollingPolicy> 元素中,我们使用了 TimeBasedRollingPolicy 来配置日志文件的滚动策略。 <fileNamePattern> 指定了滚动后的文件名称模式,其中 %d{yyyy-MM-dd}.%i 将日志文件名包含日期和索引,以实现按时间滚动和最大文件数量限制。请根据需要调整文件名称模式。

最后,在 <encoder> 元素中,我们定义了日志输出的格式。

了解了上述配置,现在来设置文件的权限。可以通过在 <appender> 元素中添加 prudent="true"FileAppenderprudent 属性,来启用 prudent 模式,然后使用 <prudent> 元素来设置文件权限。下面是修改后的示例配置:

<configuration>
    <appender name="fileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender" prudent="true">
        <prudent>true</prudent>
        <file>/path/to/logfile.log</file>
        <!-- 省略其他配置 -->
    </appender>

    <!-- 省略其他配置 -->
</configuration>

在上述示例配置中,我们设置了 prudent 属性为 true,并在 <prudent> 元素中设置文件权限。请确保 "/path/to/logfile.log" 是一个有效的日志文件路径。

使用上述配置,你可以在 logback-spring.xml 文件中设置日志文件的权限为设置的值。

idamiaoo commented 11 months ago

springboot logback 配置日志文件权限

alabulei1 commented 11 months ago

在 Spring Boot 中,可以使用 logback-spring.xml 或 application.properties/yaml 配置文件来配置日志文件的权限。下面是两种常见的配置方式:

  1. 使用 logback-spring.xml: 首先,创建一个 logback-spring.xml 文件,并在其中配置 RollingFileAppender 来设置日志文件的权限。下面是一个示例配置,用于将日志文件的权限设置为 600(即只有文件所有者具有读写权限):
<configuration>
    <appender name="fileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>/path/to/logfile.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>/path/to/logfile.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- 省略其他配置 -->
        </rollingPolicy>
        <!-- 省略其他配置 -->
    </appender>

    <root level="INFO">
        <appender-ref ref="fileAppender" />
    </root>
</configuration>

在上述示例中,我们通过配置 <appender> 元素中的 <file> 元素来指定日志文件的路径和名称。请将 "/path/to/logfile.log" 替换为实际的文件路径和名称。然后,在 <appender> 元素中添加 <filePermission> 元素来设置文件的权限,如下所示:

<filePermission>600</filePermission>

这样就可以将日志文件的权限设置为 600。

  1. 使用 application.properties/yaml 文件: 如果你使用的是 application.properties 或 application.yml 配置文件,可以通过添加以下属性来设置日志文件的权限:

application.properties:

logging.file=/path/to/logfile.log
logging.file.mode=600

application.yml:

logging:
  file: /path/to/logfile.log
  file.mode: 600

在上述配置中,logging.file 指定了日志文件的路径和名称,而 logging.file.mode 则指定了文件的权限。请将 "/path/to/logfile.log" 替换为实际的文件路径和名称。

无论你选择的配置方式是哪种,通过以上配置,你都可以设置日志文件的权限为指定的值。确保权限设置正确,并具有对文件的访问权限。

idamiaoo commented 11 months ago

logback-spring.xml 归档文件权限

alabulei1 commented 11 months ago

要设置归档文件的权限,需要使用 logback-spring.xml 配置 RollingFileAppender,并配置归档文件的权限。下面是一个示例配置,用于将归档文件的权限设置为 600(即只有文件所有者具有读写权限):

<configuration>
    <appender name="fileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>/path/to/logfile.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>/path/to/logfile.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- 省略其他配置 -->
        </rollingPolicy>
        <!-- 省略其他配置 -->
    </appender>

    <appender name="archiveAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>/path/to/archived/logfile.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>/path/to/archived/logfile-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- 省略其他配置 -->
        </rollingPolicy>
        <!-- 省略其他配置 -->
    </appender>

    <root level="INFO">
        <appender-ref ref="fileAppender" />
        <appender-ref ref="archiveAppender" />
    </root>
</configuration>

在上述示例中,我们在 <appender> 元素中配置了两个 RollingFileAppender:fileAppenderarchiveAppenderfileAppender 用于存储当前日志文件,archiveAppender 用于存储归档的日志文件。

archiveAppender 中,我们通过配置 <file> 元素指定了归档文件的路径和名称,请将 "/path/to/archived/logfile.log" 替换为实际的归档文件路径和名称。然后,使用 <filePermission> 元素设置归档文件的权限,如下所示:

<filePermission>600</filePermission>

这样就可以将归档文件的权限设置为 600。

请注意,归档文件的权限设置只会影响新创建的归档文件,并不会影响已存在的归档文件。因此,在设置归档文件的权限时,请确保空目录和之前的归档文件具有所需的权限。

通过以上配置,你可以设置 logback-spring.xml 中归档文件的权限为指定的值。确保权限设置正确,并具有对文件的访问权限。

idamiaoo commented 11 months ago

用脚本将文件里的password requisite pam_pwquality.so minlen=8 minclass=3 enforce_for_root try_first_pass local_users_only retry=3 dcredit=0 ucredit=0 lcredit=0 ocredit=0 改成password requisite pam_pwquality.so minlen=8 minclass=3 enforce_for_root try_first_pass local_users_only retry=3 dcredit=0 ucredit=0 lcredit=0 ocredit=0改成password requisite pam_pwquality.so enforce_for_root try_first_pass local_users_only retry=3 minlen=14 minclass=4

alabulei1 commented 11 months ago

你可以使用脚本来修改文件中的内容。以下是一个示例 Bash 脚本,可以使用 sed 命令查找并替换文件中的特定文本行:

#!/bin/bash

file_path="/path/to/file"  # 文件路径

# 使用 sed 命令替换文件中的文本行
sed -i 's/password    requisite     pam_pwquality.so minlen=8 minclass=3 enforce_for_root try_first_pass local_users_only retry=3 dcredit=0 ucredit=0 lcredit=0 ocredit=0/password    requisite     pam_pwquality.so enforce_for_root try_first_pass local_users_only retry=3 minlen=14 minclass=4 dcredit=0 ucredit=0 lcredit=0 ocredit=0/' "$file_path"

将上述脚本保存为一个文件(例如,modify_file.sh),然后替换 "/path/to/file" 为实际的文件路径。在终端运行脚本时,将会更改文件中的特定文本行。

请注意,使用 sed 命令替换文件内容时,原始文件将被修改。如果你想在替换之前创建文件的备份,可以在 sed 命令后添加 -i.bak,例如 sed -i.bak 's/.../' "$file_path"。这样会在替换前创建一个以 .bak 结尾的备份文件。

记得在运行脚本前做好文件的备份,并谨慎操作,以免不小心删除或更改了文件中的重要内容。

idamiaoo commented 9 months ago

职业规划中,短期与长期目标是什

alabulei1 commented 9 months ago

在职业规划中,短期目标和长期目标都是重要的,它们可以帮助你明确职业发展的方向和达成职业目标的路径。以下是关于短期目标和长期目标的一些常见解释:

短期目标(Short-term goals):

一些短期目标的例子包括:

长期目标(Long-term goals):

一些长期目标的例子包括:

需要注意的是,短期目标和长期目标并不是完全独立的,它们应该是相辅相成的。短期目标能够为你实现长期目标提供支持,而长期目标则为你的短期目标提供更广阔的视野和动力。

职业规划通常是一个长期的过程,需要不断评估和调整。确保你的短期目标与长期目标保持一致,并且在追求目标的过程中持续学习和成长。