2023年终总结

学工系统-低值易耗品管理

时间: 2023.6.21-2023.7.9

实现功能

实现对学校低值易耗品管理,包括功能:

  • 易耗品出入库
    • 单个/批量导入导出
  • 易耗品出入库申请
    • 提交申请 -> 审批列表 -> 审批详情 -> 审批(通过/驳回)
    • 生成审批表(PDF) -> 导出审批记录
    • 撤销申请
  • 易耗品领用/归还
    • 提交申请 -> 审批列表 -> 审批详情 -> 审批(通过/驳回)
    • 生成审批表(PDF) -> 导出审批记录
    • 撤销申请

个人提升

  • 首次接触文件操作
    • 批量导入、批量导出(Excel表)
    • 生成审批表(生成Word -> 转为PDF)
    • 导出审批记录(生成Excel)

学工系统-动态表单

时间:

  • 2023.7.11-2023.8.3
  • 2023.8.21-2023.10.27

实现功能

任何 提交申请 -> 审批列表 -> 审批详情 -> 审批(通过/驳回) 流程的表单均可快速生成,只需下面几步:

  1. 设置应用基本信息
  2. 选择控件
  3. 设置流程
    1. 可以动态设置多条流程,满足什么条件(根据控件设置条件)遵循某条流程
    2. 控件可设置权限,某个流程可读或可编辑某个控件信息
  4. 上传审批生成的PDF模板
  5. 发布应用

数据库设计

自上而下依次为:

  1. 应用申请表: 存放申请表数据
  2. 应用用户申请数据表: 存放用户申请表数据
  3. 应用基础信息表
  4. 流程条件表
  5. 流程条件组表
  6. 条件展示表
  7. 控件顺序表
  8. 应用文件控件配置表
  9. 应用选项控件配置表
  10. 流程节点配置表
  11. 流程节点数据表
  12. 节点回退记录表
  13. 应用选项控件配置表
  14. 应用模板表: 存放各个应用模板文件名
  15. 应用文本控件配置表
  16. 应用时间控件配置表

接口

应用基础信息

  1. 添加应用基础设置
  2. 删除应用
  3. 修改基础设置
  4. 查询基础设置
  5. 模板上传

应用控件配置

  1. 保存表单设计
  2. 修改/编辑表单设计
  3. 展示应用控件及配置

审批流程配置 [难点]

  1. 展示流程节点
  2. 条件展示
    1. 添加条件展示
    2. 展示条件节点
    3. 展示可设置条件控件
  3. 保存条件节点
  4. 添加流程节点
  5. 添加条件分支
  6. 添加条件
  7. 删除流程节点
  8. 删除条件节点
  9. 修改流程节点

应用使用

  1. 提交申请表数据并生成流程
  2. 提交数据和流程生成申请表
  3. 撤销申请
  4. 审批列表
  5. 审批详情
  6. 审批(通过/拒绝)
  7. 转交
  8. 回退
  9. 操作记录部分
    1. 展示所有申请表信息
    2. 获取某个应用控件字段信息
    3. 批量导出
  10. 强制归档(所有流程默认通过)
  11. 强制流转(流转审批人)
  12. 模板下载(下载单个申请表PDF)
  13. 代审批
  14. 强制驳回
  15. 删除单个申请表数据

用到的算法

添加条件和删除条件节点接口使用到了栈来寻找 虚拟节点(聚合节点)

java 复制代码
    // 记录当前节点(发散节点)
    AppProcessNodeConfig dummyNodeUp = dummyNode;

    // 获取当前 虚拟节点(发散节点) 所对应的 虚拟节点(聚合节点)
    Stack<AppProcessNodeConfig> dummyNodeStack = new Stack<>();
    dummyNodeStack.push(dummyNode);
    while (!dummyNodeStack.empty()) {
        dummyNode = getNextNode(dummyNode); // 获取虚拟节点的下一个节点
        List<String> prevIdList = JSONObject.parseArray(dummyNode.getPrevId(), String.class);
        List<String> nextIdList = JSONObject.parseArray(dummyNode.getNextId(), String.class);
        if (nextIdList.size() > 1) { // 子节点有多个
            dummyNodeStack.push(dummyNode);
        } else if (prevIdList.size() > 1) { // 父节点有多个
            if (dummyNodeStack.size() == 1) {
                break;
            }
            dummyNodeStack.pop();
        }
    }
    // 此时 dummyNode 为 虚拟节点(发散节点) 所对应的 虚拟节点(聚合节点)

递归

