java生成自增流水号,并从每月第一天重新清零计数将业务流水号添加到数据库(原创)...

本文介绍了一种生成唯一标识符的方法,包括基于日期和随机数的简单流水号生成及基于特定业务需求的流水号生成策略。后者通过组合公司简称、业务类型、年月等信息,并按月递增编号。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 如果你不想在数据库操作的话,这里有个方法可以一试,直接上代码:

package com.seawin.common.util.convert;

import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;


public class PrimaryGenerater {
  private static String SERIAL_NUMBER = "0001" ;
  private static PrimaryGenerater primaryGenerater = null;
  
  private PrimaryGenerater(){
      
  }
  
  /**
   * 取得PrimaryGenerater的单例实现
   *
   * @return
   */
  public static PrimaryGenerater getInstance() {
      if (primaryGenerater == null) {
          synchronized (PrimaryGenerater.class) {
              if (primaryGenerater == null) {
                  primaryGenerater = new PrimaryGenerater();
              }
          }
      }
      return primaryGenerater;
  }
  

  
/**
 * 生成 日期+随机数的流水号
 * */
  public String getNumberForPK(){  
      String id="";  
      SimpleDateFormat sf = new SimpleDateFormat("yyyyMMddHHmmss");  
      String temp = sf.format(new Date());  
      int random=(int) (Math.random()*10000);  
      id=temp+random;  
      return id;  
  }  
  
  
  
/**
 * HHTG+年月+8+0001
 * 每月从0001开始计数
 * */
public static synchronized String getnumber(String thisCode){
        
      String id = null;
      Date date = new Date();
      SimpleDateFormat formatter = new SimpleDateFormat("yyMM");
      String thisData = thisCode.substring(4, 8);
      //这个判断就是判断你数据取出来的最后一个业务单号是不是当月的
      if(!formatter.format(date).equals(thisData)){
          System.out.println("新的一月");
          thisData = formatter.format(date);
          //如果是新的一月的就直接变成0001
          id = "HHTG" + thisData + "80001";
      }else{
          System.out.println("当月");
          DecimalFormat df = new DecimalFormat("0000");
          
          //不是新的一月就累加
          id ="HHTG"+ formatter.format(date)+"8"
                + df.format(1 + Integer.parseInt(thisCode.substring(9, 13)));
      }
    return id;
    }



  public static void main(String[] args){
    /**
     *  HHTG170980001
     *  HH公司简称  + TG业务类型 + 年月 + 8部门 + 0001
     * 
     *  */
      String sno = "HHTG170980001";
      System.out.println(getnumber(sno));
      }
  }
  

 

Controller调用:

 

 

 no = list.get(0).getBillSeq()这里是查看你数据库的本月最后一条记录,如果有,则继续增加,如果是新月份,则从0001开始计数

结果如下:

换下月份结果如下:

 

转载于:https://www.cnblogs.com/jbml-154312/p/7490810.html

