Shangyitong project record

Posted by stylezeca on Sun, 02 Jan 2022 20:03:32 +0100

Background management system

Upload hospital information interface

The first is the basic information of the hospital. On the background management website, the administrator can manage and operate some information of the hospital, such as hospital name, contact person, hospital number, etc

Specific technology:

  • swagger is integrated as interface test
  • Encapsulate the result set so that the returned results follow a unified format. (status code + information + data)
  • @RequestBody annotation, the front end can transfer data to the back end in Json format
  • @RestController adopts Restfu style to pass path and parameters
@PostMapping("findPageHospSet/{current}/{limit}")
    public Result findPageHospSet(@PathVariable Long current,
                                  @PathVariable Long limit,
                                  @RequestBody(required = false) HospitalSetQueryVo hospitalSetQueryVo)
  • @PostMapping, @ GetMapping and @ RequestMapping are the same. They are used to pass paths and parameters (@ RequestMapping(method = RequestMethod.GET) is GetMapping, and post is the same)

data dictionary


Some notes:

  • @The function of ComponentScan is to assemble the classes that meet the scanning rules into the spring container according to the defined scanning path
  • @Mapper: @ mapper is added to the interface class, and the corresponding interface implementation class will be generated after compilation (mapper layer is DAO layer, which is the data persistence layer. Some database statements are written. In this project, because it is MybatisPlus, there are not many sql statements written)
  • @MapperScan: specify the package where the interface to become the implementation class is located, and then all interfaces under the package will generate corresponding implementation classes after compilation
  • @TableField: comments in MybatisPlus

    Pay attention to exist. When there is a field in the model entity class (because a "hasChildren" field is required in the elementUI), but there is no field in the database table, you can annotate the entity class:

    Indicates that this field does not exist in the database
    Final result of data dictionary:

Using EasyExcel to import and export data dictionary

Notice that some parameters in http are set