java 复制代码
   /**
    * 获取传入的 两个节点之间 所有节点的id集合 [左闭右开]
    *
    * @param node       当前节点
    * @param targetNode 目标节点
    * @param nodeIdList 用来存放id的集合
    */
    public void getIdList(AppProcessNodeConfig node, AppProcessNodeConfig targetNode, HashSet<String> nodeIdList) {
        if (!node.getId().equals(targetNode.getId())) { // 当前节点不等于目标节点
            // 把当前 节点id 存入 nodeIdList
            nodeIdList.add(node.getId());
            // 递归向下查找
            for (AppProcessNodeConfig nextNode : getNextNodeList(node)) {
                getIdList(nextNode, targetNode, nodeIdList);
            }
        }
    }

个人提升

  • 逻辑思维能力得到了很大的提升,复杂接口也能手到拈来

示例:

java 复制代码
@ApiModelProperty(value = "提交申请表数据并生成流程")
@PostMapping("/submitData")
public ResponseResult submitApplicationData(@RequestBody SubmitApplicationDataReq req, HttpServletRequest request) {
    String userId = jwtTokenUtil.getUserIdByRequest(request);
    return appApplicationDataService.submitApplicationData(req, userId);
}

实现:

java 复制代码
@Override
public ResponseResult submitApplicationData(SubmitApplicationDataReq req, String userId) {
    //校验参数
    if (isNullOrEmpty(req.getAppId(), req.getDataReqList())) {
        return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
    }
    //校验 数据数量 与 控件数量 是否匹配
    List<String> controlOrderIdList = req.getDataReqList().stream().map(ApplicationDataReq::getControlOrderId).collect(Collectors.toList());
    Integer controlNum = appControlOrderMapper.selectCount(new LambdaQueryWrapper<AppControlOrder>()
                                                           .eq(AppControlOrder::getAppId, req.getAppId())
                                                           .in(AppControlOrder::getId, controlOrderIdList));
    if (controlNum != req.getDataReqList().size()) {
        return CommonResult.failed(CommonCodeEnum.DATA_NUM_NOT_EQUALS_CONTROL_NUM);
    }
    //校验必填控件是否全部填写
    for (ApplicationDataReq dataReq : req.getDataReqList()) {
        if (isNullOrEmpty(dataReq.getControlOrderId())) {
            return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
        }
        Boolean isRequired = appDynamicUtil.getIsRequired(dataReq.getControlOrderId());
        if (isRequired == null) {
            return CommonResult.failed(CommonCodeEnum.CONTROL_NOT_EXIST);
        }
        if (isRequired) {
            if (isNullOrEmpty(dataReq.getData())) {
                return CommonResult.failed(CommonCodeEnum.REQUIRED_CONTROL_NOT_FILL);
            }
        }
    }

    List<AppProcessNodeConfigResp> processNodeRespList = new ArrayList<>();
    //获取申请人节点配置
    AppProcessNodeConfig nodeConfig = appProcessNodeConfigMapper.selectList(new LambdaQueryWrapper<AppProcessNodeConfig>()
                                                                            .eq(AppProcessNodeConfig::getAppId, req.getAppId())
                                                                            .eq(AppProcessNodeConfig::getType, APPLICANT_NODE)).get(0);
    //记录节点顺序
    int sort = 0;
    while (nodeConfig.getType() != END_NODE) {
        //添加当前节点
        AppProcessNodeConfigResp nodeConfigResp = new AppProcessNodeConfigResp();
        BeanUtils.copyProperties(nodeConfig, nodeConfigResp);
        //设置节点顺序
        nodeConfigResp.setSort(sort++);
        //设置审批人信息
        if (nodeConfigResp.getApproverType() == DYNAMIC_APP_ROLE) {
            SmsRole role = smsRoleMapper.selectById(nodeConfigResp.getApproverObjectId());
            nodeConfigResp.setObjectName(role.getRoleName());
        } else if (nodeConfigResp.getApproverType() == DYNAMIC_APP_ASSIGNER) {
            SmsUser user = smsUserMapper.selectById(nodeConfigResp.getApproverObjectId());
            AppSearchUserResp userResp = new AppSearchUserResp();
            BeanUtils.copyProperties(user, userResp);
            nodeConfigResp.setUserInfo(userResp);
        } else if (nodeConfigResp.getApproverType() == DYNAMIC_APP_SUBMITTER) {
            SmsUser user = smsUserMapper.selectById(userId);
            AppSearchUserResp userResp = new AppSearchUserResp();
            BeanUtils.copyProperties(user, userResp);
            nodeConfigResp.setUserInfo(userResp);
            nodeConfigResp.setApproverType(DYNAMIC_APP_ASSIGNER);
            nodeConfigResp.setApproverObjectId(userId);
        }
        //设置权限
        nodeConfigResp.setAuthority(JSONObject.parseArray(nodeConfig.getAuthority(), AuthorityResp.class));
        //添加本节点
        processNodeRespList.add(nodeConfigResp);

        //节点向后传递
        List<AppProcessNodeConfig> nextNodeList = appDynamicUtil.getNextNodeList(nodeConfig);
        a:
        for (AppProcessNodeConfig nextNode : nextNodeList) {
            while (true) {
                if (nextNode.getType() == COPY_NODE || nextNode.getType() == APPROVE_NODE || nextNode.getType() == END_NODE) {
                    nodeConfig = nextNode;
                    break a;
                } else if (nextNode.getType() == DUMMY_NODE) {
                    List<String> nextNodeSonIdList = JSON.parseArray(nextNode.getNextId(), String.class);
                    //虚拟节点(发散节点)
                    if (nextNodeSonIdList.size() > 1) {
                        for (int priority = 1; priority <= nextNodeSonIdList.size(); priority++) {
                            //根据 前节点id 及 优先级 检索到条件组集合
                            List<AppConditionGroup> conditionGroupList = appConditionGroupMapper.selectList(new LambdaQueryWrapper<AppConditionGroup>()
                                                                                                            .eq(AppConditionGroup::getPrevNodeId, nextNode.getId())
                                                                                                            .eq(AppConditionGroup::getPriority, priority));
                            //校验条件组是否存在
                            if (conditionGroupList.isEmpty()) {
                                return CommonResult.failed(CommonCodeEnum.APP_CONDITION_GROUP_NOT_EXIST);
                            }
                            //若当前条件组为默认条件
                            if (priority == nextNodeSonIdList.size() && conditionGroupList.get(0).getIsDefault()) {
                                AppProcessNodeConfig node = appProcessNodeConfigMapper.selectById(conditionGroupList.get(0).getNodeId());
                                nodeConfig = appDynamicUtil.getNextNode(node);
                                break a;
                            }
                            //遍历条件组中的条件是否完成
                            for (AppConditionGroup conditionGroup : conditionGroupList) {
                                List<AppCondition> conditionList = appConditionMapper.selectList(new LambdaQueryWrapper<AppCondition>()
                                                                                                 .eq(AppCondition::getConditionGroupId, conditionGroup.getId()));
                                //校验条件是否存在
                                if (conditionList.isEmpty()) {
                                    return CommonResult.failed(CommonCodeEnum.APP_CONDITION_NOT_EXIST);
                                }
                                //校验条件集合是否全部符合
                                boolean isReachedConditionList = appDynamicUtil.getIsReachedConditionList(req.getDataReqList(), conditionList, userId);
                                //若 条件组中 有 条件集合全部符合
                                if (isReachedConditionList) {
                                    AppProcessNodeConfig node = appProcessNodeConfigMapper.selectById(conditionGroupList.get(0).getNodeId());
                                    nodeConfig = appDynamicUtil.getNextNode(node);
                                    break a;
                                }
                            }
                        }
                    } else {//虚拟节点(聚合节点)
                        nextNode = appDynamicUtil.getNextNodeList(nextNode).get(0);
                    }
                }
            }
        }
    }

    return CommonResult.success(processNodeRespList, processNodeRespList.size());
}

