Compare commits

...

10 Commits

Author SHA1 Message Date
李一帆 0b18a1147e docs(sms): 删除短信管理模块API文档,多余了 2025-09-26 17:40:42 +08:00
李一帆 c33a615fbe feat: 为SMS相关控制器添加专门的分页查询DTO
- 新增SmsSpecialistPageDto、SmsHolidayPageDto、SmsLogPageDto
- 修改三个控制器的page方法使用专门的DTO
- 为Service接口和实现类添加page方法支持
- 支持按name、phone、status等字段进行条件查询
- 移除不必要的searchCount、sortField、sortOrder属性

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-26 17:35:05 +08:00
李一帆 73c826dbe4 feat(sql): 更新默认系统日期函数- 将 CREATE_TM 字段的默认值从 CURRENT_TIME 更改为 SYSDATE 2025-09-26 11:41:57 +08:00
李一帆 1eec3bfd2b feat(entity):为日期字段添加JSON格式化注解
-为SmsHoliday实体的startDate、endDate和createTm字段添加@JsonFormat注解
-为SmsLog实体的sendTm字段添加@JsonFormat注解
- 统一日期格式化模式,startDate和endDate使用yyyy-MM-dd格式
- sendTm使用yyyy-MM-dd HH:mm:ss格式- 设置时区为GMT+8以确保日期显示正确
- 引入com.fasterxml.jackson.annotation.JsonFormat包支持格式化功能
2025-09-26 11:35:37 +08:00
李一帆 c2999bdb7d chore: 更新 .gitignore 文件- 添加 aiwork/ 目录到忽略列表 2025-09-26 11:27:25 +08:00
李一帆 1b76ec353d feat(controller): 实现分页查询功能
-为 SmsHolidayController 和 SmsLogController 添加分页支持- 使用 IPage 类型替换 List 以支持分页数据结构
- 引入 FindPageDto 用于接收分页参数- 修改 page 接口实现,调用 service 的分页方法
- 更新接口返回值类型为 IPage<SmsHoliday> 和 IPage<SmsLog>
2025-09-26 11:23:59 +08:00
李一帆 66c73425ae refactor(sms): 重构短信服务相关类名和包结构
- 将 Specialist 相关类重命名为 SmsSpecialist
- 将 SmsTask 相关类重命名为 SmsHoliday
- 更新所有引用和导入语句以匹配新的类名- 调整控制器路由路径前缀统一为 /sms- 修改数据库表名 SPECIALIST 为 SMS_SPECIALIST
- 修改数据库表名 SMS_TASK 为 SMS_HOLIDAY
2025-09-26 11:07:29 +08:00
李一帆 c32e27b174 feat(sms):优化生日短信发送逻辑并添加去重机制
- 修改查询条件,增加对专家生日短信已发送标记的过滤
- 实现发送前手机号去重逻辑,避免重复发送
- 添加发送成功后更新专家短信发送标记的逻辑
- 实现定时任务每天重置专家短信发送标记- 添加定时任务,每天凌晨重置短信发送状态
- 增加发送短信抄送功能,抄送指定人员
- 优化线程安全控制,防止重复执行短信发送任务
- 更新 Specialist 实体类,增加短信发送标记字段
- 修改 SpecialistMapper,添加重置短信发送标记的方法- 更新数据库表结构,添加短信发送标记字段
2025-09-26 10:55:25 +08:00
李一帆 adecc084dd test(birthday-sms): 更新生日短信发送测试用例
- 注释掉@SpringBootTest注解以避免完整应用启动
- 注释掉@Test注解以防止测试自动运行
- 修改测试专家姓名为"唐威"- 注释掉设置专家电话号码的代码
- 更新短信模板内容为完整的生日祝福语- 保留测试方法的基本结构和调用逻辑
2025-09-26 10:55:08 +08:00
李一帆 9de2852075 feat(specialist): 分页查询接口支持分页参数
- 引入 FindPageDto 用于分页参数传递
- 修改分页接口参数类型为 FindPageDto
- 调用 service 层时传入页码参数
-保持返回结果结构不变,仅调整分页逻辑
2025-09-26 09:37:35 +08:00
28 changed files with 422 additions and 419 deletions

1
.gitignore vendored
View File

@ -5,6 +5,7 @@ yarn-error.log*
pnpm-debug.log* pnpm-debug.log*
CLAUDE.md CLAUDE.md
.claude/ .claude/
aiwork/
/target/ /target/
!.mvn/wrapper/maven-wrapper.jar !.mvn/wrapper/maven-wrapper.jar

View File

@ -1,220 +0,0 @@
# 短信管理模块 API 文档
## 1. 生日短信管理 (SmsBirthdayController)
### 1.1 修改生日短信配置
- **接口地址**: `/smsBirthday/edit`
- **请求方法**: `POST`
- **参数**:
```json
{
"id": "Long",
"executionTmStr": "String",
"status": "Integer",
"template": "String",
"createTm": "Date"
}
```
### 1.2 查询生日短信配置
- **接口地址**: `/smsBirthday/get`
- **请求方法**: `GET`
- **参数**: 无
---
## 2. 短信日志管理 (SmsLogController)
### 2.1 分页查询短信日志
- **接口地址**: `/smsLog/page`
- **请求方法**: `POST`
- **参数**:
```json
{
"sendTm": "Date",
"name": "String",
"phone": "String",
"remark": "String",
"content": "String"
}
```
### 2.2 新增短信日志
- **接口地址**: `/smsLog/add`
- **请求方法**: `POST`
- **参数**:
```json
{
"sendTm": "Date",
"name": "String",
"phone": "String",
"remark": "String",
"content": "String"
}
```
### 2.3 修改短信日志
- **接口地址**: `/smsLog/edit`
- **请求方法**: `POST`
- **参数**:
```json
{
"id": "Long",
"sendTm": "Date",
"name": "String",
"phone": "String",
"remark": "String",
"content": "String"
}
```
### 2.4 删除短信日志
- **接口地址**: `/smsLog/del/{id}`
- **请求方法**: `GET`
- **参数**:
- `id` (路径参数): Long
### 2.5 根据ID查询短信日志
- **接口地址**: `/smsLog/get/{id}`
- **请求方法**: `GET`
- **参数**:
- `id` (路径参数): Long
---
## 3. 短信任务管理 (SmsTaskController)
### 3.1 分页查询短信任务
- **接口地址**: `/smsTask/page`
- **请求方法**: `POST`
- **参数**:
```json
{
"subjectName": "String",
"startDate": "Date",
"endDate": "Date",
"executionTmStr": "String",
"status": "Integer",
"template": "String",
"createTm": "Date"
}
```
### 3.2 新增短信任务
- **接口地址**: `/smsTask/add`
- **请求方法**: `POST`
- **参数**:
```json
{
"subjectName": "String",
"startDate": "Date",
"endDate": "Date",
"executionTmStr": "String",
"status": "Integer",
"template": "String",
"createTm": "Date"
}
```
### 3.3 修改短信任务
- **接口地址**: `/smsTask/edit`
- **请求方法**: `POST`
- **参数**:
```json
{
"id": "Long",
"subjectName": "String",
"startDate": "Date",
"endDate": "Date",
"executionTmStr": "String",
"status": "Integer",
"template": "String",
"createTm": "Date"
}
```
### 3.4 删除短信任务
- **接口地址**: `/smsTask/del/{id}`
- **请求方法**: `GET`
- **参数**:
- `id` (路径参数): Long
### 3.5 根据ID查询短信任务
- **接口地址**: `/smsTask/get/{id}`
- **请求方法**: `GET`
- **参数**:
- `id` (路径参数): Long
---
## 4. 专家通讯录管理 (SpecialistController)
### 4.1 分页查询专家通讯录
- **接口地址**: `/specialist/page`
- **请求方法**: `POST`
- **参数**:
```json
{
"name": "String",
"position": "String",
"birthday": "Date",
"title": "String",
"phone": "String",
"status": "Integer",
"createTm": "Date"
}
```
### 4.2 新增专家通讯录
- **接口地址**: `/specialist/add`
- **请求方法**: `POST`
- **参数**:
```json
{
"name": "String",
"position": "String",
"birthday": "Date",
"title": "String",
"phone": "String",
"status": "Integer",
"createTm": "Date"
}
```
### 4.3 修改专家通讯录
- **接口地址**: `/specialist/edit`
- **请求方法**: `POST`
- **参数**:
```json
{
"id": "Long",
"name": "String",
"position": "String",
"birthday": "Date",
"title": "String",
"phone": "String",
"status": "Integer",
"createTm": "Date"
}
```
### 4.4 删除专家通讯录
- **接口地址**: `/specialist/del/{id}`
- **请求方法**: `GET`
- **参数**:
- `id` (路径参数): Long
### 4.5 根据ID查询专家通讯录
- **接口地址**: `/specialist/get/{id}`
- **请求方法**: `GET`
- **参数**:
- `id` (路径参数): Long
---
## 说明
- 所有接口返回格式统一为 `ResultJson<T>`
- 参数验证使用 `@Validated` 注解,新增时使用 `Insert.class`,修改时使用 `Update.class`
- 生日短信管理模块特殊处理,只维护单条记录 (ID=1)
- 文档生成时间: 2025-09-23

