| | |
| | | |
| | | <name>ruoyi</name> |
| | | <url>http://www.ruoyi.vip</url> |
| | | <description>若依管理系统</description> |
| | | <description>嘉视达</description> |
| | | |
| | | <properties> |
| | | <ruoyi.version>3.8.6</ruoyi.version> |
| | |
| | | package com.ruoyi.web.controller.api; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.common.core.domain.R; |
| | | import com.ruoyi.framework.web.service.TokenService; |
| | | import com.ruoyi.system.dto.BigGiftPlaceOrderDTO; |
| | | import com.ruoyi.system.dto.FinalPaymentDTO; |
| | | import com.ruoyi.system.dto.PhysicalGoodsPlaceOrderDTO; |
| | | import com.ruoyi.system.model.TAppUser; |
| | | import com.ruoyi.system.query.AppletOrderListQuery; |
| | | import com.ruoyi.system.service.AppletOrderService; |
| | | import com.ruoyi.system.service.TAppUserService; |
| | | import com.ruoyi.system.service.TBigGiftPackageOrderService; |
| | | import com.ruoyi.system.service.TPhysicalGoodsOrderService; |
| | | import com.ruoyi.system.vo.AppletOrderDetailVO; |
| | | import com.ruoyi.system.vo.AppletOrderListVO; |
| | | import com.ruoyi.system.vo.PlaceOrderVO; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.context.annotation.Lazy; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.validation.Valid; |
| | | |
| | |
| | | private final TAppUserService appUserService; |
| | | private final TBigGiftPackageOrderService bigGiftPackageOrderService; |
| | | private final TPhysicalGoodsOrderService physicalGoodsOrderService; |
| | | private final AppletOrderService appletOrderService; |
| | | |
| | | /** |
| | | * 订单列表分页查询 |
| | | * |
| | | * @param query 查询条件 |
| | | * @return 订单列表 |
| | | */ |
| | | @ApiOperation(value = "订单列表分页查询", response = AppletOrderListVO.class) |
| | | @PostMapping("/list") |
| | | public R<Page<AppletOrderListVO>> getOrderList(@RequestBody AppletOrderListQuery query) { |
| | | Integer userId = tokenService.getLoginUserApplet().getUserId(); |
| | | Page<AppletOrderListVO> page = appletOrderService.getOrderList(query, userId); |
| | | return R.ok(page); |
| | | } |
| | | |
| | | /** |
| | | * 大礼包商品下单 |
| | |
| | | PlaceOrderVO result = bigGiftPackageOrderService.payFinalPayment(dto.getOrderId(), userId, appUser.getOpenid()); |
| | | return R.ok(result); |
| | | } |
| | | |
| | | /** |
| | | * 实物商品订单详情 |
| | | * |
| | | * @param id 订单ID |
| | | * @return 订单详情 |
| | | */ |
| | | @ApiOperation(value = "实物商品订单详情", response = AppletOrderDetailVO.class) |
| | | @GetMapping("/physical/detail/{id}") |
| | | public R<AppletOrderDetailVO> getPhysicalOrderDetail(@PathVariable Integer id) { |
| | | Integer userId = tokenService.getLoginUserApplet().getUserId(); |
| | | AppletOrderDetailVO detail = appletOrderService.getPhysicalOrderDetail(id, userId); |
| | | if (detail == null) { |
| | | return R.fail("订单不存在或无权查看"); |
| | | } |
| | | return R.ok(detail); |
| | | } |
| | | |
| | | /** |
| | | * 大礼包订单详情 |
| | | * |
| | | * @param id 订单ID |
| | | * @return 订单详情 |
| | | */ |
| | | @ApiOperation(value = "大礼包订单详情", response = AppletOrderDetailVO.class) |
| | | @GetMapping("/big-gift/detail/{id}") |
| | | public R<AppletOrderDetailVO> getBigGiftOrderDetail(@PathVariable Integer id) { |
| | | Integer userId = tokenService.getLoginUserApplet().getUserId(); |
| | | AppletOrderDetailVO detail = appletOrderService.getBigGiftOrderDetail(id, userId); |
| | | if (detail == null) { |
| | | return R.fail("订单不存在或无权查看"); |
| | | } |
| | | return R.ok(detail); |
| | | } |
| | | |
| | | /** |
| | | * 实物商品确认收货 |
| | | * |
| | | * @param id 订单ID |
| | | * @return 操作结果 |
| | | */ |
| | | @ApiOperation(value = "实物商品确认收货") |
| | | @PostMapping("/physical/confirm/{id}") |
| | | public R<Void> confirmPhysicalOrder(@PathVariable Integer id) { |
| | | Integer userId = tokenService.getLoginUserApplet().getUserId(); |
| | | boolean success = appletOrderService.confirmPhysicalOrder(id, userId); |
| | | return success ? R.ok() : R.fail("确认收货失败,订单不存在或状态不正确"); |
| | | } |
| | | |
| | | /** |
| | | * 大礼包确认收货 |
| | | * |
| | | * @param id 订单ID |
| | | * @return 操作结果 |
| | | */ |
| | | @ApiOperation(value = "大礼包确认收货") |
| | | @PostMapping("/big-gift/confirm/{id}") |
| | | public R<Void> confirmBigGiftOrder(@PathVariable Integer id) { |
| | | Integer userId = tokenService.getLoginUserApplet().getUserId(); |
| | | boolean success = appletOrderService.confirmBigGiftOrder(id, userId); |
| | | return success ? R.ok() : R.fail("确认收货失败,订单不存在或状态不正确"); |
| | | } |
| | | } |
| | |
| | | import com.ruoyi.system.dto.UpdateUserDto; |
| | | import com.ruoyi.system.model.TAppUser; |
| | | import com.ruoyi.system.service.TAppUserService; |
| | | import com.ruoyi.system.service.TCommissionTransactionFlowService; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import lombok.RequiredArgsConstructor; |
| | |
| | | |
| | | private final TAppUserService tAppUserService; |
| | | private final TokenService tokenService; |
| | | private final TCommissionTransactionFlowService commissionTransactionFlowService; |
| | | |
| | | @ApiOperation(value = "个人中心--用户信息",response = TAppUser.class) |
| | | @GetMapping(value = "/getUserInfo") |
| | | public R<TAppUser> getUserInfo() { |
| | | Integer userId = tokenService.getLoginUserApplet().getUserId(); |
| | | return R.ok(tAppUserService.getById(userId)); |
| | | TAppUser appUser = tAppUserService.getById(userId); |
| | | |
| | | return R.ok(); |
| | | } |
| | | |
| | | @ApiOperation(value = "个人中心--修改手机号") |
| | |
| | | import com.ruoyi.system.utils.wx.body.resp.Code2SessionRespBody; |
| | | import com.ruoyi.system.utils.wx.body.resq.Code2SessionResqBody; |
| | | import com.ruoyi.system.utils.wx.model.WeixinProperties; |
| | | import com.ruoyi.system.utils.wx.pojo.AppletUserDecodeData; |
| | | import com.ruoyi.system.utils.wx.pojo.AppletUserEncrypteData; |
| | | import com.ruoyi.system.utils.wx.tools.WxAppletTools; |
| | | import com.ruoyi.system.utils.wx.tools.WxUtils; |
| | | import com.ruoyi.web.controller.tool.MsgUtils; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | |
| | | String openid = body.getOpenid(); |
| | | String sessionKey = body.getSessionKey(); |
| | | // 用户信息解密 数据验签 |
| | | // if (StringUtils.isNotBlank(data.getSignature())) { |
| | | // WxUtils.verifySignature(data.getRawData(), sessionKey, data.getSignature()); |
| | | // } |
| | | // if(StringUtils.isEmpty(data.getEncryptedData()) || StringUtils.isEmpty(data.getIv())){ |
| | | // return R.fail("已拒绝授权"); |
| | | // } |
| | | // AppletUserDecodeData appletUserDecodeData = WxUtils.encryptedData(data.getEncryptedData(), sessionKey, data.getIv()); |
| | | // appletUserDecodeData.setOpenId(openid); |
| | | if (StringUtils.hasLength(data.getSignature())) { |
| | | WxUtils.verifySignature(data.getRawData(), sessionKey, data.getSignature()); |
| | | } |
| | | if(StringUtils.isEmpty(data.getEncryptedData()) || StringUtils.isEmpty(data.getIv())){ |
| | | return R.fail("已拒绝授权"); |
| | | } |
| | | AppletUserDecodeData appletUserDecodeData = WxUtils.encryptedData(data.getEncryptedData(), sessionKey, data.getIv()); |
| | | appletUserDecodeData.setOpenId(openid); |
| | | // 先使用openId和当前手机号进行查询 |
| | | TAppUser sysAppUser = appUserService.getOne(Wrappers.lambdaQuery(TAppUser.class) |
| | | .and(e->e.eq(TAppUser::getOpenid, openid))); |
| | | if (sysAppUser==null){ |
| | | sysAppUser = new TAppUser(); |
| | | // sysAppUser.setPhoneNumber(appletUserDecodeData.getPhoneNumber()); |
| | | sysAppUser.setPhone(appletUserDecodeData.getPhoneNumber()); |
| | | sysAppUser.setOpenid(openid); |
| | | // 手机号中间四位替换为* |
| | | // sysAppUser.setNickName(appletUserDecodeData.getPhoneNumber().replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")); |
| | |
| | | order-refund-url: "https://gw.open.icbc.com.cn/api/cardbusiness/aggregatepay/b2c/online/refundqry/V1" |
| | | |
| | | # 完整的回调通知地址,支付完成后通知商户的URL |
| | | notify-url: "" |
| | | notify-url: "/icbc-notify/" |
| | | |
| | | # 回调方法的路径 |
| | | notify-url-method: "/icbc-notify/" |
| | |
| | | |
| | | keys: |
| | | # 我方私钥,用于签名请求 |
| | | my-private-key: "" |
| | | my-private-key: "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDKTiDDXWHil2veWR9nCzAkSlQIt9qyILoeLfhV/dIZ0YoqHTnIefARnSIaDP0y1Pqc7orTcNifKBQBaV8AbXIjow+VZpsysoyN4rnjmW3xOx600CaxQLKnXnl4eWkH/mHlkSb+Khv/hsmrNTwMtpf6ZE+NlVQFAXYgJZ5+JlaM4tgfdavXYXk053MffuPE/M+GZEvpFkrCpP1PJovwRvYZCeyTuNC6OEKgpkphfF7uJBbUV5GkgiG3Lzvt8WqgNo9ANzidfDGfKfrSMm3lF8YF1wt1u7ieZ99oasiR8q5nhSqXt2slAMSYpUdfsM4//6Tj8qkjnDdl0DquMBqX3S3XAgMBAAECggEAAODS8pYqsR2gKcBwSvW4+UdbUwhdSi2rMStY6yKlcVhABcvHru+ck67/cWaCCOD0z46XirBwOEynvZPUBj/zYWU5UZg270LoKkuELBFD8WqkyPsoV9ekjuZxl4zLYG4BxcnaBftv0K2onXYnMxTt1vftgntV+ImXli8jn3+SG510gIqzdp4qoQgySMmu+ew2I8Ank3yacg+ThYuJuG2T/RWyCoQKzw4vIRqilLnnqiM83AJniI5cM56EgMRCFOxsXR4fED5BHMaFJNlxNhWGVp8PE4s2S3aRsozJfMeH+SX/4/6u8fcG0w2eMFeJieNK56cOYgoXFkj6buQZCCt9XQKBgQDTcr6qYZsSk2tuXkFZiQ0JKe6FglQp8byBDU+40ESVtVLs8+ztQFE6/0lHgvwwtif2CkN6tFCcbfWfNF4g9Sgf/8G2MiCywFO8igbn7eRFePs4Ow2xdKbRP1GhRtL9nv5HGFT7O/puCxhkPZ4OVUg+e7E4eS0o+IiU8DSL5sa8YwKBgQD07jggN5H9LDbluZ7u6zvA/xBAM06Py/CCcmBCSK7Ekso7Rb0zBdTd+qhuErQEydgjRDeYGTsv0kBAPLb3nud7z/6R1pm2ofrCOT7QoJsNPcRAdyiMoRpxe+VhRbVIsSfsDQpO7uYiSpC3+kcMBPaS/3YMP9QsO7sUFpZlgkcA/QKBgAYrOtTNtDB+jIvTxCRQk0jJKGRMwRfYLZ+2O7Uwnk+f0qOHGkpxre0CtaiYcbMaHWBYPE/AIvntW1Uhuf1MKbeFRqmCHOIaKlc9z6XEkvxT40BouKWMubExH7iM0GsF0HVSiGgr/8pmGHgUNCBG7Kyl/y2em2qulY2C1IWgXfvxAoGBAMA3U4S1F7UARfTzP29w3GwRv8cifL0M56r35+Mc90r9iAgMKvcn1G6AkD4SH4TCgwTqPFb5NiHJdvLyswSRiEj6amY5bFiPtiNGma1Q3V3+401UNlZm+EGnYdR8yFBL93Cgqz5W7fRrYDeCCs+79WZXZWUnknNETQTu1QjEvMRhAoGBAKxtYlqPMBQ9KiBLipaj/jAx7xlPmhphgcGHPf7sBgDplq9vTO9dq+XK4Fhj1fV9OEmNDC0muk9l9WXBkhww7ji6k7zhY4H7DAPhe1MD/rqkcq7SKA4zeHNbt2TScAzzYpKmN8lSA7G4MgcG4IQcYpwxNzsR7Pf4W0cM0Qf4LgyZ" |
| | | |
| | | # 对方(即工行)网关公钥,用于验证返回的签名 |
| | | api-gw-public-key: "" |
| | | api-gw-public-key: "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCMpjaWjngB4E3ATh+G1DVAmQnIpiPEFAEDqRfNGAVvvH35yDetqewKi0l7OEceTMN1C6NPym3zStvSoQayjYV+eIcZERkx31KhtFu9clZKgRTyPjdKMIth/wBtPKjL/5+PYalLdomM4ONthrPgnkN4x4R0+D4+EBpXo8gNiAFsNwIDAQAB" |
| | | |
| | | # APP的编号,应用在API开放平台注册时生成 |
| | | app-id: "" |
| | | app-id: "11000000000000018953" |
| | | |
| | | # 商户编号,用于标识商户账户 |
| | | mer-id: "" |
| | |
| | | mer-prtcl-no: "" |
| | | |
| | | # 设备号,设备的唯一标识符 |
| | | decive-info: "" |
| | | decive-info: "013467007045764" |
| | | |
| | | # 微信APP的编号,便于与微信支付接口对接 |
| | | wx-app-id: "" |
| | | wx-app-id: "wx87744e4e4094a829" |
| | | |
| | | # 微信公众号的APP编号,供公众号支付使用 |
| | | wx-gzh-app-id: "" |
| | | |
| | | # 订单超时时间,单位为分钟 |
| | | expire-time: "60" |
| | | |
| | | |
| | |
| | | |
| | | keys: |
| | | # 我方私钥,用于签名请求 |
| | | my-private-key: "" |
| | | my-private-key: "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDKTiDDXWHil2veWR9nCzAkSlQIt9qyILoeLfhV/dIZ0YoqHTnIefARnSIaDP0y1Pqc7orTcNifKBQBaV8AbXIjow+VZpsysoyN4rnjmW3xOx600CaxQLKnXnl4eWkH/mHlkSb+Khv/hsmrNTwMtpf6ZE+NlVQFAXYgJZ5+JlaM4tgfdavXYXk053MffuPE/M+GZEvpFkrCpP1PJovwRvYZCeyTuNC6OEKgpkphfF7uJBbUV5GkgiG3Lzvt8WqgNo9ANzidfDGfKfrSMm3lF8YF1wt1u7ieZ99oasiR8q5nhSqXt2slAMSYpUdfsM4//6Tj8qkjnDdl0DquMBqX3S3XAgMBAAECggEAAODS8pYqsR2gKcBwSvW4+UdbUwhdSi2rMStY6yKlcVhABcvHru+ck67/cWaCCOD0z46XirBwOEynvZPUBj/zYWU5UZg270LoKkuELBFD8WqkyPsoV9ekjuZxl4zLYG4BxcnaBftv0K2onXYnMxTt1vftgntV+ImXli8jn3+SG510gIqzdp4qoQgySMmu+ew2I8Ank3yacg+ThYuJuG2T/RWyCoQKzw4vIRqilLnnqiM83AJniI5cM56EgMRCFOxsXR4fED5BHMaFJNlxNhWGVp8PE4s2S3aRsozJfMeH+SX/4/6u8fcG0w2eMFeJieNK56cOYgoXFkj6buQZCCt9XQKBgQDTcr6qYZsSk2tuXkFZiQ0JKe6FglQp8byBDU+40ESVtVLs8+ztQFE6/0lHgvwwtif2CkN6tFCcbfWfNF4g9Sgf/8G2MiCywFO8igbn7eRFePs4Ow2xdKbRP1GhRtL9nv5HGFT7O/puCxhkPZ4OVUg+e7E4eS0o+IiU8DSL5sa8YwKBgQD07jggN5H9LDbluZ7u6zvA/xBAM06Py/CCcmBCSK7Ekso7Rb0zBdTd+qhuErQEydgjRDeYGTsv0kBAPLb3nud7z/6R1pm2ofrCOT7QoJsNPcRAdyiMoRpxe+VhRbVIsSfsDQpO7uYiSpC3+kcMBPaS/3YMP9QsO7sUFpZlgkcA/QKBgAYrOtTNtDB+jIvTxCRQk0jJKGRMwRfYLZ+2O7Uwnk+f0qOHGkpxre0CtaiYcbMaHWBYPE/AIvntW1Uhuf1MKbeFRqmCHOIaKlc9z6XEkvxT40BouKWMubExH7iM0GsF0HVSiGgr/8pmGHgUNCBG7Kyl/y2em2qulY2C1IWgXfvxAoGBAMA3U4S1F7UARfTzP29w3GwRv8cifL0M56r35+Mc90r9iAgMKvcn1G6AkD4SH4TCgwTqPFb5NiHJdvLyswSRiEj6amY5bFiPtiNGma1Q3V3+401UNlZm+EGnYdR8yFBL93Cgqz5W7fRrYDeCCs+79WZXZWUnknNETQTu1QjEvMRhAoGBAKxtYlqPMBQ9KiBLipaj/jAx7xlPmhphgcGHPf7sBgDplq9vTO9dq+XK4Fhj1fV9OEmNDC0muk9l9WXBkhww7ji6k7zhY4H7DAPhe1MD/rqkcq7SKA4zeHNbt2TScAzzYpKmN8lSA7G4MgcG4IQcYpwxNzsR7Pf4W0cM0Qf4LgyZ" |
| | | |
| | | # 对方(即工行)网关公钥,用于验证返回的签名 |
| | | api-gw-public-key: "" |
| | | api-gw-public-key: "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCMpjaWjngB4E3ATh+G1DVAmQnIpiPEFAEDqRfNGAVvvH35yDetqewKi0l7OEceTMN1C6NPym3zStvSoQayjYV+eIcZERkx31KhtFu9clZKgRTyPjdKMIth/wBtPKjL/5+PYalLdomM4ONthrPgnkN4x4R0+D4+EBpXo8gNiAFsNwIDAQAB" |
| | | |
| | | # APP的编号,应用在API开放平台注册时生成 |
| | | app-id: "" |
| | | app-id: "11000000000000018953" |
| | | |
| | | # 商户编号,用于标识商户账户 |
| | | mer-id: "" |
| | |
| | | mer-prtcl-no: "" |
| | | |
| | | # 设备号,设备的唯一标识符 |
| | | decive-info: "" |
| | | decive-info: "013467007045764" |
| | | |
| | | # 微信APP的编号,便于与微信支付接口对接 |
| | | wx-app-id: "" |
| | | wx-app-id: "wx87744e4e4094a829" |
| | | |
| | | # 微信公众号的APP编号,供公众号支付使用 |
| | | wx-gzh-app-id: "" |
| | |
| | | package com.ruoyi.framework.config; |
| | | |
| | | import java.util.TimeZone; |
| | | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; |
| | | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; |
| | | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; |
| | | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; |
| | | import org.mybatis.spring.annotation.MapperScan; |
| | | import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | import org.springframework.context.annotation.EnableAspectJAutoProxy; |
| | | |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.TimeZone; |
| | | |
| | | /** |
| | | * 程序注解配置 |
| | |
| | | @MapperScan("com.ruoyi.**.mapper") |
| | | public class ApplicationConfig |
| | | { |
| | | private static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss"; |
| | | private static final String DATE_PATTERN = "yyyy-MM-dd"; |
| | | |
| | | /** |
| | | * 时区配置 |
| | | * 时区配置 + LocalDateTime全局格式化 |
| | | */ |
| | | @Bean |
| | | public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() |
| | | { |
| | | return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault()); |
| | | return builder -> builder |
| | | .timeZone(TimeZone.getDefault()) |
| | | .serializers(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATE_TIME_PATTERN))) |
| | | .deserializers(new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATE_TIME_PATTERN))) |
| | | .serializers(new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_PATTERN))) |
| | | .deserializers(new LocalDateDeserializer(DateTimeFormatter.ofPattern(DATE_PATTERN))); |
| | | } |
| | | } |
| | |
| | | import com.ruoyi.common.constant.Constants; |
| | | import com.ruoyi.common.constant.UserConstants; |
| | | import com.ruoyi.common.core.domain.entity.SysUser; |
| | | import com.ruoyi.common.core.domain.entity.TAppUserResp; |
| | | import com.ruoyi.common.core.domain.model.LoginUser; |
| | | import com.ruoyi.common.core.domain.model.LoginUserApplet; |
| | | import com.ruoyi.common.core.redis.RedisCache; |
| | |
| | | import com.ruoyi.system.service.ISysUserService; |
| | | import com.ruoyi.system.service.TAppUserService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.security.authentication.AuthenticationManager; |
| | |
| | | throw new UserNotExistsException(); |
| | | } |
| | | // 用户验证 |
| | | TAppUser user = tAppUserService.getOne(Wrappers.<TAppUser>lambdaQuery().eq(TAppUser::getPhone,username)); |
| | | TAppUser user = tAppUserService.getOne(Wrappers.<TAppUser>lambdaQuery().eq(TAppUser::getPhone,username).ne(TAppUser::getStatus,3)); |
| | | if (StringUtils.isNull(user)){ |
| | | log.info("登录用户:{} 不存在.", username); |
| | | throw new ServiceException(MessageUtils.message("user.not.exists")); |
| | | } else if (user.getStatus().equals(3)) { |
| | | log.info("登录用户:{} 已被删除.", username); |
| | | throw new ServiceException(MessageUtils.message("user.password.delete")); |
| | | log.info("登录用户:{} 不存在.创建一个新用户", username); |
| | | user = new TAppUser(); |
| | | user.setPhone(username); |
| | | user.setStatus(1); |
| | | tAppUserService.save(user); |
| | | // throw new ServiceException(MessageUtils.message("user.not.exists")); |
| | | } else { |
| | | if (user.getStatus().equals(2)) { |
| | | log.info("登录用户:{} 已被停用.", username); |
| | | throw new ServiceException(MessageUtils.message("user.blocked")); |
| | | } |
| | | } |
| | | if ("test".equals(env) || "dev".equals(env)) { |
| | | if (!code.equals("111111")) { |
| | |
| | | AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); |
| | | LoginUserApplet loginUser = new LoginUserApplet(user.getId(), null, null, null); |
| | | recordAppLoginInfo(loginUser.getUserId().toString()); |
| | | TAppUserResp sysUserResp = new TAppUserResp(); |
| | | BeanUtils.copyProperties(user, sysUserResp); |
| | | loginUser.setUser(sysUserResp); |
| | | // 生成token |
| | | return loginUser; |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.system.query.AppletOrderListQuery; |
| | | import com.ruoyi.system.vo.AppletOrderListVO; |
| | | import org.apache.ibatis.annotations.Param; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 小程序端订单Mapper |
| | | * |
| | | * @author mitao |
| | | * @since 2025-12-20 |
| | | */ |
| | | public interface AppletOrderMapper { |
| | | |
| | | /** |
| | | * 分页查询订单列表 |
| | | * |
| | | * @param page 分页参数 |
| | | * @param query 查询条件 |
| | | * @param appUserId 用户ID |
| | | * @return 订单列表 |
| | | */ |
| | | List<AppletOrderListVO> selectOrderList(Page<AppletOrderListVO> page, |
| | | @Param("query") AppletOrderListQuery query, |
| | | @Param("appUserId") Integer appUserId); |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.query; |
| | | |
| | | import com.ruoyi.common.core.domain.BasePage; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | /** |
| | | * 小程序端订单列表查询条件 |
| | | * |
| | | * @author mitao |
| | | * @since 2025-12-20 |
| | | */ |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @ApiModel(value = "AppletOrderListQuery", description = "小程序端订单列表查询条件") |
| | | public class AppletOrderListQuery extends BasePage { |
| | | |
| | | @ApiModelProperty(value = "订单状态筛选(null=全部,1=待支付,2=已付定金,3=已支付)") |
| | | private Integer status; |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.service; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.system.query.AppletOrderListQuery; |
| | | import com.ruoyi.system.vo.AppletOrderDetailVO; |
| | | import com.ruoyi.system.vo.AppletOrderListVO; |
| | | |
| | | /** |
| | | * 小程序端订单Service |
| | | * |
| | | * @author mitao |
| | | * @since 2025-12-20 |
| | | */ |
| | | public interface AppletOrderService { |
| | | |
| | | /** |
| | | * 分页查询订单列表 |
| | | * |
| | | * @param query 查询条件 |
| | | * @param appUserId 用户ID |
| | | * @return 分页结果 |
| | | */ |
| | | Page<AppletOrderListVO> getOrderList(AppletOrderListQuery query, Integer appUserId); |
| | | |
| | | /** |
| | | * 获取实物商品订单详情 |
| | | * |
| | | * @param orderId 订单ID |
| | | * @param appUserId 用户ID |
| | | * @return 订单详情 |
| | | */ |
| | | AppletOrderDetailVO getPhysicalOrderDetail(Integer orderId, Integer appUserId); |
| | | |
| | | /** |
| | | * 获取大礼包订单详情 |
| | | * |
| | | * @param orderId 订单ID |
| | | * @param appUserId 用户ID |
| | | * @return 订单详情 |
| | | */ |
| | | AppletOrderDetailVO getBigGiftOrderDetail(Integer orderId, Integer appUserId); |
| | | |
| | | /** |
| | | * 实物商品确认收货 |
| | | * |
| | | * @param orderId 订单ID |
| | | * @param appUserId 用户ID |
| | | * @return 是否成功 |
| | | */ |
| | | boolean confirmPhysicalOrder(Integer orderId, Integer appUserId); |
| | | |
| | | /** |
| | | * 大礼包确认收货 |
| | | * |
| | | * @param orderId 订单ID |
| | | * @param appUserId 用户ID |
| | | * @return 是否成功 |
| | | */ |
| | | boolean confirmBigGiftOrder(Integer orderId, Integer appUserId); |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.service.impl; |
| | | |
| | | import cn.hutool.core.bean.BeanUtil; |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.system.mapper.AppletOrderMapper; |
| | | import com.ruoyi.system.model.TBigGiftPackage; |
| | | import com.ruoyi.system.model.TBigGiftPackageOrder; |
| | | import com.ruoyi.system.model.TPhysicalGoods; |
| | | import com.ruoyi.system.model.TPhysicalGoodsOrder; |
| | | import com.ruoyi.system.query.AppletOrderListQuery; |
| | | import com.ruoyi.system.service.AppletOrderService; |
| | | import com.ruoyi.system.service.TBigGiftPackageOrderService; |
| | | import com.ruoyi.system.service.TPhysicalGoodsOrderService; |
| | | import com.ruoyi.system.vo.AppletOrderDetailVO; |
| | | import com.ruoyi.system.vo.AppletOrderListVO; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.context.annotation.Lazy; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 小程序端订单Service实现 |
| | | * |
| | | * @author mitao |
| | | * @since 2025-12-20 |
| | | */ |
| | | @Service |
| | | @RequiredArgsConstructor(onConstructor_ = @Lazy) |
| | | public class AppletOrderServiceImpl implements AppletOrderService { |
| | | |
| | | private final AppletOrderMapper appletOrderMapper; |
| | | private final TPhysicalGoodsOrderService physicalGoodsOrderService; |
| | | private final TBigGiftPackageOrderService bigGiftPackageOrderService; |
| | | |
| | | @Override |
| | | public Page<AppletOrderListVO> getOrderList(AppletOrderListQuery query, Integer appUserId) { |
| | | Page<AppletOrderListVO> page = new Page<>(query.getPageNum(), query.getPageSize()); |
| | | List<AppletOrderListVO> list = appletOrderMapper.selectOrderList(page, query, appUserId); |
| | | page.setRecords(list); |
| | | return page; |
| | | } |
| | | |
| | | @Override |
| | | public AppletOrderDetailVO getPhysicalOrderDetail(Integer orderId, Integer appUserId) { |
| | | TPhysicalGoodsOrder order = physicalGoodsOrderService.getById(orderId); |
| | | if (order == null || !order.getAppUserId().equals(appUserId)) { |
| | | return null; |
| | | } |
| | | |
| | | AppletOrderDetailVO vo = BeanUtil.copyProperties(order, AppletOrderDetailVO.class); |
| | | vo.setOrderType(2); // 实物商品 |
| | | |
| | | // 解析商品JSON |
| | | if (order.getPhysicalGoods() != null) { |
| | | TPhysicalGoods goods = JSON.parseObject(order.getPhysicalGoods(), TPhysicalGoods.class); |
| | | if (goods != null) { |
| | | vo.setGoodsName(goods.getName()); |
| | | vo.setGoodsIntro(goods.getIntroduction()); |
| | | vo.setGoodsCover(goods.getCoverImage()); |
| | | vo.setGoodsPrice(goods.getTotalPrice()); |
| | | } |
| | | } |
| | | return vo; |
| | | } |
| | | |
| | | @Override |
| | | public AppletOrderDetailVO getBigGiftOrderDetail(Integer orderId, Integer appUserId) { |
| | | TBigGiftPackageOrder order = bigGiftPackageOrderService.getById(orderId); |
| | | if (order == null || !order.getAppUserId().equals(appUserId)) { |
| | | return null; |
| | | } |
| | | |
| | | AppletOrderDetailVO vo = BeanUtil.copyProperties(order, AppletOrderDetailVO.class); |
| | | vo.setOrderType(1); // 大礼包 |
| | | |
| | | // 解析商品JSON |
| | | if (order.getBigGiftPackage() != null) { |
| | | TBigGiftPackage goods = JSON.parseObject(order.getBigGiftPackage(), TBigGiftPackage.class); |
| | | if (goods != null) { |
| | | vo.setGoodsName(goods.getName()); |
| | | vo.setGoodsIntro(goods.getIntroduction()); |
| | | vo.setGoodsCover(goods.getCoverImage()); |
| | | vo.setGoodsPrice(goods.getTotalPrice()); |
| | | } |
| | | } |
| | | return vo; |
| | | } |
| | | |
| | | @Override |
| | | public boolean confirmPhysicalOrder(Integer orderId, Integer appUserId) { |
| | | TPhysicalGoodsOrder order = physicalGoodsOrderService.getById(orderId); |
| | | // 校验订单存在、归属、状态(2=完成支付才能确认收货) |
| | | if (order == null || !order.getAppUserId().equals(appUserId) || order.getStatus() != 2) { |
| | | return false; |
| | | } |
| | | order.setStatus(3); // 确认收货 |
| | | order.setDeliveryTime(LocalDateTime.now()); |
| | | return physicalGoodsOrderService.updateById(order); |
| | | } |
| | | |
| | | @Override |
| | | public boolean confirmBigGiftOrder(Integer orderId, Integer appUserId) { |
| | | TBigGiftPackageOrder order = bigGiftPackageOrderService.getById(orderId); |
| | | // 校验订单存在、归属、状态(3=完成支付才能确认收货) |
| | | if (order == null || !order.getAppUserId().equals(appUserId) || order.getStatus() != 3) { |
| | | return false; |
| | | } |
| | | order.setStatus(4); // 确认收货 |
| | | order.setDeliveryTime(LocalDateTime.now()); |
| | | return bigGiftPackageOrderService.updateById(order); |
| | | } |
| | | } |
| | |
| | | import com.ruoyi.system.config.ICBCConfig; |
| | | import com.ruoyi.system.model.TBigGiftPackageOrder; |
| | | import com.ruoyi.system.model.TPhysicalGoodsOrder; |
| | | import com.ruoyi.system.service.ICBCNotifyService; |
| | | import com.ruoyi.system.service.TBigGiftPackageOrderService; |
| | | import com.ruoyi.system.service.TCommissionTransactionFlowService; |
| | | import com.ruoyi.system.service.TPhysicalGoodsOrderService; |
| | | import com.ruoyi.system.service.*; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.context.annotation.Lazy; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import javax.annotation.Resource; |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.io.IOException; |
| | |
| | | */ |
| | | @Service |
| | | @Slf4j |
| | | @RequiredArgsConstructor(onConstructor_ = @Lazy) |
| | | public class ICBCNotifyServiceImpl implements ICBCNotifyService { |
| | | |
| | | @Resource |
| | | private ICBCConfig icbcConfig; |
| | | private final ICBCConfig icbcConfig; |
| | | |
| | | @Resource |
| | | private TBigGiftPackageOrderService bigGiftPackageOrderService; |
| | | private final TBigGiftPackageOrderService bigGiftPackageOrderService; |
| | | |
| | | @Resource |
| | | private TPhysicalGoodsOrderService physicalGoodsOrderService; |
| | | private final TPhysicalGoodsOrderService physicalGoodsOrderService; |
| | | |
| | | @Resource |
| | | private TCommissionTransactionFlowService commissionTransactionFlowService; |
| | | private final TCommissionTransactionFlowService commissionTransactionFlowService; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | |
| | | physicalGoodsOrderService.updateById(order); |
| | | log.info("订单{}状态更新为完成支付", orderCode); |
| | | |
| | | // 实物商品支付完成后,立即计算分佣 |
| | | commissionTransactionFlowService.calculateAndSaveCommission( |
| | | order.getId(), |
| | | 2, // 订单类型:2=实物商品 |
| | | order.getOrderPrice(), |
| | | order.getAppUserId() |
| | | ); |
| | | log.info("订单{}分佣计算完成", orderCode); |
| | | } else { |
| | | log.error("订单{}【工商银行】实物商品支付失败,错误信息:{}", orderCode, bizContent.getString("return_msg")); |
| | | } |
| | |
| | | package com.ruoyi.system.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.system.mapper.TCommissionTransactionFlowMapper; |
| | | import com.ruoyi.system.model.TAppUser; |
| | | import com.ruoyi.system.model.TCommissionTransactionFlow; |
| | | import com.ruoyi.system.mapper.TCommissionTransactionFlowMapper; |
| | | import com.ruoyi.system.model.THtmlConfig; |
| | | import com.ruoyi.system.service.TAppUserService; |
| | | import com.ruoyi.system.service.TCommissionTransactionFlowService; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.system.service.THtmlConfigService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.context.annotation.Lazy; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.time.LocalDateTime; |
| | |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | @RequiredArgsConstructor(onConstructor_ = @Lazy) |
| | | public class TCommissionTransactionFlowServiceImpl extends ServiceImpl<TCommissionTransactionFlowMapper, TCommissionTransactionFlow> implements TCommissionTransactionFlowService { |
| | | |
| | | @Resource |
| | | private TAppUserService appUserService; |
| | | private final TAppUserService appUserService; |
| | | |
| | | @Resource |
| | | private THtmlConfigService htmlConfigService; |
| | | private final THtmlConfigService htmlConfigService; |
| | | |
| | | @Override |
| | | public void calculateAndSaveCommission(Integer orderId, Integer orderType, BigDecimal orderPrice, Integer appUserId) { |
| | |
| | | |
| | | save(flow); |
| | | log.info("保存佣金流水成功,用户ID:{},佣金金额:{}", userId, commissionAmount); |
| | | |
| | | // 更新用户累计积分(佣金金额1:1转积分) |
| | | TAppUser user = appUserService.getById(userId); |
| | | if (user != null) { |
| | | int currentIntegral = user.getAccumulativeIntegral() != null ? user.getAccumulativeIntegral() : 0; |
| | | user.setAccumulativeIntegral(currentIntegral + commissionAmount.intValue()); |
| | | appUserService.updateById(user); |
| | | log.info("更新用户累计积分成功,用户ID:{},新增积分:{},累计积分:{}", userId, commissionAmount.intValue(), user.getAccumulativeIntegral()); |
| | | } |
| | | } |
| | | |
| | | /** |
| New file |
| | |
| | | package com.ruoyi.system.vo; |
| | | |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * 小程序端订单详情VO |
| | | * |
| | | * @author mitao |
| | | * @since 2025-12-20 |
| | | */ |
| | | @Data |
| | | @ApiModel(value = "AppletOrderDetailVO", description = "小程序端订单详情VO") |
| | | public class AppletOrderDetailVO { |
| | | |
| | | // ========== 订单基础信息 ========== |
| | | |
| | | @ApiModelProperty(value = "订单ID") |
| | | private Integer id; |
| | | |
| | | @ApiModelProperty(value = "订单编号") |
| | | private String code; |
| | | |
| | | @ApiModelProperty(value = "订单类型(1=大礼包,2=实物商品)") |
| | | private Integer orderType; |
| | | |
| | | @ApiModelProperty(value = "订单状态") |
| | | private Integer status; |
| | | |
| | | @ApiModelProperty(value = "下单时间") |
| | | private LocalDateTime createTime; |
| | | |
| | | // ========== 商品信息(从JSON解析) ========== |
| | | |
| | | @ApiModelProperty(value = "商品名称") |
| | | private String goodsName; |
| | | |
| | | @ApiModelProperty(value = "商品简介") |
| | | private String goodsIntro; |
| | | |
| | | @ApiModelProperty(value = "商品封面图") |
| | | private String goodsCover; |
| | | |
| | | @ApiModelProperty(value = "商品总价") |
| | | private BigDecimal goodsPrice; |
| | | |
| | | // ========== 金额信息 ========== |
| | | |
| | | @ApiModelProperty(value = "订单价格") |
| | | private BigDecimal orderPrice; |
| | | |
| | | @ApiModelProperty(value = "支付金额") |
| | | private BigDecimal paymentPrice; |
| | | |
| | | @ApiModelProperty(value = "订金(大礼包)") |
| | | private BigDecimal deposit; |
| | | |
| | | @ApiModelProperty(value = "尾款(大礼包)") |
| | | private BigDecimal finalPayment; |
| | | |
| | | @ApiModelProperty(value = "支付方式(1=订金+尾款,2=全款)(大礼包)") |
| | | private Integer paymentType; |
| | | |
| | | // ========== 时间信息 ========== |
| | | |
| | | @ApiModelProperty(value = "付款时间(实物商品)") |
| | | private LocalDateTime paymentTime; |
| | | |
| | | @ApiModelProperty(value = "订金付款时间(大礼包)") |
| | | private LocalDateTime depositTime; |
| | | |
| | | @ApiModelProperty(value = "尾款付款时间(大礼包)") |
| | | private LocalDateTime finalPaymentTime; |
| | | |
| | | @ApiModelProperty(value = "确认收货时间") |
| | | private LocalDateTime deliveryTime; |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.vo; |
| | | |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * 小程序端订单列表VO |
| | | * |
| | | * @author mitao |
| | | * @since 2025-12-20 |
| | | */ |
| | | @Data |
| | | @ApiModel(value = "AppletOrderListVO", description = "小程序端订单列表VO") |
| | | public class AppletOrderListVO { |
| | | |
| | | @ApiModelProperty(value = "订单ID") |
| | | private Integer id; |
| | | |
| | | @ApiModelProperty(value = "订单编号") |
| | | private String code; |
| | | |
| | | @ApiModelProperty(value = "商品名称") |
| | | private String goodsName; |
| | | |
| | | @ApiModelProperty(value = "商品简介") |
| | | private String goodsIntro; |
| | | |
| | | @ApiModelProperty(value = "商品封面图") |
| | | private String goodsCover; |
| | | |
| | | @ApiModelProperty(value = "商品总价") |
| | | private BigDecimal goodsPrice; |
| | | |
| | | @ApiModelProperty(value = "下单时间") |
| | | private LocalDateTime createTime; |
| | | |
| | | @ApiModelProperty(value = "订单类型(1=大礼包,2=实物商品)") |
| | | private Integer orderType; |
| | | |
| | | @ApiModelProperty(value = "订单状态(1=待支付,2=已付定金,3=已支付)") |
| | | private Integer status; |
| | | } |
| New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| | | <mapper namespace="com.ruoyi.system.mapper.AppletOrderMapper"> |
| | | |
| | | <select id="selectOrderList" resultType="com.ruoyi.system.vo.AppletOrderListVO"> |
| | | SELECT t.id, t.code, t.goodsName, t.goodsIntro, t.goodsCover, t.goodsPrice, t.createTime, t.orderType, t.status |
| | | FROM ( |
| | | SELECT |
| | | o.id, |
| | | o.code, |
| | | JSON_UNQUOTE(JSON_EXTRACT(o.big_gift_package, '$.name')) AS goodsName, |
| | | JSON_UNQUOTE(JSON_EXTRACT(o.big_gift_package, '$.introduction')) AS goodsIntro, |
| | | JSON_UNQUOTE(JSON_EXTRACT(o.big_gift_package, '$.coverImage')) AS goodsCover, |
| | | JSON_UNQUOTE(JSON_EXTRACT(o.big_gift_package, '$.totalPrice')) AS goodsPrice, |
| | | o.create_time AS createTime, |
| | | 1 AS orderType, |
| | | o.status |
| | | FROM t_big_gift_package_order o |
| | | WHERE o.del_flag = 0 AND o.app_user_id = #{appUserId} |
| | | |
| | | UNION ALL |
| | | |
| | | SELECT |
| | | o.id, |
| | | o.code, |
| | | JSON_UNQUOTE(JSON_EXTRACT(o.physical_goods, '$.name')) AS goodsName, |
| | | JSON_UNQUOTE(JSON_EXTRACT(o.physical_goods, '$.introduction')) AS goodsIntro, |
| | | JSON_UNQUOTE(JSON_EXTRACT(o.physical_goods, '$.coverImage')) AS goodsCover, |
| | | JSON_UNQUOTE(JSON_EXTRACT(o.physical_goods, '$.totalPrice')) AS goodsPrice, |
| | | o.create_time AS createTime, |
| | | 2 AS orderType, |
| | | CASE WHEN o.status = 2 THEN 3 ELSE o.status END AS status |
| | | FROM t_physical_goods_order o |
| | | WHERE o.del_flag = 0 AND o.app_user_id = #{appUserId} |
| | | ) t |
| | | <where> |
| | | <if test="query.status != null"> |
| | | <choose> |
| | | <when test="query.status == 3"> |
| | | AND (t.status = 3 OR (t.orderType = 1 AND t.status = 4)) |
| | | </when> |
| | | <otherwise> |
| | | AND t.status = #{query.status} |
| | | </otherwise> |
| | | </choose> |
| | | </if> |
| | | </where> |
| | | ORDER BY t.createTime DESC |
| | | </select> |
| | | |
| | | </mapper> |