学工系统-会议室管理

时间: 2023.8.3-2023.8.29

实现功能

学校对于会议室进行预约、申请、管理等操作

接口

  1. 楼栋管理
    1. 添加楼栋
    2. 删除楼栋
    3. 展示楼栋树
  2. 会议室管理
    1. 添加会议室
    2. 删除会议室
    3. 编辑会议室
    4. 展示会议室
    5. 展示规定时间段可用会议室
  3. 会议室预约
    1. 提交预定申请
    2. 撤销预定申请
    3. 审批列表
    4. 审批详情
    5. 审批(同意/拒绝)
    6. 展示指定日期会议室列表及时间
    7. 展示指定会议室信息及时间细节(按周展示)

个人提升

  • 对关于时间的操作更上一层楼
    • 对某周内每一天数据进行查询数据库并包装
    • 校验当前时间段是否存在会议
    • 校验开始时间和结束时间限制
    • 校验是否超出会议室单次可预订时长上限
    • 校验是否超出会议室最早可提前预定时间
    • ...

南充经济开发区化工管廊集控平台

时间: 2023.10.25-2023.11.19

实现功能

  • 与第三方厂商对接,完成对视频监控(浙江大华技术股份有限公司)、气体监测数据整合并展示到大屏中。
  • 后台需实现对设备(视频监控、气体监测器)的管理
  • 数据统计

