使用责任链模式在Idea插件里开发一个麻将游戏,摸鱼必备

1. 简介

这个idea插件呢就是 xechat-idea当然也有vscode版本的,vscode可以搜索xechat-vscode,博主本人有相关的笔记链接

说明一下,vscode版本的可以直接在vscode插件市场下载,但是登录的话原有的官方服务器不可用了。现存的有两个地址可用。可用服务器列表的前两个。

其中第二个服务器需要前方qq群754126966找邪恶鼓励师申请token ,下边的登录命令以第一个服务器为例

  • 查询服务器列表
shell 复制代码
#showServer
  • 登录服务器
shell 复制代码
#login {你的名字} -h lesscoding.net -p 1024 
或者 
#login {你的名字} -s 0
  • 打开麻将
shell 复制代码
#play 12

可以邀请同服务器好友同玩,也可直接开始和ai玩

点击free之后会将游戏页面独立出来一个窗口,该窗口可以调节透明度,摸鱼的时候不容易被发现

2. 开发过程

1. 什么是责任链

我的理解就是每个处理器只处理自己该处理的东西,除非遇到特殊条件,否则就交给下一个处理器来处理

2. 麻将游戏开发。

首先我这里现在只实现了最基本的 平胡七小对 胡牌判断,其他的胡牌类型都写在枚举里边了,有时间的话可以再写

然后 麻将氛围 1-9万 1-9饼 1-9条 东南西北 中发白

判断顺子的时候需要判断牌的数值是否连续,前边三种万饼条只要数字连续就可以,后边的两种只能组成刻子或者是杠。所以它们的id要隔开。于是就有了下方的这个类

1. 麻将枚举类

java 复制代码
package cn.xeblog.plugin.game.mahjong.enums;


import cn.hutool.core.util.StrUtil;
import lombok.Getter;

import java.util.Arrays;


/**
 * @author eleven
 * @date 2024/3/20 8:38
 * @apiNote
 */
@Getter
public enum MahjongEnum {
    //🀇
    YI_WAN(1, "\uD83C\uDC07", "一万"),
    //🀈
    ER_WAN(2, "\uD83C\uDC08", "二万"),
    //🀉
    SAN_WAN(3, "\uD83C\uDC09", "三万"),
    //🀊
    SI_WAN(4, "\uD83C\uDC0A", "四万"),
    //🀋
    WU_WAN(5, "\uD83C\uDC0B", "五万"),
    //🀌
    LIU_WAN(6, "\uD83C\uDC0C","六万"),
    //🀍
    QI_WAN(7, "\uD83C\uDC0D", "七万"),
    //🀎
    BA_WAN(8, "\uD83C\uDC0E", "八万"),
    //🀏
    JIU_WAN(9, "\uD83C\uDC0F", "九万"),

    //🀐
    YI_TIAO(11, "\uD83C\uDC10", "一条"),
    //🀑
    ER_TIAO(12, "\uD83C\uDC11", "二条"),
    //🀒
    SAN_TIAO(13, "\uD83C\uDC12", "三条"),
    //🀓
    SI_TIAO(14, "\uD83C\uDC13", "四条"),
    //🀔
    WU_TIAO(15, "\uD83C\uDC14", "五条"),
    //🀕
    LIU_TIAO(16, "\uD83C\uDC15", "六条"),
    //🀖
    QI_TIAO(17, "\uD83C\uDC16", "七条"),
    //🀗
    BA_TIAO(18, "\uD83C\uDC17", "八条"),
    //🀘
    JIU_TIAO(19, "\uD83C\uDC18", "九条"),

    // 🀙
    YI_BING(21, "\uD83C\uDC19", "一饼"),
    //🀚
    ER_BING(22, "\uD83C\uDC1A", "二饼"),
    //🀛
    SAN_BING(23, "\uD83C\uDC1B", "三饼"),
    //🀜
    SI_BING(24, "\uD83C\uDC1C", "四饼"),
    //🀝
    WU_BING(25, "\uD83C\uDC1D", "五饼"),
    //🀞
    LIU_BING(26, "\uD83C\uDC1E", "六饼"),
    //🀟
    QI_BING(27, "\uD83C\uDC1F", "七饼"),
    //🀠
    BA_BING(28, "\uD83C\uDC20", "八饼"),
    //🀡
    JIU_BING(29, "\uD83C\uDC21", "九饼"),