View File

@ -1,7 +1,7 @@
-- 姓名,职务,生日,称呼,电话号码,生效状态,创建日期 -- 姓名,职务,生日,称呼,电话号码,生效状态,创建日期
-- 达梦数据库SPECIALIST表建表语句 -- 达梦数据库SPECIALIST表建表语句
CREATE TABLE SPECIALIST ( CREATE TABLE SMS_SPECIALIST (
ID BIGINT IDENTITY(1,1) PRIMARY KEY, ID BIGINT IDENTITY(1,1) PRIMARY KEY,
NAME VARCHAR(50) NOT NULL COMMENT '姓名', NAME VARCHAR(50) NOT NULL COMMENT '姓名',
POSITION VARCHAR(100) COMMENT '职务', POSITION VARCHAR(100) COMMENT '职务',
@ -9,7 +9,9 @@ CREATE TABLE SPECIALIST (
ADDRESS VARCHAR(20) COMMENT '区域', ADDRESS VARCHAR(20) COMMENT '区域',
PHONE VARCHAR(20) UNIQUE COMMENT '电话号码', PHONE VARCHAR(20) UNIQUE COMMENT '电话号码',
STATUS INT DEFAULT 1 COMMENT '生效状态 1:有效 0:无效', STATUS INT DEFAULT 1 COMMENT '生效状态 1:有效 0:无效',
CREATE_TM DATETIME DEFAULT CURRENT_TIME COMMENT '创建日期' CREATE_TM DATETIME DEFAULT SYSDATE COMMENT '创建日期',
FLAG_BIRTHDAY_SENT_TODAY INT DEFAULT 0,
FLAG_HOLIDAY_SENT_TODAY INT DEFAULT 0
); );
@ -17,7 +19,7 @@ CREATE TABLE SPECIALIST (
--20252025-10-012025-10-0308:00:002025-09-15 --20252025-10-012025-10-0308:00:002025-09-15
--20262026-02-172026-02-1708:00:002025-09-15 --20262026-02-172026-02-1708:00:002025-09-15
-- 达梦数据库TASK表建表语句 -- 达梦数据库TASK表建表语句
CREATE TABLE SMS_TASK ( CREATE TABLE SMS_HOLIDAY (
ID BIGINT IDENTITY(1,1) PRIMARY KEY, ID BIGINT IDENTITY(1,1) PRIMARY KEY,
SUBJECT_NAME VARCHAR(200) NOT NULL COMMENT '主题名称', SUBJECT_NAME VARCHAR(200) NOT NULL COMMENT '主题名称',
START_DATE DATE COMMENT '任务开始日期', START_DATE DATE COMMENT '任务开始日期',
@ -25,7 +27,7 @@ CREATE TABLE SMS_TASK (
EXECUTION_TM_STR VARCHAR(8) COMMENT '任务执行时间', EXECUTION_TM_STR VARCHAR(8) COMMENT '任务执行时间',
STATUS INT DEFAULT 1 COMMENT '状态 1:有效 0:无效', STATUS INT DEFAULT 1 COMMENT '状态 1:有效 0:无效',
TEMPLATE VARCHAR(500), TEMPLATE VARCHAR(500),
CREATE_TM DATETIME DEFAULT CURRENT_TIME COMMENT '创建日期' CREATE_TM DATETIME DEFAULT SYSDATE COMMENT '创建日期'
); );

View File

@ -21,7 +21,7 @@ import java.util.List;
*/ */
@Api(tags = "生日短信 - Controller") @Api(tags = "生日短信 - Controller")
@RestController @RestController
@RequestMapping("/smsBirthday") @RequestMapping("/sms/birthday")
public class SmsBirthdayController { public class SmsBirthdayController {
@Autowired @Autowired

View File

@ -1,9 +1,11 @@
package com.whdc.controller; package com.whdc.controller;
import com.whdc.model.entity.SmsTask; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.whdc.model.dto.SmsHolidayPageDto;
import com.whdc.model.entity.SmsHoliday;
import com.whdc.model.group.Insert; import com.whdc.model.group.Insert;
import com.whdc.model.group.Update; import com.whdc.model.group.Update;
import com.whdc.service.ISmsTaskService; import com.whdc.service.ISmsHolidayService;
import com.whdc.utils.ResultJson; import com.whdc.utils.ResultJson;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
@ -21,27 +23,27 @@ import java.util.List;
*/ */
@Api(tags = "短信任务 - Controller") @Api(tags = "短信任务 - Controller")
@RestController @RestController
@RequestMapping("/smsTask") @RequestMapping("/sms/holiday")
public class SmsTaskController { public class SmsHolidayController {
@Autowired @Autowired
private ISmsTaskService smsTaskService; private ISmsHolidayService smsTaskService;
@ApiOperation(value = "分页查询") @ApiOperation(value = "分页查询")
@PostMapping(value = "page") @PostMapping(value = "page")
public ResultJson<List<SmsTask>> page(@RequestBody SmsTask dto) { public ResultJson<IPage<SmsHoliday>> page(@RequestBody SmsHolidayPageDto dto) {
return ResultJson.ok(smsTaskService.list()); return ResultJson.ok(smsTaskService.page(dto));
} }
@ApiOperation(value = "新增") @ApiOperation(value = "新增")
@PostMapping(value = "add") @PostMapping(value = "add")
public ResultJson<Boolean> add(@RequestBody @Validated(Insert.class) SmsTask model) { public ResultJson<Boolean> add(@RequestBody @Validated(Insert.class) SmsHoliday model) {
return ResultJson.ok(smsTaskService.save(model)); return ResultJson.ok(smsTaskService.save(model));
} }
@ApiOperation(value = "修改") @ApiOperation(value = "修改")
@PostMapping(value = "edit") @PostMapping(value = "edit")
public ResultJson<Boolean> edit(@RequestBody @Validated(Update.class) SmsTask model) { public ResultJson<Boolean> edit(@RequestBody @Validated(Update.class) SmsHoliday model) {
return ResultJson.ok(smsTaskService.updateById(model)); return ResultJson.ok(smsTaskService.updateById(model));
} }
@ -53,7 +55,7 @@ public class SmsTaskController {
@ApiOperation(value = "根据ID查询") @ApiOperation(value = "根据ID查询")
@GetMapping(value = "get/{id}") @GetMapping(value = "get/{id}")
public ResultJson<SmsTask> getById(@PathVariable("id") Long id) { public ResultJson<SmsHoliday> getById(@PathVariable("id") Long id) {
return ResultJson.ok(smsTaskService.getById(id)); return ResultJson.ok(smsTaskService.getById(id));
} }
} }

View File

@ -1,5 +1,7 @@
package com.whdc.controller; package com.whdc.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.whdc.model.dto.SmsLogPageDto;
import com.whdc.model.entity.SmsLog; import com.whdc.model.entity.SmsLog;
import com.whdc.model.group.Insert; import com.whdc.model.group.Insert;
import com.whdc.model.group.Update; import com.whdc.model.group.Update;
@ -21,7 +23,7 @@ import java.util.List;
*/ */
@Api(tags = "短信日志 - Controller") @Api(tags = "短信日志 - Controller")
@RestController @RestController
@RequestMapping("/smsLog") @RequestMapping("/sms/log")
public class SmsLogController { public class SmsLogController {
@Autowired @Autowired
@ -29,8 +31,8 @@ public class SmsLogController {
@ApiOperation(value = "分页查询") @ApiOperation(value = "分页查询")
@PostMapping(value = "page") @PostMapping(value = "page")
public ResultJson<List<SmsLog>> page(@RequestBody SmsLog dto) { public ResultJson<IPage<SmsLog>> page(@RequestBody SmsLogPageDto dto) {
return ResultJson.ok(smsLogService.list()); return ResultJson.ok(smsLogService.page(dto));
} }
@ApiOperation(value = "新增") @ApiOperation(value = "新增")

View File

@ -1,9 +1,11 @@
package com.whdc.controller; package com.whdc.controller;
import com.whdc.model.entity.Specialist; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.whdc.model.dto.SmsSpecialistPageDto;
import com.whdc.model.entity.SmsSpecialist;
import com.whdc.model.group.Insert; import com.whdc.model.group.Insert;
import com.whdc.model.group.Update; import com.whdc.model.group.Update;
import com.whdc.service.ISpecialistService; import com.whdc.service.ISmsSpecialistService;
import com.whdc.utils.ResultJson; import com.whdc.utils.ResultJson;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
@ -11,8 +13,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.List;
/** /**
* *
* *
@ -21,28 +21,28 @@ import java.util.List;
*/ */
@Api(tags = "专家通讯录 - Controller") @Api(tags = "专家通讯录 - Controller")
@RestController @RestController
@RequestMapping("/specialist") @RequestMapping("/sms/specialist")
public class SpecialistController { public class SmsSpecialistController {
@Autowired @Autowired
private ISpecialistService specialistService; private ISmsSpecialistService specialistService;
@ApiOperation(value = "分页查询") @ApiOperation(value = "分页查询")
@PostMapping(value = "page") @PostMapping(value = "page")
public ResultJson<List<Specialist>> page(@RequestBody Specialist dto) { public ResultJson<IPage<SmsSpecialist>> page(@RequestBody SmsSpecialistPageDto dto) {
return ResultJson.ok(specialistService.list()); return ResultJson.ok(specialistService.page(dto));
} }
@ApiOperation(value = "新增") @ApiOperation(value = "新增")
@PostMapping(value = "add") @PostMapping(value = "add")
public ResultJson<Boolean> add(@RequestBody @Validated(Insert.class) Specialist model) { public ResultJson<Boolean> add(@RequestBody @Validated(Insert.class) SmsSpecialist model) {
return ResultJson.ok(specialistService.save(model)); return ResultJson.ok(specialistService.save(model));
} }
@ApiOperation(value = "修改") @ApiOperation(value = "修改")
@PostMapping(value = "edit") @PostMapping(value = "edit")
public ResultJson<Boolean> edit(@RequestBody @Validated(Update.class) Specialist model) { public ResultJson<Boolean> edit(@RequestBody @Validated(Update.class) SmsSpecialist model) {
return ResultJson.ok(specialistService.updateById(model)); return ResultJson.ok(specialistService.updateById(model));
} }
@ -54,7 +54,7 @@ public class SpecialistController {
@ApiOperation(value = "根据ID查询") @ApiOperation(value = "根据ID查询")
@GetMapping(value = "get/{id}") @GetMapping(value = "get/{id}")
public ResultJson<Specialist> getById(@PathVariable("id") Long id) { public ResultJson<SmsSpecialist> getById(@PathVariable("id") Long id) {
return ResultJson.ok(specialistService.getById(id)); return ResultJson.ok(specialistService.getById(id));
} }
} }

View File

@ -1,7 +1,7 @@
package com.whdc.mapper; package com.whdc.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.whdc.model.entity.SmsTask; import com.whdc.model.entity.SmsHoliday;
/** /**
* Mapper * Mapper
@ -9,5 +9,5 @@ import com.whdc.model.entity.SmsTask;
* @author lyf * @author lyf
* @since 2025-09-23 * @since 2025-09-23
*/ */
public interface SmsTaskMapper extends BaseMapper<SmsTask> { public interface SmsHolidayMapper extends BaseMapper<SmsHoliday> {
} }

View File

@ -0,0 +1,16 @@
package com.whdc.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.whdc.model.entity.SmsSpecialist;
import org.apache.ibatis.annotations.Update;
/**
* Mapper
*
* @author lyf
* @since 2025-09-23
*/
public interface SmsSpecialistMapper extends BaseMapper<SmsSpecialist> {
@Update("update specialist set flag_birthday_sent_today = 0")
void resetFlagBirthdaySentToday();
}

View File

@ -1,13 +0,0 @@
package com.whdc.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.whdc.model.entity.Specialist;
/**
* Mapper
*
* @author lyf
* @since 2025-09-23
*/
public interface SpecialistMapper extends BaseMapper<Specialist> {
}

View File

@ -0,0 +1,28 @@
package com.whdc.model.dto;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* DTO
*
* @author lyf
* @since 2025-09-26
*/
@Data
public class SmsHolidayPageDto {
@ApiModelProperty(value = "当前页", example = "1")
private Integer pageNumber = 1;
@ApiModelProperty(value = "每页条数", example = "10")
private Integer pageSize = 10;
@ApiModelProperty(value = "主题名称")
private String subjectName;
public Page getPage() {
return new Page(pageNumber, pageSize);
}
}

View File

@ -0,0 +1,31 @@
package com.whdc.model.dto;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* DTO
*
* @author lyf
* @since 2025-09-26
*/
@Data
public class SmsLogPageDto {
@ApiModelProperty(value = "当前页", example = "1")
private Integer pageNumber = 1;
@ApiModelProperty(value = "每页条数", example = "10")
private Integer pageSize = 10;
@ApiModelProperty(value = "姓名")
private String name;
@ApiModelProperty(value = "电话号码")
private String phone;
public Page getPage() {
return new Page(pageNumber, pageSize);
}
}

View File

@ -0,0 +1,34 @@
package com.whdc.model.dto;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* DTO
*
* @author lyf
* @since 2025-09-26
*/
@Data
public class SmsSpecialistPageDto {
@ApiModelProperty(value = "当前页", example = "1")
private Integer pageNumber = 1;
@ApiModelProperty(value = "每页条数", example = "10")
private Integer pageSize = 10;
@ApiModelProperty(value = "姓名")
private String name;
@ApiModelProperty(value = "电话号码")
private String phone;
@ApiModelProperty(value = "状态 1:有效 0:无效")
private Integer status;
public Page getPage() {
return new Page(pageNumber, pageSize);
}
}

View File

@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@ -23,8 +24,8 @@ import java.util.Date;
@EqualsAndHashCode @EqualsAndHashCode
@Accessors(chain = true) @Accessors(chain = true)
@ApiModel(description = "节日短信") @ApiModel(description = "节日短信")
@TableName("SMS_TASK") @TableName("SMS_HOLIDAY")
public class SmsTask implements Serializable { public class SmsHoliday implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
@ -46,6 +47,7 @@ public class SmsTask implements Serializable {
*/ */
@TableField("START_DATE") @TableField("START_DATE")
@ApiModelProperty(value = "任务开始日期") @ApiModelProperty(value = "任务开始日期")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date startDate; private Date startDate;
/** /**
@ -53,6 +55,7 @@ public class SmsTask implements Serializable {
*/ */
@TableField("END_DATE") @TableField("END_DATE")
@ApiModelProperty(value = "任务结束日期") @ApiModelProperty(value = "任务结束日期")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date endDate; private Date endDate;
/** /**
@ -81,5 +84,6 @@ public class SmsTask implements Serializable {
*/ */
@TableField("CREATE_TM") @TableField("CREATE_TM")
@ApiModelProperty(value = "创建日期") @ApiModelProperty(value = "创建日期")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date createTm; private Date createTm;
} }

View File

@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@ -40,6 +41,7 @@ public class SmsLog implements Serializable {
*/ */
@TableField("SEND_TM") @TableField("SEND_TM")
@ApiModelProperty(value = "发送时间") @ApiModelProperty(value = "发送时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date sendTm; private Date sendTm;
/** /**

View File

@ -22,8 +22,8 @@ import java.util.Date;
@EqualsAndHashCode @EqualsAndHashCode
@Accessors(chain = true) @Accessors(chain = true)
@ApiModel(description = "专家") @ApiModel(description = "专家")
@TableName("SPECIALIST") @TableName("SMS_SPECIALIST")
public class Specialist implements Serializable { public class SmsSpecialist implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
@ -83,4 +83,12 @@ public class Specialist implements Serializable {
@ApiModelProperty(value = "创建日期") @ApiModelProperty(value = "创建日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTm; private Date createTm;
//标记今天已经发送过生日短信的flag
@TableField("FLAG_BIRTHDAY_SENT_TODAY")
private Integer flagBirthdaySentToday;
//标记今天已经发送过节日短信的flag
@TableField("FLAG_HOLIDAY_SENT_TODAY")
private Integer flagHolidaySentToday;
} }

View File

@ -2,7 +2,7 @@ package com.whdc.service;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import com.whdc.model.entity.SmsBirthday; import com.whdc.model.entity.SmsBirthday;
import com.whdc.model.entity.Specialist; import com.whdc.model.entity.SmsSpecialist;
import java.util.List; import java.util.List;
@ -16,10 +16,10 @@ public interface ISmsBirthdayService extends IService<SmsBirthday> {
/** /**
* *
*/ */
List<Specialist> listBirthdayToday(); List<SmsSpecialist> listBirthdayToday();
/** /**
* *
*/ */
void sendBirthdaySms(List<Specialist> specialists); void sendBirthdaySms(List<SmsSpecialist> specialists);
} }

View File

@ -0,0 +1,38 @@
package com.whdc.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.whdc.model.dto.SmsHolidayPageDto;
import com.whdc.model.entity.SmsHoliday;
import java.util.List;
/**
*
*
* @author lyf
* @since 2025-09-23
*/
public interface ISmsHolidayService extends IService<SmsHoliday> {
Page<SmsHoliday> page(SmsHolidayPageDto dto);
/**
*
*/
List<SmsHoliday> listActiveTasks();
/**
*
*/
List<SmsHoliday> listTodayTasks();
/**
*
*/
void sendThemeSms(SmsHoliday smsTask);
/**
*
*/
void sendBatchThemeSms(List<SmsHoliday> smsTasks);
}

View File

@ -1,6 +1,8 @@
package com.whdc.service; package com.whdc.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import com.whdc.model.dto.SmsLogPageDto;
import com.whdc.model.entity.SmsLog; import com.whdc.model.entity.SmsLog;
/** /**
@ -10,4 +12,6 @@ import com.whdc.model.entity.SmsLog;
* @since 2025-09-23 * @since 2025-09-23
*/ */
public interface ISmsLogService extends IService<SmsLog> { public interface ISmsLogService extends IService<SmsLog> {
Page<SmsLog> page(SmsLogPageDto dto);
} }

View File

@ -0,0 +1,17 @@
package com.whdc.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.whdc.model.dto.SmsSpecialistPageDto;
import com.whdc.model.entity.SmsSpecialist;
/**
*
*
* @author lyf
* @since 2025-09-23
*/
public interface ISmsSpecialistService extends IService<SmsSpecialist> {
Page<SmsSpecialist> page(SmsSpecialistPageDto dto);
}

View File

@ -1,35 +0,0 @@
package com.whdc.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.whdc.model.entity.SmsTask;
import com.whdc.model.entity.Specialist;
import java.util.List;
/**
*
*
* @author lyf
* @since 2025-09-23
*/
public interface ISmsTaskService extends IService<SmsTask> {
/**
*
*/
List<SmsTask> listActiveTasks();
/**
*
*/
List<SmsTask> listTodayTasks();
/**
*
*/
void sendThemeSms(SmsTask smsTask);
/**
*
*/
void sendBatchThemeSms(List<SmsTask> smsTasks);
}

View File

@ -1,13 +0,0 @@
package com.whdc.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.whdc.model.entity.Specialist;
/**
*
*
* @author lyf
* @since 2025-09-23
*/
public interface ISpecialistService extends IService<Specialist> {
}

View File

@ -4,10 +4,10 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.whdc.mapper.SmsBirthdayMapper; import com.whdc.mapper.SmsBirthdayMapper;
import com.whdc.mapper.SmsLogMapper; import com.whdc.mapper.SmsLogMapper;
import com.whdc.mapper.SpecialistMapper; import com.whdc.mapper.SmsSpecialistMapper;
import com.whdc.model.entity.SmsBirthday; import com.whdc.model.entity.SmsBirthday;
import com.whdc.model.entity.SmsLog; import com.whdc.model.entity.SmsLog;
import com.whdc.model.entity.Specialist; import com.whdc.model.entity.SmsSpecialist;
import com.whdc.service.ISmsBirthdayService; import com.whdc.service.ISmsBirthdayService;
import com.whdc.utils.SmsHelper; import com.whdc.utils.SmsHelper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -18,8 +18,7 @@ import org.springframework.stereotype.Service;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Collections; import java.util.*;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
/** /**
@ -33,7 +32,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBirthday> implements ISmsBirthdayService { public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBirthday> implements ISmsBirthdayService {
@Autowired @Autowired
private SpecialistMapper specialistMapper; private SmsSpecialistMapper specialistMapper;
@Autowired @Autowired
private SmsLogMapper smsLogMapper; private SmsLogMapper smsLogMapper;
@ -42,22 +41,23 @@ public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBi
private SmsHelper smsHelper; private SmsHelper smsHelper;
// 记录今日是否已发送生日短信 - 使用原子变量保证线程安全 // 记录今日是否已发送生日短信 - 使用原子变量保证线程安全
private final AtomicBoolean birthdaySmsSentToday = new AtomicBoolean(false); private final AtomicBoolean sending = new AtomicBoolean(false);
@Override @Override
public List<Specialist> listBirthdayToday() { public List<SmsSpecialist> listBirthdayToday() {
try { try {
// 获取当前日期的月和日 // 获取当前日期的月和日
LocalDate today = LocalDate.now(); LocalDate today = LocalDate.now();
String monthDay = today.format(DateTimeFormatter.ofPattern("MM-dd")); String monthDay = today.format(DateTimeFormatter.ofPattern("MM-dd"));
// 查询今天过生日的专家(状态为有效的) // 查询今天过生日的专家(状态为有效的)
LambdaQueryWrapper<Specialist> queryWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<SmsSpecialist> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper queryWrapper
.apply("DATE_FORMAT(BIRTHDAY, '%m-%d') = {0}", monthDay) .apply("DATE_FORMAT(BIRTHDAY, '%m-%d') = {0}", monthDay)
.eq(Specialist::getStatus, 1); // 1:有效 .eq(SmsSpecialist::getFlagBirthdaySentToday, 0)
.eq(SmsSpecialist::getStatus, 1); // 1:有效
List<Specialist> specialists = specialistMapper.selectList(queryWrapper); List<SmsSpecialist> specialists = specialistMapper.selectList(queryWrapper);
return specialists != null ? specialists : Collections.emptyList(); return specialists != null ? specialists : Collections.emptyList();
} catch (Exception e) { } catch (Exception e) {
// 记录异常并返回空列表 // 记录异常并返回空列表
@ -66,7 +66,7 @@ public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBi
} }
@Override @Override
public void sendBirthdaySms(List<Specialist> specialists) { public void sendBirthdaySms(List<SmsSpecialist> specialists) {
if (specialists == null || specialists.isEmpty()) { if (specialists == null || specialists.isEmpty()) {
log.info("没有需要发送生日短信的专家"); log.info("没有需要发送生日短信的专家");
return; return;
@ -89,8 +89,14 @@ public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBi
int totalSpecialists = specialists.size(); int totalSpecialists = specialists.size();
log.info("开始向{}位专家发送生日短信", totalSpecialists); log.info("开始向{}位专家发送生日短信", totalSpecialists);
//去重
Set<String> distinct = new HashSet<>();
for (int i = 0; i < specialists.size(); i++) { for (int i = 0; i < specialists.size(); i++) {
Specialist specialist = specialists.get(i); SmsSpecialist specialist = specialists.get(i);
if (distinct.contains(specialist.getPhone())) {
continue;
}
String content = template.replace("{姓名}", specialist.getName()); String content = template.replace("{姓名}", specialist.getName());
try { try {
log.info("正在发送第{}/{}位专家{}的生日短信", i + 1, totalSpecialists, specialist.getName()); log.info("正在发送第{}/{}位专家{}的生日短信", i + 1, totalSpecialists, specialist.getName());
@ -98,14 +104,18 @@ public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBi
// 创建短信日志记录 // 创建短信日志记录
SmsLog smsLog = new SmsLog(); SmsLog smsLog = new SmsLog();
smsLog.setName(specialist.getName()) smsLog.setName(specialist.getName())
.setPhone(specialist.getPhone()) .setPhone(specialist.getPhone())
.setContent(content) .setContent(content)
.setRemark("生日短信") .setRemark("生日短信")
.setSendTm(new java.util.Date()); .setSendTm(new java.util.Date());
// 使用SmsHelper发送个性化短信 // 使用SmsHelper发送个性化短信
List<String> phoneList = Collections.singletonList(specialist.getPhone()); List<String> phoneList = Collections.singletonList(specialist.getPhone());
String sendResult = smsHelper.send(phoneList, content); String sendResult = smsHelper.send(phoneList, content);
// 更新专家的flagBirthdaySentToday
specialist.setFlagBirthdaySentToday(1);
specialistMapper.updateById(specialist);
distinct.add(specialist.getPhone());
log.info("向专家{}发送生日短信结果: {}", specialist.getName(), sendResult); log.info("向专家{}发送生日短信结果: {}", specialist.getName(), sendResult);
// 根据发送结果设置备注 // 根据发送结果设置备注
@ -114,7 +124,8 @@ public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBi
} else { } else {
smsLog.setRemark("生日短信-发送失败: " + sendResult); smsLog.setRemark("生日短信-发送失败: " + sendResult);
} }
//应唐科要求,每次发送短讯要抄送给唐威,陈锋
carbonCopy(content);
// 保存短信日志记录 // 保存短信日志记录
smsLogMapper.insert(smsLog); smsLogMapper.insert(smsLog);
@ -137,10 +148,10 @@ public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBi
try { try {
SmsLog failedSmsLog = new SmsLog(); SmsLog failedSmsLog = new SmsLog();
failedSmsLog.setName(specialist.getName()) failedSmsLog.setName(specialist.getName())
.setPhone(specialist.getPhone()) .setPhone(specialist.getPhone())
.setContent(content) .setContent(content)
.setRemark("生日短信-发送异常: " + e.getMessage()) .setRemark("生日短信-发送异常: " + e.getMessage())
.setSendTm(new java.util.Date()); .setSendTm(new java.util.Date());
smsLogMapper.insert(failedSmsLog); smsLogMapper.insert(failedSmsLog);
} catch (Exception logException) { } catch (Exception logException) {
log.error("保存发送失败的短信日志时发生异常: {}", logException.getMessage(), logException); log.error("保存发送失败的短信日志时发生异常: {}", logException.getMessage(), logException);
@ -151,6 +162,24 @@ public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBi
log.info("生日短信批量发送任务完成"); log.info("生日短信批量发送任务完成");
} }
/**
*
*/
private void carbonCopy(String content) {
List<String> phoneList = new ArrayList<>();
phoneList.add("18154318312"); //唐威
phoneList.add("13247155309"); //陈锋
phoneList.add("15671545233"); //李
smsHelper.send(phoneList, content);
}
/**
* 0flagBirthdaySentToday0
*/
@Scheduled(cron = "0 0 0 * * ?")
public void resetFlagBirthdaySentToday() {
specialistMapper.resetFlagBirthdaySentToday();
}
/** /**
* *
* *
@ -158,12 +187,6 @@ public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBi
@Scheduled(cron = "0 * * * * ?") @Scheduled(cron = "0 * * * * ?")
public void checkAndSendBirthdaySms() { public void checkAndSendBirthdaySms() {
try { try {
// 如果已经发送过,不再重复发送
if (birthdaySmsSentToday.get()) {
log.debug("今日已经发送过生日短信,跳过执行");
return;
}
// 获取生日短信配置 // 获取生日短信配置
SmsBirthday smsBirthday = this.getById(1L); SmsBirthday smsBirthday = this.getById(1L);
if (smsBirthday == null || smsBirthday.getStatus() != 1) { if (smsBirthday == null || smsBirthday.getStatus() != 1) {
@ -178,6 +201,12 @@ public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBi
return; return;
} }
// 如果已经发送过,不再重复发送
if (sending.get()) {
log.debug("其他线程正在执行生日短信发送任务");
return;
}
// 获取当前时间 // 获取当前时间
LocalDateTime now = LocalDateTime.now(); LocalDateTime now = LocalDateTime.now();
@ -187,26 +216,30 @@ public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBi
// 解析为完整的LocalDateTime // 解析为完整的LocalDateTime
LocalDateTime scheduledTime = LocalDateTime.parse( LocalDateTime scheduledTime = LocalDateTime.parse(
todayWithExecutionTime, todayWithExecutionTime,
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
); );
// 检查当前时间是否大于等于执行时间 // 检查当前时间是否大于等于执行时间
if (now.isEqual(scheduledTime) || now.isAfter(scheduledTime)) { if (now.isEqual(scheduledTime) || now.isAfter(scheduledTime)) {
// 原子性检查和设置发送状态 // 原子性检查和设置发送状态
if (birthdaySmsSentToday.compareAndSet(false, true)) { if (sending.compareAndSet(false, true)) {
log.info("到达生日短信发送时间: {}, 开始执行发送任务", executionTime); log.info("到达生日短信发送时间: {}, 开始执行发送任务", executionTime);
// 查询今天过生日的专家 // 查询今天过生日的专家
List<Specialist> birthdaySpecialists = listBirthdayToday(); List<SmsSpecialist> birthdaySpecialists = listBirthdayToday();
if (birthdaySpecialists.isEmpty()) { if (birthdaySpecialists.isEmpty()) {
log.info("今天没有专家过生日,无需发送生日短信"); log.info("今天没有专家过生日,无需发送生日短信");
} else { } else {
log.info("今天有{}位专家过生日,开始发送生日短信", birthdaySpecialists.size()); log.info("今天有{}位专家过生日,开始发送生日短信", birthdaySpecialists.size());
// 发送生日短信 try {
sendBirthdaySms(birthdaySpecialists); // 发送生日短信
sendBirthdaySms(birthdaySpecialists);
} finally {
sending.set(false);
}
log.info("生日短信发送任务完成"); log.info("生日短信发送任务完成");
} }
} else { } else {
@ -229,13 +262,14 @@ public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBi
* *
* 00:00:01 * 00:00:01
*/ */
@Scheduled(cron = "1 0 0 * * ?") //todo delete this
public void resetDailySendStatus() { // @Scheduled(cron = "1 0 0 * * ?")
try { // public void resetDailySendStatus() {
birthdaySmsSentToday.set(false); // try {
log.info("生日短信每日发送状态已重置"); // sending.set(false);
} catch (Exception e) { // log.info("生日短信每日发送状态已重置");
log.error("重置生日短信每日发送状态异常: {}", e.getMessage(), e); // } catch (Exception e) {
} // log.error("重置生日短信每日发送状态异常: {}", e.getMessage(), e);
} // }
// }
} }

View File

@ -1,19 +1,22 @@
package com.whdc.service.impl; package com.whdc.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.whdc.mapper.SmsLogMapper; import com.whdc.mapper.SmsLogMapper;
import com.whdc.mapper.SmsTaskMapper; import com.whdc.mapper.SmsHolidayMapper;
import com.whdc.mapper.SpecialistMapper; import com.whdc.mapper.SmsSpecialistMapper;
import com.whdc.model.dto.SmsHolidayPageDto;
import com.whdc.model.entity.SmsLog; import com.whdc.model.entity.SmsLog;
import com.whdc.model.entity.SmsTask; import com.whdc.model.entity.SmsHoliday;
import com.whdc.model.entity.Specialist; import com.whdc.model.entity.SmsSpecialist;
import com.whdc.service.ISmsTaskService; import com.whdc.service.ISmsHolidayService;
import com.whdc.utils.SmsHelper; import com.whdc.utils.SmsHelper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@ -32,10 +35,24 @@ import java.util.concurrent.atomic.AtomicBoolean;
*/ */
@Service @Service
@Slf4j @Slf4j
public class SmsTaskServiceImpl extends ServiceImpl<SmsTaskMapper, SmsTask> implements ISmsTaskService { public class SmsHolidayServiceImpl extends ServiceImpl<SmsHolidayMapper, SmsHoliday> implements ISmsHolidayService {
public Page<SmsHoliday> page(SmsHolidayPageDto dto) {
LambdaQueryWrapper<SmsHoliday> queryWrapper = new LambdaQueryWrapper<>();
// 添加查询条件
if (StringUtils.hasText(dto.getSubjectName())) {
queryWrapper.like(SmsHoliday::getSubjectName, dto.getSubjectName());
}
// 默认按创建时间降序排列
queryWrapper.orderByDesc(SmsHoliday::getCreateTm);
return page(dto.getPage(), queryWrapper);
}
@Autowired @Autowired
private SpecialistMapper specialistMapper; private SmsSpecialistMapper specialistMapper;
@Autowired @Autowired
private SmsLogMapper smsLogMapper; private SmsLogMapper smsLogMapper;
@ -47,18 +64,18 @@ public class SmsTaskServiceImpl extends ServiceImpl<SmsTaskMapper, SmsTask> impl
private final Map<Long, AtomicBoolean> taskSmsSentToday = new ConcurrentHashMap<>(); private final Map<Long, AtomicBoolean> taskSmsSentToday = new ConcurrentHashMap<>();
@Override @Override
public List<SmsTask> listActiveTasks() { public List<SmsHoliday> listActiveTasks() {
try { try {
LocalDate today = LocalDate.now(); LocalDate today = LocalDate.now();
// 查询当前活动中的主题日短信任务 // 查询当前活动中的主题日短信任务
LambdaQueryWrapper<SmsTask> queryWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<SmsHoliday> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper queryWrapper
.eq(SmsTask::getStatus, 1) // 1:有效 .eq(SmsHoliday::getStatus, 1) // 1:有效
.le(SmsTask::getStartDate, today) // 开始日期小于等于今天 .le(SmsHoliday::getStartDate, today) // 开始日期小于等于今天
.ge(SmsTask::getEndDate, today); // 结束日期大于等于今天 .ge(SmsHoliday::getEndDate, today); // 结束日期大于等于今天
List<SmsTask> tasks = this.list(queryWrapper); List<SmsHoliday> tasks = this.list(queryWrapper);
return tasks != null ? tasks : Collections.emptyList(); return tasks != null ? tasks : Collections.emptyList();
} catch (Exception e) { } catch (Exception e) {
log.error("查询活动中的主题日短信任务失败: {}", e.getMessage(), e); log.error("查询活动中的主题日短信任务失败: {}", e.getMessage(), e);
@ -67,19 +84,19 @@ public class SmsTaskServiceImpl extends ServiceImpl<SmsTaskMapper, SmsTask> impl
} }
@Override @Override
public List<SmsTask> listTodayTasks() { public List<SmsHoliday> listTodayTasks() {
try { try {
// 获取当前活动中的任务 // 获取当前活动中的任务
List<SmsTask> activeTasks = listActiveTasks(); List<SmsHoliday> activeTasks = listActiveTasks();
if (activeTasks.isEmpty()) { if (activeTasks.isEmpty()) {
return Collections.emptyList(); return Collections.emptyList();
} }
LocalDate today = LocalDate.now(); LocalDate today = LocalDate.now();
List<SmsTask> todayTasks = new java.util.ArrayList<>(); List<SmsHoliday> todayTasks = new java.util.ArrayList<>();
// 筛选今天需要执行的任务 // 筛选今天需要执行的任务
for (SmsTask task : activeTasks) { for (SmsHoliday task : activeTasks) {
// 检查是否已经发送过 // 检查是否已经发送过
AtomicBoolean sentFlag = taskSmsSentToday.computeIfAbsent(task.getId(), k -> new AtomicBoolean(false)); AtomicBoolean sentFlag = taskSmsSentToday.computeIfAbsent(task.getId(), k -> new AtomicBoolean(false));
if (sentFlag.get()) { if (sentFlag.get()) {
@ -118,14 +135,14 @@ public class SmsTaskServiceImpl extends ServiceImpl<SmsTaskMapper, SmsTask> impl
} }
@Override @Override
public void sendThemeSms(SmsTask smsTask) { public void sendThemeSms(SmsHoliday smsTask) {
if (smsTask == null) { if (smsTask == null) {
log.warn("主题日短信任务为空"); log.warn("主题日短信任务为空");
return; return;
} }
// 获取所有有效专家 // 获取所有有效专家
List<Specialist> specialists = getValidSpecialists(); List<SmsSpecialist> specialists = getValidSpecialists();
if (specialists.isEmpty()) { if (specialists.isEmpty()) {
log.info("没有有效专家,跳过主题日短信发送"); log.info("没有有效专家,跳过主题日短信发送");
return; return;
@ -142,7 +159,7 @@ public class SmsTaskServiceImpl extends ServiceImpl<SmsTaskMapper, SmsTask> impl
log.info("开始向{}位专家发送主题日短信[{}]", totalSpecialists, smsTask.getSubjectName()); log.info("开始向{}位专家发送主题日短信[{}]", totalSpecialists, smsTask.getSubjectName());
for (int i = 0; i < specialists.size(); i++) { for (int i = 0; i < specialists.size(); i++) {
Specialist specialist = specialists.get(i); SmsSpecialist specialist = specialists.get(i);
// 替换模板中的占位符 // 替换模板中的占位符
String content = template.replace("{姓名}", specialist.getName()) String content = template.replace("{姓名}", specialist.getName())
.replace("{主题}", smsTask.getSubjectName()); .replace("{主题}", smsTask.getSubjectName());
@ -211,14 +228,14 @@ public class SmsTaskServiceImpl extends ServiceImpl<SmsTaskMapper, SmsTask> impl
} }
@Override @Override
public void sendBatchThemeSms(List<SmsTask> smsTasks) { public void sendBatchThemeSms(List<SmsHoliday> smsTasks) {
if (smsTasks == null || smsTasks.isEmpty()) { if (smsTasks == null || smsTasks.isEmpty()) {
log.info("没有需要发送的主题日短信任务"); log.info("没有需要发送的主题日短信任务");
return; return;
} }
log.info("开始批量发送{}个主题日短信任务", smsTasks.size()); log.info("开始批量发送{}个主题日短信任务", smsTasks.size());
for (SmsTask task : smsTasks) { for (SmsHoliday task : smsTasks) {
try { try {
sendThemeSms(task); sendThemeSms(task);
} catch (Exception e) { } catch (Exception e) {
@ -231,13 +248,13 @@ public class SmsTaskServiceImpl extends ServiceImpl<SmsTaskMapper, SmsTask> impl
/** /**
* *
*/ */
private List<Specialist> getValidSpecialists() { private List<SmsSpecialist> getValidSpecialists() {
try { try {
LambdaQueryWrapper<Specialist> queryWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<SmsSpecialist> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Specialist::getStatus, 1); // 1:有效 queryWrapper.eq(SmsSpecialist::getStatus, 1); // 1:有效
queryWrapper.isNotNull(Specialist::getPhone); // 手机号不为空 queryWrapper.isNotNull(SmsSpecialist::getPhone); // 手机号不为空
List<Specialist> specialists = specialistMapper.selectList(queryWrapper); List<SmsSpecialist> specialists = specialistMapper.selectList(queryWrapper);
return specialists != null ? specialists : Collections.emptyList(); return specialists != null ? specialists : Collections.emptyList();
} catch (Exception e) { } catch (Exception e) {
log.error("查询有效专家失败: {}", e.getMessage(), e); log.error("查询有效专家失败: {}", e.getMessage(), e);
@ -253,7 +270,7 @@ public class SmsTaskServiceImpl extends ServiceImpl<SmsTaskMapper, SmsTask> impl
public void checkAndSendThemeSms() { public void checkAndSendThemeSms() {
try { try {
// 查询今日需要执行的主题日任务 // 查询今日需要执行的主题日任务
List<SmsTask> todayTasks = listTodayTasks(); List<SmsHoliday> todayTasks = listTodayTasks();
if (todayTasks.isEmpty()) { if (todayTasks.isEmpty()) {
log.debug("今日没有需要执行的主题日短信任务"); log.debug("今日没有需要执行的主题日短信任务");

View File

@ -1,10 +1,14 @@
package com.whdc.service.impl; package com.whdc.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.whdc.mapper.SmsLogMapper; import com.whdc.mapper.SmsLogMapper;
import com.whdc.model.dto.SmsLogPageDto;
import com.whdc.model.entity.SmsLog; import com.whdc.model.entity.SmsLog;
import com.whdc.service.ISmsLogService; import com.whdc.service.ISmsLogService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
/** /**
* *
@ -14,4 +18,21 @@ import org.springframework.stereotype.Service;
*/ */
@Service @Service
public class SmsLogServiceImpl extends ServiceImpl<SmsLogMapper, SmsLog> implements ISmsLogService { public class SmsLogServiceImpl extends ServiceImpl<SmsLogMapper, SmsLog> implements ISmsLogService {
public Page<SmsLog> page(SmsLogPageDto dto) {
LambdaQueryWrapper<SmsLog> queryWrapper = new LambdaQueryWrapper<>();
// 添加查询条件
if (StringUtils.hasText(dto.getName())) {
queryWrapper.like(SmsLog::getName, dto.getName());
}
if (StringUtils.hasText(dto.getPhone())) {
queryWrapper.like(SmsLog::getPhone, dto.getPhone());
}
// 默认按发送时间降序排列
queryWrapper.orderByDesc(SmsLog::getSendTm);
return page(dto.getPage(), queryWrapper);
}
} }

View File

@ -0,0 +1,41 @@
package com.whdc.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.whdc.mapper.SmsSpecialistMapper;
import com.whdc.model.dto.SmsSpecialistPageDto;
import com.whdc.model.entity.SmsSpecialist;
import com.whdc.service.ISmsSpecialistService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
/**
*
*
* @author lyf
* @since 2025-09-23
*/
@Service
public class SmsSpecialistServiceImpl extends ServiceImpl<SmsSpecialistMapper, SmsSpecialist> implements ISmsSpecialistService {
public Page<SmsSpecialist> page(SmsSpecialistPageDto dto) {
LambdaQueryWrapper<SmsSpecialist> queryWrapper = new LambdaQueryWrapper<>();
// 添加查询条件
if (StringUtils.hasText(dto.getName())) {
queryWrapper.like(SmsSpecialist::getName, dto.getName());
}
if (StringUtils.hasText(dto.getPhone())) {
queryWrapper.like(SmsSpecialist::getPhone, dto.getPhone());
}
if (dto.getStatus() != null) {
queryWrapper.eq(SmsSpecialist::getStatus, dto.getStatus());
}
// 默认按创建时间降序排列
queryWrapper.orderByDesc(SmsSpecialist::getCreateTm);
return page(dto.getPage(), queryWrapper);
}
}

View File

@ -1,17 +0,0 @@
package com.whdc.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.whdc.mapper.SpecialistMapper;
import com.whdc.model.entity.Specialist;
import com.whdc.service.ISpecialistService;
import org.springframework.stereotype.Service;
/**
*
*
* @author lyf
* @since 2025-09-23
*/
@Service
public class SpecialistServiceImpl extends ServiceImpl<SpecialistMapper, Specialist> implements ISpecialistService {
}

View File

@ -1,10 +1,8 @@
package com.whdc.service.impl; package com.whdc.service.impl;
import com.whdc.model.entity.Specialist; import com.whdc.model.entity.SmsSpecialist;
import com.whdc.utils.SmsHelper; import com.whdc.utils.SmsHelper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ActiveProfiles;
import java.util.Arrays; import java.util.Arrays;
@ -17,24 +15,25 @@ import java.util.List;
* @author lyf * @author lyf
* @since 2025-09-25 * @since 2025-09-25
*/ */
@SpringBootTest(classes = com.whdc.FxkhTxlApiApplication.class) //@SpringBootTest(classes = com.whdc.FxkhTxlApiApplication.class)
@ActiveProfiles("dev") @ActiveProfiles("dev")
public class BirthdaySmsServiceTest { public class BirthdaySmsServiceTest {
@Autowired @Autowired
private SmsHelper smsHelper; private SmsHelper smsHelper;
@Test // @Test
public void testSendBirthdaySms() { public void testSendBirthdaySms() {
System.out.println("=== 开始测试生日短信发送功能 ==="); System.out.println("=== 开始测试生日短信发送功能 ===");
// 创建测试专家数据 // 创建测试专家数据
Specialist specialist = new Specialist(); SmsSpecialist specialist = new SmsSpecialist();
specialist.setName("李"); specialist.setName("唐威");
// specialist.setPhone("18154318312");
specialist.setPhone("15671545233"); specialist.setPhone("15671545233");
specialist.setStatus(1); specialist.setStatus(1);
List<Specialist> specialists = Arrays.asList(specialist); List<SmsSpecialist> specialists = Arrays.asList(specialist);
System.out.println("专家信息:"); System.out.println("专家信息:");
System.out.println("姓名:" + specialist.getName()); System.out.println("姓名:" + specialist.getName());
@ -43,7 +42,7 @@ public class BirthdaySmsServiceTest {
try { try {
// 直接使用你提供的模板内容测试发送逻辑 // 直接使用你提供的模板内容测试发送逻辑
String template = "{姓名},测试短信接口"; String template = "尊敬的{姓名}专家,岁月如歌又一载,今朝生辰贺君来,今天是您的生日,湖北省水利厅向您致以最真挚的生日祝福!感谢您为湖北防汛抗旱事业保驾护航,您的每一份建议都凝聚着智慧,每一次指导都给予我们力量。在这个特别的日子里,愿您被时光温柔以待,事业如朗月,生活胜春烟,所遇皆美好,所得皆所愿!";
System.out.println("使用模板:" + template); System.out.println("使用模板:" + template);
// 调用内部方法进行测试 // 调用内部方法进行测试
@ -60,10 +59,10 @@ public class BirthdaySmsServiceTest {
/** /**
* 使 * 使
*/ */
private void testSendWithTemplate(List<Specialist> specialists, String template) { private void testSendWithTemplate(List<SmsSpecialist> specialists, String template) {
System.out.println("开始使用模板发送短信..."); System.out.println("开始使用模板发送短信...");
for (Specialist specialist : specialists) { for (SmsSpecialist specialist : specialists) {
try { try {
// 替换模板中的占位符 // 替换模板中的占位符
String content = template.replace("{姓名}", specialist.getName()); String content = template.replace("{姓名}", specialist.getName());