接口

  1. 视频监控
    1. 获取鉴权
    2. 刷新认证信息
    3. 获取组织树
    4. 获取监控设备列表
    5. 监控实时预览
    6. 监控录像回放
    7. ...
  2. 气体监测
    1. 更新当前登录⼈最新的全部分组下所有设备所有探头
    2. 获取设备在线离线列表数据
    3. 获取当前登录⼈全部的分组
    4. 获取大屏展示探测器的数据
    5. 获取设备下探测器列表
    6. 获取探测器历史数据+故障和告警列表
    7. 获取首页的信息分析
    8. 获取告警分析
    9. 获取设备报警排名
    10. ...

个人提升

  • 对接第三方厂商经验得到提升

  • 对接视频监控

    1. 添加第三方依赖

      xml 复制代码
      <!-- ICC鉴权 -->
      <dependency>
          <groupId>com.dahuatech.icc</groupId>
          <artifactId>java-sdk-oauth</artifactId>
          <version>${icc.sdk.version}</version>
          <exclusions>
              <exclusion>
                  <artifactId>java-sdk-core</artifactId>
                  <groupId>com.dahuatech.icc</groupId>
              </exclusion>
          </exclusions>
      </dependency>
      <dependency>
          <groupId>com.dahuatech.icc</groupId>
          <artifactId>java-sdk-core</artifactId>
          <version>${icc.sdk.version}</version>
      </dependency>
      <!-- ICC基础资源SDK -->
      <dependency>
          <groupId>com.dahuatech.icc</groupId>
          <artifactId>java-sdk-oauth</artifactId>
          <version>${icc.sdk.version}</version>
      </dependency>
      <!-- ICC 事件中心sdk -->
      <dependency>
          <groupId>com.dahuatech.icc</groupId>
          <artifactId>java-sdk-event</artifactId>
          <version>${icc.sdk.version}</version>
      </dependency>
    2. 根据第三方系统规则获取鉴权

    3. 带鉴权、签名等(根据第三方规则)去请求第三方接口获取到数据,对数据进行处理(存数据库/返回给前端展示)

  • 对接气体监测

    1. 添加mqtt依赖

      xml 复制代码
      <dependency>
          <groupId>com.github.tocrhz</groupId>
          <artifactId>mqtt-spring-boot-starter</artifactId>
          <version>1.2.7</version>
      </dependency>
    2. 利用第三方规则请求其接口再对回应数据进行处理

      java 复制代码
      /**
       * 获取token
       * @return token
       */
      public String getGasToken() {
          String gasValue = cacheUtil.getObject(gasKey, String.class);
          // redis中获取到token
          if (!isNullOrEmpty(gasValue)) {
              // log.info("redis中有access_token:{}", gasValue);
              return gasValue;
          }
          // redis未获取token,请求token
          String gasTokenResult = OkHttpUtils.builder().url(tokenUrl)
              .addParam("appKey", gasConfigProperties.getApp_key())
              .addParam("appSecret", gasConfigProperties.getApp_secret())
              .post(true)
              .async();
          JSONObject gasTokenJsonObject = JSONObject.parseObject(gasTokenResult);
          // {"code":0,"data":{"access_token":"da8c3b33-972d-4f8c-a839-e0e913ad465c"}}
          // 校验code
          Integer code = gasTokenJsonObject.getInteger("code");
          if (code != 0) {
              ExceptionCast.cast(CommonResult.failed(CommonCodeEnum.GAS_ACCESS_TOKEN_FAIL));
          }
          // 获取access_token
          String access_token = gasTokenJsonObject.getJSONObject("data").getString("access_token");
          cacheUtil.add(gasKey, access_token, 2, TimeUnit.HOURS);
          // log.info("access_token:{}", access_token);
      
          return access_token;
      }
    3. 利用mqtt监听器监听到的数据有选择性地对数据库进行操作(插入、修改、删除等)

      java 复制代码
      	/**
           * 接受来⾃设备的实时浓度数据
           * @param topic 主题
           * @param groupId 分组id
           * @param deviceId 设备id
           * @param slaveEventResp 每个探测器 15分钟数据上传⼀次(设备上报属性)
           */
          @MqttSubscribe(value = lastTopic, qos = 0)
          @Transactional
          public void subLast(String topic,@NamedValue("groupId") String groupId,@NamedValue("deviceId") String deviceId,@Payload String slaveEventResp) {
      //        log.info("开始接受每个探测器15分钟数据上传⼀次......");
      //        log.info("receive from : {}", topic);
      //        log.info("groupId :{}",groupId);
      //        log.info("deviceId : {}",deviceId);
      //        log.info("设备上报 : {}", slaveEventResp);
      
              updateSlaveData(groupId,deviceId,SLAVE_REPORT_TYPE,slaveEventResp);
      //        log.info("接受每个探测器15分钟数据上传⼀次结束!!!!!!");
      
          }