    //🀀
    DONG_FENG(31, "\uD83C\uDC00", "东风"),
    //🀁
    NAN_FENG(33, "\uD83C\uDC01", "南风"),
    //🀂
    XI_FENG(35, "\uD83C\uDC02", "西风"),
    //🀃
    BEI_FENG(37, "\uD83C\uDC03", "北风"),
    //🀄
    HONG_ZHONG(41, "\uD83C\uDC04", "中"),
    //🀅
    FA_CAI(43, "\uD83C\uDC05", "发"),
    //🀆
    BAI_BAN(45, "\uD83C\uDC06", "白"),

    UNKNOWN(-9527, "\uD83C\uDC22", "暗杠")
    ;

    private final Integer id;

    private final String value;

    private final String tipsText;

    MahjongEnum(Integer id, String value, String tipsText) {
        this.id = id;
        this.value =value;
        this.tipsText =tipsText;
    }

    public static MahjongEnum getById(Integer searchId) {
        return Arrays.stream(MahjongEnum.values())
                .filter(item -> item.getId() != null && item.getId().equals(searchId))
                .findFirst()
                .orElse(UNKNOWN);
    }

    @Override
    public String toString() {
        return StrUtil.format("[{}:{}:{}]", id, value, tipsText);
    }
}

2. 接着我们写一个判断刻子,顺子,杠,的工具类

这里边的刻子就是有三个一样的,就是四个一样的,顺子就是三个连着的。

一开始的时候没想好怎么写判断顺子,但是后来给枚举加上id之后,只需要判断有没有三个id连续在一起就行了

判断是否能听牌就更加简单粗暴了,如果当前玩家的手牌不能赢,那么就把摸得牌替换完其他所有牌,如果能够胡的话那就是能够听牌。

例如你摸了一个发财没胡,那我就把这个牌替换成1-9万1-9条1-9饼东南西北中白,只要有一个能胡,那就是听牌了。

3. 胡牌类型枚举

从腾讯麻将里找的规则 完整代码地址

java 复制代码
@Getter
public enum HuType {
    TIAN_HU(0, 88, "天胡", "", "庄家发完手牌后直接胡牌"),
    DI_HU(1, 88, "地胡", "", "发完手牌后,非庄家第一次摸牌就自摸胡牌。如在第一次摸牌前有任意玩家吃碰杠则不算地胡"),
    SHO_SAN_YAO(2, 88, "十三幺",
            values(DONG_FENG, NAN_FENG, XI_FENG, BEI_FENG) +
                    values(HONG_ZHONG, FA_CAI, BAI_BAN) +
                    values(YI_WAN, JIU_WAN) +
                    values(YI_TIAO, JIU_TIAO) +
                    values(YI_BING, JIU_BING) +
                    values(YI_WAN),
            "由3种序数牌的一、九牌,7种字牌及其中一对作将组成的胡牌。不计五门齐、不求人、单钓将、门前清、全带么");  
}

4. 摸牌类型枚举

java 复制代码
package cn.xeblog.plugin.game.mahjong.enums;

/**
 * @author eleven
 * @date 2024/3/20 11:24
 * @apiNote
 */
public enum TouchType {
    // 摸
    TOUCH,
    // 吃
    EAT,
    // 碰
    BUMP,
    // 杠
    BAR,
    // 测试听牌
    TEST
    ;
}

3. 责任链开发

1. 创建一个责任链的处理对象

java 复制代码
package cn.xeblog.plugin.game.mahjong.model.dto;

import cn.hutool.core.collection.CollUtil;
import cn.xeblog.plugin.game.mahjong.enums.MahjongEnum;
import cn.xeblog.plugin.game.mahjong.enums.TouchType;
import cn.xeblog.plugin.game.mahjong.utils.MahjongUtils;
import groovy.transform.builder.Builder;
import lombok.Data;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * @author eleven
 * @date 2024/3/20 9:06
 * @apiNote
 */
@Data
@Builder
public class MahjongRequest {
    // 摸得牌, 可能是 吃碰杠,或者自己摸的
    private MahjongEnum touchMahjong;
    // 玩家的手牌
    private List<MahjongEnum> handMahjong;
    // 当前轮次,判断天胡,地胡,人胡
    private Integer round;