//Export dictionary data interface
    @Override
    public void exportDictData(HttpServletResponse response) {
        //Set download information
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        String fileName = "dict";
        response.setHeader("Content-disposition","attachment:filename="+fileName+".xlsx");
        //query data base
        List<Dict> dictList = baseMapper.selectList(null);
        //dict --> dictEeVo
       List<DictEeVo> dictEeVoList = new ArrayList<>();
        for(Dict dict: dictList){
            DictEeVo dictEeVo = new DictEeVo();
            BeanUtils.copyProperties(dict,dictEeVo);
            dictEeVoList.add(dictEeVo);
        }
        //Call method for write operation
        try {
            EasyExcel.write(response.getOutputStream(), DictEeVo.class).sheet("dict")
                    .doWrite(dictEeVoList);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //Import data dictionary
    @CacheEvict(value = "dict", allEntries = true) //After importing data, all contents in the cache "dict" are cleared
    @Override
    public void importDictData(MultipartFile file) {
        try {
            EasyExcel.read(file.getInputStream(),DictEeVo.class, new DictListener(baseMapper)).sheet().doRead();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

Data dictionary cache

Because the data dictionary is not often modified, it is the information of some provinces and nationalities, so it can be put in the cache to speed up the reading speed
redis configuration class:

@Configuration
@EnableCaching
public class RedisConfig {
    //    @EnableCaching: Mark @ EnableCaching, enable caching, and configure Redis cache manager.
//    @The EnableCaching annotation triggers the post processor to check whether there are cache annotations in the public method of each Spring bean.
//    If such a comment is found, a proxy is automatically created to intercept method calls and handle the corresponding cache behavior.

    /**
     * Customize the key rule and automatically generate the key of the corresponding rule
     *
     * @return
     */
    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }

    /**
     * Set RedisTemplate rules
     *
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //Solve the problem of query cache conversion exception
        ObjectMapper om = new ObjectMapper();
        // Specify the fields to be serialized, field,get, set, and modifier range. ANY includes private and public
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // Specify the type of serialized input. The class must be non final modified. Final modified classes, such as string and integer, will run exceptions
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        //Serial number key value
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     * Setting CacheManager cache rules
     *
     * @param factory
     * @return
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        //Solve the problem of query cache conversion exception
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        // Configure serialization (solve the problem of garbled code), and the expiration time is 600 seconds
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();

        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }
}

Some notes:

  • @cacheable: it will be executed only once. When it is marked on a method, it means that the method supports caching. Spring will cache its return value after it is called to ensure that the results can be directly obtained from the cache when the method is executed with the same parameters next time.
  • @Cacheput: @ cacheput marked method will not check whether there are previously executed results in the cache before execution, but will execute the method every time and store the execution results in the specified cache in the form of key value pairs., It is generally used in the method of adding
  • @CacheEvict is used to mark on the method or class that needs to clear cache elements. When marked on a class, it means that the execution of all methods in it will trigger the cache cleanup operation.

Interface for uploading Department scheduling


This interface is open to hospital staff (this project simulates a hospital's internal website, and hospital managers pass some information to our background management platform through the interface on this website)
Each hospital has its own system. By calling the interface provided by us, it can upload hospital information, department information and shift scheduling information to our platform to facilitate patients to view registration. At the same time, the hospital can also query the above information uploaded by itself.
The database of this part is mongodb.

Differences between MongoDB and MySQL:

MySQL and MongoDB are common open source databases, but MySQL is a traditional relational database, while MongoDB is a non relational database, also known as document database, which is a NoSQL database. They have their own advantages. The key is to see where they are used. Therefore, the SQL statements we are familiar with are not applicable to MongoDB, because SQL statements are the standard language of relational databases.

1, Relational database MySQL
1. There are different storage methods on different engines.
2. Query statements use traditional sql statements, have a relatively mature system and a high degree of maturity.
3. The share of open source databases is increasing, and the share of mysql pages is growing.
4. The disadvantage is that the efficiency will be significantly slower when processing massive data.

2, Non relational database MongoDB
Non relational database (nosql) is a document database. Let's first explain the document database, that is, it can store data of xml, json and bson types. These data are self described and present a hierarchical tree data structure. The data structure consists of key value (key = > value) pairs.

1. Storage mode: virtual memory + persistence.
2. Query statement: it is a unique query method of MongoDB.
3. Suitable for scenarios: event recording, content management or blog platform, etc.
4. Architecture features: high availability can be achieved through replica set and fragmentation.
5. Data processing: the data is stored on the hard disk, but the data that needs to be read frequently will be loaded into the memory and stored in the physical memory, so as to achieve high-speed reading and writing.
6. Maturity and breadth: the emerging database has a low maturity. Nosql database is the closest to relational database. It is one of the more perfect DB, and the applicable population is growing.

3, MongoDB strengths and weaknesses
Advantages:
1. The performance of MongoDB in an appropriate level of memory is very fast. It stores hot data in physical memory, making the reading and writing of hot data very fast.
2. MongoDB's high availability and cluster architecture has very high scalability.
3. In the replica set, when the master database encounters problems and cannot continue to provide services, the replica set will elect a new master database to continue to provide services.
4. MongoDB's Bson and JSon format data are very suitable for document format storage and query.
inferiority:
1. Transaction operations are not supported. MongoDB does not have its own transaction mechanism. If you need to implement the transaction mechanism in MongoDB, you need to logically implement the transaction by yourself through an additional table.
2. Little application experience. Due to the short rise time of NoSQL, there is less application experience compared with relational database.
3. MongoDB takes up too much space.

Manage hospital information

In this module, because different service modules are used at the same time (in reality, it may be micro services on different servers), remote calls are required. Here, the registry is used. Nacos is used in this project

@EnableDiscoveryClient: add it to the main startup class, register the service in the registry (Nacos, Zookeeper, consult, etc.), and set the address and port number of the current service in the properties file to complete the registration

Feign is also required to complete the cross service invocation. Feign is a declarative web service client, which makes it easier to call between microservices, similar to the controller calling a service.

The SpringbootApplication startup class is annotated with @ FeignClient and @ EnableDiscoveryClient.

Create an excuse with @ FeignClient(value = "service CMN") annotation, followed by the name of the registered service. After being declared as feign client, other spring managed classes, such as service, can be injected directly

@FeignClient(value = "service-cmn")
@Repository
public interface DictFeignClient {

    //Query according to dictcode and value
    @GetMapping("/admin/cmn/dict/getName/{dictCode}/{value}")
    public String getName(@PathVariable("dictCode") String dictCode, @PathVariable("value") String value);

    //Query by value
    @GetMapping("/admin/cmn/dict/getName/{value}")
    public String getName(@PathVariable("value") String value);
}


Manage shift scheduling information



Click shift scheduling to display shift scheduling information

Service gateway

This project uses GateWay in spring cloud

User approval user management

After real name authentication, users can be managed and approved in the background

Foreground user system

reference resources Beijing unified reservation and Registration Platform
Some simple functions, such as searching the name of the hospital, clicking the hospital to select the Department scheduling, will not be repeated. Mainly about login and registration.

Login function

Mobile number login and wechat code scanning login

Gateway filter

Since there is a login problem, there is a permission problem. Some websites can only be accessed by internal personnel, some can only be accessed after users log in, and some can be accessed directly. This function is realized by using the filter in GateWay, inheriting GlobalFilter, and then rewriting some methods to implement the code (in the GateWay service):

@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        System.out.println("==="+path);
		
		// The following part of the code mainly deals with the permission problem
        //Internal service interface, external access is not allowed
        if(antPathMatcher.match("/**/inner/**", path)) {
            ServerHttpResponse response = exchange.getResponse();
            return out(response, ResultCodeEnum.PERMISSION);
        }

        //api Interface, asynchronous request, verification, user must log in
        if(antPathMatcher.match("/api/**/auth/**", path)) {
            Long userId = this.getUserId(request);
            if(StringUtils.isEmpty(userId)) {
                ServerHttpResponse response = exchange.getResponse();
                return out(response, ResultCodeEnum.LOGIN_AUTH);
            }
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }

    /**
     * api Interface authentication failed, return data
     * @param response
     * @return
     */
    private Mono<Void> out(ServerHttpResponse response, ResultCodeEnum resultCodeEnum) {
        Result result = Result.build(null, resultCodeEnum);
        byte[] bits = JSONObject.toJSONString(result).getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bits);
        //Specify the code, otherwise Chinese garbled code will appear in the browser
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        return response.writeWith(Mono.just(buffer));
    }

    /**
     * Get the current login user id
     * @param request
     * @return
     */
    private Long getUserId(ServerHttpRequest request) {
        String token = "";
        List<String> tokenList = request.getHeaders().get("token");
        if(null  != tokenList) {
            token = tokenList.get(0);
        }
        if(!StringUtils.isEmpty(token)) {
            return JwtHelper.getUserId(token);
        }
        return null;
    }
}

JWT

Reference Introduction
Json web token (JWT) is a JSON based open standard (RFC 7519) implemented to transfer declarations between network application environments The token is designed to be compact and secure, especially suitable for single sign on (SSO) scenarios of distributed sites. The declaration of JWT is generally used to transfer the authenticated user identity information between identity providers and service providers, so as to obtain resources from the resource server. Some additional declaration information necessary for other business logic can also be added. The token can also be directly used for authentication or encrypted.

This project uses JWT to generate token s

OAuth2

Official Guide
The specific process is clearly described in the official guide. Here is a summary:

  1. The third party initiates the wechat authorization login request. After the wechat user allows the authorization of the third-party application, wechat will pull up the application or redirect to the third-party website, and bring the code parameter of the authorization temporary bill;
  2. Add AppID and AppSecret through the code parameter, and exchange access through the API_ token;
  3. Through access_token to call the interface to obtain the user's basic data resources or help the user realize basic operations.

Mobile number verification code login

Integrate JWT and generate token.
Overall process:

  1. The user enters the mobile phone number and clicks to obtain the verification code
  2. Alibaba cloud SMS service sends the generated verification code to users, puts the verification code in redis, and sets the expiration time.
  3. The user receives and inputs the verification code, and the server obtains and compares it with the verification code of the corresponding user in redis. If it is different, an exception will be thrown. If it is the same, the login will succeed

Wechat code scanning login

Wechat Development Guide
According to the development guide, fill in the corresponding information and send it back to the front end, where the QR code can be displayed to the user.

//1. Generate wechat scanning QR code
    //Return the parameters required to generate QR code
    @GetMapping("getLoginParam")
    @ResponseBody
    public Result genQrConnect() {
        try {
            Map<String, Object> map = new HashMap<>();
            map.put("appid", ConstantWxPropertiesUtils.WX_OPEN_APP_ID);
            map.put("scope","snsapi_login");
            String wxOpenRedirectUrl = ConstantWxPropertiesUtils.WX_OPEN_REDIRECT_URL;
            wxOpenRedirectUrl = URLEncoder.encode(wxOpenRedirectUrl, "utf-8");
            map.put("redirect_uri",wxOpenRedirectUrl);
            map.put("state",System.currentTimeMillis()+"");
            return Result.ok(map);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }
    }

After the wechat code scanning login is successful, it will be redirected to the mobile phone number verification code login interface. After a user logs in with a mobile phone number, if it is the first time to log in, the user's wechat and mobile phone number will be bound together in the database.

Upload image files to alicloud OSS

Real name authentication and background audit

If there is no real name authentication, real name authentication is required before registration

Manage the patient (add, delete, modify and query)

You can register your family with your own account

Reservation registration details

Click a department to display the registration information that can be reserved, and click a information to display the relevant content

Select a department and make an appointment for registration

If the commodity service and the order service are two different micro services, the order service needs to call the commodity service to deduct inventory during the order placing process. According to the traditional way, the order placing process can only return to the success of the order after the call is completed. If the inventory deduction of goods and services is delayed or failed due to network fluctuations and other reasons, it will bring cross user experience. If such processing is obviously inappropriate in high concurrency scenarios, how to optimize it? You need to use message queuing
Message queuing provides an asynchronous communication mechanism. The sender of a message does not have to wait until the message is successfully processed before returning, but returns immediately. Message middleware is responsible for handling network communication. If the network connection is unavailable, the message can be temporarily stored in the queue. When the network is unblocked, it will forward the message to the corresponding program or service. Of course, the premise is that these services subscribe to the queue.
Using message oriented middleware between services can not only improve the concurrency, but also reduce the coupling between services

Wechat code scanning order

This part of the module mainly calls some services of wechat, which will not be repeated
It is worth mentioning that the generated wechat two-dimensional code is placed in Redis and expires in two hours

Topics: Java Spring