家校通项目

时间: 2023.11.8-2023.12.12

实现功能

  • 对接第三方厂商门禁系统
  • 接收门禁闸机发送的请求并对数据进行处理
  • 实现对学生出入校进行记录(时间、照片等)
  • 家长可绑定学生信息(一个家长可绑定多个学生)
    • 家长可以增删改查学生基本信息
    • 家长可以查看学生的出入校记录以及门禁照片
    • 家长可以修改学生门禁照片

接口

  • 平台主体

    1. 获取绑定学生信息
    2. 获取人脸识别记录
    3. 获取所有学生出入校记录
    4. 学生家长信息批量绑定
    5. 展示学生家长绑定信息
    6. 批量导入家长信息
    7. 展示家长信息
    8. 查看当前门禁系统无照片人员信息
    9. 批量导出绑定信息
    10. 批量导出出入校记录
  • 接收闸机请求

    1. 接收人脸识别记录
  • 与第三方平台交互

    1. 城市社区

    2. 楼栋管理

      1. 创建楼栋
      2. 更新楼栋
      3. 删除楼栋
      4. 查询社区所有楼栋
    3. 房屋管理

      1. 创建房屋
      2. 更新房屋
      3. 删除房屋
      4. 查询楼栋内所有房屋
    4. 住户管理

      1. 添加住户信息
      2. 更新住户信息
      3. 删除住户信息
      4. 绑定住户信息
      5. 解绑住户信息
      6. 白名单下发
      7. 白名单删除
      8. 住户下发
      9. 查询房屋内住户
    5. 设备管理

      1. 设备通行记录
      2. 设置设备参数
      3. ...
  • 门禁信息

    1. 更新个人门禁信息
    2. 获取个人门禁信息
    3. 查看当前门禁系统照片
  • 其他

    1. 学生走住读批量导入
    2. 查询学生列表
    3. 修改学生状态
    4. 查看走住读列表
    5. ...

个人提升

  • 对文件的操作更加熟练文件操作
  • 对第三方厂商平台对接更加得心应手

医药销售APP及管理后台

时间: 2023.11.29-2024.1.4 [第一阶段完成]

负责模块

  • 药品活动管理
  • 优惠券[后台管理与客户端领取及使用]
  • 收货地址管理
  • 购物车
  • 积分商城
  • 订单模块
    • 金额计算
    • 对接微信支付
    • 下单
    • 退款
    • 定时任务
    • 后续需对接
      • 快递及发票第三方平台
      • 对接支付宝支付、云闪付支付等

接口

这里我主要展示自己第一次接触的订单模块,其他模块都基本上是增删改查操作以及对文件的处理

微信支付回调模块涉及公司业务不变展示,大家有任何问题可以私聊我了解.

有不对的或者可以改进的地方都可以一起交流,有则改之无则加勉!

  • 订单模块
    • 计算金额
    • 结算
    • 提交订单
    • 取消订单
    • 我的订单
    • 订单详情
    • 确认发货
    • 确认收货
    • 获取所有订单
    • 微信支付回调
  • 退款模块
    • 申请退款
    • 同意退款
    • 拒绝退款
    • 获取全部退款信息
    • 获取退款详情信息

定时任务

ScheduleConfig

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
    }

    @Bean
    public Executor taskExecutor() {
        return Executors.newScheduledThreadPool(10); //指定线程池大小
    }
}

OrderScheduleTask

java 复制代码
@Component
@Log4j2
public class OrderScheduleTask {
    @Resource
    private MmsOrderScheduleService mmsOrderScheduleService;
    @Resource
    private MmsOrderService mmsOrderService;
    @Resource
    private MmsOrderRefundService mmsOrderRefundService;
    @Resource
    private WxPayUtil wxPayUtil;
    @Resource
    private OrderUtil orderUtil;
    @Resource
    private MmsUserCouponRelationService mmsUserCouponRelationService;