    /**
     * 0 摸排 1吃牌 2碰牌 3杠牌
     */
    private TouchType touchType;
    // 是否胡牌
    private Boolean win;
    // 输出最后的胡牌类型和番数
    private StringBuilder stringBuilder;
    /**
     * 庄家
     */
    private Boolean banker;
    
    private Long score;
    // 玩家的刻子,顺子,杠,暗杠
    private MahjongGroup group;
    
    // 当前玩家手牌可以听牌的列表
    private List<MahjongEnum> tingList;



    public StringBuilder getStringBuilder() {
        if (stringBuilder == null) {
            stringBuilder = new StringBuilder();
        }
        return stringBuilder;
    }

    public void addScore(Long addScore) {
        Long defaultScore = Optional.ofNullable(score).orElse(0L);
        this.score = defaultScore + addScore;
    }

    public Boolean getWin() {
        return win != null && win;
    }

    public Boolean getBanker() {
        return banker != null && banker;
    }

    public List<MahjongEnum> getAllHandMahjong() {
        List<MahjongEnum> result = new ArrayList<>();
        List<MahjongEnum> handMahjong = getHandMahjong();
        if (CollUtil.isNotEmpty(handMahjong)) {
            result.addAll(handMahjong);
        }
        if (null != touchMahjong) {
            result.add(touchMahjong);
        }
        return MahjongUtils.sortById(result);
    }

    public Integer getRound() {
        return Optional.ofNullable(round).orElse(0);
    }

    public MahjongGroup getGroup() {
        return Optional.ofNullable(group).orElse(new MahjongGroup());
    }
}

2. 首先创建一个抽象类

这个抽象类定义了下一个处理器是谁,公共的处理方法 handler,定义了一个抽象方法 isValidWin用来给让每一个实现来判断是否符合自己的胡牌逻辑

java 复制代码
package cn.xeblog.plugin.game.mahjong.handler;

import cn.xeblog.plugin.game.mahjong.model.dto.MahjongRequest;
import lombok.Data;

/**
 * @author eleven
 * @date 2024/3/20 9:04
 * @apiNote
 */
@Data
public abstract class HuHandler {

    private HuHandler nextHandler;

    public void handle(MahjongRequest request) {
        if (nextHandler != null) {
            nextHandler.handle(request);
        }
    }

    public abstract boolean isValidWin(MahjongRequest request);

}

3. 创建一个责任链的构造器

抄的别人的,好像是固定写法

java 复制代码
package cn.xeblog.plugin.game.mahjong.handler;

import cn.xeblog.plugin.game.mahjong.handler.common.*;

import java.util.Arrays;

/**
 * @author eleven
 * @date 2024/3/20 13:40
 * @apiNote
 */
public class HuHandlerBuilder {
    private HuHandler head;

    private HuHandler tail;

    public HuHandlerBuilder addHandler(HuHandler handler) {
        if (this.head == null) {
            this.head = this.tail = handler;
            return this;
        }
        this.tail.setNextHandler(handler);
        this.tail = handler;
        return this;
    }

    public HuHandlerBuilder addHandler(HuHandler... handlers) {
        if (handlers.length != 0) {
            Arrays.stream(handlers).forEach(this::addHandler);
        }
        return this;
    }

    public HuHandler build() {
        return this.head;
    }

    public static HuHandler fastCommonBuild() {
        return new HuHandlerBuilder().addHandler(
                new PingHuHandler(),
                new SevenPairsHuHandler(),
                new SelfTouchHuHandler(),
                new ShiSanYaoHandler(),
                new TianHuHandler(),
                new DiHuHandler(),
                new RenHuHandler()
        ).build();
    }

}

4. 创建一个平胡处理类

具体逻辑自己看看吧,反正就那么回事

java 复制代码
package cn.xeblog.plugin.game.mahjong.handler.common;

import cn.hutool.core.collection.CollUtil;
import cn.xeblog.plugin.game.mahjong.enums.MahjongEnum;
import cn.xeblog.plugin.game.mahjong.handler.HuHandler;
import cn.xeblog.plugin.game.mahjong.model.dto.MahjongGroup;
import cn.xeblog.plugin.game.mahjong.model.dto.MahjongRequest;
import cn.xeblog.plugin.game.mahjong.utils.MahjongUtils;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author eleven
 * @date 2024/3/20 9:07
 * @apiNote
 */