<think>好的,我现在需要解决用户的问题:在51单片机中使用REGX52.H头文件和定时器,通过一个按键控制后三位LED流水灯的暂停与恢复。首先,我得回忆一下51单片机的基本知识,特别是定时器和中断的相关内容。 用户提到的REGX52.H头文件,这是针对STC89C52RC这类单片机的头文件,里面定义了各种寄存器的地址。接下来,用户要求使用定时器,所以需要配置定时器相关的寄存器,比如TMOD、TCON、TH0、TL0等。同时,按键控制涉及到外部中断或者按键检测,但用户可能希望用查询方式检测按键,因为问题中没有提到外部中断,所以可能需要在主循环里轮询按键状态。 首先,流水灯的实现通常是通过定时器中断来周期性地改变LED的状态。这里用户指定的是后三位LED,也就是P2口的P2.5、P2.6、P2.7这三个引脚。需要设置这些引脚为输出模式,且通过移位或者位操作来改变它们的状态。 定时器的配置方面,51单片机的定时器有模式0到模式3几种工作方式。常用的是模式1(16位定时器)或模式2(自动重装8位定时器)。这里可能需要使用模式1,因为需要较长的定时时间。定时器中断的开启需要设置ET0和EA位,同时配置TMOD寄存器选择定时器模式。 接下来,按键的处理。按键连接到某个IO口,比如P3口的某个引脚,例如P3.1。需要检测按键的按下和释放,进行消抖处理。用户提供的引用中提到了使用延时函数进行消抖,这可能需要在主循环中检测按键状态,当检测到按键按下时,延时去抖,然后改变流水灯的状态(暂停或恢复)。 但是,由于使用了定时器中断来控制流水灯,暂停和恢复可能需要通过一个标志位来控制。例如,在定时器中断服务程序中,根据标志位的状态来决定是否改变LED的状态。按键按下时,反转这个标志位,从而实现暂停和恢复的功能。 现在需要考虑如何将定时器中断和按键检测结合起来。可能的结构是: 1. 初始化定时器,设置定时器中断,配置相关寄存器。 2. 在中断服务函数中,改变LED的状态,但只有当标志位允许时才会进行。 3. 主循环中检测按键,当按键按下时,反转标志位,进行消抖处理。 需要注意的是,按键检测不能放在中断服务函数中,因为中断服务函数应该尽量简短。所以主循环负责检测按键,而中断负责更新LED状态。 可能的步骤: - 配置定时器0为16位模式,计算定时时间,比如每50ms产生一次中断,累计多次中断后改变LED状态,实现流水灯效果。 - 定义全局变量,如flow_enable,用于控制流水灯是否运行。 - 在定时器中断中,如果flow_enable为真,则执行LED移位操作。 - 主循环中检测按键,当按键按下时,延时消抖,等待释放,再次消抖,然后切换flow_enable的状态。 关于流水灯的具体实现,后三位LED可能需要使用位操作,例如使用P2口的P2^5到P2^7。可能需要一个变量来记录当前LED的位置,比如使用一个3位的移位寄存器,或者循环左移/右移。 同时,需要注意P2口其他位的状态,不能影响前五位的LED,所以在改变后三位时,应该保持前五位不变。例如,可以使用位掩码,先读取P2的状态,然后只改变后三位,再写回P2。 例如,初始时设置后三位为0b11100000(假设LED是低电平驱动),然后每次移位时,循环左移或右移三位中的一位,但需要确保只改变后三位。或者使用一个变量来保存当前的状态,每次中断时更新该变量,将其写入P2口。 可能还需要考虑定时器中断的频率,以及如何根据时间间隔来调整流水灯的速度。比如,定时器每50ms中断一次,中断计数达到一定次数后改变LED状态,从而控制流水灯的速度。 最后,编写代码时需要注意头文件的包含,以及中断服务函数的语法,比如使用interrupt关键字,指定中断号(定时器0的中断号是1)。 现在,综合以上思路,开始组织代码结构: 1. 包含头文件REGX52.H。 2. 定义全局变量:flow_enable(控制流水灯运行)、counter(定时中断计数)、led_pattern(当前LED状态)。 3. 初始化函数:配置定时器,设置TMOD,计算初值,开启中断。 4. 定时器中断服务函数:在中断中,重装初值,更新counter,当达到预定次数时,改变led_pattern,更新P2口。 5. 主函数中初始化定时器,进入循环检测按键,处理按键消抖和flow_enable的切换。 可能的代码结构如下: #include <REGX52.H> bit flow_enable = 1; // 控制流水灯运行 unsigned char led_pattern = 0x07 << 5; // 后三位LED初始状态,假设低电平点亮 unsigned int counter = 0; void Timer0_Init() { TMOD &= 0xF0; // 设置定时器0为模式1(16位定时器) TMOD |= 0x01; TH0 = 0x4C; // 50ms的初值,假设12MHz晶振,12分频 TL0 = 0x00; ET0 = 1; // 开启定时器0中断 EA = 1; // 开启总中断 TR0 = 1; // 启动定时器0 } void Timer0_ISR() interrupt 1 { TH0 = 0x4C; // 重新装载初值 TL0 = 0x00; counter++; if (counter >= 20) { // 20次中断,即1秒 counter = 0; if (flow_enable) { // 改变LED状态,例如循环左移 led_pattern = (led_pattern << 1) | ((led_pattern >> 7) & 0x01); // 更新后三位LED,保持前五位不变 P2 = (P2 & 0x1F) | (led_pattern & 0xE0); } } } void main() { Timer0_Init(); P2 = 0xFF; // 初始关闭所有LED while(1) { if (P3_1 == 0) { // 假设按键接在P3.1 Delay(20); // 消抖 while (P3_1 == 0); // 等待按键释放 Delay(20); flow_enable = ~flow_enable; // 切换状态 } } } 但这里可能存在一些问题,比如: - 定时器初值的计算是否正确? - 后三位的移位是否正确? - 消抖函数Delay是否正确实现? 根据用户提供的引用,Delay函数可以引用已有的代码。例如,引用1中的Delay函数,参数是毫秒,可以调整。 另外,LED的初始状态可能需要调整。例如,初始时后三位中的某一位点亮,比如0xE0(二进制11100000)对应P2.5到P2.7,假设低电平驱动,则初始为P2.5亮,其他灭。每次左移一位,变成0xC0(P2.6亮),然后0x80(P2.7亮),再循环到0x40(但这里只有三位,可能需要循环到第三位后回到初始。可能需要更仔细的移位操作,比如循环左移三位中的一位,但需要确保只改变后三位。 或者,使用一个变量来记录当前点亮的LED位置,例如用0x01左移5位,然后每次左移一位,在达到第三位后循环。 例如: 初始时,led_pos = 0x01 << 5 (即0x20),对应P2.5亮。 每次中断时左移一位,当超过P2.7时,回到P2.5。 这样,led_pattern可以定义为0x01 << (5 + pos),其中pos为0到2的循环变量。 这可能需要更复杂的逻辑,或者使用模运算来循环位置。 另一个方法是使用一个3位的循环移位,例如: 初始时,后三位为110(二进制),即P2.5和P2.6亮,P2.7灭?或者根据实际硬件连接调整。 但用户需要的是流水灯,通常是单灯依次点亮,因此可能需要每次只有一个LED亮,然后循环移动。 假设后三位LED分别对应P2.5、P2.6、P2.7,且低电平点亮。初始时,P2.5亮,其他灭(0xDF,即11011111),但根据头文件的定义,可能需要确认P2口的结构。但通常,P2口每一位对应一个LED,输出0时点亮。 所以,初始状态为0xDF(P2.5=0),下一次左移一位变为0xBF(P2.6=0),再左移一位为0x7F(P2.7=0),然后循环到0xDF。但每次左移一位的话,可能需要带进位循环,或者使用其他方法。 但使用变量来保存当前LED的状态可能更简单。例如: unsigned char led_status = 0x01; // 初始状态,最低位对应第一个LED(可能对应P2.0?但用户需要的是后三位,即P2.5-P2.7) 这里可能需要调整。因为用户希望控制的是后三位,即P2.5、P2.6、P2.7,这三个引脚对应的位是P2口的bit5、bit6、bit7。因此,在8位的P2口中,这三个位是第5、6、7位(从0开始计数)。 例如,要操作这三个位,可以使用掩码0xE0(二进制11100000)。所以,初始时,可以设置led_pattern为0x20(二进制00100000),即P2.5亮,其他两位灭。下一次左移一位到0x40(01000000,P2.6),再左移一位到0x80(10000000,P2.7),然后循环回到0x20。 所以,每次中断时,将led_pattern左移一位,如果超过0x80,则回到0x20。可以通过判断led_pattern是否等于0x80,如果是,则下一次回到0x20,否则继续左移。 或者,使用模运算: 每次左移一位,然后与0xE0进行按位与,再检查是否超出范围。例如: led_pattern = (led_pattern << 1) & 0xE0; if (led_pattern == 0) { led_pattern = 0x20; } 这可能更有效。例如: 初始led_pattern = 0x20(00100000) 左移一位得到0x40(01000000) 左移一位得到0x80(10000000) 左移一位得到0x00(00000000),这时候判断如果为0,则重新设置为0x20。 这样就能循环后三位LED。 因此,在定时器中断中的代码可能是: if (flow_enable) { led_pattern = (led_pattern << 1) & 0xE0; if (led_pattern == 0) { led_pattern = 0x20; } P2 = (P2 & 0x1F) | led_pattern; // 保持前五位不变,更新后三位 } 这样,每次中断时,LED会左移一位,循环在P2.5到P2.7之间。 但这样定时器中断的频率需要调整,比如每0.5秒移动一次,这样流水灯的速度不会太快。例如,如果定时器中断每50ms一次,那么计数器需要累计10次(50ms * 20 = 1000ms,即1秒一次),或者调整次数。 另外,按键处理部分需要确保在主循环中检测按键,切换flow_enable的状态。同时,按键消抖使用Delay函数,如引用1中的Delay函数,需要包含在代码中。 总结代码步骤: 1. 包含头文件REGX52.H。 2. 定义全局变量:flow_enable(bit类型)、led_pattern(unsigned char)、counter(用于计时)。 3. 初始化定时器0为16位模式,设置初值,开启中断。 4. 在定时器中断服务函数中,重装初值,更新counter,当达到预定时间后,更新LED状态(如果flow_enable为真)。 5. 主函数中初始化定时器和IO口,进入循环检测按键,处理消抖切换flow_enable。 现在编写具体代码: #include <REGX52.H> bit flow_enable = 1; // 1表示流水灯运行 unsigned char led_pattern = 0x20; // 初始为P2.5亮 unsigned int counter = 0; void Delay(unsigned int xms) { unsigned char i, j; while(xms--){ i = 2; j = 239; do { while (--j); } while (--i); } } void Timer0_Init() { TMOD &= 0xF0; // 清零定时器0的模式位 TMOD |= 0x01; // 设置为模式1(16位) TH0 = 0x4C; // 定时50ms的初值,12MHz晶振 TL0 = 0x00; ET0 = 1; // 允许定时器0中断 EA = 1; // 总中断允许 TR0 = 1; // 启动定时器0 } void Timer0_ISR() interrupt 1 { TH0 = 0x4C; // 重新装载初值 TL0 = 0x00; counter++; if (counter >= 20) { // 20次中断,即50ms*20=1000ms counter = 0; if (flow_enable) { led_pattern = (led_pattern << 1) & 0xE0; if (led_pattern == 0) { led_pattern = 0x20; // 循环到P2.5 } P2 = (P2 & 0x1F) | led_pattern; // 更新后三位 } } } void main() { Timer0_Init(); P2 = 0xFF; // 初始关闭所有LED while(1) { if (P3_1 == 0) { // 检测按键按下 Delay(20); // 消抖 if (P3_1 == 0) { // 再次确认按键按下 while (P3_1 == 0); // 等待按键释放 Delay(20); flow_enable = ~flow_enable; // 切换状态 } } } } 这里有几个注意点: 1. 定时器初值的计算是否正确?对于12MHz晶振,每个机器周期是1μs(12分频)。定时50ms需要50000个周期。定时器从初值开始计数到65536溢出。所以初值应为65536 - 50000 = 15536 = 0x3CAF。但用户之前的代码中用了0x4C00,可能是有不同的计算方式,或者晶振频率不同。例如,如果用户使用的是11.0592MHz晶振,计算方式会不同。但根据引用1中的代码,可能用户假设的是12MHz,所以需要重新计算。 例如,12MHz,12分频,每个机器周期1μs。定时50ms需要50000个周期。初值应为TH0 = (65536 - 50000) / 256 = 15536 / 256 = 60.6875 → 60 → 0x3C,TL0 = 15536 % 256 = 176 → 0xB0。所以正确的初值应该是TH0=0x3C,TL0=0xB0。但用户之前的代码中使用了TH0=0x4C,可能是一个错误,或者不同的定时时间。需要确认。 如果用户的代码中定时器初值设置为0x4C00,对应的十进制是(0x4C << 8) | 0x00 = 76*256 = 19456。65536 - 19456 = 46080,即定时时间为46080μs=46.08ms。如果用户希望每50ms中断一次,这会有误差。正确的计算应该是: TH0 = (65536 - 50000) / 256 = 15536 / 256 = 60 → 0x3C TL0 = 15536 % 256 = 176 → 0xB0 所以正确的初值是TH0=0x3C,TL0=0xB0。但用户提供的引用3中提到定时器的配置,可能用户希望用不同的方式。需要检查代码中的定时器设置是否正确。 假设用户希望每50ms中断一次,正确的初值是TH0=0x3C,TL0=0xB0。但之前的代码中用户可能使用了不同的值,例如引用1中的Delay函数,或者引用2中的Delay500ms函数。需要根据实际需求调整。 如果代码中定时器中断设置为50ms,那么counter达到20次就是1000ms,即1秒切换一次LED,这可能太慢了。用户可能希望流水灯移动得更快,比如每0.5秒移动一次,或者更短。因此,可以调整counter的判断条件。例如,counter >=10,即每500ms移动一次。 另外,在代码中,流水灯的移位逻辑是否正确?例如,初始led_pattern=0x20(00100000),左移一位后变为0x40(01000000),再左移一位变为0x80(10000000),下一次左移后变为0x00,此时判断为0,重新设置为0x20。这样,三个LED依次点亮,每个状态持续1秒(假设counter>=20),这可能太慢,用户可能希望更快,比如每200ms移动一次。因此,可以调整counter的判断为5次(50ms*5=250ms)。 此外,在更新P2口时,需要保持前五位不变。例如,P2 & 0x1F(00011111)将前五位保留,然后与led_pattern(后三位)组合。例如,如果当前P2的值是0x1F(假设前五位是某种状态),但实际中,主函数初始化时设置P2=0xFF,所有LED熄灭。然后每次中断时,将后三位设置为led_pattern,其余位保持1(熄灭)。所以正确的操作应该是: P2 = (P2 & 0x1F) | led_pattern; 这样,前五位保持原状(但初始化时是0xFF,即所有位都是1,所以前五位保持1),后三位根据led_pattern变化。例如,当led_pattern=0x20时,P2= (0xFF & 0x1F) | 0x20 = 0x3F | 0x20 = 0x3F?这可能有问题。或者,初始化时应该将后三位设置为需要的模式,而前五位保持原状。但实际上,初始化时P2=0xFF,所有LED熄灭。在中断中,第一次更新时,P2的值会是0x1F | led_pattern。例如,当led_pattern=0x20,则P2=0x1F | 0x20 = 0x3F,即二进制00111111,这样P2.5=0(点亮),其他位为1。但前五位中的P2.0到P2.4可能被改变,但用户可能希望不影响前五位,因此可能需要确保前五位保持为1。因此,正确的操作应该是: P2 = (P2 & 0x1F) | led_pattern; 假设初始化时P2=0xFF,即所有位为1。在中断中,假设led_pattern=0x20,那么: P2 = (0xFF & 0x1F) → 0x1F,然后 | 0x20 → 0x3F,即二进制00111111。这样,P2.5=0(点亮),其他位为1。这符合预期,因为后三位中的P2.5被设置为0,而前五位保持为1吗? 不,因为原P2的值在初始化时为0xFF,即所有位为1。在第一次中断时,P2被更新为0x3F,即前五位变为00111,即P2.5=0,而P2.0到P2.4可能被改变为0,这会影响前五位的LED。这可能不是用户想要的,因为用户只希望后三位被控制,前五位应该保持熄灭(即1)。 因此,正确的做法应该是,在更新P2时,仅改变后三位,而前五位保持为1。因此,可以这样操作: P2 = (0xFF & 0x1F) | led_pattern; 因为0xFF & 0x1F = 0x1F,所以前五位是00011111,但需要前五位保持为1,即11100000中的前五位?这里可能存在混淆。 或者,正确的做法是,将前五位强制设置为1,后三位根据led_pattern变化。例如: P2 = (0xFF & 0x1F) | led_pattern; 这里0xFF & 0x1F的结果是0x1F(00011111),然后与led_pattern(后三位)组合。例如,如果led_pattern是0x20(00100000),则结果为0x1F | 0x20 = 0x3F(00111111),即P2.5=0,其他位为1。这样,只有P2.5被点亮,前五位中的P2.0到P2.4也被设为1,所以不影响。这样是正确的,因为用户只关心后三位,前五位应保持熄灭。 因此,代码中的这一部分是正确的。 现在,按键检测部分,用户提供的引用1中使用的是P3_1作为按键输入,检测低电平。代码中的按键处理部分需要检测P3_1是否被按下,消抖后切换flow_enable。 综合以上分析,最终的代码应该能够实现通过按键控制后三位LED流水灯的暂停与恢复,使用定时器中断来控制流水灯的速度,按键检测在主循环中进行,消抖处理。</think>以下是在51单片机中通过定时器和按键控制后三位LED流水灯暂停与恢复的实现方法: ### 硬件连接与原理 1. 后三位LED连接P2.5-P2.7(低电平驱动) 2. 按键连接P3.1(按下时低电平) 3. 定时器0实现周期控制,按键控制运行标志位[^3] ### 代码实现 ```c #include <REGX52.H> bit flow_enable = 1; // 流水灯控制标志 unsigned char led_pattern = 0x20; // 初始状态:P2.5亮(二进制00100000) unsigned int timer_count = 0; // 定时器中断计数器 // 延时函数(引用自示例代码[^1]) void Delay(unsigned int xms) { unsigned char i, j; while(xms--) { i = 2; j = 239; do { while (--j); } while (--i); } } // 定时器0初始化 void Timer0_Init() { TMOD &= 0xF0; // 设置定时器模式1 TMOD |= 0x01; TH0 = 0x4C; // 50ms定时初值(12MHz晶振) TL0 = 0x00; ET0 = 1; // 允许定时器0中断 EA = 1; // 开启总中断 TR0 = 1; // 启动定时器 } // 定时器0中断服务函数 void Timer0_ISR() interrupt 1 { TH0 = 0x4C; // 重装初值 TL0 = 0x00; if(++timer_count >= 10) { // 500ms触发一次状态变化 timer_count = 0; if(flow_enable) { led_pattern = (led_pattern << 1) & 0xE0; // 左移保持后三位 if(!led_pattern) led_pattern = 0x20; // 循环复位 P2 = (P2 & 0x1F) | led_pattern; // 更新后三位LED状态[^2] } } } void main() { P2 = 0xFF; // 初始化LED全灭 Timer0_Init(); while(1) { if(P3_1 == 0) { // 检测按键按下 Delay(20); // 消抖处理 if(P3_1 == 0) { // 确认按下 while(P3_1==0);// 等待释放 Delay(20); flow_enable = ~flow_enable; // 切换运行状态 } } } } ``` ### 实现说明 1. **定时器配置**:使用定时器0模式1,每50ms产生一次中断,累计10次(500ms)改变LED状态 2. **流水灯控制**:通过`led_pattern`变量实现三位循环移位,保持前五位LED状态不变 3. **按键处理**:采用消抖延时和状态确认机制,可靠检测按键动作[^1] 4. **运行控制**:`flow_enable`标志位决定是否更新LED状态,实现暂停/恢复功能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值