    @Resource
    private PlatformTransactionManager transactionManager; // 事务

    @Scheduled(cron = "0 */1 * * * ?") // 每隔1分钟执行一次
    public void schedulingTask() {
        // 获取所有 未结束且已达过期时间 的定时任务
        Date nowDate = new Date(); // 当前时间
        List<MmsOrderSchedule> orderScheduleList = mmsOrderScheduleService.list(new LambdaQueryWrapper<MmsOrderSchedule>()
                .eq(MmsOrderSchedule::getIsClose, false) // 状态未结束
                .le(MmsOrderSchedule::getExpireTime, nowDate)); // 过期时间 早于 当前时间
        if (orderScheduleList.isEmpty()) { // 没有定时任务直接结束方法
            return;
        }

        for (MmsOrderSchedule orderSchedule : orderScheduleList) {
            // 开启事务
            DefaultTransactionDefinition dt = new DefaultTransactionDefinition();
            // 嵌套事务 PROPAGATION_REQUIRES_NEW 每次开启一个新的事务
            dt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            // 设置嵌套事务
            TransactionStatus status = transactionManager.getTransaction(dt);

            try {
                // 获取定时任务订单
                MmsOrder order = mmsOrderService.getById(orderSchedule.getOrderId());
                if (order == null) { //订单不存在
                    continue;
                }

                // 根据 进度类型(0.支付进度 1.发货进度 2.订单结束进度) 进行不同处理
                switch (orderSchedule.getType()) {
                    case OrderScheduleConstant.ORDER_SCHEDULE_TYPE_PAYMENT: // 支付进度
                        // 主动查询用户支付情况
                        Transaction transaction = wxPayUtil.queryOrderByOutTradeNo(order.getId());
                        // 用户支付成功
                        if (transaction != null && transaction.getTradeState() == Transaction.TradeStateEnum.SUCCESS) {
                            if (!orderUtil.isPaid(order.getId())) {
                                // 修改订单状态等信息
                                order.setPaymentTransactionId(transaction.getTransactionId()); // 支付订单号
                                order.setPaymentAmount(centsToYuan(transaction.getAmount().getTotal())); // 支付金额
                                order.setPaymentMethod(OrderConstant.ORDER_PAYMENT_METHOD_WECHAT); // 订单支付方式: 微信支付
                                order.setPaymentTime(TimeUtil.rfc3339ParseDate(transaction.getSuccessTime())); // 支付时间
                                order.setOrderStatus(OrderConstant.ORDER_STATUS_TO_BE_DELIVERED); // 待发货
                            }
                        } else { // 支付失败
                            order.setOrderStatus(OrderConstant.ORDER_STATUS_ORDER_CANCELLED); // 订单状态: 0.取消订单
                            order.setIsClose(OrderConstant.ORDER_IS_CLOSE_YES); // 结束订单
                            order.setEndTime(nowDate); // 订单结束时间
                        }

                        break;
                    case OrderScheduleConstant.ORDER_SCHEDULE_TYPE_DELIVERY: // 发货进度
                        order.setOrderStatus(OrderConstant.ORDER_STATUS_ORDER_CANCELLED); // 订单状态: 0.取消订单
                        order.setIsClose(OrderConstant.ORDER_IS_CLOSE_YES); // 结束订单
                        order.setEndTime(nowDate); // 订单结束时间

                        // 付款金额大于0,自动退款
                        if (order.getPaymentAmount().compareTo(BigDecimal.valueOf(0)) > 0) {
                            // 查询不到支付订单号直接回滚
                            if (isNullOrEmpty(order.getPaymentTransactionId())) {
                                rollback(status); // 手动回滚事务
                                continue;
                            }

                            // 待退款中的订单改为退款失败
                            List<MmsOrderRefund> orderRefundList = mmsOrderRefundService.list(new LambdaQueryWrapper<MmsOrderRefund>()
                                    .eq(MmsOrderRefund::getOrderId, order.getId()) // 订单编号
                                    .eq(MmsOrderRefund::getStatus, OrderRefundConstant.ORDER_REFUND_STATUS_TO_BE_REFUNDED)); // 待退款
                            if (!orderRefundList.isEmpty()) {
                                MmsOrderRefund orderRefund = new MmsOrderRefund();
                                orderRefund.setStatus(OrderRefundConstant.ORDER_REFUND_STATUS_REFUNDED_FAIL); // 退款失败
                                List<String> orderRefundIdList = orderRefundList.stream().map(MmsOrderRefund::getId).collect(Collectors.toList());
                                boolean refundUpdSuccess = mmsOrderRefundService.update(orderRefund, new LambdaQueryWrapper<MmsOrderRefund>()
                                        .in(MmsOrderRefund::getId, orderRefundIdList));
                                if (!refundUpdSuccess) { //修改失败
                                    rollback(status); // 手动回滚事务
                                    continue;
                                }
                            }

                            // 记录退款行为
                            MmsOrderRefund orderRefund = new MmsOrderRefund();
                            orderRefund.setUserId(order.getUserId());
                            orderRefund.setOrderId(order.getId());
                            orderRefund.setMedicineInfo(order.getMedicineInfo());
                            orderRefund.setStatus(OrderRefundConstant.ORDER_REFUND_STATUS_REFUNDING); // 退货状态: 退款中
                            orderRefund.setProcessApproverId(ProcessApproverConstant.PROCESS_APPROVER_SYSTEM); // 审批人: 系统-定时任务
                            orderRefund.setNotes(OrderRefundConstant.ORDER_REFUND_REASON_DELIVERY_TIMEOUT);
                            boolean refundSaveSuccess = mmsOrderRefundService.save(orderRefund);
                            if (!refundSaveSuccess) {
                                rollback(status); // 手动回滚事务
                                continue;
                            }

                            // 发起预退款
                            RefundReq preRefundReq = new RefundReq();
                            preRefundReq.setTransaction_id(order.getPaymentTransactionId()); // 微信交易订单号
                            preRefundReq.setOut_trade_no(order.getId()); // 商户订单号
                            preRefundReq.setOut_refund_no(orderRefund.getId()); // 退款单号
                            preRefundReq.setReason(OrderRefundConstant.ORDER_REFUND_REASON_USER_SUBMIT); // 退款原因: 用户申请退款
                            // 设置订单金额 单位:分(订单金额 = 原金额(元) * 100)
                            long total = order.getPaymentAmount().multiply(BigDecimal.valueOf(100)).longValue();
                            preRefundReq.setTotal(total); // 订单金额 单位:分
                            preRefundReq.setRefund(total); // 退款金额 单位:分(全部退)
                            Refund refund = wxPayUtil.createRefund(preRefundReq);
                            if (refund == null) {
                                rollback(status); // 手动回滚事务
                                continue;
                            }

                            // 修改退款记录
                            orderRefund.setRefundId(refund.getRefundId()); // 微信退款id
                            orderRefund.setRefundAmount(centsToYuan(refund.getAmount().getTotal())); // 退款金额
                            orderRefund.setRefundTime(TimeUtil.rfc3339ParseDate(refund.getSuccessTime())); // 退款时间
                            orderRefund.setStatus(OrderRefundConstant.ORDER_REFUND_STATUS_REFUNDED); // 退货状态: 已退款
                            boolean refundUpdSuccess = mmsOrderRefundService.updateById(orderRefund);
                            if (!refundUpdSuccess) {
                                rollback(status); // 手动回滚事务
                                continue;
                            }
                        }

                        // 退还优惠券
                        if (!isNullOrEmpty(order.getCouponInfo())) { // 存在优惠券信息
                            MmsUserCouponRelationResp couponInfo = JSONObject.parseObject(order.getCouponInfo(), MmsUserCouponRelationResp.class);
                            if (couponInfo != null) {
                                MmsUserCouponRelation userCoupon = mmsUserCouponRelationService.getById(couponInfo.getId());
                                if (userCoupon != null) {
                                    // 优惠券状态修改为未使用
                                    userCoupon.setIsUse(UserCouponRelationConstant.USER_COUPON_RELATION_NOT_USE);
                                    boolean couponUpdSuccess = mmsUserCouponRelationService.updateById(userCoupon);
                                    if (!couponUpdSuccess) { // 修改失败
                                        rollback(status); // 手动回滚事务
                                        continue;
                                    }
                                }
                            }
                        }

                        break;
                    case OrderScheduleConstant.ORDER_SCHEDULE_TYPE_ORDER_END: // 订单结束进度
                        order.setOrderStatus(OrderConstant.ORDER_STATUS_ALREADY_RECEIVED); // 订单状态: 4.已收货
                        order.setIsClose(OrderConstant.ORDER_IS_CLOSE_YES); // 结束订单
                        order.setEndTime(nowDate); // 订单结束时间

                        // 发起分账
                        // 支付订单号不存在
                        if (isNullOrEmpty(order.getPaymentTransactionId())) {
                            continue;
                        }
                        wxPayUtil.profitSharingUnfreezeOrder(order.getId(), order.getPaymentTransactionId());

                        // 待退款中的订单改为退款失败
                        List<MmsOrderRefund> orderRefundList = mmsOrderRefundService.list(new LambdaQueryWrapper<MmsOrderRefund>()
                                .eq(MmsOrderRefund::getOrderId, order.getId()) // 订单编号
                                .eq(MmsOrderRefund::getStatus, OrderRefundConstant.ORDER_REFUND_STATUS_TO_BE_REFUNDED)); // 待退款
                        if (!orderRefundList.isEmpty()) {
                            MmsOrderRefund orderRefund = new MmsOrderRefund();
                            orderRefund.setStatus(OrderRefundConstant.ORDER_REFUND_STATUS_REFUNDED_FAIL); // 退款失败
                            List<String> orderRefundIdList = orderRefundList.stream().map(MmsOrderRefund::getId).collect(Collectors.toList());
                            boolean refundUpdSuccess = mmsOrderRefundService.update(orderRefund, new LambdaQueryWrapper<MmsOrderRefund>()
                                    .in(MmsOrderRefund::getId, orderRefundIdList));
                            if (!refundUpdSuccess) { //修改失败
                                rollback(status); // 手动回滚事务
                                continue;
                            }
                        }

                        break;
                }

                // 修改订单
                boolean orderUpdSuccess = mmsOrderService.updateById(order);
                if (!orderUpdSuccess) {
                    rollback(status); // 手动回滚事务
                    continue;
                }

                // 关闭定时任务
                orderSchedule.setIsClose(true); // 结束定时任务
                orderSchedule.setEndTime(nowDate); // 结束时间
                orderSchedule.setUpdateUserId(ProcessApproverConstant.PROCESS_APPROVER_SYSTEM); // 修改人
                boolean scheduleUpdSuccess = mmsOrderScheduleService.updateById(orderSchedule);
                if (!scheduleUpdSuccess) {
                    rollback(status); // 手动回滚事务
                    continue;
                }
            } catch (Exception e) {
                e.printStackTrace();
                // 手动回滚事务
                transactionManager.rollback(status);
            } finally {
                if (status.isNewTransaction() && !status.isCompleted()) {
                    transactionManager.commit(status);
                }
            }
        }
    }