public class PingHuHandler extends HuHandler {
    @Override
    public void handle(MahjongRequest request) {
        boolean validWin = isValidWin(request);
        if (validWin) {
            request.setWin(true);
            request.addScore(1L);
            request.getStringBuilder().append(" 平胡(1)");
        }
        super.handle(request);
    }

    @Override
    public boolean isValidWin(MahjongRequest request) {
        List<MahjongEnum> handMahjong = request.getAllHandMahjong();
        Map<Integer, List<MahjongEnum>> map = MahjongUtils.getCountMap(handMahjong);
        return valid4442(map, request) || valid33332(map, request) || validCommon(handMahjong, request);
    }

    private boolean validCommon(List<MahjongEnum> handMahjong, MahjongRequest request) {
        Set<MahjongEnum> gang;
        //MahjongGroup group = request.getGroup();
        MahjongGroup group = new MahjongGroup();
        //List<MahjongEnum> pairs = getGroupAndPairsV1(handMahjong, group);
        List<MahjongEnum> pairs = getGroupAndPairsV2(handMahjong, group);
        if (pairs == null) return false;
        request.setGroup(group);
        int aaaOrAbcNum = group.getGang().size() +
                group.getKeZi().size() +
                group.getShunZi().size();

        return aaaOrAbcNum == 4 && (CollUtil.size(pairs) == 1 || CollUtil.isEmpty(handMahjong));

    }

    private List<MahjongEnum> getGroupAndPairsV2(List<MahjongEnum> handMahjong, MahjongGroup group) {
        List<MahjongEnum> pairs = getGroupAndPairsV1(handMahjong, group);
        Map<Integer, List<MahjongEnum>> map = handMahjong.stream()
                .collect(Collectors.groupingBy(MahjongEnum::getId));
        if (CollUtil.isNotEmpty(pairs)) {
            boolean doWhileFlag = pairs.stream().anyMatch(item -> map.get(item.getId()).size() > 2);
            while (pairs.size() > 1 && doWhileFlag) {
                getGroupAndPairsV1(handMahjong, group);
            }
        }
        return pairs;
    }

    @Nullable
    private static List<MahjongEnum> getGroupAndPairsV1(List<MahjongEnum> handMahjong, MahjongGroup group) {
        Set<MahjongEnum> gang;
        while (CollUtil.isNotEmpty(gang = MahjongUtils.getGang(handMahjong))) {
            group.addGang(gang);
            handMahjong.removeAll(gang);
        }
        List<MahjongEnum> pairs = MahjongUtils.getPairs(handMahjong);
        if (CollUtil.isEmpty(pairs)) {
            return null;
        }
        Set<MahjongEnum> keZi;
        while (CollUtil.isNotEmpty(keZi = MahjongUtils.getAAA(handMahjong))) {
            group.addKeZi(keZi);
            handMahjong.removeAll(keZi);
        }

        List<MahjongEnum> shunZi;
        while (CollUtil.isNotEmpty(shunZi = MahjongUtils.getABC(handMahjong))) {
            group.addShunZi(shunZi);
            shunZi.forEach(handMahjong::remove);

        }
        pairs = MahjongUtils.getPairs(handMahjong);

        if (CollUtil.isNotEmpty(pairs)) {
            group.setPairs(pairs.get(0));
        }
        return pairs;
    }

    /**
     * 校验三组四个一样的和一个对子
     *
     * @param map 分组数据
     * @return boolean
     */
    public boolean valid4442(Map<Integer, List<MahjongEnum>> map, MahjongRequest request) {
        return validSame(map, request);
    }

    /**
     * 校验四组三个一样的和一个对子
     *
     * @param map
     * @return
     */
    public boolean valid33332(Map<Integer, List<MahjongEnum>> map, MahjongRequest request) {
        // todo 现在只校验了四组三个一样
        return validSame(map, request);
    }


    public boolean validSame(Map<Integer, List<MahjongEnum>> map, MahjongRequest request) {
        MahjongGroup group = request.getGroup();
        for (List<MahjongEnum> value : map.values()) {
            int size = value.size();
            if (size == 4) {
                group.addGang(Set.copyOf(value));
            }
            if (size == 3) {
                group.addKeZi(Set.copyOf(value));
            }
            if (size == 2) {
                group.setPairs(value.get(0));
            }
        }
        return group.getPairs() != null &&
                (group.getGang().size() == 4 ||
                        group.getKeZi().size() == 4);
    }
}

5. 创建一个七小对处理器

