微信拼车小程序-前后端实现(Java后端,微信小程序前端)

概述

拼车小程序。支持多个账号,部署后可以分配到多个小区使用。 发布行程

详细

项目介绍

顺风车拼车小程序,前后端代码(Java+微信小程序),可以用于学习,或者用于拼车类小程序二次开发。

功能介绍

发布行程
  • 支持出发地、目的地、时间、车牌号等基本字段
  • 支持需要乘客回答问题再加入
  • 也支持“提醒”,可以第二天同样的信息继续发布,方便快捷。

发布行程

查看行程
  • 首页列表查看所有未关闭的顺风拼车行程。
  • 如果行程太多,可以一键回到顶部。

    行程列表

在行程详情页面,可以看到:

  • 查看拼车行程的基本信息
  • 可以关注司机(关注之后司机发布的拼车行程后可以收到订阅消息推送)
  • 拼车沟通(详见以下“即时聊天”)

  • 行程详情

即时聊天

可以进行即时聊天,沟通顺风拼车,你可以发送

  • 文字(包括字符表情)
  • 位置

即时聊天

路线订阅

可以通过订阅“关键词”,系统检车到相关的信息,会发送订阅通知

个人中心

广告系统

除了【系统公告】菜单,其他菜单都是可以配置的,可以配置

  • 菜单名称(一般四个字)
  • 菜单icon
  • 菜单跳转动作(支持H5链接,或者小程序)

横幅广告也是可以配置的。

行程列表

系统公告

支持富文本

系统公告

目录结构

开发细节

本项目的开发体验一般是:

  • 先设计表结构,并写好字段备注
  • 运行代码生成器,生成代码(普通表可以生成POJO等,常量表可以生成常量)
  • 开始开发逻辑

代码生成器

详细见 util 下的 BeanGenerator.java

    # Bean生成器配置
    gen-config:
      # 普通bean
      normal_package_name: com.ubatis.circleserver.bean
      normal_extend_class:
      normal_prefix:
      normal_suffix: Bean
      normal_out_dir: src/main/java/com/ubatis/circleserver/bean
      # 参数的
      param_package_name: com.ubatis.circleserver.bean.param
      param_extend_class: com.ubatis.circleserver.util.daoutils.MyParams
      param_prefix: Param
      param_suffix:
      param_out_dir: src/main/java/com/ubatis/circleserver/bean/param
      # 常量文件的路径
      constant_package: com.ubatis.circleserver.util.constant
      constant_path: src/main/java/com/ubatis/circleserver/util/constant

Spring JDBC 封装

通过代码生成器生成的Param开头的Bean,是可以操作数据库的,用法如下

    // 增
    ParamAcUser paramAcUser = new ParamAcUser();
    paramAcUser.save();
    // paramAcUser.saveAndGetId();  保存并返回id
    // paramAcUser.saveAndGetId(String idname); 指定id字段
    // paramAcUser.saveDesignateField(String fileds); 保存指定的字段,逗号隔开
    // paramAcUser.saveExceptField(String fileds); 保存除外的字段,逗号隔开
    // 删
    ParamSysConfig paramSysConfig = new ParamSysConfig();
    paramSysConfig.setId(123);
    paramSysConfig.delete(); // 默认键是id,可以指定
    // 查
    .queryForPage() // 分页查询
    .queryForList() // 查询列表
    .queryForMap() // 查询对象
    .queryForInt() 
    可以结合Kotlin查询,构造动态SQL,方便灵活,便于管理
    // 改
    update()
    update(String idname) // 指定id更新
    update(String[] idNames) // 指定多个字段
    updateExceptField(String exceptFields) // 更新除了指定字段
    updateExceptField(String idname, String exceptFields) // 更新指定字段
    updateExceptField(String[] idNames, String exceptFields)
    updateDesignateField(String designateFields)
    updateDesignateField(String idname, String designateFields)
    updateDesignateField(String[] idNames, String designateFields)

即时聊天

    // 单例模式存储Session
    public enum Sessions {
        SESSIONS;
        private ConcurrentHashMap<String, Session> sessionMap = null;
        public int maxOnline = 0; // 最大在线人数
        Sessions(){
            sessionMap =  new ConcurrentHashMap<>();
        }
        public ConcurrentHashMap<String, Session> getSessionMap(){
            if (sessionMap.size() > maxOnline) {
                maxOnline = sessionMap.size();
            }
            return sessionMap;
        }
    }
    // 发送消息
    public void sendMessage(String openid, String action, String content) {
            if (Sessions.SESSIONS.getSessionMap().containsKey(openid)) {
                Sessions.SESSIONS.getSessionMap().get(openid).sendText(JsonUtil.toJson(CM.getReturnHeaderInfo(content, action)));
            }
        }