    /**
     * 将以分为单位的金额转换为以元为单位的金额(BigDecimal)
     *
     * @param <T>          可以是Integer或Long类型
     * @param moneyInCents 以分为单位的金额(T)
     * @return 以元为单位的金额(BigDecimal)
     */
    private <T extends Number> BigDecimal centsToYuan(T moneyInCents) {
        // 将分转换为元, 需要除以100, 并将结果存储为BigDecimal类型
        return new BigDecimal(moneyInCents.longValue()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
    }

    /**
     * 手动回滚事务
     *
     * @param status 嵌套事务
     */
    private void rollback(TransactionStatus status) {
        // 手动回滚事务
        transactionManager.rollback(status);
        if (status.isNewTransaction() && !status.isCompleted()) {
            transactionManager.commit(status);
        }
    }

}

个人提升

  • 定时任务

    定时任务其实并没有多么复杂,有些需求难免会遇到需要定时任务去解决的地方,了解定时任务之后再去处理这样的需求便会简单很多了

  • 微信支付

    首次接触对接微信支付受益匪浅,虽然很复杂,需要很多步骤,但是实现下来成就感满满,经过对微信支付的对接,我相信对其他支付(支付宝、云闪付等)的对接会更加得心应手

总结

今年的收货特别多,技术层面以及逻辑思维能力得到了极大的提升,也交到了特别好的同事们,绝对称得上自己工作以来的一个良好开端,希望自己可以不忘初心,继续努力下去!!!

相关推荐
瓜牛_gn1 小时前
mysql特性
数据库·mysql
奶糖趣多多2 小时前
Redis知识点
数据库·redis·缓存
阿伟*rui2 小时前
配置管理,雪崩问题分析,sentinel的使用
java·spring boot·sentinel
CoderIsArt3 小时前
Redis的三种模式:主从模式,哨兵与集群模式
数据库·redis·缓存
XiaoLeisj4 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
paopaokaka_luck4 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
dayouziei4 小时前
java的类加载机制的学习
java·学习
师太,答应老衲吧5 小时前
SQL实战训练之,力扣:2020. 无流量的帐户数(递归)
数据库·sql·leetcode
Yaml46 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~6 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端