java 复制代码
package cn.xeblog.plugin.game.mahjong.handler.common;

import cn.xeblog.plugin.game.mahjong.enums.MahjongEnum;
import cn.xeblog.plugin.game.mahjong.handler.HuHandler;
import cn.xeblog.plugin.game.mahjong.model.dto.MahjongRequest;

import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @author eleven
 * @date 2024/3/20 10:12
 * @apiNote 七小对
 */
public class SevenPairsHuHandler extends HuHandler {

    @Override
    public void handle(MahjongRequest request) {
        boolean validWin = isValidWin(request);
        if (validWin) {
            request.setWin(true);
            request.getStringBuilder().append("七小对");
            request.addScore(2L);
        }
        super.handle(request);
    }

    @Override
    public boolean isValidWin(MahjongRequest request) {
        List<MahjongEnum> handMahjong = request.getAllHandMahjong();
        Map<Integer, List<MahjongEnum>> map = handMahjong.stream()
                .collect(Collectors.groupingBy(MahjongEnum::getId));
        AtomicInteger count = new AtomicInteger();
        map.forEach((k, v) -> {
            if (v.size() % 2 == 0) {
                count.getAndIncrement();
            }
        });
        return count.get() == 7;
    }
}

6. 更多类型可以参考HuType这个枚举类自己写

7. 测试

java 复制代码
/**
 * @author eleven
 * @date 2024/3/20 11:08
 * @apiNote
 */
public class MahjongTest {


    /**
     * 七小对
     */
    @Test
    public void sevenPairs() {
        List<MahjongEnum> mahjongEnums = MahjongUtils.wan();
        mahjongEnums.remove(BA_WAN);
        mahjongEnums.remove(JIU_WAN);
        List<MahjongEnum> handMahjong = new ArrayList<>(13);
        handMahjong.addAll(mahjongEnums);
        handMahjong.addAll(mahjongEnums);
        handMahjong.remove(0);
        MahjongRequest request = new MahjongRequest();
        request.setHandMahjong(handMahjong);
        request.setTouchMahjong(MahjongEnum.YI_WAN);
        request.setTouchType(TouchType.TOUCH);
        request.setWin(false);
        printMahjong(request.getAllHandMahjong());
        HuHandler build = HuHandlerBuilder.fastCommonBuild();
        build.handle(request);
        printResponse(request);
    }

    /**
     * 前两种情况能够正常判断,后边两种情况判断出错。
     * 所以需要对特殊牌型进行特殊处理
     */
    @Test
    public void getPairs() {
        List<MahjongEnum> handMahjong = new ArrayList<>();
        Collections.addAll(handMahjong,
                YI_BING, YI_BING,
                YI_BING, ER_BING, SAN_BING,
                YI_WAN, ER_WAN, SAN_WAN,
                SI_WAN, WU_WAN, LIU_WAN,
                QI_WAN, BA_WAN, JIU_WAN
        );
        printMahjong(handMahjong);
        MahjongUtils.getPairs(handMahjong)
                .forEach(System.out::println);

        handMahjong.clear();
        Collections.addAll(handMahjong,
                YI_BING, YI_BING,
                YI_TIAO, ER_TIAO, SAN_TIAO,
                YI_WAN, ER_WAN, SAN_WAN,
                SI_WAN, WU_WAN, LIU_WAN,
                QI_WAN, BA_WAN, JIU_WAN
        );
        printMahjong(handMahjong);
        MahjongUtils.getPairs(handMahjong)
                .forEach(System.out::println);

        handMahjong.clear();
        Collections.addAll(handMahjong,
                YI_TIAO, YI_TIAO, YI_TIAO, YI_TIAO,
                YI_WAN, YI_WAN, YI_WAN, YI_WAN,
                YI_TIAO, YI_TIAO, YI_TIAO, YI_TIAO,
                FA_CAI, FA_CAI
        );
        printMahjong(handMahjong);
        // todo 判断错误应该为 FA_CAI
        MahjongUtils.getPairs(handMahjong)
                .forEach(System.out::println);

        handMahjong.clear();
        Collections.addAll(handMahjong,
                YI_TIAO, YI_TIAO, YI_TIAO,
                YI_WAN, YI_WAN, YI_WAN,
                YI_TIAO, YI_TIAO, YI_TIAO,
                DONG_FENG, DONG_FENG, DONG_FENG,
                FA_CAI, FA_CAI
        );
        printMahjong(handMahjong);
        MahjongUtils.getPairs(handMahjong)
                .forEach(System.out::println);
    }

