8 changed files with 391 additions and 10 deletions
-
9src/src/main/java/com/my/graphiteDigesterBg/diframe/ActiveRecordField.java
-
179src/src/main/java/com/my/graphiteDigesterBg/diframe/DiActiveRecord.java
-
10src/src/main/java/com/my/graphiteDigesterBg/diframe/DiActiveRecordCriteria.java
-
15src/src/main/java/com/my/graphiteDigesterBg/diframe/DiApiControllerBase.java
-
95src/src/main/java/com/my/graphiteDigesterBg/diframe/api/DiApiUser.java
-
40src/src/main/java/com/my/graphiteDigesterBg/diframe/mapper/DiActiveRecordMapper.java
-
49src/src/main/java/com/my/graphiteDigesterBg/diframe/model/DiMdbUser.java
-
4src/src/main/resources/application.yml
@ -0,0 +1,9 @@ |
|||
package com.my.graphiteDigesterBg.diframe; |
|||
import java.lang.annotation.ElementType; |
|||
import java.lang.annotation.Retention; |
|||
import java.lang.annotation.RetentionPolicy; |
|||
import java.lang.annotation.Target; |
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
@Target({ElementType.FIELD}) |
|||
public @interface ActiveRecordField { |
|||
} |
@ -1,12 +1,181 @@ |
|||
package com.my.graphiteDigesterBg.diframe; |
|||
import com.fasterxml.jackson.annotation.JsonIgnore; |
|||
import com.my.graphiteDigesterBg.diframe.mapper.DiActiveRecordMapper; |
|||
abstract public class DiActiveRecord { |
|||
import java.lang.reflect.Constructor; |
|||
import java.lang.reflect.Field; |
|||
import java.lang.reflect.InvocationTargetException; |
|||
import java.lang.reflect.Method; |
|||
import java.util.ArrayList; |
|||
import java.util.HashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
public class DiActiveRecord { |
|||
// is new record |
|||
@JsonIgnore |
|||
public Boolean isNewRecord = true; |
|||
|
|||
// save model |
|||
public void save() { |
|||
var context = DiApplicationContextProvider.getContext(); |
|||
DiActiveRecordMapper mapper = context.getBean(DiActiveRecordMapper.class); |
|||
String tableName = DiActiveRecord.getTableNameFromModelClass(this.getClass()); |
|||
Map<String,Object> data = DiActiveRecord.exportModelData(this); |
|||
if ( this.isNewRecord ) { |
|||
mapper.insert(tableName, data); |
|||
} else { |
|||
mapper.update(tableName, (Integer)data.get("id"), data); |
|||
} |
|||
} |
|||
|
|||
// delete model |
|||
public void delete() { |
|||
var context = DiApplicationContextProvider.getContext(); |
|||
DiActiveRecordMapper mapper = context.getBean(DiActiveRecordMapper.class); |
|||
String tableName = DiActiveRecord.getTableNameFromModelClass(this.getClass()); |
|||
Map<String,Object> data = DiActiveRecord.exportModelData(this); |
|||
mapper.delete(tableName, (Integer)data.get("id")); |
|||
} |
|||
|
|||
// set attributes |
|||
public void setAttributes( Map<String,Object> attributes ) { |
|||
for ( Map.Entry<String,Object> entry : attributes.entrySet() ) { |
|||
String key = entry.getKey(); |
|||
Field field = null; |
|||
try { |
|||
field = this.getClass().getDeclaredField(key); |
|||
} catch (NoSuchFieldException e) { |
|||
continue ; |
|||
} |
|||
|
|||
Class<?> fieldType = field.getType(); |
|||
if ( !fieldType.isAssignableFrom(entry.getValue().getClass()) ) { |
|||
throw new RuntimeException("Attribute type mismatch: " + key); |
|||
} |
|||
|
|||
field.setAccessible(true); |
|||
try { |
|||
field.set(this, entry.getValue()); |
|||
} catch (IllegalAccessException e) { |
|||
throw new RuntimeException(e); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// find by id |
|||
public static void findOne(Integer id) { |
|||
public static <T extends DiActiveRecord> T findOne( Class<T> modelClass, Integer id) { |
|||
return DiActiveRecord.findOne(modelClass, Map.of("id", id)); |
|||
} |
|||
|
|||
// find one by conditions |
|||
public static <T extends DiActiveRecord> T findOne( Class<T> modelClass, Map<String,Object> conditions) { |
|||
var criteria = new DiActiveRecordCriteria(); |
|||
criteria.tableName = DiActiveRecord.getTableNameFromModelClass(modelClass); |
|||
criteria.conditions = conditions; |
|||
criteria.limit = 1; |
|||
|
|||
var context = DiApplicationContextProvider.getContext(); |
|||
DiActiveRecordMapper mapper = context.getBean(DiActiveRecordMapper.class); |
|||
var rows = mapper.find(criteria); |
|||
if (rows.isEmpty()) { |
|||
return null; |
|||
} |
|||
|
|||
T model = DiActiveRecord.fillModel(modelClass, rows.get(0)); |
|||
model.isNewRecord = false; |
|||
return model; |
|||
} |
|||
|
|||
// find all by criteria |
|||
public static <T extends DiActiveRecord> List<T> find( Class<T> modelClass, DiActiveRecordCriteria criteria ) { |
|||
criteria.tableName = DiActiveRecord.getTableNameFromModelClass(modelClass); |
|||
var context = DiApplicationContextProvider.getContext(); |
|||
DiActiveRecordMapper mapper = context.getBean(DiActiveRecordMapper.class); |
|||
var row = mapper.findById("di_app_users", id); |
|||
// // @TODO : 这里要根据配置参数计算 ~~~ |
|||
System.out.println("DiActiveRecord.findById"); |
|||
var rows = mapper.find(criteria); |
|||
|
|||
List<T> models = new ArrayList<>(); |
|||
for ( var row : rows ) { |
|||
T model = DiActiveRecord.fillModel(modelClass, row); |
|||
model.isNewRecord = false; |
|||
models.add(model); |
|||
} |
|||
return models; |
|||
} |
|||
|
|||
// get table name from model class |
|||
private static <T> String getTableNameFromModelClass( Class<T> modelClass) { |
|||
Method tableNameGetter = null; |
|||
try { |
|||
tableNameGetter = modelClass.getMethod("getTableName"); |
|||
} catch (NoSuchMethodException e) { |
|||
throw new RuntimeException(e); |
|||
} |
|||
String tableName = null; |
|||
try { |
|||
tableName = (String)tableNameGetter.invoke(null); |
|||
} catch (IllegalAccessException | InvocationTargetException e) { |
|||
throw new RuntimeException(e); |
|||
} |
|||
return tableName; |
|||
} |
|||
|
|||
// fill model |
|||
private static <T> T fillModel(Class<T> modelClass, Map<String,Object> data) { |
|||
Constructor<?> modelConstructor = null; |
|||
try { |
|||
modelConstructor = modelClass.getDeclaredConstructor(); |
|||
} catch (NoSuchMethodException e) { |
|||
throw new RuntimeException(e); |
|||
} |
|||
|
|||
T model = null; |
|||
try { |
|||
model = (T)modelConstructor.newInstance(); |
|||
} catch (InvocationTargetException | InstantiationException | IllegalAccessException e) { |
|||
throw new RuntimeException(e); |
|||
} |
|||
|
|||
for ( Map.Entry<String,Object> entry : data.entrySet() ) { |
|||
String key = entry.getKey(); |
|||
Field field = null; |
|||
try { |
|||
field = model.getClass().getDeclaredField(key); |
|||
} catch (NoSuchFieldException e) { |
|||
continue ; |
|||
} |
|||
|
|||
Class<?> fieldType = field.getType(); |
|||
if ( !fieldType.isAssignableFrom(entry.getValue().getClass()) ) { |
|||
throw new RuntimeException("Attribute type mismatch: " + key); |
|||
} |
|||
|
|||
field.setAccessible(true); |
|||
try { |
|||
field.set(model, entry.getValue()); |
|||
} catch (IllegalAccessException e) { |
|||
throw new RuntimeException(e); |
|||
} |
|||
} |
|||
|
|||
return model; |
|||
} |
|||
|
|||
// model to map |
|||
private static Map<String,Object> exportModelData(Object model) { |
|||
Map<String,Object> data = new HashMap<>(); |
|||
var fields = model.getClass().getDeclaredFields(); |
|||
for ( var field : fields ) { |
|||
var annotation = field.getAnnotation(ActiveRecordField.class); |
|||
if ( annotation == null ) { |
|||
continue ; |
|||
} |
|||
|
|||
field.setAccessible(true); |
|||
try { |
|||
data.put(field.getName(), field.get(model)); |
|||
} catch (IllegalAccessException e) { |
|||
throw new RuntimeException(e); |
|||
} |
|||
} |
|||
return data; |
|||
} |
|||
} |
@ -0,0 +1,10 @@ |
|||
package com.my.graphiteDigesterBg.diframe; |
|||
import java.util.Map; |
|||
public class DiActiveRecordCriteria { |
|||
// table name |
|||
public String tableName; |
|||
// conditions |
|||
public Map<String,Object> conditions; |
|||
// limit |
|||
public Integer limit; |
|||
} |
@ -1,18 +1,107 @@ |
|||
package com.my.graphiteDigesterBg.diframe.api; |
|||
import com.my.graphiteDigesterBg.diframe.DiActiveRecord; |
|||
import com.my.graphiteDigesterBg.diframe.DiActiveRecordCriteria; |
|||
import com.my.graphiteDigesterBg.diframe.DiApiControllerBase; |
|||
import com.my.graphiteDigesterBg.diframe.DiApiResponse; |
|||
import com.my.graphiteDigesterBg.diframe.model.DiMdbUser; |
|||
import jakarta.servlet.http.HttpServletRequest; |
|||
import org.springframework.stereotype.Controller; |
|||
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.ResponseBody; |
|||
import java.util.Map; |
|||
import java.util.UUID; |
|||
@Controller |
|||
public class DiApiUser extends DiApiControllerBase { |
|||
@ResponseBody |
|||
@RequestMapping("/api/user/login") |
|||
public DiApiResponse login() { |
|||
DiMdbUser.findOne(1); |
|||
public DiApiResponse login(@RequestBody Map<String,Object> params) { |
|||
Integer id = (Integer)params.get("id"); |
|||
String password = (String)params.get("password"); |
|||
|
|||
var user = DiActiveRecord.findOne(DiMdbUser.class, id); |
|||
if ( null == user || !user.matchPassword(password) ) { |
|||
return this.error("无效的账号或密码"); |
|||
} |
|||
|
|||
user.accessToken = UUID.randomUUID().toString(); |
|||
user.accessTokenExpiredAt = (int)(System.currentTimeMillis() / 1000) + 3600 * 24; |
|||
user.save(); |
|||
return this.success(Map.of( |
|||
"accessToken", user.accessToken, |
|||
"accessTokenExpiredAt", user.accessTokenExpiredAt |
|||
)); |
|||
} |
|||
|
|||
@ResponseBody |
|||
@RequestMapping("/api/user/logout") |
|||
public DiApiResponse logout( HttpServletRequest request ) { |
|||
String accessToken = request.getHeader("App-Access-Token"); |
|||
var user = DiActiveRecord.findOne(DiMdbUser.class, Map.of("accessToken", accessToken)); |
|||
if ( null == user ) { |
|||
return this.success(); |
|||
} |
|||
|
|||
user.accessToken = ""; |
|||
user.accessTokenExpiredAt = 0; |
|||
user.save(); |
|||
return this.success(); |
|||
} |
|||
|
|||
@ResponseBody |
|||
@PostMapping("/api/user/save") |
|||
public DiApiResponse save( HttpServletRequest request, @RequestBody Map<String,Object> params ) { |
|||
DiMdbUser curUser = this.getUserFromRequest(request); |
|||
Integer id = (Integer)params.get("id"); |
|||
Map<String,Object> data = (Map<String,Object>)params.get("data"); |
|||
|
|||
var user = new DiMdbUser(); |
|||
user.salt = UUID.randomUUID().toString().substring(0, 8); |
|||
user.createdAt = (int)(System.currentTimeMillis() / 1000); |
|||
user.createdBy = curUser.id; |
|||
if ( null != id ) { |
|||
user = DiActiveRecord.findOne(DiMdbUser.class, id); |
|||
} |
|||
|
|||
user.setAttributes(data); |
|||
user.save(); |
|||
return this.success(); |
|||
} |
|||
|
|||
public void logout() {} |
|||
@ResponseBody |
|||
@PostMapping("/api/user/delete") |
|||
public DiApiResponse delete( @RequestBody Map<String,Object> params ) { |
|||
Integer id = (Integer)params.get("id"); |
|||
var user = DiActiveRecord.findOne(DiMdbUser.class, id); |
|||
if ( null == user ) { |
|||
return this.success(); |
|||
} |
|||
|
|||
user.delete(); |
|||
return this.success(); |
|||
} |
|||
|
|||
@ResponseBody |
|||
@PostMapping("/api/user/updatePassword") |
|||
public DiApiResponse updatePassword( @RequestBody Map<String,Object> params ) { |
|||
Integer id = (Integer)params.get("id"); |
|||
String password = (String)params.get("password"); |
|||
var user = DiActiveRecord.findOne(DiMdbUser.class, id); |
|||
if ( null == user ) { |
|||
return this.error("无效的用户"); |
|||
} |
|||
user.password = user.hashPassword(password); |
|||
user.save(); |
|||
return this.success(); |
|||
} |
|||
|
|||
@ResponseBody |
|||
@RequestMapping("/api/user/list") |
|||
public DiApiResponse list() { |
|||
var criteria = new DiActiveRecordCriteria(); |
|||
criteria.limit = 10; |
|||
var users = DiActiveRecord.find(DiMdbUser.class, criteria); |
|||
return this.success(Map.of("list",users)); |
|||
} |
|||
} |
@ -1,9 +1,45 @@ |
|||
package com.my.graphiteDigesterBg.diframe.mapper; |
|||
import org.apache.ibatis.annotations.Mapper; |
|||
import org.apache.ibatis.annotations.Select; |
|||
import com.my.graphiteDigesterBg.diframe.DiActiveRecordCriteria; |
|||
import org.apache.ibatis.annotations.*; |
|||
|
|||
import java.util.List; |
|||
import java.util.Map; |
|||
@Mapper |
|||
public interface DiActiveRecordMapper { |
|||
@Select( |
|||
"<script>" + |
|||
"SELECT * FROM ${tableName} " + |
|||
"<if test='conditions != null'>" + |
|||
"WHERE " + |
|||
"<foreach collection='conditions' item='value' index='key' separator=' AND '>${key} = #{value}</foreach>" + |
|||
"</if>" + |
|||
"LIMIT #{limit}" + |
|||
"</script>" |
|||
) |
|||
List<Map<String,Object>> find(DiActiveRecordCriteria criteria); |
|||
|
|||
@Select("SELECT * FROM ${tableName} WHERE id = #{id}") |
|||
Map<String,Object> findById(String tableName, Integer id); |
|||
|
|||
@Insert( |
|||
"<script>" + |
|||
"INSERT INTO ${tableName} " + |
|||
"<foreach collection='data' item='value' index='key' open='(' separator=',' close=')'>${key}</foreach>" + |
|||
"VALUES " + |
|||
"<foreach collection='data' item='value' index='key' open='(' separator=',' close=')'>#{value}</foreach>" + |
|||
"</script>" |
|||
) |
|||
Integer insert(String tableName, Map<String,Object> data); |
|||
|
|||
@Update( |
|||
"<script>" + |
|||
"UPDATE ${tableName} SET " + |
|||
"<foreach collection='data' item='value' index='key' separator=','>${key} = #{value}</foreach>" + |
|||
"WHERE id = #{id}" + |
|||
"</script>" |
|||
) |
|||
Integer update(String tableName, Integer id, Map<String,Object> data); |
|||
|
|||
@Delete("DELETE FROM ${tableName} WHERE id = #{id}") |
|||
Integer delete(String tableName, Integer id); |
|||
} |
@ -1,5 +1,54 @@ |
|||
package com.my.graphiteDigesterBg.diframe.model; |
|||
import com.fasterxml.jackson.annotation.JsonIgnore; |
|||
import com.my.graphiteDigesterBg.diframe.ActiveRecordField; |
|||
import com.my.graphiteDigesterBg.diframe.DiActiveRecord; |
|||
import org.springframework.util.DigestUtils; |
|||
public class DiMdbUser extends DiActiveRecord { |
|||
@ActiveRecordField |
|||
public Integer id; |
|||
|
|||
@ActiveRecordField |
|||
public String account; |
|||
|
|||
@JsonIgnore |
|||
@ActiveRecordField |
|||
public String password; |
|||
|
|||
@JsonIgnore |
|||
@ActiveRecordField |
|||
public String salt; |
|||
|
|||
@ActiveRecordField |
|||
public Integer roleId; |
|||
|
|||
@ActiveRecordField |
|||
public Integer createdAt; |
|||
|
|||
@ActiveRecordField |
|||
public Integer createdBy; |
|||
|
|||
@JsonIgnore |
|||
@ActiveRecordField |
|||
public String accessToken; |
|||
|
|||
@JsonIgnore |
|||
@ActiveRecordField |
|||
public Integer accessTokenExpiredAt; |
|||
|
|||
// get table name |
|||
public static String getTableName() { |
|||
return "app_users"; |
|||
} |
|||
|
|||
// check if password matches |
|||
public Boolean matchPassword(String password) { |
|||
String hash = this.hashPassword(password); |
|||
return this.password.equals(hash); |
|||
} |
|||
|
|||
// hash password |
|||
public String hashPassword(String password) { |
|||
String salt = this.salt; |
|||
return DigestUtils.md5DigestAsHex((salt + password + salt).getBytes()); |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue