SYT项目笔记

编程入门 行业动态 更新时间:2024-10-28 02:35:30

SYT项目<a href=https://www.elefans.com/category/jswz/34/1770047.html style=笔记"/>

SYT项目笔记

实体类

package com.chris.model.cmn;import com.atguigu.yygh.model.base.BaseEntity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** <p>* Dict* </p>** @author qy*/
@Data
@ApiModel(description = "数据字典")
@TableName("dict")
public class Dict {private static final long serialVersionUID = 1L;@ApiModelProperty(value = "id")private Long id;@ApiModelProperty(value = "创建时间")@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")@TableField("create_time")private Date createTime;@ApiModelProperty(value = "更新时间")@TableField("update_time")private Date updateTime;@ApiModelProperty(value = "逻辑删除(1:已删除,0:未删除)")@TableLogic@TableField("is_deleted")private Integer isDeleted;@ApiModelProperty(value = "其他参数")@TableField(exist = false)private Map<String,Object> param = new HashMap<>();@ApiModelProperty(value = "上级id")@TableField("parent_id")private Long parentId;@ApiModelProperty(value = "名称")@TableField("name")private String name;@ApiModelProperty(value = "值")@TableField("value")private String value;@ApiModelProperty(value = "编码")@TableField("dict_code")private String dictCode;@ApiModelProperty(value = "子节点")@TableField(exist = false)private List<Dict> Children;}

服务实现的查询方法

@Overridepublic List<Dict> findChildrenData(Long id) {QueryWrapper<Dict> dictQueryWrapper = new QueryWrapper<>();dictQueryWrapper.eq("parent_id",id);List<Dict> dicts = dictMapper.selectList(dictQueryWrapper);for (Dict dict:dicts) {Long dictId = dict.getId();//判断是否还有子级Boolean aBoolean = hasChildren(dictId);if(aBoolean){//如果还有子级,调用当前的方法进行递归查询List<Dict> childrenData = findChildrenData(dictId);dict.setChildren(childrenData);}}return dicts;}public Boolean hasChildren(Long id){QueryWrapper<Dict> dictQueryWrapper = new QueryWrapper<>();dictQueryWrapper.eq("parent_id",id);Integer counts= dictMapper.selectCount(dictQueryWrapper);return counts>0;}

easyexcel读写文件

实体类

package com.atguigu.easyexcel;import com.alibaba.excel.annotation.ExcelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** 在类上添加easyexcel的注解作为excel的表头*/@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserData {@ExcelProperty("用户id")private Integer uid;@ExcelProperty("用户姓名")private String uname;@ExcelProperty("邮件")private String email;
}

写入

package com.atguigu.easyexcel;import com.alibaba.excel.EasyExcel;import java.util.ArrayList;
import java.util.List;/**
*将list集合写入excel文件
*/public class WriteData {public static void main(String[] args) {String fileName="D:\\Downloads\\users.xlsx";List<UserData> list=new ArrayList<>();UserData user1 = new UserData(1, "user1", "rgoijei@gmail");UserData user2 = new UserData(2, "user2", "rgoijej@gmail");list.add(user1);list.add(user2);EasyExcel.write(fileName,UserData.class).sheet("用户信息").doWrite(list);}
}

读取文件内容

  1. 准备一个listener
package com.atguigu.easyexcel;import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import java.util.Map;public class MyListener extends AnalysisEventListener<UserData> {//读取每一行后业务动作可以在这里定义@Overridepublic void invoke(UserData userData, AnalysisContext analysisContext) {System.out.println(userData);}//所有读取动作完成后的动作在这里定义@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {System.out.println("读取操作结束");}//获取excel文件表头的方法@Overridepublic void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {System.out.println("excel表头信息是"+headMap);}
}
  1. 读取文件内容
package com.atguigu.easyexcel;import com.alibaba.excel.EasyExcel;public class ReadData {public static void main(String[] args) {String fileName="D:\\Downloads\\users.xlsx";EasyExcel.read(fileName, UserData.class, new MyListener()).sheet("用户信息").doRead();}
}

实体类准备

dict

package com.atguigu.yygh.model.cmn;import com.alibaba.excel.annotation.ExcelProperty;
import com.atguigu.yygh.model.base.BaseEntity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** <p>* Dict* </p>** @author qy*/
@Data
@ApiModel(description = "数据字典")
@TableName("dict")
public class Dict {private static final long serialVersionUID = 1L;@ExcelProperty("id")@ApiModelProperty(value = "id")private Long id;@ExcelProperty("创建时间")@ApiModelProperty(value = "创建时间")@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")@TableField("create_time")private Date createTime;@ExcelProperty(value = "更新时间")@ApiModelProperty(value = "更新时间")@TableField("update_time")private Date updateTime;@ExcelProperty(value = "是否删除")@ApiModelProperty(value = "逻辑删除(1:已删除,0:未删除)")@TableLogic@TableField("is_deleted")private Integer isDeleted;@ExcelProperty(value = "其他参数")@ApiModelProperty(value = "其他参数")@TableField(exist = false)private Map<String,Object> param = new HashMap<>();@ExcelProperty(value = "上级id")@ApiModelProperty(value = "上级id")@TableField("parent_id")private Long parentId;@ExcelProperty(value = "名称")@ApiModelProperty(value = "名称")@TableField("name")private String name;@ExcelProperty(value = "值")@ApiModelProperty(value = "值")@TableField("value")private String value;@ExcelProperty(value = "编码")@ApiModelProperty(value = "编码")@TableField("dict_code")private String dictCode;@ExcelProperty(value = "是否包含子节点")@ApiModelProperty(value = "是否包含子节点")@TableField(exist = false)private boolean hasChildren;}

dicteevo,实际导出的是这个类,导入的话也是先用这个类,但是后台在写入数据库的时候要转化为dict类

package com.atguigu.yygh.vo.cmn;import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.metadata.BaseRowModel;
import lombok.Data;/*** <p>* Dict* </p>** @author qy*/
@Data
public class DictEeVo {@ExcelProperty(value = "id" ,index = 0)private Long id;@ExcelProperty(value = "上级id" ,index = 1)private Long parentId;@ExcelProperty(value = "名称" ,index = 2)private String name;@ExcelProperty(value = "值" ,index = 3)private String value;@ExcelProperty(value = "编码" ,index = 4)private String dictCode;}

接口导出excel文件

public void export(HttpServletResponse response) throws IOException {//设置响应内容格式response.setContentType("application/vnd.ms-excel");response.setCharacterEncoding("utf-8");//设置导出文件名称String fileName = "Dicts";response.setHeader("Content-disposition", "attachment:filename=" + fileName + ".xlsx");//准备导出的数据List<Dict> dicts = dictMapper.selectList(null);List<DictEeVo> exportedData=new ArrayList<>();for (Dict dict:dicts) {DictEeVo dictEeVo = new DictEeVo();BeanUtils.copyProperties(dict,dictEeVo);exportedData.add(dictEeVo);}//将数据作为响应流进行导出EasyExcel.write(response.getOutputStream(),DictEeVo.class).sheet().doWrite(exportedData);}

上传excel数据

1.写listener

package com.atguigu.yygh.cmn.controller;import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.atguigu.yygh.cmn.mapper.DictMapper;
import com.atguigu.yygh.model.cmn.Dict;
import com.atguigu.yygh.vo.cmn.DictEeVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;public class EasyExcelListener extends AnalysisEventListener<DictEeVo> {//这里mapper autowire不了只能添加为类的私有属性然后创建构造器private DictMapper dictMapper;public EasyExcelListener(DictMapper dictMapper){this.dictMapper=dictMapper;}@Overridepublic void invoke(DictEeVo dictEeVo, AnalysisContext analysisContext) {Dict dict = new Dict();//这里实际插入数据库的是dict对象,把dicteevo对象里面的属性复制到dict对象里面BeanUtils.copyProperties(dictEeVo,dict);//将dict对象插入到数据库dictMapper.insert(dict);}//文件读取文成后的动作可以放在这里@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {}
}

2.服务实现

@Overridepublic void upload(MultipartFile file) throws IOException {InputStream inputStream = file.getInputStream();EasyExcel.read(inputStream,DictEeVo.class,new EasyExcelListener(dictMapper)).sheet().doRead();}

spring cache+redis实现字典存入redis

依赖
<!-- redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- spring2.X集成redis所需common-pool2--><dependency><groupId>org.apachemons</groupId><artifactId>commons-pool2</artifactId><version>2.6.0</version></dependency>
配置类
package com.atguigu.yyghmon.config;import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;import java.lang.reflect.Method;
import java.time.Duration;@Configuration
@EnableCaching
public class RedisConfig {/*** 自定义key规则* @return*/@Beanpublic KeyGenerator keyGenerator() {return new KeyGenerator() {@Overridepublic 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();}};}/*** 设置RedisTemplate规则* @param redisConnectionFactory* @return*/@Beanpublic RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();redisTemplate.setConnectionFactory(redisConnectionFactory);Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);//解决查询缓存转换异常的问题ObjectMapper om = new ObjectMapper();
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和publicom.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);//序列号key valueredisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);redisTemplate.setHashKeySerializer(new StringRedisSerializer());redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);redisTemplate.afterPropertiesSet();return redisTemplate;}/*** 设置CacheManager缓存规则* @param factory* @return*/@Beanpublic CacheManager cacheManager(RedisConnectionFactory factory) {RedisSerializer<String> redisSerializer = new StringRedisSerializer();Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);//解决查询缓存转换异常的问题ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);// 配置序列化(解决乱码的问题),过期时间600秒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;}
}
接口实现类添加注解
//当导入新增新的值的时候,清空当前的所有记录@CacheEvict(value = "dict", allEntries=true)@Overridepublic void upload(MultipartFile file) throws IOException {InputStream inputStream = file.getInputStream();EasyExcel.read(inputStream,DictEeVo.class,new EasyExcelListener(dictMapper)).sheet().doRead();}//查询的时候redis没有记录则从数据库查,有的话到redis中查数据//redis中存入的key值为:dict::com.atguigu.yygh.cmn.service.impl.DictServiceImplfindAll@Cacheable(value = "dict", keyGenerator = "keyGenerator")@Overridepublic List<Dict> findAll() {return dictMapper.selectList(null);}

按照集合元素中的某个字段进行分组归类

//mongo数据库查询得到一个科室的集合
List<Department> departments = mongoTemplate.find(new Query(new Criteria("hoscode").is(hoscode)), Department.class);
//将所有的科室按照大科室的编码进行分组返回一个map,map的键为大科室的编码,值为该大科室编码下的所有科室的集合
Map<String, List<Department>> map = departments.stream().collect(Collectors.groupingBy(Department::getBigcode));

mongotemplate聚合查询求和

public Integer getAvailableTotalReservations(String depcode) {//查询条件Criteria criteria = new Criteria().where("depcode").is(depcode);//聚合条件Aggregation aggregation=Aggregation.newAggregation(Aggregation.match(criteria),//可以对多个列进行聚合Aggregation.group("hoscode","depcode").sum("availableNumber").as("restBookCount").sum("reservedNumber").as("alreadybooked"));//第三个参数传入Map.class,返回的是一个listAggregationResults<Map> aggregate = mongoTemplate.aggregate(aggregation, Schedule.class,Map.class);//调用getMappedResults获取结果List<Map> mappedResults = aggregate.getMappedResults();System.out.println(mappedResults);        return null;}

mongotemplate分页查询

public Map findByPage(Integer pageNum, Integer pageSize, String hoscode) {Pageable pageable = PageRequest.of(pageNum-1, pageSize);//pageable第一页从0开始Query query = new Query();query.with(pageable);query.addCriteria(Criteria.where("hoscode").is(hoscode));/**或者这样写行query.addCriteria(new Criteria("hoscode").is(hoscode));*/List<Schedule> schedules = mongoTemplate.find(query, Schedule.class);//查总数long count = mongoTemplate.count(new Query(new Criteria("hoscode").is(hoscode)), Schedule.class);Map<String,Object> resultMap=new HashMap();resultMap.put("records",schedules);resultMap.put("total",count);return resultMap;}

jwt工具类

依赖

<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId>
</dependency>

工具栏

public class JwtHelper {private static long tokenExpiration = 24*60*60*1000;//token有效时长private static String tokenSignKey = "123456";//密钥//生成tokenpublic static String createToken(Long userId, String userName) {String token = Jwts.builder().setSubject("YYGH-USER").setExpiration(new Date(System.currentTimeMillis() + tokenExpiration)).claim("userId", userId).claim("userName", userName).signWith(SignatureAlgorithm.HS512, tokenSignKey)pressWith(CompressionCodecs.GZIP)pact();return token;}//通过token获取useridpublic static Long getUserId(String token) {if(StringUtils.isEmpty(token)) return null;Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);Claims claims = claimsJws.getBody();Integer userId = (Integer)claims.get("userId");return userId.longValue();}//通过token获取用户名public static String getUserName(String token) {if(StringUtils.isEmpty(token)) return "";Jws<Claims> claimsJws 
= Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);Claims claims = claimsJws.getBody();return (String)claims.get("userName");}public static void main(String[] args) {String token = JwtHelper.createToken(1L, "55");System.out.println(token);System.out.println(JwtHelper.getUserId(token));System.out.println(JwtHelper.getUserName(token));}
}

邮件验证码实现

参考文章:

依赖
<dependency><groupId>org.apachemons</groupId><artifactId>commons-email</artifactId><version>1.5</version>
</dependency>
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.10</version>
</dependency>
发送邮件的工具类
package com.atguigu.yyghmon.helper;import cn.hutool.core.util.RandomUtil;
import com.sun.mail.util.MailSSLSocketFactory;import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.security.GeneralSecurityException;
import java.util.Properties;public class MailHelper {public static void sendCode(String mail,String randomCode) throws MessagingException, GeneralSecurityException {//创建一个配置文件并保存Properties properties = new Properties();properties.setProperty("mail.host","smtp.qq");properties.setProperty("mail.transport.protocol","smtp");properties.setProperty("mail.smtp.auth","true");//QQ存在一个特性设置SSL加密MailSSLSocketFactory sf = new MailSSLSocketFactory();sf.setTrustAllHosts(true);properties.put("mail.smtp.ssl.enable", "true");properties.put("mail.smtp.ssl.socketFactory", sf);//创建一个session对象Session session = Session.getDefaultInstance(properties, new Authenticator() {@Overrideprotected PasswordAuthentication getPasswordAuthentication() {return new PasswordAuthentication("825177485@qq","itbsnnryaymzbajf");}});//开启debug模式session.setDebug(true);//获取连接对象Transport transport = session.getTransport();//连接服务器transport.connect("smtp.qq","825177485@qq","itbsnnryaymzbajf");//创建邮件对象MimeMessage mimeMessage = new MimeMessage(session);//邮件发送人mimeMessage.setFrom(new InternetAddress("825177485@qq"));//邮件接收人mimeMessage.setRecipient(Message.RecipientType.TO,new InternetAddress(mail));//邮件标题mimeMessage.setSubject("验证码");//邮件内容mimeMessage.setContent("您的验证码是"+randomCode,"text/html;charset=UTF-8");//发送邮件transport.sendMessage(mimeMessage,mimeMessage.getAllRecipients());//关闭连接transport.close();}
}
服务接口实现发送随机验证码并且存入到redis中
@Overridepublic void sendMailCode(String mail) throws MessagingException, GeneralSecurityException {//hutool工具类生成随机码String randomCode = RandomUtil.randomNumbers(6);//发送验证码MailHelper.sendCode(mail,randomCode);//将验证码存入redis中redisTemplate.opsForValue().set(mail,randomCode,2, TimeUnit.MINUTES);}
登入校验并返回jwt服务接口实现
@Overridepublic Map loginViaMail(String mail, String code) {//获取redis中存的验证码Object o = redisTemplate.opsForValue().get(mail);//判断前端传过来的验证码和redis中的是否一致if(o.toString().equals(code)){QueryWrapper<UserInfo> userInfoQueryWrapper = new QueryWrapper<>();userInfoQueryWrapper.eq("mail", mail);UserInfo userInfo = userInfoMapper.selectOne(userInfoQueryWrapper);Long id = userInfo.getId();String name = userInfo.getName();//工具类传入id和name生成tokenString token = JwtHelper.createToken(id, name);Map<String, Object> map = new HashMap<>();map.put("name",name);map.put("token",token);return map;}throw new YyghException("验证码不匹配",403);}

gateway过滤器过滤所有未登入的接口请求

package com.atguigu.gateway.filter;import com.alibaba.fastjson.JSONObject;
import com.atguigu.yyghmon.helper.JwtHelper;
import com.atguigu.yyghmon.result.Result;
import com.atguigu.yyghmon.result.ResultCodeEnum;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.nio.charset.StandardCharsets;
import java.util.List;@Component
public class LoginFilter implements GlobalFilter, Ordered {private AntPathMatcher antPathMatcher = new AntPathMatcher();@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();String path = request.getURI().getPath();System.out.println("==="+path);//内部服务接口,不允许外部访问if(antPathMatcher.match("/**/inner/**", path)) {ServerHttpResponse response = exchange.getResponse();return out(response, ResultCodeEnum.PERMISSION);}//获取登入用户的idLong userId = this.getUserId(request);//校验用户必须登录if(antPathMatcher.match("/*/hosp/**", path)) {if(StringUtils.isEmpty(userId)) {ServerHttpResponse response = exchange.getResponse();return out(response, ResultCodeEnum.LOGIN_AUTH);}}return chain.filter(exchange);}@Overridepublic int getOrder() {return 0;}/*** api接口鉴权失败返回数据* @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);//指定编码,否则在浏览器中会中文乱码response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");return response.writeWith(Mono.just(buffer));}/*** 获取当前登录用户id* @param request* @return*/private Long getUserId(ServerHttpRequest request) {//通过请求头的token对应的jwt串来解析用户idString 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;}
}

gateway的配置文件

server:port: 9000spring:application:name: service-gatewaycloud:nacos:discovery:server-addr: 192.168.66.111:8848gateway:routes:- id: hosp## 使用了lb形式,从注册中心负载均衡的获取uriuri: lb://service-hosp## 配置断言predicates:##这种表示路径hosp前面还有一部分,第二部分才是hosp,比如/api/hosp- Path=/*/hosp/**- id: user## 使用了lb形式,从注册中心负载均衡的获取uriuri: lb://service-user## 配置断言predicates:##这种表示路径直接就是跟着user,比如/user/getcode- Path=/user/**

文件名去重设置

String newFileName = UUID.randomUUID().toString().replace("-","")+fileName;

更多推荐

SYT项目笔记

本文发布于:2024-02-04 08:47:56,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1673166.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:笔记   项目   SYT

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!