发动态
综合 最新发布 最新回复
图文
列表
[微笑]
开源硬件平台
求助
怎么连线可以让图一可以实现图二按下开关供电,再按开关断电,我需要用type C但是没有找到图二这种
嘉立创EDA
开源文章:华南农业大学珠江学院单片机协会11月培训——嘉立创EDA软件制作拓展坞\n\n简介:新程砺技,“坞” 出新知 | 华南农业大学珠江学院单片机协会 USB 拓展坞制作培训\n\n文章链接:[https://oshwhub.com/article/scm-association-training-jialichuang-eda-software-production-development-dock-in-november]\n#高校动态#
开源硬件平台
开源文章:第21届智能车竞赛电路板嘉立创制作流程规范\n\n简介:本次竞赛嘉立创EDA将为每个参赛队伍提供150元耗材券支持!\n\n文章链接:[https://oshwhub.com/article/the-21st-intelligent-car-competition-circuit-board-jialichuang-production-process-specification]\n#活动资讯#
开源硬件平台
【面板免费打样&包邮】快和我一起来立创商城领面板定制免费打样券!https://activity.szlcsc.com/invite/mid.html?inviteLinkId=G7OHvCbpkAA
面板定制
一、为什么要“优雅”?产品一句话: “凡哥,接口明天上线,支持 10w 并发,数据脱敏,不能丢单,不能重复,还要安全。” 优雅不是装,是为了让自己少加班、少背锅、少掉发。 今天晓凡就把压箱底的东西掏出来,手把手带你撸一套能扛生产的模板。为方便阅读,晓凡以Java代码为例给出“核心代码 + 使用姿势”,全部亲测可直接使用。二、项目骨架(Spring Boot 3.x) demo-api ├── src/main/java/com/example/demo │ ├── config // 配置:限流、加解密、日志等 │ ├── annotation // 自定义注解(幂等、日志、脱敏) │ ├── aspect // 切面统一干活 │ ├── interceptor // 拦截器(签名、白名单) │ ├── common // 统一返回、异常、常量 │ ├── controller // 对外暴露 │ ├── service │ └── DemoApplication.java └── pom.xml 三、 签名(防篡改)对外提供的接口要做签名认证,认证不通过的请求不允许访问接口、提供服务思路 “时间戳 + 随机串 + 业务参数”排好序,最后 APP_SECRET 拼后面,SHA256 一下。 前后端、第三方都统一,拒绝吵架。工具类 public class SignUtil { /** * 生成签名 * @param map 除 sign 外的所有参数 * @param secret 分配给你的私钥 */ public static String sign(Map<String, String> map, String secret) { // 1. 参数名升序排列 Map<String, String> tree = new TreeMap<>(map); // 2. 拼成 k=v&k=v String join = tree.entrySet().stream() .map(e -> e.getKey() + "=" + e.getValue()) .collect(Collectors.joining("&")); // 3. 最后拼密钥 String raw = join + "&key=" + secret; // 4. SHA256 return DigestUtils.sha256Hex(raw).toUpperCase(); } /** 验签:直接比对即可 */ public static boolean verify(Map<String, String> map, String secret, String requestSign) { return sign(map, secret).equals(requestSign); } } 拦截器统一验签 @Component public class SignInterceptor implements HandlerInterceptor { @Value("${sign.secret}") private String secret; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 只拦截接口 if (!(handler instanceof HandlerMethod)) return true; Map<String, String> params = Maps.newHashMap(); request.getParameterMap().forEach((k, v) -> params.put(k, v[0])); String sign = params.remove("sign"); // 签名不参与计算 if (!SignUtil.verify(params, secret, sign)) { throw new BizException("签名错误"); } return true; } } ​坑位技术大厂,前端-后端-测试,新一线和一二线城市等地均有坑位,感兴趣可以试试。待遇和稳定性都不错~​四、 加密(防泄露)敏感数据在网络传输过程中都应该加密处理思路 AES 对称加密,密钥放配置中心,支持一键开关。 只对敏感字段加密,别一上来全包加密,排查日志想打人。AES 工具 public class AesUtil { private static final String ALG = "AES/CBC/PKCS5Padding"; // 16 位 private static final String KEY = "1234567890abcdef"; private static final String IV = "abcdef1234567890"; public static String encrypt(String src) { try { Cipher cipher = Cipher.getInstance(ALG); SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes(), "AES"); IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes()); cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); return Base64.getEncoder().encodeToString(cipher.doFinal(src.getBytes())); } catch (Exception e) { throw new RuntimeException("加密失败", e); } } public static String decrypt(String src) { try { Cipher cipher = Cipher.getInstance(ALG); SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes(), "AES"); IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes()); cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); return new String(cipher.doFinal(Base64.getDecoder().decode(src))); } catch (Exception e) { throw new RuntimeException("解密失败", e); } } } 五、 IP 白名单限制请求的IP,增加IP白名单,一般在网关层处理配置 white: ips: 127.0.0.1,10.0.0.0/8,192.168.0.0/16 拦截器 @Component public class WhiteListInterceptor implements HandlerInterceptor { @Value("#{'${white.ips}'.split(',')}") private List<String> allowList; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String ip = IpUtil.getIp(request); boolean ok = allowList.stream() .anyMatch(rule -> IpUtil.match(ip, rule)); if (!ok) throw new BizException("IP 不允许访问"); return true; } } 六、 限流(Sentinel 注解版)尤其对外提供的接口,无法保障调用频率,应该做限流处理,保障接口服务正常的提供服务依赖 <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-spring-boot-starter</artifactId> <version>1.8.6</version> </dependency> 配置 spring: application: name: demo-api sentinel: transport: dashboard: localhost:8080 使用姿势 @GetMapping("/order/{id}") @SentinelResource(value = "getOrder", blockHandler = "getOrderBlock") public Result<OrderVO> getOrder(@PathVariable Long id) { return Result.success(orderService.get(id)); } // 限流兜底 public Result<OrderVO> getOrderBlock(Long id, BlockException e) { return Result.fail("访问太频繁,稍后再试"); } 七、 参数校验(JSR303 + 分组)即使前端做了非空,规范性校验,服务端参数校验任然是必不可少的DTO public class OrderCreateDTO { @NotNull(message = "用户 ID 不能为空") private Long userId; @NotEmpty(message = "商品列表不能为空") @Size(max = 20, message = "一次最多买 20 件") private List<Item> items; @Valid @NotNull private PayInfo payInfo; @Data public static class PayInfo { @Min(value = 1, message = "金额必须大于 0") private Integer amount; } } 分组接口 java 体验AI代码助手 代码解读 复制代码 public interface Create {} Controller @PostMapping("/order") public Result<Long> create(@RequestBody @Validated(Create.class) OrderCreateDTO dto) { Long orderId = orderService.create(dto); return Result.success(orderId); } 八、 统一返回值提供统一的返回结果,不应该返回值五花八门 @Data @AllArgsConstructor @NoArgsConstructor public class Result<T> implements Serializable { private int code; private String msg; private T data; public static <T> Result<T> success(T data) { return new Result<>(200, "success", data); } public static <T> Result<T> fail(String msg) { return new Result<>(500, msg, null); } /** 返回 200 但提示业务失败 */ public static <T> Result<T> bizFail(int code, String msg) { return new Result<>(code, msg, null); } } 九、 统一异常处理系统报错信息需要提供友好的提示,避免暴露出SQL异常的信息给调用方和客户端。 @RestControllerAdvice public class GlobalExceptionHandler { private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); /** 业务异常 */ @ExceptionHandler(BizException.class) public Result<Void> handle(BizException e) { log.warn("业务异常:{}", e.getMessage()); return Result.bizFail(e.getCode(), e.getMessage()); } /** 参数校验失败 */ @ExceptionHandler(MethodArgumentNotValidException.class) public Result<Void> handleValid(MethodArgumentNotValidException e) { String msg = e.getBindingResult() .getFieldErrors() .stream() .map(DefaultMessageSourceResolvable::getDefaultMessage) .collect(Collectors.joining(",")); return Result.fail(msg); } /** 兜底 */ @ExceptionHandler(Exception.class) public Result<Void> handleAll(Exception e) { log.error("系统异常", e); return Result.fail("服务器开小差"); } } 十、 请求日志(切面 + 注解)记录请求的入参日志和返回日志,出问题时方便快速定位。也给运维人员提供了方便注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface ApiLog {} 切面 @Aspect @Component public class LogAspect { private static final Logger log = LoggerFactory.getLogger("api.log"); @Around("@annotation(apiLog)") public Object around(ProceedingJoinPoint p, ApiLog apiLog) throws Throwable { long start = System.currentTimeMillis(); ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest req = attr.getRequest(); String uri = req.getRequestURI(); String params = JSON.toJSONString(p.getArgs()); Object result; try { result = p.proceed(); } catch (Exception e) { log.error("【{}】params={} error={}", uri, params, e.getMessage()); throw e; } finally { long cost = System.currentTimeMillis() - start; log.info("【{}】params={} cost={}ms", uri, params, cost); } return result; } } 用法 @ApiLog @PostMapping("/order") public Result<Long> create(...) {} 十一、幂等设计(Token & 分布式锁双保险)对于一些涉及到数据一致性的接口一定要做好幂等设计,以防数据出现重复问题思路下单前先申请一个幂等 Token(存在 Redis,5 分钟失效)。下单时带着 Token,后端用 Lua 脚本“判断存在并删除”,原子性保证只能用一次。对并发极高场景,再补一层分布式锁(Redisson)。代码 @Service public class IdempotentService { @Resource private StringRedisTemplate redis; /** 申请 Token */ public String createToken() { String token = UUID.fastUUID().toString(); redis.opsForValue().set("token:" + token, "1", Duration.ofMinutes(5)); return token; } /** 验证并删除 */ public boolean checkToken(String token) { String key = "token:" + token; // 原子删除成功才算用过 return Boolean.TRUE.equals(redis.delete(key)); } } Controller @GetMapping("/token") public Result<String> getToken() { return Result.success(idempotentService.createToken()); } @PostMapping("/order") @ApiLog public Result<Long> create(@RequestBody @Valid OrderCreateDTO dto, @RequestHeader("Idempotent-Token") String token) { if (!idempotentService.checkToken(token)) { throw new BizException("请勿重复提交"); } Long orderId = orderService.create(dto); return Result.success(orderId); } 十二、限制记录条数(分页 + SQL 保护)对于批量数据接口,一定要限制返回的记录条数,不让会造成恶意攻击导致服务器宕机。MyBatis-Plus 分页插件 @Configuration public class MybatisConfig { @Bean public MybatisPlusInterceptor interceptor() { MybatisPlusInterceptor i = new MybatisPlusInterceptor(); i.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return i; } } Service public Page<OrderVO> list(OrderListDTO dto) { // 前端不传默认 10 条,最多 200 long size = Math.min(dto.getPageSize(), 200); Page<Order> page = new Page<>(dto.getPageNo(), size); LambdaQueryWrapper<Order> w = Wrappers.lambdaQuery(); if (StrUtil.isNotBlank(dto.getUserName())) { w.like(Order::getUserName, dto.getUserName()); } Page<Order> po = orderMapper.selectPage(page, w); return po.convert(o -> BeanUtil.copyProperties(o, OrderVO.class)); } 十三、 压测(JMeter + 自带脚本)上线前,务必要对API接口进行压力测试,知道各个接口的qps情况。以便我们能够更好的预估,需要部署多少服务节点,对于API接口的稳定性至关重要。起服务: java -jar -Xms1g -Xmx1g demo-api.jarJMeter 线程组: 500 线程、Ramp-up 10s、循环 20。观测:Sentinel 控制台看 QPS、RTtop -H 看 CPUarthas 火焰图找慢方法调优:限流阈值 = 压测 80% 最高水位发现慢 SQL 加索引热点数据加本地缓存(Caffeine)十四、异步处理如果同步处理业务,耗时会非常长。这种情况下,为了提升API接口性能,我们可以改为异步处理下单成功后,发 MQ 异步发短信/扣库存,接口 RT 直接降一半。 @Async("asyncExecutor") // 自定义线程池 public void sendSmsAsync(Long userId, String content) { smsService.send(userId, content); } 十五、数据脱敏业务中对与用户的敏感数据,如密码等需要进行脱敏处理返回前统一用 Jackson 序列化过滤器,字段加注解就行,代码零侵入。 @JsonSerialize(using = SensitiveSerializer.class) @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Sensitive { SensitiveType type(); } public enum SensitiveType { PHONE, ID_CARD, BANK_CARD } public class SensitiveSerializer extends JsonSerializer<String> { @Override public void serialize(String value, JsonGenerator g, SerializerProvider p) throws IOException { if (StrUtil.isBlank(value)) { g.writeString(value); return; } g.writeString(DesensitizeUtil.desPhone(value)); } } 十六、完整的接口文档(Knife4j)提供在线接口文档,既方便开发调试接口,也方便运维人员排查错误依赖 <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-openapi3-spring-boot-starter</artifactId> <version>4.1.0</version> </dependency> 配置 knife4j: enable: true setting: language: zh_cn 启动后访问 http://localhost:8080/doc.html 支持在线调试、导出 PDF、Word。十七、小结接口开发就像炒菜:签名、加密是“食材保鲜”限流、幂等是“火候掌控”日志、文档是“摆盘拍照”每道工序做到位,才能端到桌上“色香味”俱全。 上面 13 段核心代码,直接粘过去就能跑,跑通后再按业务微调,基本能扛 90% 的生产场景。 祝你在领导问起接口怎么样了?的时候,可以淡淡来一句: “接口已经准备好了,压测报告发群里了。”——转载自:程序员晓凡
接口开发,咱得整得“优雅”点
开源硬件平台
pcb板上如何画弧形槽孔
嘉立创EDA
遇到恶性bug
本地版本V2.2.43 全在线模式 同一工程画好的图线上登录没有更新 是很久之前的样子。后续需要V3版本复制一个pcb外壳 但是工程在本地 外壳在线上 导致不能继续作业了[晕]
开源硬件平台
不知道为什么我可以选中飞线,能关闭吗
不知道为什么我可以选中飞线,能关闭吗,飞线能选中的话有点影响手感 #嘉立创EDA# @嘉立创EDA小吴
嘉立创EDA
嘉立创纸盒
【求助】编辑器件保存不了
有的可以保存,有的就是一直保存不了,一直是正在连接服务器。[疑问]
嘉立创EDA
多谐振荡器ne555p。
怎么连接板子
硬创社
嘉立创打印
嘉立创打印视频传了半个小时都传不上,所以放图片了哦 #嘉立创3D打印# #嘉立创免费3D打印#
3D打印
#扩展API# 入职开始写插件
嘉立创PCB
积分商城许愿
双11、19周年庆活动收获满满,幸福感爆棚,太爱嘉立创了,听说积分商城可以许愿,家里厨电还差绞肉机和破壁机,期待积分商城可以上新,期待下一次活动[愉快]
金豆商城专区
开源项目:T12-JBC210-245-470 功率闭环焊台\n\n简介:多发热芯兼容焊台,内部48V电源最大功率300W(鉴于发热芯,目前软件已经限制最大功率不超各发热芯额定功率,以JBC470为例最大可设定250W功率),优点全程功率闭环控制,不惧内阻差异导致功率超限!\n\n开源链接:[https://oshwhub.com/nokia82/t12-jbc210-245-470-han-tai]\n#开源复刻# #STM单片机# #DIY设计#
开源硬件平台
开源项目:基于MP4201与MP4583的大功率可调电源设计\n\n简介:12V~100V输入,可编程1~60V输出@ MAX 20A,支持CC与CV与各种包括SCP、OVP、OCP等保护,支持屏幕控制与显示各项数据\n\n开源链接:[https://oshwhub.com/catalystsocrates/ji-yu-mp4201-de-ke-diao-dian-yuan-she-ji]\n#电源模块# #开源复刻# #电源/能源#
开源硬件平台
开源文章:杰理AC690N的那些事(卅六)compile2burn\n\n简介:详细的了解一下杰理的编译和烧录过程。怎样从一堆C文件逐步编译、装载,最后变成一个可以烧录的固件镜像文件。 最后在给出一个手动烧录的详细步骤。\n\n文章链接:[https://oshwhub.com/article/jerry-ac690n-compile2burn]
开源硬件平台
社区数据
今日帖子
-
今日互动量
-
在线人数
-
帖子总量
-
用户总量
-
推荐话题 换一批
#嘉立创PCB#
#DIY设计#
#嘉立创免费3D打印#
#嘉立创3D打印#
#立创开源六周年#
#技术干货#
#嘉立创EDA校园讲师#
#高校动态#
查看更多热门话题
功能讨论
()
主题
打赏记录
服务时间:周一至周六 9::00-18:00 · 联系地址:中国·深圳(福田区商报路奥林匹克大厦27楼) · 媒体沟通:pr@jlc.com · 集团介绍
移动社区