    @Test
    public void shiSanYao() {
        MahjongRequest request = new MahjongRequest();
        List<MahjongEnum> handMahjong = new ArrayList<>(14);
        Collections.addAll(handMahjong,
                DONG_FENG, NAN_FENG, XI_FENG, BEI_FENG,
                HONG_ZHONG, FA_CAI, BAI_BAN,
                YI_WAN, JIU_WAN,
                YI_TIAO, JIU_TIAO,
                YI_BING, JIU_BING);
        request.setHandMahjong(handMahjong);
        request.setTouchMahjong(JIU_BING);
        request.setRound(0);
        request.setBanker(true);
        request.setTouchType(TouchType.TOUCH);
        printMahjong(request.getAllHandMahjong());
        HuHandlerBuilder.fastCommonBuild().handle(request);
        printResponse(request);
    }
    
    @Test
    public void pingHuTest() {
        List<MahjongEnum> handMahjong = new ArrayList<>();
        Collections.addAll(handMahjong,
                YI_WAN, YI_WAN, YI_WAN, YI_WAN,
                YI_BING, YI_BING,
                YI_BING, ER_BING, SAN_BING,
                SI_WAN, SI_WAN, SI_WAN,
                BAI_BAN, BAI_BAN, BAI_BAN
        );
        MahjongRequest request = new MahjongRequest();
        request.setHandMahjong(handMahjong);
        HuHandler huHandler = HuHandlerBuilder.fastCommonBuild();
        huHandler.handle(request);
        printMahjong(request.getAllHandMahjong());
        printResponse(request);
    }

    @Test
    public void tingTest() {
        List<MahjongEnum> list = List.of(SAN_WAN, SI_WAN, WU_WAN,
                SAN_TIAO, ER_TIAO,
                WU_WAN, LIU_WAN, QI_WAN,
                SAN_BING, SI_BING, WU_BING,
                BAI_BAN, BAI_BAN);
        System.out.println(MahjongUtils.getTingList(list));
    }

    public MahjongRequest getReq(MahjongEnum... enums) {
        MahjongRequest request = new MahjongRequest();
        request.setHandMahjong(List.of(enums));
        return request;
    }

    private void printResponse(MahjongRequest request) {
        System.out.println(StrUtil.format("当前回合{},身份:{},得分{}\n胡牌类型{}\n组合{}\n",
                request.getRound(),
                request.getBanker() ? "庄" : "闲",
                request.getScore(),
                request.getStringBuilder(),
                request.getGroup()));
    }


    private void printMahjong(List<MahjongEnum> list) {
        System.out.println("===============当前牌型============");
        List<List<MahjongEnum>> partition = Lists.partition(list, 4);
        partition.forEach(item -> {
                    item.forEach(it -> System.out.print(it + "\t"));
                    System.out.println();
                }
        );
        System.out.println("==================================");
    }
}
相关推荐
红尘散仙1 小时前
我把终端小说阅读器接上了 AI Agent:TRNovel 现在能用 skill 生成书源了
人工智能·后端·rust
卷毛的技术笔记3 小时前
告别硬编码!Spring AI Alibaba 实现 AI Agent 智能工具调用(Tool Calling)
java·人工智能·后端·python·spring·ai编程
会编程的土豆3 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
喵个咪3 小时前
GoWind Toolkit Go后端代码生成 完整全流程实战
后端·go·orm
basketball6164 小时前
Go 语言从入门到进阶:4. 数组和MAP使用方法总结
开发语言·后端·golang
qq_2518364574 小时前
SpringBoot+Vue 共享电池柜管理系统 完整实现 前后端分离项目实战 完整代码
vue.js·spring boot·后端
zhangxingchao4 小时前
AI 大模型核心六:量化、Workflow 与 Agent、多轮 RAG
前端·人工智能·后端
IT_陈寒5 小时前
Vite打包时遇到的坑,原来问题出在这里
前端·人工智能·后端
ayqy贾杰6 小时前
基层管理的三板斧,在AI时代行不通了
前端·后端·团队管理
Apifox6 小时前
Apifox 5 月更新|Postman 导入优化、Runner 支持非 root 运行、请求代码自动带鉴权
前端·后端·安全