客服消息自动回复

    public String wcPushMsgPost(Map<String, Object> map, String appId) {
            // 回复者openid
            String userOpenid = map.get("FromUserName").toString().trim();
            //
            if ("miniprogrampage".equals(map.get("MsgType")) || "text".equals(map.get("MsgType"))) {
                String replyContent = null;
                if (map.get("Content") != null) {
                    replyContent = map.get("Content").toString().trim();
                } else {
                    replyContent = "快捷回复";
                }
                // 添加自动回复的记录
                ParamAcUserTrace paramNewAcUserTrace = new ParamAcUserTrace();
                paramNewAcUserTrace.setTrace_type(CS.USER_TRACE_REPLY_TEXT);
                paramNewAcUserTrace.setOperator(userOpenid);
                paramNewAcUserTrace.setObject(replyContent.length() > 250 ? replyContent.substring(0, 240) + "......" : replyContent); // 最多长度255
                userService.addUserTrace(paramNewAcUserTrace);
                // 回复对应的付款码
                // 获取最近的点击支付的记录
                AcUserTraceBean clickPayTrace = userService.getLatestUserRequestPayCodeTrace(userOpenid);
                // 回复消息用到的材料
                String str2beSend = null;
                AcUserPaymentCodeBean targetPaymentCodeInfoBean = null;
                FnCarpoolOrderBean fnCarpoolOrderBean = null;
                AcUserBean targetBePaidUser = null;
                long circleId = 0;
                // 找到对应的订单
                fnCarpoolOrderBean = new ParamFnCarpoolOrder("id", clickPayTrace.getObject()).queryForObject();
                circleId = fnCarpoolOrderBean.getCircle_id();
                // logger.info("fnCarpoolOrderBean=======: {}", JsonUtil.toJson(fnCarpoolOrderBean));
                targetPaymentCodeInfoBean = new ParamAcUserPaymentCode("openid", fnCarpoolOrderBean.getDriver_openid()).queryForObject();
                targetBePaidUser = new ParamAcUser("openid", fnCarpoolOrderBean.getDriver_openid()).queryForObject();
                // logger.info("acUserBeanDriver=======: {}", JsonUtil.toJson(acUserBeanDriver));
                // logger.info("acUserPaymentCodeBean=======: {}", "" + acUserPaymentCodeBean.getPayment_code().length());
                str2beSend = TextTmplKt.getPayOrderTemperate(
                        fnCarpoolOrderBean.getFrom_where()
                        , fnCarpoolOrderBean.getTo_where()
                        , DateUtil.timestamp2DateTime(fnCarpoolOrderBean.getStart_time())
                        , targetBePaidUser.getNickname()
                        , StringUtils.isNullOrEmpty(fnCarpoolOrderBean.getPrice()) ? "请和车主商议" : fnCarpoolOrderBean.getPrice()
                        , mSysConfig.getPay_code_prefix() + Md5Util.Md5(targetBePaidUser.getOpenid()).trim() +"?t=" + DateUtil.getCurrentTimestamp()
                );
                // 乘客记录设置为已回复-已支付
                ParamFnCarpoolPassenger paramFnCarpoolPassenger = new ParamFnCarpoolPassenger();
                paramFnCarpoolPassenger.setOrder_id(fnCarpoolOrderBean.getId());
                paramFnCarpoolPassenger.setPassenger_openid(userOpenid);
                paramFnCarpoolPassenger.setJoin_state(CS.CARPOOL_JOIN_STATE_IN);
                paramFnCarpoolPassenger.setHas_paid(CS.BOOLEAN_STRING_Y);
                int update = paramFnCarpoolPassenger.update("order_id,passenger_openid,join_state");
                logger.info("乘客【{}】回复消息:{},默认设置为已支付 - {}", userOpenid, replyContent, update);
                //
                if (targetPaymentCodeInfoBean == null) {
                    replyMessage(circleId, userOpenid, MSGTYPE_TEXT, "车主尚未上传收款码");
                } else {
                    // 先发文字提示
                    replyMessage(circleId, userOpenid, MSGTYPE_TEXT, str2beSend);
                    // 再发一个图片
                    replyMessage(circleId, userOpenid, MSGTYPE_IMAGE, targetPaymentCodeInfoBean.getPayment_code());
                }
            } else if ("event".equals(map.get("MsgType")) && "user_enter_tempsession".equals(map.get("Event"))) {
                AcUserBean acUserBean = new ParamAcUser("openid", userOpenid).queryForObject();
                replyMessage(acUserBean.getCircle_id(), userOpenid, MSGTYPE_TEXT, "请回复任意内容,获取车主收款码");
                // 添加记录
                ParamAcUserTrace paramNewAcUserTrace = new ParamAcUserTrace();
                paramNewAcUserTrace.setTrace_type(CS.USER_TRACE_OPEN_CONTACT);
                paramNewAcUserTrace.setOperator(userOpenid);
                paramNewAcUserTrace.setObject("进入客服消息");
                userService.addUserTrace(paramNewAcUserTrace);
            }
            return "SUCCESS";
        }

Redis监听实现行程生命周期管理

编写一个类,实现 KeyExpirationEventMessageListener

    @Component
    public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
        @Override
        public void onMessage(Message message, byte[] pattern) {
            String expiredKey = message.toString();
            logger.info("expiredKey:{}", expiredKey);
            // 即可做监听的动作
        }
    }

用第三方服务实现告警

    // 利用线程池,发送告警
    // 告警用的是server酱:http://sc.ftqq.com/?c=default
    public void alertFF(String title, String errorStr) {
            String currentDate = DateUtil.getCurrentDate();
            if (currentDate.equals(mToday)) {
                count++;
            } else {
                mToday = currentDate;
                count = 0;
            }
            if (!sysConfig.getUser_name().equals(username) && count < MAX_ALERT_TIMES) {
                HttpClientUtil.doGetAsync(getALERT_URL(title, MyStringUtil.removeSpecialChar(errorStr)), result -> {
                    logger.info("result:{}", result);
                });
            }
        }

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

西安未央

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值